← Back to team overview

openerp-community team mailing list archive

[Merge] lp:~credativ/openobject-addons/elico-6.1-fixes-gap-analysis-template-management into lp:~openerp-community/openobject-addons/elico-6.1

 

Tom Pickering has proposed merging lp:~credativ/openobject-addons/elico-6.1-fixes-gap-analysis-template-management into lp:~openerp-community/openobject-addons/elico-6.1.

Requested reviews:
  OpenERP Community (openerp-community)

For more details, see:
https://code.launchpad.net/~credativ/openobject-addons/elico-6.1-fixes-gap-analysis-template-management/+merge/218422

This branch adds several features to the Gap Analysis module which help to facilitate template-orientated workflows. These include:

- The ability to view associated Gap Analysis Lines and Gap Analysis Template Lines from a Functionality's form view.

- The ability to add Functionalities to Gap Analyses and Gap Analysis Templates from the 'All Functionalities' view.

- 'Group By...' options in the All 'Functionalities' view.

- Automatic updates to Gap Analysis Templates when a corresponding Functionality template is modified.

- Avoiding creation of duplicate Gap Analysis lines when importing from a Gap Analysis Template.

-- 
https://code.launchpad.net/~credativ/openobject-addons/elico-6.1-fixes-gap-analysis-template-management/+merge/218422
Your team OpenERP Community is requested to review the proposed merge of lp:~credativ/openobject-addons/elico-6.1-fixes-gap-analysis-template-management into lp:~openerp-community/openobject-addons/elico-6.1.
=== modified file 'gap_analysis/__openerp__.py'
--- gap_analysis/__openerp__.py	2013-06-22 02:18:01 +0000
+++ gap_analysis/__openerp__.py	2014-05-06 13:30:34 +0000
@@ -63,6 +63,7 @@
         'gap_analysis_sequence.xml',
         'gap_analysis.xml',
         'wizard/import_from_tmpl.xml',
+        'wizard/line_create_wizard_view.xml'
 	],
     #'demo_xml': ['gap_analysis_demo.xml'], 
     'test': [],
@@ -73,4 +74,4 @@
     'certificate': '',
 }
 
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== modified file 'gap_analysis/gap_analysis.py'
--- gap_analysis/gap_analysis.py	2013-06-22 02:18:01 +0000
+++ gap_analysis/gap_analysis.py	2014-05-06 13:30:34 +0000
@@ -86,7 +86,8 @@
         my_type = self.pool.get('gap_analysis.workload.type').browse(cr, uid, type_id)
         val['duration'] = my_type.duration
         return {'value': val}
-    
+
+
 gap_analysis_workload()
 
 
@@ -150,33 +151,129 @@
     _name = "gap_analysis.functionality"
     _description = "Gap Analysis Functionalities"
     
