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

Proposed by Amit Vora(OpenERP)
Status: Needs review
Proposed branch: lp:~openerp-dev/openobject-addons/trunk-kanban-column-avo
Merge into: lp:openobject-addons
Diff against target: 1150 lines (+663/-82)
20 files modified
crm/__init__.py (+1/-1)
crm/crm.py (+65/-3)
crm/crm_lead.py (+0/-2)
crm/tests/__init__.py (+24/-0)
crm/tests/test_crm_stages_case.py (+70/-0)
hr_recruitment/__init__.py (+1/-1)
hr_recruitment/hr_recruitment.py (+106/-41)
hr_recruitment/hr_recruitment_data.xml (+6/-0)
hr_recruitment/hr_recruitment_demo.xml (+6/-1)
hr_recruitment/hr_recruitment_menu.xml (+28/-0)
hr_recruitment/hr_recruitment_view.xml (+11/-26)
hr_recruitment/tests/__init__.py (+24/-0)
hr_recruitment/tests/test_hr_recruitment_stage.py (+71/-0)
project/project.py (+61/-3)
project/tests/__init__.py (+2/-1)
project/tests/project_stage_management.py (+74/-0)
project_issue/__init__.py (+1/-1)
project_issue/project_issue.py (+10/-2)
project_issue/tests/__init__.py (+28/-0)
project_issue/tests/project_stage_management.py (+74/-0)
To merge this branch: bzr merge lp:~openerp-dev/openobject-addons/trunk-kanban-column-avo
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+214028@code.launchpad.net

Description of the change

To post a comment you must log in.
9259. By Amit Vora(OpenERP)

[IMP] override copy method display stages in perticular parent

9260. By Amit Vora(OpenERP)

[MRG] merge with main branch

9261. By Amit Vora(OpenERP)

[IMP] resolve conflict

9262. By Amit Vora(OpenERP)

[MRG] merge with main branch

Unmerged revisions

9262. By Amit Vora(OpenERP)

[MRG] merge with main branch

9261. By Amit Vora(OpenERP)

[IMP] resolve conflict

9260. By Amit Vora(OpenERP)

[MRG] merge with main branch

9259. By Amit Vora(OpenERP)

[IMP] override copy method display stages in perticular parent

9258. By Amit Vora(OpenERP)

[IMP] add test cases for crm,hr_recruitment,project

9257. By Amit Vora(OpenERP)

[IMP] improve code add condition in mail_message

9256. By Amit Vora(OpenERP)

[IMP] assign value to variable

9255. By Amit Vora(OpenERP)

[IMP] in crm,project module give default value case_default and in hr_recruitment module add job_ids many2many field,Add Configuration menu of Recruitment: Stages, Degrees, Source of Applications,add new boolean field to indicate default stage on create new recruitment,update demo data of hr job with link of stages

9254. By Amit Vora(OpenERP)

[MERGE] merge with main branch

9253. By Amit Vora(OpenERP)