+
+    def _search_ga_lines(self, cr, uid, ids, field_names, arg=None, context=None, tmpl=False):
+        if context is None:
+            context = {}
+
+        ga_obj      = self.pool.get('gap_analysis')
+        ga_line_obj = self.pool.get('gap_analysis.line')
+        func_obj    = self.pool.get('gap_analysis.functionality')
+
+        line_ids = ga_line_obj.search(cr, uid, [('functionality','=', ids[0])], context=context)
+
+        lids_to_gids = {}
+        tmpl_lids = []
+        ga_obj = self.pool.get('gap_analysis')
+        gap_ids = ga_line_obj.read(cr, uid, line_ids, ['gap_id'], context=context)
+
+        for gap_id in gap_ids:
+            lids_to_gids[gap_id['id']] = gap_id['gap_id'][0]
+
+        tmpl_checks = ga_obj.read(cr, uid, [gid['gap_id'][0] for gid in gap_ids], ['is_tmpl'], context=context)
+        gids_to_checks = dict(zip([c['id'] for c in tmpl_checks], [c['is_tmpl'] == tmpl for c in tmpl_checks]))
+
+        for lid in lids_to_gids.keys():
+            if gids_to_checks[lids_to_gids[lid]]:
+                tmpl_lids.append(lid)
+
+        return {ids[0] : [l for l in line_ids if l in tmpl_lids]}
+
+
+    def _search_ga_tmpl_lines(self, cr, uid, ids, field_names, arg=None, context=None):
+        return self._search_ga_lines(cr, uid, ids, field_names, arg=arg, context=context, tmpl=True)
+
+
+    def _search_ga_non_tmpl_lines(self, cr, uid, ids, field_names, arg=None, context=None):
+        return self._search_ga_lines(cr, uid, ids, field_names, arg=arg, context=context, tmpl=False)
+
+
     _columns = {
-        'name':        fields.char('Functionality', size=256, required=True, translate=True),
-        'description': fields.text('Description'),
-        'category':    fields.many2one('gap_analysis.functionality.category', 'Category', required=True, select=True),
-        'is_tmpl':     fields.boolean('Template ?', help='This Functionality is a Template ?'),
-        'proposed':    fields.boolean('Propose as template ?'),
+        'name':          fields.char('Functionality', size=256, required=True, translate=True),
+        'description':   fields.text('Description'),
+        'category':      fields.many2one('gap_analysis.functionality.category', 'Category', required=True, select=True),
+        'is_tmpl':       fields.boolean('Template ?', help='This Functionality is a Template ?'),
+        'proposed':      fields.boolean('Propose as template ?'),
         #### Default values (Templating) ####
-        'workloads':   fields.one2many('gap_analysis.workload', 'fct_id', 'Default Workloads'),
-        'openerp_fct': fields.many2one('gap_analysis.openerp', 'Default OpenERP feature', select=True),
-        'critical':    fields.integer('Default Critical Level', help='Indicator to specify the importance of this functionality in the project.'),
-        'testing':     fields.float('Test (hour)'),
-        'effort':      fields.many2one('gap_analysis.effort', 'Default Effort', help="Development Effort for this functionality."),
-        'duration_wk': fields.float('Default Duration (hour)', help='Since this effort has no pre-defined duration, you must set one.'),
-        'unknown_wk':  fields.boolean('Must set the duration manually ? (Default)',),
+        'workloads':     fields.one2many('gap_analysis.workload', 'fct_id', 'Default Workloads'),
+        'openerp_fct':   fields.many2one('gap_analysis.openerp', 'Default OpenERP feature', select=True),
+        'critical':      fields.integer('Default Critical Level', help='Indicator to specify the importance of this functionality in the project.'),
+        'testing':       fields.float('Test (hour)'),
+        'effort':        fields.many2one('gap_analysis.effort', 'Default Effort', help="Development Effort for this functionality."),
+        'duration_wk':   fields.float('Default Duration (hour)', help='Since this effort has no pre-defined duration, you must set one.'),
+        'unknown_wk':    fields.boolean('Must set the duration manually ? (Default)',),
+        'ga_lines':      fields.function(_search_ga_non_tmpl_lines, type='one2many', relation='gap_analysis.line', method=True, string='Gap Analysis Lines'),
+        'ga_tmpl_lines': fields.function(_search_ga_tmpl_lines, type='one2many', relation='gap_analysis.line', method=True, string='Gap Analysis Template Lines'),
     }
+
+
+    def open_line_wizard(self, cr, uid, ids, context=None):
+        if context is None:
+            context={}
+
+        title = context.get('default_tmpl', False) and 'Create New Template Entry' or 'Create New Entry'
+
+        return {
+                   'type' : 'ir.actions.act_window',
+                   'name' : title,
+                   'res_model': 'gap_analysis.line_create_wizard',
+                   'view_type' : 'form',
+                   'view_mode' : 'form',
+                   'target' : 'new',
+                   'context' : context,
+               }
     
+
+    def onchange_is_tmpl(self, cr, uid, ids, is_tmpl, context=None):
+        if context is None:
+            context = {}
+        
+        if not is_tmpl or not len(ids):
+            return {}
+
+        ga_line_pool = self.pool.get('gap_analysis.line')
+        related_lines = ga_line_pool.search(cr, uid, [('functionality','=',ids[0])], context=context)
+
+        if not related_lines:
+            return {}
+
+        source_line = max(related_lines)
+        fields_to_read = ['workloads', 'openerp_fct', 'critical', 'testing', 'effort', 'duration_wk', 'unknown_wk']
+        val = ga_line_pool.read(cr, uid, source_line, fields_to_read, context=context)
+
+        if val.get('workloads', False):
+            wkld_pool = self.pool.get('gap_analysis.workload')
+            new_wklds = []
+            for wkld in val['workloads']:
+                wkld_data = wkld_pool.read(cr, uid, wkld, ['type', 'duration'])
+                new_wklds.append({'fct_id' : ids[0], 'gap_line_id' : False, 'type' : wkld_data['type'][0], 'duration' : wkld_data['duration']})
+            val['workloads'] = new_wklds
+
+        return {'value' : val}
+        
+
     def onchange_effort_id(self, cr, uid, ids, effort_id, unknown_wk):
         val = {}
-        my_effort = self.pool.get('gap_analysis.effort').browse(cr, uid, effort_id)
-        val['unknown_wk'] = my_effort.unknown
+        if effort_id:
+            my_effort = self.pool.get('gap_analysis.effort').browse(cr, uid, effort_id)
+            val['unknown_wk'] = my_effort.unknown
+        else:
+            val['unknown_wk'] = False
         return {'value': val}
     
     
     def write(self, cr, uid, ids, vals, context=None):
         if 'is_tmpl' in vals and vals['is_tmpl'] == True:
             vals['proposed'] = False
-        return super(gap_analysis_functionality, self).write(cr, uid, ids, vals, context=context)
+        res = super(gap_analysis_functionality, self).write(cr, uid, ids, vals, context=context)
+        if res:
+            id_list = isinstance(ids, list) and ids[:] or [ids]
+            line_pool = self.pool.get('gap_analysis.line')
+            for fid in id_list:
+                lids = line_pool.search(cr, uid, [('gap_id.is_tmpl','=',True),('functionality','=',fid)], context=context)
+                line_pool.set_values_by_fct(cr, uid, lids, fid, context=context)
+            
+        return res
     
 gap_analysis_functionality()
 
@@ -351,7 +448,7 @@
 class gap_analysis_line(osv.osv):
     _name = "gap_analysis.line"
     _description = "Gap-analysis Lines"
-    
+
     def _estimated_line_time_cost(self, cursor, uid, ids, fields, arg, context=None):
         result = {}
         gap = False
@@ -404,39 +501,70 @@
                 if w.type.id == arg:
                     amount += w.duration            
         return amount
-    
-    
-    def onchange_functionality_id(self, cr, uid, ids, functionality_id, gap_line_id):
-        val = {}
-        functionality_tmpl = self.pool.get('gap_analysis.functionality').browse(cr, uid, functionality_id)
-        if functionality_tmpl.effort:
-            val['effort'] = functionality_tmpl.effort.id
-        if functionality_tmpl.category:
-            val['category'] = functionality_tmpl.category.id
-        if functionality_tmpl.testing:
-            val['testing'] = functionality_tmpl.testing
-        if functionality_tmpl.unknown_wk:
-            val['unknown_wk'] = functionality_tmpl.unknown_wk
-        if functionality_tmpl.duration_wk:
-            val['duration_wk'] = functionality_tmpl.duration_wk
-        if functionality_tmpl.critical:
-            val['critical'] = functionality_tmpl.critical
-        if functionality_tmpl.openerp_fct:
-            val['openerp_fct'] = functionality_tmpl.openerp_fct.id
-        if functionality_tmpl.workloads:
-            workload_pool = self.pool.get('gap_analysis.workload')
-            my_workloads  = []
-            for workload in functionality_tmpl.workloads:
-                workload_vals = {'type':workload.type.id,'duration':workload.duration,}
-                if gap_line_id:
-                    workload_vals['gap_line_id'] = gap_line_id
-                workload_id = workload_pool.create(cr, uid, workload_vals)
-                if workload_id:
-                    my_workloads.append(workload_id)
-            if my_workloads:
+
+
+    def set_values_by_fct(self, cr, uid, ids, fct_id, ret_fmt_onchange=False, context=None):
+        vals = []
+        fct_tmpl = self.pool.get('gap_analysis.functionality').browse(cr, uid, fct_id)
+        id_list = isinstance(ids, list) and ids[:] or [ids]
+        for lid in id_list:
+            val = {}
+            if fct_tmpl.effort:
+                val['effort'] = fct_tmpl.effort.id
+            if fct_tmpl.category:
+                val['category'] = fct_tmpl.category.id
+            if fct_tmpl.testing:
+                val['testing'] = fct_tmpl.testing
+            if fct_tmpl.unknown_wk:
+                val['unknown_wk'] = fct_tmpl.unknown_wk
+            if fct_tmpl.duration_wk:
+                val['duration_wk'] = fct_tmpl.duration_wk
+            if fct_tmpl.critical:
+                val['critical'] = fct_tmpl.critical
+            if fct_tmpl.openerp_fct:
+                val['openerp_fct'] = fct_tmpl.openerp_fct.id
+            if fct_tmpl.workloads:
+                workload_pool = self.pool.get('gap_analysis.workload')
+                my_workloads  = []
+                for workload in fct_tmpl.workloads:
+                    workload_vals = {'type':workload.type.id,'duration':workload.duration,}
+                    if lid:
+                        workload_vals['gap_line_id'] = lid
+                    my_workloads.append(workload_vals)
                 val['workloads'] = my_workloads
-        
-        return {'value': val}
+            if ret_fmt_onchange:
+                vals.append(val)
+            else:
+                my_workloads = val.get('workloads', False)
+                old_wkld_data = self.read(cr, uid, lid, ['workloads'], context=context)
+                if old_wkld_data:
+                    old_wklds = old_wkld_data['workloads']
+                    wkld_unlink_tuples = [(2, wid) for wid in old_wklds]
+                    val['workloads'] = wkld_unlink_tuples
+                self.write(cr, uid, lid, val, context=context)
+                if my_workloads:
+                    wkld_tuples = []
+                    for wkld in my_workloads:
+                        wkld_tuples.append((0, 0, wkld))
+                    self.write(cr, uid, lid, {'workloads':wkld_tuples}, context=context)
+        return ret_fmt_onchange and vals or True
+
+    
+    def onchange_functionality_id(self, cr, uid, ids, fct_id, context=None):
+        ret = {}
+        ids = isinstance(ids, list) and ids or [ids]
+        val = self.set_values_by_fct(cr, uid, ids, fct_id, ret_fmt_onchange=True, context=context)
+        ret.update({'value' : val[0]})
+        gap_id = self.browse(cr, uid, ids[0], context=context).gap_id.id
+        ga_lines = self.search(cr, uid, [('gap_id','=',gap_id),('id','!=',ids[0])], context=context)
+        if ga_lines:
+            ga_fcts = self.read(cr, uid, ga_lines, ['functionality'], context=context)
+            if fct_id in [fct['functionality'][0] for fct in ga_fcts]:
+                ret.update({'warning' : {
+                                            'title'   : 'Warning: Functionality Duplication.',
+                                            'message' : 'This functionality already exists within this Gap Analysis.',
+                                        }})
+        return ret
     
     
     def onchange_effort_id(self, cr, uid, ids, effort_id, unknown_wk):
@@ -444,8 +572,13 @@
         my_effort = self.pool.get('gap_analysis.effort').browse(cr, uid, effort_id)
         val['unknown_wk'] = my_effort.unknown
         return {'value': val}
-    
-    
+
+
+    def _get_dummy_gap_is_tmpl(self, cr, uid, ids, fields, arg, context=None):
+        gid = self.read(cr, uid, ids[0], ['gap_id'], context=context)['gap_id'][0]
+        return {ids[0] : self.pool.get('gap_analysis').read(cr, uid, gid, ['is_tmpl'], context=context)['is_tmpl']}
+
+
     _columns = {
         'gap_id':        fields.many2one('gap_analysis', 'Gap-analysis', required=True, ondelete='cascade', select=True, readonly=True),
         'seq':           fields.char('Sequence', size=48),
@@ -463,6 +596,7 @@
         'effort':        fields.many2one('gap_analysis.effort', 'Effort', help="Development Effort for this functionality."),
         'duration_wk':   fields.float('Duration (hour)', help='Since this effort has no pre-defined duration, you must set one.'),
         'unknown_wk':    fields.boolean('Must set the duration manually ?',),
+        'dummy_gap_is_tmpl': fields.function(_get_dummy_gap_is_tmpl, type='boolean', name='dummy_gap_is_tmpl', method=True),
     }
     _defaults = {
         'unknown_wk':  lambda *a: False,
@@ -475,4 +609,4 @@
     
 gap_analysis_line()
 
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== modified file 'gap_analysis/gap_analysis.xml'
--- gap_analysis/gap_analysis.xml	2013-06-22 02:18:01 +0000
+++ gap_analysis/gap_analysis.xml	2014-05-06 13:30:34 +0000
@@ -24,36 +24,104 @@
                 <form string="Gap Analysis Functionalities">
                     <field name="category"/>
                     <field name="name"/>
-                    <field name="is_tmpl"  groups="gap_analysis.res_group_gap1"/>
+                    <field name="is_tmpl"  groups="gap_analysis.res_group_gap1" on_change="onchange_is_tmpl(is_tmpl)"/>
                     <field name="proposed" attrs="{'invisible':[('is_tmpl','=',True)]}"/>
                     <newline/>
-                    <separator string="Description" colspan="4"/>
-                    <field name="description" colspan="4" nolabel="1"/>
-                    <newline/>
-                    <group groups="gap_analysis.res_group_gap1" attrs="{'invisible':[('is_tmpl','=',False)]}" colspan="4" col="4">
-		                <separator colspan="4" string="Template Default Values"/>
+
+                    <notebook colspan="4">
+                        <page string="Details">
+                            <separator string="Description" colspan="4"/>
+                            <field name="description" colspan="4" nolabel="1"/>
+                            <newline/>
+                            <group groups="gap_analysis.res_group_gap1" attrs="{'invisible':[('is_tmpl','=',False)]}" colspan="4" col="4">
+		                <separator colspan="4" string="Default Values"/>
 		                <field name="critical"/>
 		                <field name="openerp_fct"/>
 		                <field name="effort" on_change="onchange_effort_id(effort,unknown_wk)"/>
 		                <field name="unknown_wk" invisible="1"/>
-				        <field name="duration_wk" attrs="{'invisible':[('unknown_wk','=',False)]}"/>
-				        <field name="testing"/>
+		                    <field name="duration_wk" attrs="{'invisible':[('unknown_wk','=',False)]}"/>
+		                    <field name="testing"/>
 		                <newline/>
+
 		                <separator colspan="4" string="Workloads"/>
-		                <field colspan="4" name="workloads" nolabel="1" widget="one2many_list">
-			                <form string="Workloads">
-			                    <field name="type" on_change="onchange_type_id(type)" colspan="4"/>
-					            <field name="duration"/>
-			                </form>
-					        <tree string="Workloads">
-					            <field name="type"/>
-					        	<field name="duration"/>
-			                </tree>
-	                    </field>
-	            	</group>
+                                    <field colspan="4" name="workloads" nolabel="1" widget="one2many_list">
+                                    <form string="Workloads">
+                                        <field name="type" on_change="onchange_type_id(type)" colspan="4"/>
+                                        <field name="duration"/>
+                                    </form>
+                                    <tree>
+                                        <field name="type"/>
+                                        <field name="duration"/>
+                                    </tree>
+                                </field>
+                                <newline/>
+                            </group>
+                        </page>
+
+                        <page string="Analyses">
+                            <group colspan="4" col="4">
+                                <separator colspan="4" string="Instances"/>
+                                <field colspan="4" name="ga_lines" nolabel="1">
+                                    <tree colors="grey:keep==False;">
+                                        <field name="gap_id" string="Gap Analysis"/>
+                                        <field name="code"/>
+                                        <field name="keep" invisible="1"/>
+                                        <field name="phase"/>
+                                        <field name="critical"/>
+                                        <field name="effort"/>
+                                        <field name="testing"/>
+                                        <field name="duration_wk"/>
+                                        <field name="total_time"/>
+                                        <field name="total_cost"/>
+                                    </tree>
+                                </field>
+                                <button name="open_line_wizard" string="Add to Analysis" type="object" icon="gtk-add" context="{'default_tmpl':False}"/>
+                            </group>
+                            <group attrs="{'invisible':[('is_tmpl','=',False)]}" colspan="4" col="4">
+                                <separator colspan="4" string="Gap Templates"/>
+                                <field colspan="4" name="ga_tmpl_lines" nolabel="1" options="{'deletable': false}">
+                                    <tree colors="grey:keep==False;">
+                                        <field name="gap_id" string="Template"/>
+                                        <field name="code"/>
+                                        <field name="keep" invisible="1"/>
+                                        <field name="phase"/>
+                                        <field name="critical"/>
+                                        <field name="effort"/>
+                                        <field name="testing"/>
+                                        <field name="duration_wk"/>
+                                    </tree>
+                                </field>
+                                <button name="open_line_wizard" string="Add to Template" type="object" icon="gtk-add" context="{'default_tmpl':True}"/>
+                            </group>
+                        </page>
+                    </notebook>
                 </form>
             </field>
         </record>
+
+        <record id="view_gap_analysis_functionality_filter" model="ir.ui.view">
+            <field name="name">gap_analysis.functionality.search</field>
+            <field name="model">gap_analysis.functionality</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Search Functionality">
+                    <filter string="Template" icon="star-on" domain="[('is_tmpl','=',True)]"/>
+                    <filter string="Regular" icon="star-off" domain="[('is_tmpl','=',False)]"/>
+                    <separator orientation="vertical"/>
+                    <filter string="Proposed" icon="terp-idea" domain="[('proposed','=',True)]"/>
+                    <field name="category"/>
+                    <field name="name" string="Name"/>
+                    <field name="description"/>
+                    <field name="openerp_fct" string="OpenERP Feature"/>
+                    <newline/>
+                    <group expand="0" string="Group By..." colspan="4">
+                        <filter string="Category" icon="terp-stage" context="{'group_by':'category'}"/>
+                        <filter string="Critical Level" icon="terp-gnome-cpu-frequency-applet+" context="{'group_by':'critical'}"/>
+                        <filter string="Effort" icon="terp-project" context="{'group_by':'effort'}"/>
+                    </group>
+                </search>
+            </field>
+        </record>
         
         <!-- Functionality Categories -->
     	<record id="view_gap_analysis_functionality_category_tree" model="ir.ui.view">
@@ -202,19 +270,20 @@
                                 <form string="Functionalities">
 	                                <group colspan="4" col="4">
 	                                    <field name="id" invisible="1"/>
-	                                    <field name="functionality" on_change="onchange_functionality_id(functionality,id)" colspan="4" domain="[('is_tmpl','=',True)]"/>
-	                                    <field name="category"/>
+	                                    <field name="dummy_gap_is_tmpl" invisible="1"/>
+	                                    <field name="functionality" on_change="onchange_functionality_id(functionality)" colspan="4" domain="[('is_tmpl','=',True)]"/>
+	                                    <field name="category" attrs="{'readonly':[('dummy_gap_is_tmpl','=',True)]}"/>
 	                                    <field name="keep"/>
-	                                    <field name="critical"/>
-	                                    <field name="openerp_fct"/>
+	                                    <field name="critical" attrs="{'readonly':[('dummy_gap_is_tmpl','=',True)]}"/>
+	                                    <field name="openerp_fct" attrs="{'readonly':[('dummy_gap_is_tmpl','=',True)]}"/>
 	                                    <field name="contributors"/>
-	                                    <field name="testing"/>
-	                                    <field name="effort" on_change="onchange_effort_id(effort,unknown_wk)"/>
-	                                    <field name="unknown_wk" invisible="1"/>
-			                            <field name="duration_wk" attrs="{'invisible':[('unknown_wk','=',False)]}"/>
+	                                    <field name="testing" attrs="{'readonly':[('dummy_gap_is_tmpl','=',True)]}"/>
+	                                    <field name="effort" on_change="onchange_effort_id(effort,unknown_wk)" attrs="{'readonly':[('dummy_gap_is_tmpl','=',True)]}"/>
+	                                    <field name="unknown_wk" invisible="1" attrs="{'readonly':[('dummy_gap_is_tmpl','=',True)]}"/>
+			                            <field name="duration_wk" attrs="{'invisible':[('unknown_wk','=',False)],'readonly':[('dummy_gap_is_tmpl','=',True)]}"/>
 	                                    <newline/>
 	                                    <separator colspan="4" string="Workloads"/>
-	                                    <field colspan="4" name="workloads" nolabel="1" widget="one2many_list">
+	                                    <field colspan="4" name="workloads" nolabel="1" widget="one2many_list" attrs="{'readonly':[('dummy_gap_is_tmpl','=',True)]}">
 	                                    	<form string="Workloads">
 	                                        	<field name="type" on_change="onchange_type_id(type)"/>
 			                                    <field name="duration"/>
@@ -295,6 +364,7 @@
             <field name="res_model">gap_analysis.functionality</field>
             <field name="view_type">form</field>
             <field name="view_mode">tree,form</field>
+            <field name="search_view">gap_analysis.view_gap_analysis_functionality_filter</field>
         </record>
 		<record id="act_gap_analysis_fct_tmpl" model="ir.actions.act_window">
             <field name="name">Functionality Templates</field>
@@ -376,4 +446,4 @@
 	        	<menuitem id="menu_gap_022" name="Efforts" 					parent="menu_gap_02" sequence="6" action="act_gap_analysis_effort" />
 	        	<menuitem id="menu_gap_023" name="OpenERP Features" 		parent="menu_gap_02" sequence="9" action="act_gap_analysis_openerp" />
     </data>
-</openerp>
\ No newline at end of file
+</openerp>

=== modified file 'gap_analysis/wizard/__init__.py'
--- gap_analysis/wizard/__init__.py	2013-06-22 02:18:01 +0000
+++ gap_analysis/wizard/__init__.py	2014-05-06 13:30:34 +0000
@@ -22,5 +22,6 @@
 
 import import_from_tmpl
 import import_from_sheet
+import line_create_wizard
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== modified file 'gap_analysis/wizard/import_from_sheet.py'
--- gap_analysis/wizard/import_from_sheet.py	2013-06-22 02:18:01 +0000
+++ gap_analysis/wizard/import_from_sheet.py	2014-05-06 13:30:34 +0000
@@ -222,8 +222,7 @@
             
             # Check Functionality
             fct_ids = g_fct_pool.search(cr, uid, [('name','ilike',gap_line.functionality)])
-	    if True:
-#            if not fct_ids:
+            if not fct_ids:
                 # Create Functionality
                 fct_id = g_fct_pool.create(cr, uid, {'name':gap_line.functionality,'description': gap_line.function_desc,'category':gap_line.category,})
                 print('Gap Import: Fct %s created (%s)'%(gap_line.functionality,fct_id))

=== modified file 'gap_analysis/wizard/import_from_tmpl.py'
--- gap_analysis/wizard/import_from_tmpl.py	2013-06-22 02:18:01 +0000
+++ gap_analysis/wizard/import_from_tmpl.py	2014-05-06 13:30:34 +0000
@@ -38,10 +38,19 @@
         this = self.browse(cr, uid, ids[0], context=context)
         gap_line_pool = self.pool.get('gap_analysis.line')
         workload_pool = self.pool.get('gap_analysis.workload')
-                
+               
         for id in context.get('active_ids', []): #for each gap in which we want to import stuff
+            current_line_ids = gap_line_pool.search(cr, uid, [('gap_id','=',id)], context=context)
+            current_fcts = gap_line_pool.read(cr, uid, current_line_ids, ['functionality'])
+            current_fct_ids = [f['functionality'][0] for f in current_fcts]
+ 
             #copy gap_line with functionalities and workloads
             for gap_line in this.template.gap_lines:
+
+                if gap_line.functionality.id in current_fct_ids:
+                    # Functionality already exists in the Gap.
+                    continue
+
                 line_vals = {
                     'gap_id':        id,
                     'functionality': gap_line.functionality.id,
@@ -54,7 +63,7 @@
                     'duration_wk':   gap_line.duration_wk,
                     'unknown_wk':    gap_line.unknown_wk,
                     'testing':       gap_line.testing,
-                    'category':      gap_line.category,
+                    'category':      gap_line.category.id,
                 }
                 gap_line_id = gap_line_pool.create(cr, uid, line_vals, context=context)
                 
@@ -70,4 +79,4 @@
 
 gap_analysis_import_from_tmpl()
 
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== added file 'gap_analysis/wizard/line_create_wizard.py'
--- gap_analysis/wizard/line_create_wizard.py	1970-01-01 00:00:00 +0000
+++ gap_analysis/wizard/line_create_wizard.py	2014-05-06 13:30:34 +0000
@@ -0,0 +1,75 @@
+
+
+from osv import osv, fields
+from osv.osv import except_osv
+from tools.translate import _
+
+class line_create_wizard(osv.TransientModel):
+    _name = 'gap_analysis.line_create_wizard'
+
+    _columns = {
+        'gap_id' : fields.many2one('gap_analysis', 'Gap Analysis', required=True),
+        'tmpl' : fields.boolean('', help='This is used to control the gap_id domain presented in the wizard'),
+    }
+
+
+    def create_line(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
+
+        fct_fields     = [
+                             'workloads',
+                             'critical',
+                             'testing',
+                             'duration_wk',
+                             'unknown_wk',
+                         ]
+        
+        fct_rel_fields = [
+                             'category',
+                             'openerp_fct',
+                             'effort',
+                         ]
+
+        line_pool   = self.pool.get('gap_analysis.line')
+        fct_pool    = self.pool.get('gap_analysis.functionality')
+        self_browse = self.browse(cr, uid, ids[0], context=context)
+
+        # If adding to a template, make sure all the functionalities are also templates.
+        if self_browse.gap_id.is_tmpl:
+            tmpl_stats = fct_pool.read(cr, uid, context['active_ids'], ['is_tmpl','name'], context=context)
+            if not all([tmpl_stat['is_tmpl'] for tmpl_stat in tmpl_stats]):
+                non_tmpls = [tmpl_stat['name'] for tmpl_stat in tmpl_stats if not tmpl_stat['is_tmpl']]
+                raise except_osv(
+                                    _('Error: Non-template functionalities may not be added to a Gap Analysis Template.'),
+                                    _('Please configure these functionalities accordingly and retry:\n\n%s') % ('\n\n'.join(non_tmpls))
+                                )
+
+        fct_defs    = fct_pool.read(cr, uid, context['active_ids'], fct_fields + fct_rel_fields, context=context)
+        gap_id      = self_browse.gap_id.id
+        new_params = {'gap_id' : gap_id}
+
+        for fct_def in fct_defs:
+            for rel_field in fct_rel_fields:
+                fct_def[rel_field] = fct_def[rel_field] and fct_def[rel_field][0]
+
+            if fct_def['workloads']:
+                wkld_pool = self.pool.get('gap_analysis.workload')
+                wkld_cps = []
+                for wkld in fct_def['workloads']:
+                    wkld_cp = wkld_pool.copy(cr, uid, wkld, {'fct_id' : False}, context=context)
+                    wkld_cps.append(wkld_cp)
+                fct_def['workloads'] = [(6, 0, wkld_cps)]
+
+            new_params.update(functionality=fct_def['id'])
+
+            fct_def.update(new_params)
+            del fct_def['id']
+            line_pool.create(cr, uid, fct_def, context=context)
+
+        return {'type' : 'ir.actions.act_window_close'}
+
+
+line_create_wizard()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== added file 'gap_analysis/wizard/line_create_wizard_view.xml'
--- gap_analysis/wizard/line_create_wizard_view.xml	1970-01-01 00:00:00 +0000
+++ gap_analysis/wizard/line_create_wizard_view.xml	2014-05-06 13:30:34 +0000
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<openerp>
+    <data>
+        <record id="line_create_wizard_view" model="ir.ui.view">
+            <field name="name">gap.analysis.line.create.wizard</field>
+            <field name="model">gap_analysis.line_create_wizard</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <form string="Create Gap Analysis Lines">
+                    <group col="2" colspan="2">
+                        <field name="tmpl" invisible="1"/>
+                        <field name="gap_id" domain="[('is_tmpl','=',tmpl),'|',('is_tmpl','=',True),('state','=','draft')]"/>
+                    </group>
+                    <group colspan="2"/>
+                    <separator colspan="4"/>
+                    <group colspan="2"/>
+                    <group col="2" colspan="2">
+                        <button icon="gtk-cancel" special="cancel" string="Cancel" colspan="1"/>
+                        <button icon="gtk-apply" name="create_line" string="Create" type="object" colspan="1" default_focus="1"/>
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <record model="ir.actions.act_window" id="action_gap_analysis_mass_add_to_analysis">
+            <field name="name">Add to Analysis</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="view_id" ref="line_create_wizard_view"/>
+            <field name="domain">[]</field>
+            <field name="context">{'default_tmpl' : False}</field>
+            <field name="res_model">gap_analysis.line_create_wizard</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">form</field>
+            <field name="usage">menu</field>
+            <field name="target">new</field>
+        </record>
+
+        <record model="ir.values" id="values_gap_analysis_mass_add_to_analysis">
+            <field name="model_id" ref="gap_analysis.model_gap_analysis_functionality" />
+            <field name="object" eval="1" />
+            <field name="name">Add to Analysis</field>
+            <field name="key2">client_action_multi</field>
+            <field name="value" eval="'ir.actions.act_window,' + str(ref('action_gap_analysis_mass_add_to_analysis'))" />
+            <field name="key">action</field>
+            <field name="model">gap_analysis.functionality</field>
+        </record>
+
+        <record model="ir.actions.act_window" id="action_gap_analysis_mass_add_to_template">
+            <field name="name">Add to Template</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="view_id" ref="line_create_wizard_view"/>
+            <field name="domain">[]</field>
+            <field name="context">{'default_tmpl' : True}</field>
+            <field name="res_model">gap_analysis.line_create_wizard</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">form</field>
+            <field name="usage">menu</field>
+            <field name="target">new</field>
+        </record>
+
+        <record model="ir.values" id="values_gap_analysis_mass_add_to_template">
+            <field name="model_id" ref="gap_analysis.model_gap_analysis_functionality" />
+            <field name="object" eval="1" />
+            <field name="name">Add to Template</field>
+            <field name="key2">client_action_multi</field>
+            <field name="value" eval="'ir.actions.act_window,' + str(ref('action_gap_analysis_mass_add_to_template'))" />
+            <field name="key">action</field>
+            <field name="model">gap_analysis.functionality</field>
+        </record>
+
+    </data>
+</openerp>


Follow ups