[MRG] merge with lp:~openerp-dev/openobject-addons/trunk-kanban-column-hr-avo

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-05-12 11:41:49 +0000
3+++ crm/__init__.py 2014-05-20 06:41:52 +0000
4@@ -30,6 +30,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/crm.py'
14--- crm/crm.py 2014-05-14 15:21:54 +0000
15+++ crm/crm.py 2014-05-20 06:41:52 +0000
16@@ -77,16 +77,78 @@
17 string='Type', size=16, required=True,
18 help="This field is used to distinguish stages related to Leads from stages related to Opportunities, or to specify stages available for both types."),
19 }
20-
21+ def _get_default_section_ids(self, cr, uid, context=None):
22+ """ Gives default section by checking if present in the context """
23+ section_id = self.pool.get('crm.lead')._resolve_section_id_from_context(cr, uid, context=context) or False
24+ return section_id and [section_id] or None
25+
26 _defaults = {
27 'sequence': 1,
28 'probability': 0.0,
29 'on_change': True,
30 'fold': False,
31 'type': 'both',
32- 'case_default': True,
33+ 'section_ids': _get_default_section_ids,
34+ 'case_default': lambda self, cr, uid, ctx={}: ctx.get('default_section_id', False) == False,
35 }
36-
37+
38+ _sql_constraints = [('stage_name_uniq', 'unique(name)', 'Name should be unique.')]
39+
40+ def copy(self, cr, uid, id, default=None, context=None):
41+ if context is None:
42+ context = {}
43+ if default is None:
44+ default = {}
45+ default['section_ids'] = []
46+ section = self.browse(cr, uid, id, context=context)
47+ if not default.get('name', False):
48+ default.update(name=_("%s (copy)") % (section.name))
49+ return super(crm_case_stage, self).copy(cr, uid, id, default, context)
50+
51+ def create(self, cr, uid, vals, context=None):
52+ if context is None: context = {}
53+ section_id = context.get('default_section_id')
54+ type_id = False
55+ if section_id:
56+ #check already exist or not
57+ type_ids = self.search(cr, uid, [('name', '=', vals.get('name'))], context=context, limit=1)
58+ if type_ids and len(type_ids):
59+ type_id = type_ids[0]
60+ if not type_id:
61+ type_id = super(crm_case_stage, self).create(cr, uid, vals, context=context)
62+ return type_id
63+
64+ def write(self, cr, uid, ids, vals, context=None):
65+ if context is None:context = {}
66+ section_id = context.get('default_section_id')
67+ section_obj = self.pool.get('crm.case.section')
68+ if section_id:
69+ context.update({'section_id': section_id})
70+ if vals.get('name', False):
71+ for stage in self.browse(cr, uid, ids, context=context):
72+ new_stage_id = self.copy(cr, uid, stage.id, default=vals, context=context)
73+ section_obj.write(cr, uid, [section_id], {'stage_ids': [(3, stage.id),(4, new_stage_id),]}, context=context)
74+ self._update_leads(cr, uid, section_id, stage.id, new_stage_id, context=context)
75+ return True
76+ return super(crm_case_stage, self).write(cr, uid, ids, vals, context=context)
77+
78+ def unlink(self, cr, uid, ids, context=None):
79+ if context is None: context = {}
80+ section_id = context.get('default_section_id')
81+ section_obj = self.pool.get('crm.case.section')
82+ if not section_id:
83+ return super(crm_case_stage, self).unlink(cr, uid, ids, context=context)
84+
85+ for stage in self.browse(cr, uid, ids, context=context):
86+ section_obj.write(cr, uid, section_id, {'stage_ids': [(3, stage.id)]}, context=context)
87+ self._update_leads(cr, uid, section_id, stage.id, False, context=context)
88+ return True
89+
90+ def _update_leads(self, cr, uid, section_id, old_stage_id, new_stage_id, context=None):
91+ if context is None: context = {}
92+ crm_lead_obj = self.pool.get('crm.lead')
93+ lead_ids = crm_lead_obj.search(cr, uid, [('stage_id', '=', old_stage_id),('section_id', '=', section_id)], context=context)
94+ return crm_lead_obj.write(cr, uid, lead_ids, {'stage_id': new_stage_id} , context=context)
95
96 class crm_case_categ(osv.osv):
97 """ Category of Case """
98
99=== modified file 'crm/crm_lead.py'
100--- crm/crm_lead.py 2014-05-12 11:41:49 +0000
101+++ crm/crm_lead.py 2014-05-20 06:41:52 +0000
102@@ -139,8 +139,6 @@
103 if section_id:
104 search_domain += ['|', ('section_ids', '=', section_id)]
105 search_domain += [('id', 'in', ids)]
106- else:
107- search_domain += ['|', ('id', 'in', ids), ('case_default', '=', True)]
108 # retrieve type from the context (if set: choose 'type' or 'both')
109 type = self._resolve_type_from_context(cr, uid, context=context)
110 if type:
111
112=== added directory 'crm/tests'
113=== added file 'crm/tests/__init__.py'
114--- crm/tests/__init__.py 1970-01-01 00:00:00 +0000
115+++ crm/tests/__init__.py 2014-05-20 06:41:52 +0000
116@@ -0,0 +1,24 @@
117+# -*- coding: utf-8 -*-
118+##############################################################################
119+#
120+# OpenERP, Open Source Business Applications
121+# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
122+#
123+# This program is free software: you can redistribute it and/or modify
124+# it under the terms of the GNU Affero General Public License as
125+# published by the Free Software Foundation, either version 3 of the
126+# License, or (at your option) any later version.
127+#
128+# This program is distributed in the hope that it will be useful,
129+# but WITHOUT ANY WARRANTY; without even the implied warranty of
130+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
131+# GNU Affero General Public License for more details.
132+#
133+# You should have received a copy of the GNU Affero General Public License
134+# along with this program. If not, see <http://www.gnu.org/licenses/>.
135+#
136+##############################################################################
137+from . import test_crm_stages_case
138+checks = [
139+ test_crm_stages_case,
140+ ]
141\ No newline at end of file
142
143=== added file 'crm/tests/test_crm_stages_case.py'
144--- crm/tests/test_crm_stages_case.py 1970-01-01 00:00:00 +0000
145+++ crm/tests/test_crm_stages_case.py 2014-05-20 06:41:52 +0000
146@@ -0,0 +1,70 @@
147+# -*- coding: utf-8 -*-
148+##############################################################################
149+#
150+# OpenERP, Open Source Business Applications
151+# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
152+#
153+# This program is free software: you can redistribute it and/or modify
154+# it under the terms of the GNU Affero General Public License as
155+# published by the Free Software Foundation, either version 3 of the
156+# License, or (at your option) any later version.
157+#
158+# This program is distributed in the hope that it will be useful,
159+# but WITHOUT ANY WARRANTY; without even the implied warranty of
160+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
161+# GNU Affero General Public License for more details.
162+#
163+# You should have received a copy of the GNU Affero General Public License
164+# along with this program. If not, see <http://www.gnu.org/licenses/>.
165+#
166+##############################################################################
167+from openerp.osv.orm import except_orm
168+from openerp.tests import common
169+
170+
171+class TestCrmStagesCase(common.TransactionCase):
172+ def setUp(self):
173+ super(TestCrmStagesCase, self).setUp()
174+
175+ def test_00_stage_management(self):
176+ cr, uid = self.cr, self.uid
177+ crm_stage_stage = self.registry('crm.case.stage')
178+ crm_case_section = self.registry('crm.case.section')
179+ crm_lead = self.registry('crm.lead');
180+ crm_case_section_id = crm_case_section.create(cr, uid, {'name':'test_section'},context={})
181+ context = {'default_section_id': crm_case_section_id}
182+
183+ ''' For create when default_section_id pass in context
184+ If same name is present than returns its id otherwise create new.
185+ and link with given section.
186+ '''
187+ stage_type_id = crm_stage_stage.create(cr, uid, {'name':'First'}, {})
188+ self.assertEqual(stage_type_id, crm_stage_stage.create(cr, uid, {'name':'First'}, context=context))
189+
190+ ''' For edit when default_section_id pass in context
191+ If same name is present than returns its id otherwise create new.
192+ and link with given section and remove old link with section and crm_lead.
193+ '''
194+ crm_case_section.write(cr, uid, [crm_case_section_id], {'stage_ids': [(4, stage_type_id),]}, context=context)
195+ lead_id = crm_lead.create(cr, uid, {'name':'Test1', 'stage_id': stage_type_id, 'section_id': crm_case_section_id})
196+
197+ crm_stage_stage.write(cr, uid, [stage_type_id],{'name':'Second'}, context=context)
198+
199+ stage_m2m_newlist = crm_case_section.browse(cr, uid, crm_case_section_id, context=context).stage_ids
200+ check_crm_lead_stage_id = crm_lead.browse(cr, uid, lead_id, context).stage_id.id
201+ crm_case_section_id_new = crm_stage_stage.search(cr, uid, [('name','=', 'Second')], context=context)
202+
203+ self.assertIn(crm_case_section_id_new[0], [x.id for x in stage_m2m_newlist])
204+ self.assertEqual(check_crm_lead_stage_id, crm_case_section_id_new[0])
205+
206+ ''' For unlink when default_section_id pass in context
207+ It will unlink relation, not delete.
208+ '''
209+ stage_m2m_oldlist = crm_case_section.browse(cr, uid, crm_case_section_id, context=context).stage_ids
210+ crm_stage_stage.unlink(cr, uid, crm_case_section_id_new, context=context)
211+ stage_m2m_newlist = crm_case_section.browse(cr, uid, crm_case_section_id, context=context).stage_ids
212+
213+ self.assertEqual(len(stage_m2m_oldlist) -1, len(stage_m2m_newlist))
214+
215+ unlink_id = crm_stage_stage.search(cr, uid, [('name','=', 'Second')], context=context)
216+ self.assertEqual(unlink_id, crm_case_section_id_new)
217\ No newline at end of file
218
219=== modified file 'hr_recruitment/__init__.py'
220--- hr_recruitment/__init__.py 2012-04-02 05:44:18 +0000
221+++ hr_recruitment/__init__.py 2014-05-20 06:41:52 +0000
222@@ -23,5 +23,5 @@
223 import report
224 import wizard
225 import res_config
226-
227+import tests
228 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
229
230=== modified file 'hr_recruitment/hr_recruitment.py'
231--- hr_recruitment/hr_recruitment.py 2014-05-08 12:35:29 +0000
232+++ hr_recruitment/hr_recruitment.py 2014-05-20 06:41:52 +0000
233@@ -45,20 +45,88 @@
234 _name = "hr.recruitment.stage"
235 _description = "Stage of Recruitment"
236 _order = 'sequence'
237+
238 _columns = {
239 'name': fields.char('Name', size=64, required=True, translate=True),
240 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of stages."),
241- '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."),
242+ 'job_ids': fields.many2many('hr.job', 'job_stage_rel', 'stage_id','job_id', 'Stages'),
243 'requirements': fields.text('Requirements'),
244+ 'case_default': fields.boolean('Default for New Job'),
245 '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."),
246 'fold': fields.boolean('Folded in Kanban View',
247 help='This stage is folded in the kanban view when'
248 'there are no records in that stage to display.'),
249 }
250+ def _get_default_job_ids(self, cr, uid, context=None):
251+ """ Gives default jon_id by checking if present in the context """
252+ job_id = self.pool.get('hr.applicant')._resolve_job_id_from_context(cr, uid, context=context) or False
253+ return job_id and [job_id] or None
254+
255 _defaults = {
256 'sequence': 1,
257+ 'job_ids': _get_default_job_ids,
258+ 'case_default': lambda self, cr, uid, ctx={}: ctx.get('default_job_id', False) == False,
259 }
260-
261+
262+ _sql_constraints = [('stage_name_uniq', 'unique(name)', 'Name should be unique.')]
263+
264+ def copy(self, cr, uid, id, default=None, context=None):
265+ if context is None:
266+ context = {}
267+ if default is None:
268+ default = {}
269+ default['job_ids'] = []
270+ job = self.browse(cr, uid, id, context=context)
271+ if not default.get('name', False):
272+ default.update(name=_("%s (copy)") % (job.name))
273+ return super(hr_recruitment_stage, self).copy(cr, uid, id, default, context)
274+
275+ def create(self, cr, uid, vals, context=None):
276+ if context is None: context = {}
277+ job_id = context.get('default_job_id')
278+ type_id = False
279+ if job_id:
280+ #check already exist or not
281+ context.update({'job_id': job_id})
282+ type_ids = self.search(cr, uid, [('name', '=', vals.get('name'))], context=context, limit=1)
283+ if type_ids and len(type_ids):
284+ type_id = type_ids[0]
285+ if not type_id:
286+ type_id = super(hr_recruitment_stage, self).create(cr, uid, vals, context=context)
287+ return type_id
288+
289+ def write(self, cr, uid, ids, vals, context=None):
290+ if context is None: context = {}
291+ job_id = context.get('default_job_id')
292+ job_obj = self.pool.get('hr.job')
293+ if job_id:
294+ context.update({'job_id': job_id})
295+ if vals.get('name', False):
296+ for stage in self.browse(cr, uid, ids, context=context):
297+ new_stage_id = self.copy(cr, uid, stage.id, default=vals, context=context)
298+ job_obj.write(cr, uid, [job_id], {'stage_ids': [(3, stage.id),(4, new_stage_id),]}, context=context)
299+ self._update_application(cr, uid, job_id, stage.id, new_stage_id, context=context)
300+ return True
301+ return super(hr_recruitment_stage, self).write(cr, uid, ids, vals, context=context)
302+
303+ def unlink(self, cr, uid, ids, context=None):
304+ if context is None: context = {}
305+ job_id = context.get('default_job_id')
306+ job_obj = self.pool.get('hr.job')
307+ if not job_id:
308+ return super(hr_recruitment_stage, self).unlink(cr, uid, ids, context=context)
309+
310+ for stage in self.browse(cr, uid, ids, context=context):
311+ job_obj.write(cr, uid, job_id, {'stage_ids': [(3, stage.id)]}, context=context)
312+ self._update_application(cr, uid, job_id, stage.id, False, context=context)
313+ return True
314+
315+ def _update_application(self, cr, uid, job_id, old_stage_id, new_stage_id, context=None):
316+ if context is None: context = {}
317+ hr_applicant_obj = self.pool.get('hr.applicant')
318+ applicant_ids = hr_applicant_obj.search(cr, uid, [('stage_id', '=', old_stage_id),('job_id', '=', job_id)], context=context)
319+ return hr_applicant_obj.write(cr, uid, applicant_ids, {'stage_id': new_stage_id} , context=context)
320+
321 class hr_recruitment_degree(osv.osv):
322 """ Degree of HR Recruitment """
323 _name = "hr.recruitment.degree"
324@@ -89,29 +157,27 @@
325 }
326 _mail_mass_mailing = _('Applicants')
327
328- def _get_default_department_id(self, cr, uid, context=None):
329+ def _get_default_job_id(self, cr, uid, context=None):
330 """ Gives default department by checking if present in the context """
331- return (self._resolve_department_id_from_context(cr, uid, context=context) or False)
332+ return (self._resolve_job_id_from_context(cr, uid, context=context) or False)
333
334 def _get_default_stage_id(self, cr, uid, context=None):
335 """ Gives default stage_id """
336- department_id = self._get_default_department_id(cr, uid, context=context)
337- return self.stage_find(cr, uid, [], department_id, [('fold', '=', False)], context=context)
338-
339- def _resolve_department_id_from_context(self, cr, uid, context=None):
340- """ Returns ID of department based on the value of 'default_department_id'
341- context key, or None if it cannot be resolved to a single
342- department.
343+ job_id = self._get_default_job_id(cr, uid, context=context)
344+ return self.stage_find(cr, uid, [], job_id, [('sequence', '=', '1')], context=context)
345+ def _resolve_job_id_from_context(self, cr, uid, context=None):
346+ """ Returns ID of job based on the value of 'default_job_id'
347+ context key, or None if it cannot be resolved to a single
348 """
349 if context is None:
350 context = {}
351- if type(context.get('default_department_id')) in (int, long):
352- return context.get('default_department_id')
353- if isinstance(context.get('default_department_id'), basestring):
354- department_name = context['default_department_id']
355- department_ids = self.pool.get('hr.department').name_search(cr, uid, name=department_name, context=context)
356- if len(department_ids) == 1:
357- return int(department_ids[0][0])
358+ if type(context.get('default_job_id')) in (int, long):
359+ return context.get('default_job_id')
360+ if isinstance(context.get('default_job_id'), basestring):
361+ job_name = context['default_job_id']
362+ job_ids = self.pool.get('hr.job').name_search(cr, uid, name=job_name, context=context)
363+ if len(job_ids) == 1:
364+ return int(job_ids[0][0])
365 return None
366
367 def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
368@@ -123,23 +189,19 @@
369 order = "%s desc" % order
370 # retrieve section_id from the context and write the domain
371 # - ('id', 'in', 'ids'): add columns that should be present
372- # - OR ('department_id', '=', False), ('fold', '=', False): add default columns that are not folded
373- # - OR ('department_id', 'in', department_id), ('fold', '=', False) if department_id: add department columns that are not folded
374- department_id = self._resolve_department_id_from_context(cr, uid, context=context)
375+ # - OR ('job_id', '=', False), ('fold', '=', False): add default columns that are not folded
376+ # - OR ('job_id', 'in', job_id), ('fold', '=', False) if job_id: add department columns that are not folded
377+ job_id = self._resolve_job_id_from_context(cr, uid, context=context)
378 search_domain = []
379- if department_id:
380- search_domain += ['|', ('department_id', '=', department_id)]
381- search_domain += ['|', ('id', 'in', ids), ('department_id', '=', False)]
382+ if job_id:
383+ search_domain += [('job_ids', '=', job_id)]
384 stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
385- result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
386- # restore order of the search
387- result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
388-
389 fold = {}
390 for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context):
391 fold[stage.id] = stage.fold or False
392+ result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
393 return result, fold
394-
395+
396 def _compute_day(self, cr, uid, ids, fields, args, context=None):
397 """
398 @param cr: the current row, from the database cursor,
399@@ -188,8 +250,7 @@
400 'partner_id': fields.many2one('res.partner', 'Contact'),
401 'create_date': fields.datetime('Creation Date', readonly=True, select=True),
402 'write_date': fields.datetime('Update Date', readonly=True),
403- 'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage', track_visibility='onchange',
404- domain="['|', ('department_id', '=', department_id), ('department_id', '=', False)]"),
405+ 'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage', track_visibility='onchange',domain="[('job_ids', '=', job_id)]"),
406 'last_stage_id': fields.many2one('hr.recruitment.stage', 'Last Stage',
407 help='Stage of the applicant before being in the current stage. Used for lost cases analysis.'),
408 'categ_ids': fields.many2many('hr.applicant_category', string='Tags'),
409@@ -230,7 +291,7 @@
410 'active': lambda *a: 1,
411 'user_id': lambda s, cr, uid, c: uid,
412 'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
413- 'department_id': lambda s, cr, uid, c: s._get_default_department_id(cr, uid, c),
414+ 'job_id': lambda s, cr, uid, c: s._get_default_job_id(cr, uid, c),
415 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'hr.applicant', context=c),
416 'color': 0,
417 'date_last_stage_update': fields.datetime.now,
418@@ -242,6 +303,7 @@
419
420 def onchange_job(self, cr, uid, ids, job_id=False, context=None):
421 department_id = False
422+ user_id = False
423 if job_id:
424 job_record = self.pool.get('hr.job').browse(cr, uid, job_id, context=context)
425 department_id = job_record and job_record.department_id and job_record.department_id.id or False
426@@ -273,18 +335,16 @@
427 if isinstance(cases, (int, long)):
428 cases = self.browse(cr, uid, cases, context=context)
429 # collect all section_ids
430- department_ids = []
431+ job_ids = []
432 if section_id:
433- department_ids.append(section_id)
434+ job_ids.append(section_id)
435 for case in cases:
436- if case.department_id:
437- department_ids.append(case.department_id.id)
438- # OR all section_ids and OR with case_default
439+ if case.job_id:
440+ job_ids.append(case.job_id.id)# OR all section_ids and OR with case_default
441 search_domain = []
442- if department_ids:
443- search_domain += ['|', ('department_id', 'in', department_ids)]
444- search_domain.append(('department_id', '=', False))
445 # AND with the domain in parameter
446+ if job_ids:
447+ search_domain += [('job_ids', 'in', job_ids)]
448 search_domain += list(domain)
449 # perform search, return the first found
450 stage_ids = self.pool.get('hr.recruitment.stage').search(cr, uid, search_domain, order=order, context=context)
451@@ -424,7 +484,7 @@
452 subtype="hr_recruitment.mt_job_applicant_new", context=context)
453
454 # post processing: if stage changed, post a message in the chatter
455- if vals.get('stage_id'):
456+ if vals.get('stage_id') and ids:
457 stage = self.pool['hr.recruitment.stage'].browse(cr, uid, vals['stage_id'], context=context)
458 if stage.template_id:
459 # TDENOTE: probably factorize me in a message_post_with_template generic method FIXME
460@@ -528,6 +588,7 @@
461 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True,
462 help="Email alias for this job position. New emails will automatically "
463 "create new applicants for this job position."),
464+ 'stage_ids':fields.many2many('hr.recruitment.stage', 'job_stage_rel','job_id', 'stage_id', 'Stages'),
465 'address_id': fields.many2one('res.partner', 'Job Location', help="Address where employees are working"),
466 'application_ids': fields.one2many('hr.applicant', 'job_id', 'Applications'),
467 'application_count': fields.function(_count_all, type='integer', string='Applications', multi=True),
468@@ -538,11 +599,15 @@
469 'color': fields.integer('Color Index'),
470 }
471
472+ def _get_stage_common(self, cr, uid, context):
473+ return self.pool.get('hr.recruitment.stage').search(cr, uid, [('case_default','=',1)], context=context)
474+
475 def _address_get(self, cr, uid, context=None):
476 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
477 return user.company_id.partner_id.id
478
479 _defaults = {
480+ 'stage_ids': _get_stage_common,
481 'address_id': _address_get
482 }
483
484
485=== modified file 'hr_recruitment/hr_recruitment_data.xml'
486--- hr_recruitment/hr_recruitment_data.xml 2014-02-12 09:56:06 +0000
487+++ hr_recruitment/hr_recruitment_data.xml 2014-05-20 06:41:52 +0000
488@@ -92,30 +92,36 @@
489 <record model="hr.recruitment.stage" id="stage_job1">
490 <field name="name">Initial Qualification</field>
491 <field name="sequence">1</field>
492+ <field name="case_default" eval="True"/>
493 </record>
494 <record model="hr.recruitment.stage" id="stage_job2">
495 <field name="name">First Interview</field>
496 <field name="template_id" ref="applicant_interest"/>
497 <field name="sequence">2</field>
498+ <field name="case_default" eval="True"/>
499 </record>
500 <record model="hr.recruitment.stage" id="stage_job3">
501 <field name="name">Second Interview</field>
502 <field name="sequence">3</field>
503+ <field name="case_default" eval="True"/>
504 </record>
505 <record model="hr.recruitment.stage" id="stage_job4">
506 <field name="name">Contract Proposed</field>
507 <field name="sequence">4</field>
508+ <field name="case_default" eval="True"/>
509 </record>
510 <record model="hr.recruitment.stage" id="stage_job5">
511 <field name="name">Contract Signed</field>
512 <field name="sequence">5</field>
513 <field name="fold" eval="True"/>
514+ <field name="case_default" eval="True"/>
515 </record>
516 <record model="hr.recruitment.stage" id="stage_job6">
517 <field name="name">Refused</field>
518 <field name="sequence">6</field>
519 <field name="template_id" ref="applicant_refuse"/>
520 <field name="fold" eval="True"/>
521+ <field name="case_default" eval="True"/>
522 </record>
523
524 <record id="mail_alias_jobs" model="mail.alias">
525
526=== modified file 'hr_recruitment/hr_recruitment_demo.xml'
527--- hr_recruitment/hr_recruitment_demo.xml 2014-04-15 09:30:47 +0000
528+++ hr_recruitment/hr_recruitment_demo.xml 2014-05-20 06:41:52 +0000
529@@ -126,12 +126,15 @@
530 <record id="hr.job_developer" model="hr.job">
531 <field name="state">recruit</field>
532 <field name="no_of_recruitment">4</field>
533+ <field name= "stage_ids" eval="[(4, stage_job1),(4, stage_job2),(4, stage_job3),(4, stage_job4),(4, stage_job5),(4, stage_job6)]" />
534 <field name="survey_id" ref="recruitment_form"/>
535 </record>
536 <record id="hr.job_ceo" model="hr.job">
537+ <field name= "stage_ids" eval="[(4, stage_job3),(4, stage_job4),(4, stage_job5),(4, stage_job6)]" />
538 <field name="survey_id" ref="recruitment_form"/>
539 </record>
540 <record id="hr.job_cto" model="hr.job">
541+ <field name= "stage_ids" eval="[(4, stage_job1),(4, stage_job4),(4, stage_job5),(4, stage_job6)]" />
542 <field name="survey_id" ref="recruitment_form"/>
543 </record>
544 <record id="hr.job_consultant" model="hr.job">
545@@ -142,19 +145,21 @@
546 <record id="hr.job_hrm" model="hr.job">
547 <field name="no_of_recruitment">1</field>
548 <field name="state">recruit</field>
549+ <field name= "stage_ids" eval="[(4, stage_job1),(4, stage_job2),(4, stage_job3),(4, stage_job4)]" />
550 <field name="survey_id" ref="recruitment_form"/>
551 </record>
552 <record id="hr.job_marketing" model="hr.job">
553 <field name="state">recruit</field>
554 <field name="no_of_recruitment">3</field>
555+ <field name= "stage_ids" eval="[(4, stage_job1),(4, stage_job2),(4, stage_job3),(4, stage_job4)]" />
556 <field name="survey_id" ref="recruitment_form"/>
557 </record>
558 <record id="hr.job_trainee" model="hr.job">
559 <field name="state">recruit</field>
560 <field name="no_of_recruitment">6</field>
561+ <field name= "stage_ids" eval="[(4, stage_job1),(4, stage_job2),(4, stage_job3),(4, stage_job4)]" />
562 <field name="survey_id" ref="recruitment_form"/>
563 </record>
564- -->
565 <record id="message_application_demo" model="mail.message">
566 <field name="model">hr.applicant</field>
567 <field name="res_id" ref="hr_case_advertisement"/>
568
569=== modified file 'hr_recruitment/hr_recruitment_menu.xml'
570--- hr_recruitment/hr_recruitment_menu.xml 2014-02-11 11:41:21 +0000
571+++ hr_recruitment/hr_recruitment_menu.xml 2014-05-20 06:41:52 +0000
572@@ -61,5 +61,33 @@
573 name="Applications"
574 parent="base.menu_crm_case_job_req_main"
575 id="menu_crm_case_categ0_act_job" action="crm_case_categ0_act_job" sequence="2"/>
576+
577+ <menuitem parent="hr.menu_hr_configuration" id="hr.menu_hr_job" action="hr.action_hr_job" sequence="2"/>
578+
579+ <menuitem name="Recruitment"
580+ id="menu_hr_recruitment_recruitment"
581+ parent="hr.menu_hr_configuration"
582+ sequence="40"/>
583+
584+ <menuitem
585+ id="menu_hr_recruitment_stage"
586+ name="Stages"
587+ parent="menu_hr_recruitment_recruitment"
588+ action="hr_recruitment_stage_act"
589+ sequence="1" groups="base.group_no_one"/>
590+ <menuitem
591+ id="menu_hr_recruitment_degree"
592+ name="Degrees"
593+ parent="menu_hr_recruitment_recruitment"
594+ action="hr_recruitment_degree_action"
595+ sequence="5" groups="base.group_no_one"/>
596+
597+ <menuitem
598+ id="menu_hr_recruitment_source"
599+ parent="menu_hr_recruitment_recruitment"
600+ action="hr_recruitment_source_action"
601+ groups="base.group_no_one"
602+ sequence="10"/>
603+
604 </data>
605 </openerp>
606
607=== modified file 'hr_recruitment/hr_recruitment_view.xml'
608--- hr_recruitment/hr_recruitment_view.xml 2014-05-08 15:25:36 +0000
609+++ hr_recruitment/hr_recruitment_view.xml 2014-05-20 06:41:52 +0000
610@@ -370,6 +370,10 @@
611 </div>
612 </div>
613 </xpath>
614+ <xpath expr="//field[@name='requirements']" position="after">
615+ <separator string="Recruitment Stages" />
616+ <field name="stage_ids" nolabel="1"/>
617+ </xpath>
618 <xpath expr="//field[@name='department_id']" position="after">
619 <field name="user_id" class="oe_inline"/>
620 </xpath>
621@@ -518,7 +522,6 @@
622 <tree string="Stages">
623 <field name="sequence" invisible="1"/>
624 <field name="name"/>
625- <field name="department_id"/>
626 <field name="fold"/>
627 </tree>
628 </field>
629@@ -534,17 +537,18 @@
630 <group string="Stage Definition">
631 <group>
632 <field name="name"/>
633- <field name="department_id"/>
634- </group>
635- <group>
636 <field name="sequence"/>
637+ <field name="template_id" domain= "[('model_id.model', '=', 'hr.applicant')]"/>
638+ </group>
639+ <group>
640 <field name="fold"/>
641- <field name="template_id" domain= "[('model_id.model', '=', 'hr.applicant')]"/>
642+ <field name="case_default"/>
643 </group>
644 </group>
645 <separator string="Requirements"/>
646 <field name="requirements"/>
647 </sheet>
648+ <field name="job_ids" invisible="1"/>
649 </form>
650 </field>
651 </record>
652@@ -565,13 +569,6 @@
653 </field>
654 </record>
655
656- <menuitem
657- id="menu_hr_recruitment_stage"
658- name="Stages"
659- parent="menu_hr_recruitment_recruitment"
660- action="hr_recruitment_stage_act"
661- sequence="1" groups="base.group_no_one"/>
662-
663 <!-- Degree Tree View -->
664 <record model="ir.ui.view" id="hr_recruitment_degree_tree">
665 <field name="name">hr.recruitment.degree.tree</field>
666@@ -608,13 +605,6 @@
667 <field name="view_id" ref="hr_recruitment_degree_tree"/>
668 </record>
669
670- <menuitem
671- id="menu_hr_recruitment_degree"
672- name="Degrees"
673- parent="menu_hr_recruitment_recruitment"
674- action="hr_recruitment_degree_action"
675- sequence="5" groups="base.group_no_one"/>
676-
677 <!-- Source Tree View -->
678 <record model="ir.ui.view" id="hr_recruitment_source_tree">
679 <field name="name">hr.recruitment.source.tree</field>
680@@ -625,6 +615,7 @@
681 </tree>
682 </field>
683 </record>
684+
685 <record model="ir.ui.view" id="hr_recruitment_source_form">
686 <field name="name">hr.recruitment.source.form</field>
687 <field name="model">hr.recruitment.source</field>
688@@ -638,18 +629,12 @@
689 </form>
690 </field>
691 </record>
692+
693 <record id="hr_recruitment_source_action" model="ir.actions.act_window">
694 <field name="name">Sources of Applicants</field>
695 <field name="res_model">hr.recruitment.source</field>
696 <field name="view_type">form</field>
697 </record>
698
699- <menuitem
700- id="menu_hr_recruitment_source"
701- parent="menu_hr_recruitment_recruitment"
702- action="hr_recruitment_source_action"
703- groups="base.group_no_one"
704- sequence="10"/>
705-
706 </data>
707 </openerp>
708
709=== added directory 'hr_recruitment/tests'
710=== added file 'hr_recruitment/tests/__init__.py'
711--- hr_recruitment/tests/__init__.py 1970-01-01 00:00:00 +0000
712+++ hr_recruitment/tests/__init__.py 2014-05-20 06:41:52 +0000
713@@ -0,0 +1,24 @@
714+# -*- coding: utf-8 -*-
715+##############################################################################
716+#
717+# OpenERP, Open Source Business Applications
718+# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
719+#
720+# This program is free software: you can redistribute it and/or modify
721+# it under the terms of the GNU Affero General Public License as
722+# published by the Free Software Foundation, either version 3 of the
723+# License, or (at your option) any later version.
724+#
725+# This program is distributed in the hope that it will be useful,
726+# but WITHOUT ANY WARRANTY; without even the implied warranty of
727+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
728+# GNU Affero General Public License for more details.
729+#
730+# You should have received a copy of the GNU Affero General Public License
731+# along with this program. If not, see <http://www.gnu.org/licenses/>.
732+#
733+##############################################################################
734+from . import test_hr_recruitment_stage
735+checks = [
736+ test_hr_recruitment_stage,
737+ ]
738\ No newline at end of file
739
740=== added file 'hr_recruitment/tests/test_hr_recruitment_stage.py'
741--- hr_recruitment/tests/test_hr_recruitment_stage.py 1970-01-01 00:00:00 +0000
742+++ hr_recruitment/tests/test_hr_recruitment_stage.py 2014-05-20 06:41:52 +0000
743@@ -0,0 +1,71 @@
744+# -*- coding: utf-8 -*-
745+##############################################################################
746+#
747+# OpenERP, Open Source Business Applications
748+# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
749+#
750+# This program is free software: you can redistribute it and/or modify
751+# it under the terms of the GNU Affero General Public License as
752+# published by the Free Software Foundation, either version 3 of the
753+# License, or (at your option) any later version.
754+#
755+# This program is distributed in the hope that it will be useful,
756+# but WITHOUT ANY WARRANTY; without even the implied warranty of
757+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
758+# GNU Affero General Public License for more details.
759+#
760+# You should have received a copy of the GNU Affero General Public License
761+# along with this program. If not, see <http://www.gnu.org/licenses/>.
762+#
763+##############################################################################
764+from openerp.osv.orm import except_orm
765+from openerp.tests import common
766+
767+
768+class TestHrRecruitmentStage(common.TransactionCase):
769+ def setUp(self):
770+ super(TestHrRecruitmentStage, self).setUp()
771+
772+ def test_00_stage_management(self):
773+ cr, uid = self.cr, self.uid
774+ hr_job = self.registry('hr.job')
775+ hr_recruitment_stage= self.registry('hr.recruitment.stage')
776+ hr_applicant = self.registry('hr.applicant');
777+ hr_job_id = hr_job.create(cr, uid, {'name':'test_section'},context={})
778+ context = {'default_job_id': hr_job_id}
779+
780+ ''' For create when default_job_id pass in context
781+ If same name is present than returns its id otherwise create new.
782+ and link with given job.
783+ '''
784+
785+ check_len_id = hr_recruitment_stage.create(cr, uid, {'name':'First'},{})
786+ self.assertEqual(check_len_id, hr_recruitment_stage.create(cr, uid, {'name':'First'}, context=context))
787+
788+ ''' For edit when default_job_id pass in context
789+ If same name is present than returns its id otherwise create new.
790+ and link with given job and remove old link with job and project_task.
791+ '''
792+ hr_job.write(cr, uid, [hr_job_id], {'stage_ids': [(4, check_len_id),]}, context=context)
793+ hr_recruitment_id = hr_applicant.create(cr, uid, {'name':'Test1', 'stage_id': check_len_id, 'job_id':hr_job_id})
794+
795+ hr_recruitment_stage.write(cr, uid, [check_len_id],{'name':'Second'}, context=context)
796+
797+ stage_m2m_newlist = hr_job.browse(cr, uid, hr_job_id, context=context).stage_ids
798+ check_hr_recruitment_stage_id = hr_applicant.browse(cr, uid, hr_recruitment_id, context).stage_id.id
799+ hr_recruitment_stage_id_new = hr_recruitment_stage.search(cr, uid, [('name','=', 'Second')], context=context)
800+
801+ self.assertIn(hr_recruitment_stage_id_new[0], [x.id for x in stage_m2m_newlist])
802+ self.assertEqual(check_hr_recruitment_stage_id, hr_recruitment_stage_id_new[0])
803+
804+ ''' For unlink when default_job_id pass in context
805+ It will unlink relation, not delete.
806+ '''
807+ stage_m2m_oldlist = hr_job.browse(cr, uid, hr_job_id, context=context).stage_ids
808+ hr_recruitment_stage.unlink(cr, uid, hr_recruitment_stage_id_new, context=context)
809+ stage_m2m_newlist = hr_job.browse(cr, uid, hr_job_id, context=context).stage_ids
810+
811+ self.assertEqual(len(stage_m2m_oldlist) -1, len(stage_m2m_newlist))
812+ unlink_id = hr_recruitment_stage.search(cr, uid, [('name','=', 'Second')], context=context)
813+ self.assertEqual(unlink_id, hr_recruitment_stage_id_new)
814+
815
816=== modified file 'project/project.py'
817--- project/project.py 2014-05-13 11:18:37 +0000
818+++ project/project.py 2014-05-20 06:41:52 +0000
819@@ -55,9 +55,68 @@
820 _defaults = {
821 'sequence': 1,
822 'project_ids': _get_default_project_ids,
823+ 'case_default': lambda self, cr, uid, ctx={}: ctx.get('default_project_id', False) == False,
824 }
825 _order = 'sequence'
826-
827+
828+ _sql_constraints = [('stage_name_uniq', 'unique(name)', 'Name should be unique.')]
829+
830+ def copy(self, cr, uid, id, default=None, context=None):
831+ if context is None:
832+ context = {}
833+ if default is None:
834+ default = {}
835+ default['project_ids'] = []
836+ proj = self.browse(cr, uid, id, context=context)
837+ if not default.get('name', False):
838+ default.update(name=_("%s (copy)") % (proj.name))
839+ return super(project_task_type, self).copy(cr, uid, id, default, context)
840+
841+ def create(self, cr, uid, vals, context=None):
842+ if context is None: context = {}
843+ project_id = context.get('default_project_id')
844+ type_id = False
845+ if project_id:
846+ #check already exist or not
847+ context.update({'project_id': project_id})
848+ type_ids = self.search(cr, uid, [('name','=', vals.get('name'))], context=context, limit=1)
849+ if type_ids and len(type_ids):
850+ type_id = type_ids[0]
851+ if not type_id:
852+ type_id = super(project_task_type, self).create(cr, uid, vals, context=context)
853+ return type_id
854+
855+ def write(self, cr, uid, ids, vals, context=None):
856+ if context is None: context = {}
857+ project_id = context.get('default_project_id')
858+ project_obj = self.pool.get('project.project')
859+ if project_id:
860+ context.update({'project_id': project_id})
861+ if vals.get('name', False):
862+ for stage in self.browse(cr, uid, ids, context=context):
863+ new_stage_id = self.copy(cr, uid, stage.id, default=vals, context=context)
864+ project_obj.write(cr, uid, [project_id], {'type_ids': [(3, stage.id),(4, new_stage_id),]}, context=context)
865+ self._update_tasks(cr, uid, project_id, stage.id, new_stage_id, context=context)
866+ return True
867+ return super(project_task_type, self).write(cr, uid, ids, vals, context=context)
868+
869+ def unlink(self, cr, uid, ids, context=None):
870+ if context is None: context = {}
871+ project_id = context.get('default_project_id')
872+ project_obj = self.pool.get('project.project')
873+ if not project_id:
874+ return super(project_task_type, self).unlink(cr, uid, ids, context=context)
875+
876+ for stage in self.browse(cr, uid, ids, context=context):
877+ project_obj.write(cr, uid, project_id, {'type_ids': [(3, stage.id)]}, context=context)
878+ self._update_tasks(cr, uid, project_id, stage.id, False, context=context)
879+ return True
880+
881+ def _update_tasks(self, cr, uid, project_id, old_stage_id, new_stage_id, context=None):
882+ if context is None: context = {}
883+ task_obj = self.pool.get('project.task')
884+ task_ids = task_obj.search(cr, uid, [('stage_id', '=', old_stage_id),('project_id', '=', project_id)], context=context)
885+ return task_obj.write(cr, uid, task_ids, {'stage_id': new_stage_id} , context=context)
886
887 class project(osv.osv):
888 _name = "project.project"
889@@ -611,8 +670,7 @@
890 search_domain = []
891 project_id = self._resolve_project_id_from_context(cr, uid, context=context)
892 if project_id:
893- search_domain += ['|', ('project_ids', '=', project_id)]
894- search_domain += [('id', 'in', ids)]
895+ search_domain += [('project_ids', '=', project_id)]
896 stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
897 result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
898 # restore order of the search
899
900=== modified file 'project/tests/__init__.py'
901--- project/tests/__init__.py 2013-07-10 10:15:08 +0000
902+++ project/tests/__init__.py 2014-05-20 06:41:52 +0000
903@@ -19,10 +19,11 @@
904 #
905 ##############################################################################
906
907-from . import test_project_flow
908+from . import test_project_flow,project_stage_management
909
910 checks = [
911 test_project_flow,
912+ project_stage_management,
913 ]
914
915 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
916
917=== added file 'project/tests/project_stage_management.py'
918--- project/tests/project_stage_management.py 1970-01-01 00:00:00 +0000
919+++ project/tests/project_stage_management.py 2014-05-20 06:41:52 +0000
920@@ -0,0 +1,74 @@
921+# -*- coding: utf-8 -*-
922+##############################################################################
923+#
924+# OpenERP, Open Source Business Applications
925+# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
926+#
927+# This program is free software: you can redistribute it and/or modify
928+# it under the terms of the GNU Affero General Public License as
929+# published by the Free Software Foundation, either version 3 of the
930+# License, or (at your option) any later version.
931+#
932+# This program is distributed in the hope that it will be useful,
933+# but WITHOUT ANY WARRANTY; without even the implied warranty of
934+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
935+# GNU Affero General Public License for more details.
936+#
937+# You should have received a copy of the GNU Affero General Public License
938+# along with this program. If not, see <http://www.gnu.org/licenses/>.
939+#
940+##############################################################################
941+from openerp.osv.orm import except_orm
942+from openerp.tests import common
943+
944+
945+class TestCrmStagesCase(common.TransactionCase):
946+ def setUp(self):
947+ super(TestCrmStagesCase, self).setUp()
948+
949+ def test_00_stage_management(self):
950+
951+ cr, uid = self.cr, self.uid
952+ project_project = self.registry('project.project')
953+ project_task_type= self.registry('project.task.type')
954+ project_task = self.registry('project.task');
955+ project_project_id = project_project.create(cr, uid, {'name':'Project1'},context={})
956+ context = {'default_project_id': project_project_id}
957+
958+ ''' For create when default_project_id pass in context
959+ If same name is present than returns its id otherwise create new.
960+ and link with given project
961+ '''
962+
963+ task_type_id = project_task_type.create(cr, uid, {'name':'First'}, {})
964+ self.assertEqual(task_type_id, project_task_type.create(cr, uid, {'name':'First'}, context=context))
965+
966+ ''' For edit when default_project_id pass in context
967+ If same name is present than returns its id otherwise create new.
968+ and link with given project and remove old link with project and project_task.
969+ '''
970+ project_project.write(cr, uid, [project_project_id], {'type_ids': [(4, task_type_id),]}, context=context)
971+ task_id = project_task.create(cr, uid, {'name':'Test1', 'stage_id': task_type_id, 'project_id':project_project_id})
972+ context.update({'stage_model':'project.task'})
973+
974+ project_task_type.write(cr, uid, [task_type_id],{'name':'Second'}, context=context)
975+
976+ stage_m2m_newlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
977+ check_project_task_stage_id = project_task.browse(cr, uid, task_id, context).stage_id.id
978+ task_type_id_new = project_task_type.search(cr, uid, [('name','=', 'Second')], context=context)
979+
980+ self.assertIn(task_type_id_new[0], [x.id for x in stage_m2m_newlist])
981+ self.assertEqual(check_project_task_stage_id, task_type_id_new[0])
982+
983+ ''' For unlink when default_project_id pass in context
984+ It will unlink relation, not delete.
985+ '''
986+ stage_m2m_oldlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
987+ project_task_type.unlink(cr, uid, task_type_id_new, context=context)
988+ stage_m2m_newlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
989+
990+ self.assertEqual(len(stage_m2m_oldlist) -1, len(stage_m2m_newlist))
991+
992+ unlink_id = project_task_type.search(cr, uid, [('name','=', 'Second')], context=context)
993+ self.assertEqual(unlink_id, task_type_id_new)
994+
995\ No newline at end of file
996
997=== modified file 'project_issue/__init__.py'
998--- project_issue/__init__.py 2012-03-15 06:19:19 +0000
999+++ project_issue/__init__.py 2014-05-20 06:41:52 +0000
1000@@ -23,5 +23,5 @@
1001 import project_issue
1002 import report
1003 import res_config
1004-
1005+import tests
1006 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1007
1008=== modified file 'project_issue/project_issue.py'
1009--- project_issue/project_issue.py 2014-05-08 15:25:36 +0000
1010+++ project_issue/project_issue.py 2014-05-20 06:41:52 +0000
1011@@ -108,8 +108,7 @@
1012 search_domain = []
1013 project_id = self._resolve_project_id_from_context(cr, uid, context=context)
1014 if project_id:
1015- search_domain += ['|', ('project_ids', '=', project_id)]
1016- search_domain += [('id', 'in', ids)]
1017+ search_domain += [('project_ids', '=', project_id)]
1018 # perform search
1019 stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
1020 result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
1021@@ -547,6 +546,15 @@
1022 def write(self, cr, uid, ids, vals, context=None):
1023 self._check_create_write_values(cr, uid, vals, context=context)
1024 return super(project_project, self).write(cr, uid, ids, vals, context=context)
1025+
1026+class project_task_type(osv.Model):
1027+ _inherit = "project.task.type"
1028+
1029+ def _update_tasks(self, cr, uid, project_id, old_stage_id, new_stage_id, context=None):
1030+ super(project_task_type, self)._update_tasks(cr, uid, project_id, old_stage_id, new_stage_id, context=context)
1031+ issue_obj = self.pool.get('project.issue')
1032+ issue_ids = issue_obj.search(cr, uid, [('stage_id', '=', old_stage_id),('project_id', '=', project_id)], context=context)
1033+ return issue_obj.write(cr, uid, issue_ids, {'stage_id': new_stage_id} , context=context)
1034
1035 class res_partner(osv.osv):
1036 def _issue_count(self, cr, uid, ids, field_name, arg, context=None):
1037
1038=== added directory 'project_issue/tests'
1039=== added file 'project_issue/tests/__init__.py'
1040--- project_issue/tests/__init__.py 1970-01-01 00:00:00 +0000
1041+++ project_issue/tests/__init__.py 2014-05-20 06:41:52 +0000
1042@@ -0,0 +1,28 @@
1043+# -*- coding: utf-8 -*-
1044+##############################################################################
1045+#
1046+# OpenERP, Open Source Business Applications
1047+# Copyright (c) 2013-TODAY OpenERP S.A. <http://www.openerp.com>
1048+#
1049+# This program is free software: you can redistribute it and/or modify
1050+# it under the terms of the GNU Affero General Public License as
1051+# published by the Free Software Foundation, either version 3 of the
1052+# License, or (at your option) any later version.
1053+#
1054+# This program is distributed in the hope that it will be useful,
1055+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1056+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1057+# GNU Affero General Public License for more details.
1058+#
1059+# You should have received a copy of the GNU Affero General Public License
1060+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1061+#
1062+##############################################################################
1063+
1064+from . import project_stage_management
1065+
1066+checks = [
1067+ project_stage_management,
1068+]
1069+
1070+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1071
1072=== added file 'project_issue/tests/project_stage_management.py'
1073--- project_issue/tests/project_stage_management.py 1970-01-01 00:00:00 +0000
1074+++ project_issue/tests/project_stage_management.py 2014-05-20 06:41:52 +0000
1075@@ -0,0 +1,74 @@
1076+# -*- coding: utf-8 -*-
1077+##############################################################################
1078+#
1079+# OpenERP, Open Source Business Applications
1080+# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
1081+#
1082+# This program is free software: you can redistribute it and/or modify
1083+# it under the terms of the GNU Affero General Public License as
1084+# published by the Free Software Foundation, either version 3 of the
1085+# License, or (at your option) any later version.
1086+#
1087+# This program is distributed in the hope that it will be useful,
1088+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1089+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1090+# GNU Affero General Public License for more details.
1091+#
1092+# You should have received a copy of the GNU Affero General Public License
1093+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1094+#
1095+##############################################################################
1096+from openerp.osv.orm import except_orm
1097+from openerp.tests import common
1098+
1099+
1100+class TestCrmStagesCase(common.TransactionCase):
1101+ def setUp(self):
1102+ super(TestCrmStagesCase, self).setUp()
1103+
1104+ def test_00_stage_management(self):
1105+
1106+ cr, uid = self.cr, self.uid
1107+ project_project = self.registry('project.project')
1108+ project_task_type= self.registry('project.task.type')
1109+ project_issue = self.registry('project.issue');
1110+ project_project_id = project_project.create(cr, uid, {'name':'Project1'},context={})
1111+ context = {'default_project_id': project_project_id}
1112+
1113+ ''' For create when default_project_id pass in context
1114+ If same name is present than returns its id otherwise create new.
1115+ and link with given project
1116+ '''
1117+
1118+ task_type_id = project_task_type.create(cr, uid, {'name':'First'}, {})
1119+ self.assertEqual(task_type_id, project_task_type.create(cr, uid, {'name':'First'}, context=context))
1120+
1121+ ''' For edit when default_project_id pass in context
1122+ If same name is present than returns its id otherwise create new.
1123+ and link with given project and remove old link with project and project_task.
1124+ '''
1125+ project_project.write(cr, uid, [project_project_id], {'type_ids': [(4, task_type_id),]}, context=context)
1126+ issue_id = project_issue.create(cr, uid, {'name':'Test1', 'stage_id': task_type_id, 'project_id':project_project_id})
1127+ context.update({'stage_model':'project.issue'})
1128+
1129+ project_task_type.write(cr, uid, [task_type_id],{'name':'Second'}, context=context)
1130+
1131+ stage_m2m_newlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1132+ check_project_task_stage_id = project_issue.browse(cr, uid, issue_id, context).stage_id.id
1133+ task_type_id_new = project_task_type.search(cr, uid, [('name','=', 'Second')], context=context)
1134+
1135+ self.assertIn(task_type_id_new[0], [x.id for x in stage_m2m_newlist])
1136+ self.assertEqual(check_project_task_stage_id, task_type_id_new[0])
1137+
1138+ ''' For unlink when default_project_id pass in context
1139+ It will unlink relation, not delete.
1140+ '''
1141+ stage_m2m_oldlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1142+ project_task_type.unlink(cr, uid, task_type_id_new, context=context)
1143+ stage_m2m_newlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1144+
1145+ self.assertEqual(len(stage_m2m_oldlist) -1, len(stage_m2m_newlist))
1146+
1147+ unlink_id = project_task_type.search(cr, uid, [('name','=', 'Second')], context=context)
1148+ self.assertEqual(unlink_id, task_type_id_new)
1149+
1150\ No newline at end of file

Subscribers

People subscribed via source and target branches

to all changes: