[Add new sale_team module]
authorRavish (Open ERP) <rmu@tinyerp.com>
Fri, 28 Mar 2014 09:58:35 +0000 (15:28 +0530)
committerRavish (Open ERP) <rmu@tinyerp.com>
Fri, 28 Mar 2014 09:58:35 +0000 (15:28 +0530)
28 files changed:
addons/crm/__openerp__.py
addons/crm/crm.py
addons/crm/crm_case_section_view.xml
addons/crm/crm_lead.py
addons/crm/res_config_view.xml
addons/crm/security/crm_security.xml
addons/crm/security/ir.model.access.csv
addons/sale/__openerp__.py
addons/sale/report/__init__.py
addons/sale/report/sale_analysis_report.py [new file with mode: 0644]
addons/sale/report/sale_analysis_report_view.xml [new file with mode: 0644]
addons/sale/report/sales_crm_account_invoice_report.py [new file with mode: 0644]
addons/sale/sale.py
addons/sale/sale_view.xml
addons/sale_crm/report/__init__.py
addons/sale_crm/report/sale_report.py [deleted file]
addons/sale_crm/report/sale_report_view.xml [deleted file]
addons/sale_crm/report/sales_crm_account_invoice_report.py [deleted file]
addons/sale_crm/sale_crm.py
addons/sale_crm/sale_crm_view.xml
addons/sale_team/__init__.py [new file with mode: 0644]
addons/sale_team/__openerp__.py [new file with mode: 0644]
addons/sale_team/ir.model.access.csv [new file with mode: 0644]
addons/sale_team/res_config_view.xml [new file with mode: 0644]
addons/sale_team/sale_team.py [new file with mode: 0644]
addons/sale_team/sale_team.xml [new file with mode: 0644]
addons/sale_team/sale_team_config.py [new file with mode: 0644]
addons/sale_team/sale_team_security.xml [new file with mode: 0644]

index 1b0cbed..7d5052c 100644 (file)
@@ -51,6 +51,7 @@ Dashboard for CRM will include:
     'depends': [
         'base_action_rule',
         'base_setup',
+        'sale_team',
         'process',
         'mail',
         'email_template',
index 20e2a6e..eb10e46 100644 (file)
@@ -90,45 +90,8 @@ class crm_case_stage(osv.osv):
 
 class crm_case_section(osv.osv):
     """ Model for sales teams. """
-    _name = "crm.case.section"
-    _inherits = {'mail.alias': 'alias_id'}
-    _inherit = "mail.thread"
+    _inherit = 'crm.case.section'
     _description = "Sales Teams"
-    _order = "complete_name"
-    # number of periods for lead/opportunities/... tracking in salesteam kanban dashboard/kanban view
-    _period_number = 5
-
-    def get_full_name(self, cr, uid, ids, field_name, arg, context=None):
-        return dict(self.name_get(cr, uid, ids, context=context))
-
-    def __get_bar_values(self, cr, uid, obj, domain, read_fields, value_field, groupby_field, context=None):
-        """ Generic method to generate data for bar chart values using SparklineBarWidget.
-            This method performs obj.read_group(cr, uid, domain, read_fields, groupby_field).
-
-            :param obj: the target model (i.e. crm_lead)
-            :param domain: the domain applied to the read_group
-            :param list read_fields: the list of fields to read in the read_group
-            :param str value_field: the field used to compute the value of the bar slice
-            :param str groupby_field: the fields used to group
-
-            :return list section_result: a list of dicts: [
-                                                {   'value': (int) bar_column_value,
-                                                    'tootip': (str) bar_column_tooltip,
-                                                }
-                                            ]
-        """
-        month_begin = date.today().replace(day=1)
-        section_result = [{
-                          'value': 0,
-                          'tooltip': (month_begin + relativedelta.relativedelta(months=-i)).strftime('%B %Y'),
-                          } for i in range(self._period_number - 1, -1, -1)]
-        group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context)
-        pattern = tools.DEFAULT_SERVER_DATE_FORMAT if obj.fields_get(cr, uid, groupby_field)[groupby_field]['type'] == 'date' else tools.DEFAULT_SERVER_DATETIME_FORMAT
-        for group in group_obj:
-            group_begin_date = datetime.strptime(group['__domain'][0][2], pattern)
-            month_delta = relativedelta.relativedelta(month_begin, group_begin_date)
-            section_result[self._period_number - (month_delta.months + 1)] = {'value': group.get(value_field, 0), 'tooltip': group.get(groupby_field, 0)}
-        return section_result
 
     def _get_opportunities_data(self, cr, uid, ids, field_name, arg, context=None):
         """ Get opportunities-related data for salesteam kanban view
@@ -155,25 +118,7 @@ class crm_case_section(osv.osv):
         return res
 
     _columns = {
-        'name': fields.char('Sales Team', size=64, required=True, translate=True),
-        'complete_name': fields.function(get_full_name, type='char', size=256, readonly=True, store=True),
-        'code': fields.char('Code', size=8),
-        'active': fields.boolean('Active', help="If the active field is set to "\
-                        "true, it will allow you to hide the sales team without removing it."),
-        'change_responsible': fields.boolean('Reassign Escalated', help="When escalating to this team override the salesman with the team leader."),
-        'user_id': fields.many2one('res.users', 'Team Leader'),
-        'member_ids': fields.many2many('res.users', 'sale_member_rel', 'section_id', 'member_id', 'Team Members'),
-        'reply_to': fields.char('Reply-To', size=64, help="The email address put in the 'Reply-To' of all emails sent by OpenERP about cases in this sales team"),
-        'parent_id': fields.many2one('crm.case.section', 'Parent Team'),
-        'child_ids': fields.one2many('crm.case.section', 'parent_id', 'Child Teams'),
-        'resource_calendar_id': fields.many2one('resource.calendar', "Working Time", help="Used to compute open days"),
-        'note': fields.text('Description'),
-        'working_hours': fields.float('Working Hours', digits=(16, 2)),
         'stage_ids': fields.many2many('crm.case.stage', 'section_stage_rel', 'section_id', 'stage_id', 'Stages'),
-        'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True,
-                                    help="The email address associated with this team. New emails received will automatically "
-                                         "create new leads assigned to the team."),
-        'color': fields.integer('Color Index'),
         'use_leads': fields.boolean('Leads',
             help="The first contact you get with a potential customer is a lead you qualify before converting it into a real business opportunity. Check this box to manage leads in this sales team."),
 
@@ -190,52 +135,11 @@ class crm_case_section(osv.osv):
         return ids
 
     _defaults = {
-        'active': 1,
         'stage_ids': _get_stage_common,
         'use_leads': True,
     }
 
-    _sql_constraints = [
-        ('code_uniq', 'unique (code)', 'The code of the sales team must be unique !')
-    ]
-
-    _constraints = [
-        (osv.osv._check_recursion, 'Error ! You cannot create recursive Sales team.', ['parent_id'])
-    ]
-
-    def name_get(self, cr, uid, ids, context=None):
-        """Overrides orm name_get method"""
-        if not isinstance(ids, list):
-            ids = [ids]
-        res = []
-        if not ids:
-            return res
-        reads = self.read(cr, uid, ids, ['name', 'parent_id'], context)
-
-        for record in reads:
-            name = record['name']
-            if record['parent_id']:
-                name = record['parent_id'][1] + ' / ' + name
-            res.append((record['id'], name))
-        return res
-
-    def create(self, cr, uid, vals, context=None):
-        if context is None:
-            context = {}
-        create_context = dict(context, alias_model_name='crm.lead', alias_parent_model_name=self._name)
-        section_id = super(crm_case_section, self).create(cr, uid, vals, context=create_context)
-        section = self.browse(cr, uid, section_id, context=context)
-        self.pool.get('mail.alias').write(cr, uid, [section.alias_id.id], {'alias_parent_thread_id': section_id, 'alias_defaults': {'section_id': section_id, 'type': 'lead'}}, context=context)
-        return section_id
-
-    def unlink(self, cr, uid, ids, context=None):
-        # Cascade-delete mail aliases as well, as they should not exist without the sales team.
-        mail_alias = self.pool.get('mail.alias')
-        alias_ids = [team.alias_id.id for team in self.browse(cr, uid, ids, context=context) if team.alias_id]
-        res = super(crm_case_section, self).unlink(cr, uid, ids, context=context)
-        mail_alias.unlink(cr, uid, alias_ids, context=context)
-        return res
-
 class crm_case_categ(osv.osv):
     """ Category of Case """
     _name = "crm.case.categ"
index d3ad226..a56459a 100644 (file)
         <record model="ir.ui.view" id="crm_case_section_salesteams_view_kanban">
             <field name="name">crm.case.section.kanban</field>
             <field name="model">crm.case.section</field>
+            <field name="inherit_id" ref="sale_team.crm_case_section_salesteams_view_kanban"/>
             <field name="arch" type="xml">
-                <kanban version="7.0" class="oe_background_grey">
+            <data>
+                <xpath expr="//field[@name='name']" position="after">
                     <field name="use_leads"/>
-                    <field name="name"/>
-                    <field name="user_id"/>
-                    <field name="member_ids"/>
-                    <field name="note"/>
-                    <field name="alias_id"/>
-                    <field name="color"/>
                     <field name="monthly_open_leads"/>
                     <field name="monthly_planned_revenue"/>
-                    <templates>
-                        <t t-name="kanban-box">
-                            <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click oe_kanban_crm_salesteams">
-                                <div class="oe_dropdown_toggle oe_dropdown_kanban" groups="base.group_sale_manager">
-                                    <span class="oe_e">í</span>
-                                    <ul class="oe_dropdown_menu">
-                                        <li t-if="widget.view.is_action_enabled('edit')"><a type="edit">Sales Teams Settings</a></li>
-                                        <li t-if="widget.view.is_action_enabled('delete')"><a type="delete">Delete</a></li>
-                                        <li t-if="widget.view.is_action_enabled('edit')"><ul class="oe_kanban_colorpicker" data-field="color"/></li>
-                                    </ul>
-                                </div>
-                                <div class="oe_kanban_content">
-                                    <h4 class="oe_center"><field name="name"/></h4>
-                                    <div class="oe_kanban_alias oe_center" t-if="record.use_leads.raw_value and record.alias_id.value">
-                                        <small><span class="oe_e oe_e_alias" style="float: none;">%%</span><t t-raw="record.alias_id.raw_value[1]"/></small>
-                                    </div>
-                                    <div class="oe_items_list">
-                                        <div class="oe_salesteams_leads" t-if="record.use_leads.raw_value">
-                                            <a name="%(crm_case_form_view_salesteams_lead)d" type="action">Leads</a>
-                                            <a name="%(action_report_crm_lead_salesteam)d" type="action" class="oe_sparkline_bar_link">
-                                                <field name="monthly_open_leads" widget="sparkline_bar"
-                                                    options="{'height': '20px', 'barWidth': 4, 'barSpacing': 1, 'delayIn': '3000', 'tooltip_suffix': ' Leads'}">Open Leads per Month<br/>Click to see a detailed analysis of leads.</field>
-                                            </a>
-                                        </div>
-                                        <div class="oe_salesteams_opportunities">
-                                            <a name="%(crm_case_form_view_salesteams_opportunity)d" type="action">Opportunities</a>
-                                            <a name="%(action_report_crm_opportunity_salesteam)d" type="action">
-                                                <field name="monthly_planned_revenue" widget="sparkline_bar"
-                                                    options="{'height': '20px', 'barWidth': '4', 'barSpacing': '1', 'delayIn': '3000', 'tooltip_suffix': ' (Planned Revenue)'}">Planned Revenue per Month<br/>Click to see a detailed analysis of opportunities.</field>
-                                            </a>
-                                        </div>
-                                    </div>
-                                    <div class="oe_clear"></div>
-                                    <div class="oe_kanban_salesteams_avatars">
-                                        <t t-foreach="record.member_ids.raw_value.slice(0,10)" t-as="member">
-                                            <img t-att-src="kanban_image('res.users', 'image_small', member)" t-att-data-member_id="member"/>
-                                        </t>
-                                    </div>
-                                </div>
-                            </div>
-                        </t>
-                    </templates>
-                </kanban>
-            </field>
-        </record>
-
-        <!-- Case Sections Search view -->
-
-        <record id="crm_case_section_salesteams_search" model="ir.ui.view">
-            <field name="name">Case Sections - Search</field>
-            <field name="model">crm.case.section</field>
-            <field name="arch" type="xml">
-                <search string="Salesteams Search">
-                    <field name="name"/>
-                    <field name="parent_id"/>
-                    <field name="user_id"/>
-                    <field name="note"/>
-                    <field name="code"/>
-                    <filter name="personal" string="My Salesteams" domain="['|', ('member_ids', '=', uid), ('user_id', '=', uid)]"/>
-                    <group expand="0" string="Group By...">
-                        <filter string="Team Leader" domain="[]" context="{'group_by':'user_id'}"/>
-                        <filter string="Parent Sales Teams" domain="[]" context="{'group_by':'parent_id'}"/>
-                    </group>
-                </search>
-            </field>
-        </record>
-
-        <!-- Case Sections Action -->
-
-        <record id="crm_case_section_salesteams_act" model="ir.actions.act_window">
-            <field name="name">Sales Teams</field>
-            <field name="res_model">crm.case.section</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">kanban,tree,form</field>
-            <field name="context">{'search_default_personal': True}</field>
-            <field name="view_id" ref="crm_case_section_salesteams_view_kanban"/>
-            <field name="help" type="html">
-                <p class="oe_view_nocontent_create">
-                    Click here to define a new sales team.
-                </p><p>
-                    Use sales team to organize your different salespersons or
-                    departments into separate teams. Each team will work in
-                    its own list of opportunities.
-                </p>
-            </field>
-        </record>
-
-        <!-- Case Sections Form View -->
-
-        <record id="crm_case_section_view_form" model="ir.ui.view">
-            <field name="name">crm.case.section.form</field>
-            <field name="model">crm.case.section</field>
-            <field name="arch" type="xml">
-                <form string="Sales Team" version="7.0">
-                    <sheet>
-                        <div class="oe_title">
-                            <label for="name" class="oe_edit_only" string="Sales team"/>
-                            <h1>
-                                <field name="name" string="Salesteam"/>
-                            </h1>
-                            <div name="options_active">
-                                <field name="use_leads" class="oe_inline"/><label for="use_leads"/>
-                            </div>
-                        </div>
-                        <group>
-                            <group>
-                                <field name="user_id" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_salesman_all_leads']}"/>
-                                <field name="code"/>
-                                <field name="parent_id"/>
-                                <field name="change_responsible"/>
-                                <field name="active"/>
-                            </group>
-                            <group>
-                                <label for="alias_name" string="Email Alias"
-                                        attrs="{'invisible': [('alias_domain', '=', False)]}"/>
-                                <div name="alias_def"
-                                        attrs="{'invisible': [('alias_domain', '=', False)]}">
-                                    <field name="alias_id" class="oe_read_only oe_inline"
-                                            string="Email Alias" required="0"/>
-                                    <div class="oe_edit_only oe_inline" name="edit_alias" style="display: inline;" >
-                                        <field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline" readonly="1"/>
-                                    </div>
-                                </div>
-                                <field name="alias_contact" class="oe_inline"
-                                        string="Accept Emails From"
-                                        attrs="{'invisible': [('alias_domain', '=', False)]}"/>
-                            </group>
-                        </group>
-                        <notebook colspan="4">
-                            <page string="Team Members">
-                                <field name="member_ids" widget="many2many_kanban">
-                                    <kanban quick_create="false" create="true">
-                                        <field name="name"/>
-                                        <templates>
-                                            <t t-name="kanban-box">
-                                                <div style="position: relative">
-                                                    <a t-if="! read_only_mode" type="delete" style="position: absolute; right: 0; padding: 4px; diplay: inline-block">X</a>
-                                                    <div class="oe_module_vignette">
-                                                        <div class="oe_module_desc">
-                                                            <field name="name"/>
-                                                        </div>
-                                                    </div>
-                                                </div>
-                                            </t>
-                                        </templates>
-                                    </kanban>
-                                </field>
-                            </page>
-                            <page string="Stages">
-                                <separator string="Select Stages for this Sales Team"/>
-                                <field name="stage_ids"/>
-                            </page>
-                            <page string="Notes">
-                                <field name="note"/>
-                            </page>
-                        </notebook>
-                    </sheet>
-                    <div class="oe_chatter">
-                        <field name="message_follower_ids" widget="mail_followers" help="Follow this salesteam to automatically track the events associated to users of this team."/>
-                        <field name="message_ids" widget="mail_thread"/>
+                </xpath>
+                <xpath expr="//div[@class='oe_clear']" position="before">
+                <div class="oe_items_list">
+                    <div class="oe_salesteams_leads" t-if="record.use_leads.raw_value">
+                        <a name="%(crm_case_form_view_salesteams_lead)d" type="action">Leads</a>
+                        <a name="%(action_report_crm_lead_salesteam)d" type="action" class="oe_sparkline_bar_link">
+                            <field name="monthly_open_leads" widget="sparkline_bar"
+                                options="{'height': '20px', 'barWidth': 4, 'barSpacing': 1, 'delayIn': '3000', 'tooltip_suffix': ' Leads'}">Open Leads per Month<br/>Click to see a detailed analysis of leads.</field>
+                        </a>
                     </div>
-                </form>
-            </field>
-        </record>
-
-        <!-- Case Sections Tree View -->
-
-        <record id="crm_case_section_view_tree" model="ir.ui.view">
-            <field name="name">crm.case.section.tree</field>
-            <field name="model">crm.case.section</field>
-            <field name="field_parent">child_ids</field>
-            <field name="arch" type="xml">
-                <tree string="Sales Team">
-                    <field name="name"/>
-                    <field name="code"/>
-                    <field name="user_id"/>
-                </tree>
-            </field>
+                    <div class="oe_salesteams_opportunities">
+                        <a name="%(crm_case_form_view_salesteams_opportunity)d" type="action">Opportunities</a>
+                        <a name="%(action_report_crm_opportunity_salesteam)d" type="action">
+                            <field name="monthly_planned_revenue" widget="sparkline_bar"
+                                options="{'height': '20px', 'barWidth': '4', 'barSpacing': '1', 'delayIn': '3000', 'tooltip_suffix': ' (Planned Revenue)'}">Planned Revenue per Month<br/>Click to see a detailed analysis of opportunities.</field>
+                        </a>
+                    </div>
+                </div>
+            </xpath>
+          </data>
+         </field>
         </record>
 
         <!-- Case Sections Action -->
             <field name="res_model">crm.case.section</field>
             <field name="domain">[('parent_id','=',False)]</field>
             <field name="view_type">tree</field>
-            <field name="view_id" ref="crm_case_section_view_tree"/>
+            <field name="view_id" ref="sale_team.crm_case_section_view_tree"/>
         </record>
 
-        <record id="crm_case_section_act" model="ir.actions.act_window">
-            <field name="name">Sales Teams</field>
-            <field name="res_model">crm.case.section</field>
-            <field name="view_type">form</field>
-            <field name="view_id" ref="crm_case_section_view_tree"/>
-            <field name="help" type="html">
-                <p class="oe_view_nocontent_create">
-                    Click here to define a new sales team.
-                </p><p>
-                    Use sales team to organize your different salespersons or
-                    departments into separate teams. Each team will work in
-                    its own list of opportunities.
-                </p>
+    <record model="ir.ui.view" id="sale_team_form_view_in_crm">
+            <field name="name">crm.case.section.form</field>
+            <field name="model">crm.case.section</field>
+            <field name="inherit_id" ref="sale_team.crm_case_section_view_form"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='name']" position="after">
+                    <div name="options_active">
+                        <field name="use_leads" class="oe_inline"/><label for="use_leads"/>
+                    </div>
+                </xpath>
+                <xpath expr="//page[@string='Team Members']" position="after">
+                    <page string="Stages">
+                        <separator string="Select Stages for this Sales Team"/>
+                            <field name="stage_ids"/>
+                    </page>
+               </xpath>
             </field>
         </record>
-
-        <menuitem id="crm.menu_crm_case_section_act"
-            action="crm_case_section_salesteams_act"
-            sequence="1"
-            parent="base.menu_sales"
-            groups="base.group_multi_salesteams"/>
   </data>
 </openerp>
\ No newline at end of file
index 4da0a50..f97b7d0 100644 (file)
@@ -346,7 +346,18 @@ class crm_lead(format_address, osv.osv):
             if section_ids:
                 section_id = section_ids[0]
         return {'value': {'section_id': section_id}}
-
+        
+    def on_change_user(self, cr, uid, ids, user_id, context=None):
+        """ Override of on change user_id on lead/opportunity; when having sale
+            the new logic is :
+            - use user.default_section_id
+            - or fallback on previous behavior """
+        if user_id:
+            user = self.pool.get('res.users').browse(cr, uid, user_id, context=context)
+            if user.default_section_id and user.default_section_id.id:
+                return {'value': {'section_id': user.default_section_id.id}}
+        return super(sale_crm_lead, self).on_change_user(cr, uid, ids, user_id, context=context)
+    
     def stage_find(self, cr, uid, cases, section_id, domain=None, order='sequence', context=None):
         """ Override of the base.stage method
             Parameter of the stage search taken from the lead:
index b964cca..0ef4cfa 100644 (file)
                             </div>
                         </div>
                     </group>
-                    <separator string="Sales Teams"/>
-                    <group>
-                        <label for="id" string="Manage Sales Teams"/>
-                        <div>
-                            <div>
-                                <field name="group_multi_salesteams" class="oe_inline"/>
-                                <label for="group_multi_salesteams"/>
-                            </div>
-                            
-                        </div>
-                    </group>
                    </div>
                 <xpath expr="//group[@name='On Mail Client']" position="before">
                     <group name="default_alias">
index dfc3a56..bd2c3cd 100644 (file)
         <field name="users" eval="[(4, ref('base.user_root'))]"/>
     </record>
 
-    <record id="base.group_mono_salesteams" model="res.groups">
-        <field name="name">Do Not Use Sales Teams</field>
-        <field name="category_id" ref="base.module_category_hidden"/>
-    </record>
-    <record id="base.group_user" model="res.groups">
-        <field name="implied_ids" eval="[(4, ref('base.group_mono_salesteams'))]"/>
-    </record>
-
-    <record id="base.group_multi_salesteams" model="res.groups">
-        <field name="name">Manage Sales Teams</field>
-        <field name="category_id" ref="base.module_category_hidden"/>
-    </record>
-    
+        
     <record id="group_fund_raising" model="res.groups">
         <field name="name">Manage Fund Raising</field>
         <field name="category_id" ref="base.module_category_hidden"/>
index f3c6bdf..fde894e 100644 (file)
@@ -5,15 +5,12 @@ access_crm_segmentation,crm.segmentation,model_crm_segmentation,base.group_sale_
 access_crm_segmentation_line,crm.segmentation.line,model_crm_segmentation_line,base.group_sale_manager,1,1,1,1\r
 access_crm_case_channel_user,crm.case.channel user,model_crm_case_channel,base.group_sale_salesman,1,0,0,0\r
 access_crm_case_channel_manager,crm.case.channel manager,model_crm_case_channel,base.group_sale_manager,1,1,1,1\r
-access_crm_case_section,crm.case.section,model_crm_case_section,base.group_user,1,0,0,0\r
 access_crm_case_categ,crm.case.categ,model_crm_case_categ,base.group_sale_salesman,1,1,1,0\r
 access_crm_lead_manager,crm.lead.manager,model_crm_lead,base.group_sale_manager,1,1,1,1\r
 access_crm_phonecall_manager,crm.phonecall.manager,model_crm_phonecall,base.group_sale_manager,1,1,1,1\r
 access_crm_case_categ,crm.case.categ,model_crm_case_categ,base.group_user,1,0,0,0\r
 access_crm_lead,crm.lead,model_crm_lead,base.group_sale_salesman,1,1,1,0\r
 access_crm_phonecall,crm.phonecall,model_crm_phonecall,base.group_sale_salesman,1,1,1,0\r
-access_crm_case_section_user,crm.case.section.user,model_crm_case_section,base.group_sale_salesman,1,0,0,0\r
-access_crm_case_section_manager,crm.case.section.manager,model_crm_case_section,base.group_sale_manager,1,1,1,1\r
 access_crm_case_stage,crm.case.stage,model_crm_case_stage,,1,0,0,0\r
 access_crm_case_stage_manager,crm.case.stage,model_crm_case_stage,base.group_sale_manager,1,1,1,1\r
 access_crm_case_resource_type_user,crm_case_resource_type user,model_crm_case_resource_type,base.group_sale_salesman,1,1,1,0\r
index bb5bac8..44cbb32 100644 (file)
@@ -59,7 +59,7 @@ The Dashboard for the Sales Manager will include
     'author': 'OpenERP SA',
     'website': 'http://www.openerp.com',
     'images': ['images/sale_dashboard.jpeg','images/Sale_order_line_to_invoice.jpeg','images/sale_order.jpeg','images/sales_analysis.jpeg'],
-    'depends': ['account_voucher'],
+    'depends': ['sale_team','account_voucher'],
     'data': [
         'wizard/sale_make_invoice_advance.xml',
         'wizard/sale_line_invoice.xml',
@@ -73,6 +73,7 @@ The Dashboard for the Sales Manager will include
         'sale_view.xml',
         'res_partner_view.xml',
         'report/sale_report_view.xml',
+        'report/sale_analysis_report_view.xml',
         'process/sale_process.xml',
         'board_sale_view.xml',
         'edi/sale_order_action_data.xml',
index 12d484c..f1d48dd 100644 (file)
@@ -18,7 +18,8 @@
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
-
+import sales_crm_account_invoice_report
 import sale_report
+import sale_analysis_report
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/sale/report/sale_analysis_report.py b/addons/sale/report/sale_analysis_report.py
new file mode 100644 (file)
index 0000000..eca020f
--- /dev/null
@@ -0,0 +1,18 @@
+ # -*- coding: utf-8 -*-
+
+from openerp.osv import fields, osv
+
+
+class sale_report(osv.osv):
+    _inherit = "sale.report"
+    _columns = {
+        'section_id': fields.many2one('crm.case.section', 'Sales Team'),
+    }
+
+    def _select(self):
+        return super(sale_report, self)._select() + ", s.section_id as section_id"
+
+    def _group_by(self):
+        return super(sale_report, self)._group_by() + ", s.section_id"
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/sale/report/sale_analysis_report_view.xml b/addons/sale/report/sale_analysis_report_view.xml
new file mode 100644 (file)
index 0000000..fb62468
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+     <data>
+        <record id="view_order_product_search_sale_crm_inherit" model="ir.ui.view">
+            <field name="name">sale.report.search.sale.crm</field>
+            <field name="model">sale.report</field>
+            <field name="inherit_id" ref="sale.view_order_product_search"/>
+            <field name="arch" type="xml">
+                <filter name="User" position="after">
+                    <separator/>
+                    <filter string="Sales Team" context="{'group_by':'section_id'}"/>
+                </filter>
+            </field>
+        </record>
+     </data>
+</openerp>
\ No newline at end of file
diff --git a/addons/sale/report/sales_crm_account_invoice_report.py b/addons/sale/report/sales_crm_account_invoice_report.py
new file mode 100644 (file)
index 0000000..c12db9b
--- /dev/null
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.osv import fields,osv
+
+class account_invoice_report(osv.osv):
+    _inherit = 'account.invoice.report'
+    _columns = {
+        'section_id': fields.many2one('crm.case.section', 'Sales Team'),
+    }
+
+    def _select(self):
+        return  super(account_invoice_report, self)._select() + ", sub.section_id as section_id"
+
+    def _sub_select(self):
+        return  super(account_invoice_report, self)._sub_select() + ", ai.section_id as section_id"
+
+    def _group_by(self):
+        return super(account_invoice_report, self)._group_by() + ", ai.section_id"
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index 667f15a..93e4d90 100644 (file)
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
-
-from datetime import datetime, timedelta
+import calendar
+from openerp import tools
+from datetime import date, datetime, timedelta
 from dateutil.relativedelta import relativedelta
+from dateutil import relativedelta
 import time
 from openerp.osv import fields, osv
 from openerp.tools.translate import _
@@ -162,6 +164,11 @@ class sale_order(osv.osv):
         if not company_id:
             raise osv.except_osv(_('Error!'), _('There is no default company for the current user!'))
         return company_id
+    
+    def _get_default_section_id(self, cr, uid, context=None):
+        """ Gives default section by checking if present in the context """
+        section_id = self.pool.get('res.users').browse(cr, uid, uid, context).default_section_id.id or False
+        return section_id
 
     _columns = {
         'name': fields.char('Order Reference', size=64, required=True,
@@ -226,6 +233,7 @@ class sale_order(osv.osv):
         'payment_term': fields.many2one('account.payment.term', 'Payment Term'),
         'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position'),
         'company_id': fields.many2one('res.company', 'Company'),
+        'section_id': fields.many2one('crm.case.section', 'Sales Team'),
     }
     _defaults = {
         'date_order': fields.date.context_today,
@@ -237,7 +245,9 @@ class sale_order(osv.osv):
         'invoice_quantity': 'order',
         'partner_invoice_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['invoice'])['invoice'],
         'partner_shipping_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['delivery'])['delivery'],
-        'note': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.sale_note
+        'note': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.sale_note,
+        'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c),
+        
     }
     _sql_constraints = [
         ('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'),
@@ -372,12 +382,14 @@ class sale_order(osv.osv):
             'fiscal_position': order.fiscal_position.id or order.partner_id.property_account_position.id,
             'date_invoice': context.get('date_invoice', False),
             'company_id': order.company_id.id,
-            'user_id': order.user_id and order.user_id.id or False
+            'user_id': order.user_id and order.user_id.id or False,
+            'section_id' : order.section_id.id
         }
 
         # Care for deprecated _inv_get() hook - FIXME: to be removed after 6.1
         invoice_vals.update(self._inv_get(cr, uid, order, context=context))
         return invoice_vals
+        
 
     def _make_invoice(self, cr, uid, order, lines, context=None):
         inv_obj = self.pool.get('account.invoice')
@@ -1000,11 +1012,14 @@ class mail_compose_message(osv.Model):
             context = dict(context, mail_post_autofollow=True)
             self.pool.get('sale.order').signal_quotation_sent(cr, uid, [context['default_res_id']])
         return super(mail_compose_message, self).send_mail(cr, uid, ids, context=context)
-
+        
 
 class account_invoice(osv.Model):
     _inherit = 'account.invoice'
-
+    _columns = {
+        'section_id': fields.many2one('crm.case.section', 'Sales Team'),
+    }
+    
     def confirm_paid(self, cr, uid, ids, context=None):
         sale_order_obj = self.pool.get('sale.order')
         res = super(account_invoice, self).confirm_paid(cr, uid, ids, context=context)
@@ -1023,5 +1038,59 @@ class account_invoice(osv.Model):
             for id in ids:
                 workflow.trg_validate(uid, 'account.invoice', id, 'invoice_cancel', cr)
         return super(account_invoice, self).unlink(cr, uid, ids, context=context)
+        
+    _defaults = {
+       'section_id': lambda self, cr, uid, c=None: self.pool.get('res.users').browse(cr, uid, uid, c).default_section_id.id or False,
+    }
+        
+class crm_case_section(osv.osv):
+    _inherit = 'crm.case.section'
+
+    def _get_sale_orders_data(self, cr, uid, ids, field_name, arg, context=None):
+        obj = self.pool.get('sale.order')
+        res = dict.fromkeys(ids, False)
+        month_begin = date.today().replace(day=1)
+        date_begin = (month_begin - relativedelta.relativedelta(months=self._period_number - 1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
+        date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
+        for id in ids:
+            res[id] = dict()
+            created_domain = [('section_id', '=', id), ('state', '=', ['draft']), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)]
+            res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context)
+            validated_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'sent', 'cancel']), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)]
+            res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context)
+        return res
+
+    def _get_invoices_data(self, cr, uid, ids, field_name, arg, context=None):
+        obj = self.pool.get('account.invoice.report')
+        res = dict.fromkeys(ids, False)
+        month_begin = date.today().replace(day=1)
+        date_begin = (month_begin - relativedelta.relativedelta(months=self._period_number - 1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
+        date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
+        for id in ids:
+            created_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', date_begin), ('date', '<=', date_end)]
+            res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date'], 'price_total', 'date', context=context)
+        return res
+
+    _columns = {
+        'invoiced_forecast': fields.integer(string='Invoice Forecast',
+            help="Forecast of the invoice revenue for the current month. This is the amount the sales \n"
+                    "team should invoice this month. It is used to compute the progression ratio \n"
+                    " of the current and forecast revenue on the kanban view."),
+        'invoiced_target': fields.integer(string='Invoice Target',
+            help="Target of invoice revenue for the current month. This is the amount the sales \n"
+                    "team estimates to be able to invoice this month."),
+        'monthly_quoted': fields.function(_get_sale_orders_data,
+            type='string', readonly=True, multi='_get_sale_orders_data',
+            string='Rate of created quotation per duration'),
+        'monthly_confirmed': fields.function(_get_sale_orders_data,
+            type='string', readonly=True, multi='_get_sale_orders_data',
+            string='Rate of validate sales orders per duration'),
+        'monthly_invoiced': fields.function(_get_invoices_data,
+            type='string', readonly=True,
+            string='Rate of sent invoices per duration'),
+    }
+
+    def action_forecast(self, cr, uid, id, value, context=None):
+        return self.write(cr, uid, [id], {'invoiced_forecast': round(float(value))}, context=context)
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index c9ffb94..9a6ef2a 100644 (file)
                             <group>
                                 <group name="sales_person" groups="base.group_user">
                                     <field name="user_id" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'account.group_account_invoice', 'base.group_sale_salesman_all_leads']}"/>
+                                    <field name="section_id" options="{'no_create': True}" groups="base.group_multi_salesteams"/>
                                     <field groups="base.group_no_one" name="origin"/>
                                 </group>
                                 <group name="sale_pay">
                     <filter string="My" domain="[('user_id','=',uid)]" help="My Sales Orders" icon="terp-personal" name="my_sale_orders_filter"/>
                     <field name="partner_id" filter_domain="[('partner_id', 'child_of', self)]"/>
                     <field name="user_id"/>
+                    <field name="section_id" string="Sales Team" groups="base.group_multi_salesteams"/>
                     <field name="project_id"/>
                     <group expand="0" string="Group By...">
                         <filter string="Customer" icon="terp-personal" domain="[]" context="{'group_by':'partner_id'}"/>
                         <filter string="Salesperson" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
                         <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
                         <filter string="Order Month" icon="terp-go-month" domain="[]" context="{'group_by':'date_order'}"/>
+                        <filter string="My Sales Team(s)" icon="terp-personal+" domain="[('section_id.user_id','=',uid)]" help="My Sales Team(s)"  groups="base.group_multi_salesteams"/>
                     </group>
                </search>
             </field>
                 </xpath>
             </field>
         </record>
+        
+       <!-- Update account invoice list view!-->
+        <record model="ir.ui.view" id="account_invoice_tree">
+            <field name="name">Account Invoice</field>
+            <field name="model">account.invoice</field>
+            <field name="inherit_id" ref="account.invoice_tree"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//field[@name='user_id']" position="after">
+                        <field name="section_id" string="Sales Team" groups="base.group_multi_salesteams"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+
+        <!-- Update account invoice search view!-->
+        <record id="account_invoice_groupby_inherit" model="ir.ui.view">
+            <field name="name">account.invoice.groupby</field>
+            <field name="model">account.invoice</field>
+            <field name="inherit_id" ref="account.view_account_invoice_filter"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='user_id']" position="after">
+                    <field name="section_id"/>
+                </xpath>
+                <xpath expr="//group/filter[@string='Due Month']" position="after">
+                    <filter string="Sales Team" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
+                </xpath>
+            </field>
+        </record>
+
+        <!-- Update account invoice !-->
+        <record model="ir.ui.view" id="account_invoice_form">
+            <field name="name">Account Invoice</field>
+            <field name="model">account.invoice</field>
+            <field name="inherit_id" ref="account.invoice_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//field[@name='user_id']" position="after">
+                        <field name="section_id" groups="base.group_multi_salesteams"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+
+       
+        <!-- search by Salesteams -->
+
+        <record id="action_orders_salesteams" model="ir.actions.act_window">
+            <field name="name">Sales Orders</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="res_model">sale.order</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form,calendar,graph</field>
+            <field name="search_view_id" ref="sale.view_sales_order_filter"/>
+            <field name="domain">[('state','not in',('draft','sent','cancel'))]</field>
+            <field name="context">{
+                    'search_default_section_id': [active_id],
+                    'default_section_id': active_id,
+                }
+            </field>
+            <field name="help" type="html">
+              <p class="oe_view_nocontent_create">
+                Click to create a quotation that can be converted into a sales
+                order.
+              </p><p>
+                OpenERP will help you efficiently handle the complete sales flow:
+                quotation, sales order, delivery, invoicing and payment.
+              </p>
+            </field>
+        </record>
+
+        <record id="action_quotations_salesteams" model="ir.actions.act_window">
+            <field name="name">Quotations</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="res_model">sale.order</field>
+            <field name="view_type">form</field>
+            <field name="view_id" ref="sale.view_quotation_tree"/>
+            <field name="view_mode">tree,form,calendar,graph</field>
+            <field name="context">{
+                    'search_default_section_id': [active_id],
+                    'default_section_id': active_id,
+                    'show_address': 1,
+                }
+            </field>
+            <field name="domain">[('state','in',('draft','sent','cancel'))]</field>
+            <field name="search_view_id" ref="sale.view_sales_order_filter"/>
+            <field name="help" type="html">
+              <p class="oe_view_nocontent_create">
+                Click to create a quotation, the first step of a new sale.
+              </p><p>
+                OpenERP will help you handle efficiently the complete sale flow:
+                from the quotation to the sales order, the
+                delivery, the invoicing and the payment collection.
+              </p><p>
+                The social feature helps you organize discussions on each sales
+                order, and allow your customers to keep track of the evolution
+                of the sales order.
+              </p>
+            </field>
+        </record>
+
+        <record id="action_invoice_salesteams" model="ir.actions.act_window">
+            <field name="name">Invoices</field>
+            <field name="res_model">account.invoice</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form,calendar,graph</field>
+            <field name="view_id" ref="account.invoice_tree"/>
+            <field name="domain">[
+                ('state', 'not in', ['draft', 'cancel']),
+                ('type', '=', 'out_invoice')]</field>
+            <field name="context">{
+                    'search_default_section_id': [active_id],
+                    'default_section_id': active_id,
+                    'default_type':'out_invoice',
+                    'type':'out_invoice',
+                    'journal_type': 'sale',
+                }
+            </field>
+            <field name="search_view_id" ref="account.view_account_invoice_filter"/>
+        </record>
+
+        <record id="action_invoice_salesteams_view_tree" model="ir.actions.act_window.view">
+            <field name="sequence">1</field>
+            <field name="view_mode">tree</field>
+            <field name="act_window_id" ref="sale.action_invoice_salesteams"/>
+        </record>
+
+        <record id="action_invoice_salesteams_view_form" model="ir.actions.act_window.view">
+            <field name="sequence">2</field>
+            <field name="view_mode">form</field>
+            <field name="view_id" ref="account.invoice_form"/>
+            <field name="act_window_id" ref="sale.action_invoice_salesteams"/>
+        </record>
+
+        <record id="action_order_report_quotation_salesteam" model="ir.actions.act_window">
+            <field name="name">Quotations Analysis</field>
+            <field name="res_model">sale.report</field>
+            <field name="view_mode">graph</field>
+            <field name="domain">[('state','=','draft'),('section_id', '=', active_id)]</field>
+            <field name="context">{'search_default_order_month':1}</field>
+            <field name="help">This report performs analysis on your quotations. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application.</field>
+        </record>
+
+        <record id="action_order_report_so_salesteam" model="ir.actions.act_window">
+            <field name="name">Sales Analysis</field>
+            <field name="res_model">sale.report</field>
+            <field name="view_mode">graph</field>
+            <field name="domain">[('state','not in',('draft','sent','cancel')),('section_id', '=', active_id)]</field>
+            <field name="context">{'search_default_order_month':1}</field>
+            <field name="help">This report performs analysis on your sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application.</field>
+        </record>
+
+        <record id="action_account_invoice_report_salesteam" model="ir.actions.act_window">
+            <field name="name">Invoices Analysis</field>
+            <field name="res_model">account.invoice.report</field>
+            <field name="view_mode">graph</field>
+            <field name="domain">[('section_id', '=', active_id),('state', 'not in', ['draft', 'cancel'])]</field>
+            <field name="context">{'search_default_month':1}</field>
+            <field name="help">From this report, you can have an overview of the amount invoiced to your customer. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs.</field>
+        </record>
+
+        <record id="crm_case_section_salesteams_view_form" model="ir.ui.view">
+            <field name="name">crm.case.section.form</field>
+            <field name="model">crm.case.section</field>
+            <field name="inherit_id" ref="sale_team.crm_case_section_view_form"/>
+            <field name="arch" type="xml">
+            <data>
+                <xpath expr="//field[@name='code']" position="after">
+                    <field name="invoiced_target"/>
+                    <field name="invoiced_forecast"/>
+                </xpath>
+            </data>
+            </field>
+        </record>
+
+        <record id="crm_case_section_salesteams_view_kanban" model="ir.ui.view">
+            <field name="name">crm.case.section.kanban</field>
+            <field name="model">crm.case.section</field>
+            <field name="inherit_id" ref="sale_team.crm_case_section_salesteams_view_kanban"/>
+            <field name="arch" type="xml">
+            <data>
+                <xpath expr="//field[@name='name']" position="after">
+                    <field name="monthly_quoted"/>
+                    <field name="monthly_confirmed"/>
+                    <field name="monthly_invoiced"/>
+                    <field name="invoiced_forecast"/>
+                    <field name="invoiced_target"/>
+                </xpath>
+                <xpath expr="//div[@class='oe_clear']" position="before">
+                    <div class="oe_items_list">
+                         <div class="oe_salesteams_quotations">
+                            <a name="%(action_quotations_salesteams)d" type="action" class="oe_sparkline_bar_link">Quotations</a>
+                            <a name="%(action_order_report_quotation_salesteam)d" type="action" class="oe_sparkline_bar_link">
+                                <field name="monthly_quoted" widget="sparkline_bar" options="{'delayIn': '3000'}">
+                                    Revenue of created quotations per month.<br/>Click to see a detailed analysis.
+                                </field>
+                            </a>
+                        </div>
+                    </div>
+                    <div class="oe_items_list">
+                        <div class="oe_salesteams_orders">
+                            <a name="%(action_orders_salesteams)d" type="action">Sales Orders</a>
+                            <a name="%(action_order_report_so_salesteam)d" type="action" class="oe_sparkline_bar_link">
+                                <field name="monthly_confirmed" widget="sparkline_bar" options="{'delayIn': '3000'}">
+                                    Revenue of confirmed sales orders per month.<br/>Click to acces the Sales Analysis.
+                                </field>
+                            </a>
+                        </div>
+                    </div>
+                    <div class="oe_items_list">
+                        <div class="oe_salesteams_invoices" groups="account.group_account_invoice">
+                            <a name="%(action_invoice_salesteams)d" type="action">Invoices</a>
+                            <a name="%(action_account_invoice_report_salesteam)d" type="action" class="oe_sparkline_bar_link">
+                                <field name="monthly_invoiced" widget="sparkline_bar" options="{'delayIn': '3000'}">
+                                    Revenue of sent invoices per month.<br/>Click to see a detailed analysis of invoices.
+                                </field>
+                            </a>
+                        </div>
+                    </div>
+                </xpath>
+                <xpath expr="//div[@class='oe_clear']" position="after">
+                    <div class="oe_center" t-if="record.invoiced_target.raw_value">
+                        <field name="monthly_invoiced" widget="gauge" style="width:160px; height: 120px; cursor: pointer;"
+                            options="{'max_field': 'invoiced_target'}">Invoiced</field>
+                        <field name="invoiced_forecast" widget="gauge" style="width:160px; height: 120px; cursor: pointer;"
+                            options="{'max_field': 'invoiced_target', 'on_change': 'action_forecast'}">Forecast</field>
+                    </div>
+                    <div class="oe_center oe_salesteams_help" style="color:#bbbbbb;" t-if="!record.invoiced_target.raw_value">
+                        <br/>Define an invoicing target in the sales team settings to see the period's achievement and forecast at a glance.
+                    </div>
+                </xpath>
+            </data>
+           </field>
+        </record>
     </data>
 </openerp>
index d86b3e6..3fd45ac 100644 (file)
@@ -19,8 +19,8 @@
 #
 ##############################################################################
 
-import sales_crm_account_invoice_report
-import sale_report
+
+
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
 
diff --git a/addons/sale_crm/report/sale_report.py b/addons/sale_crm/report/sale_report.py
deleted file mode 100644 (file)
index eca020f..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
- # -*- coding: utf-8 -*-
-
-from openerp.osv import fields, osv
-
-
-class sale_report(osv.osv):
-    _inherit = "sale.report"
-    _columns = {
-        'section_id': fields.many2one('crm.case.section', 'Sales Team'),
-    }
-
-    def _select(self):
-        return super(sale_report, self)._select() + ", s.section_id as section_id"
-
-    def _group_by(self):
-        return super(sale_report, self)._group_by() + ", s.section_id"
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/sale_crm/report/sale_report_view.xml b/addons/sale_crm/report/sale_report_view.xml
deleted file mode 100644 (file)
index fb62468..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-     <data>
-        <record id="view_order_product_search_sale_crm_inherit" model="ir.ui.view">
-            <field name="name">sale.report.search.sale.crm</field>
-            <field name="model">sale.report</field>
-            <field name="inherit_id" ref="sale.view_order_product_search"/>
-            <field name="arch" type="xml">
-                <filter name="User" position="after">
-                    <separator/>
-                    <filter string="Sales Team" context="{'group_by':'section_id'}"/>
-                </filter>
-            </field>
-        </record>
-     </data>
-</openerp>
\ No newline at end of file
diff --git a/addons/sale_crm/report/sales_crm_account_invoice_report.py b/addons/sale_crm/report/sales_crm_account_invoice_report.py
deleted file mode 100644 (file)
index c12db9b..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-#    OpenERP, Open Source Management Solution
-#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
-#
-#    This program is free software: you can redistribute it and/or modify
-#    it under the terms of the GNU Affero General Public License as
-#    published by the Free Software Foundation, either version 3 of the
-#    License, or (at your option) any later version.
-#
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU Affero General Public License for more details.
-#
-#    You should have received a copy of the GNU Affero General Public License
-#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-from openerp.osv import fields,osv
-
-class account_invoice_report(osv.osv):
-    _inherit = 'account.invoice.report'
-    _columns = {
-        'section_id': fields.many2one('crm.case.section', 'Sales Team'),
-    }
-
-    def _select(self):
-        return  super(account_invoice_report, self)._select() + ", sub.section_id as section_id"
-
-    def _sub_select(self):
-        return  super(account_invoice_report, self)._sub_select() + ", ai.section_id as section_id"
-
-    def _group_by(self):
-        return super(account_invoice_report, self)._group_by() + ", ai.section_id"
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index 9bcb5ad..510daf3 100644 (file)
@@ -26,117 +26,11 @@ from dateutil import relativedelta
 from openerp import tools
 from openerp.osv import osv, fields
 
-class res_users(osv.Model):
-    _inherit = 'res.users'
-    _columns = {
-        'default_section_id': fields.many2one('crm.case.section', 'Default Sales Team'),
-    }
-
-    def __init__(self, pool, cr):
-        init_res = super(res_users, self).__init__(pool, cr)
-        # duplicate list to avoid modifying the original reference
-        self.SELF_WRITEABLE_FIELDS = list(self.SELF_WRITEABLE_FIELDS)
-        self.SELF_WRITEABLE_FIELDS.extend(['default_section_id'])
-        return init_res
-
 class sale_order(osv.osv):
     _inherit = 'sale.order'
     _columns = {
-        'section_id': fields.many2one('crm.case.section', 'Sales Team'),
         'categ_ids': fields.many2many('crm.case.categ', 'sale_order_category_rel', 'order_id', 'category_id', 'Categories', \
             domain="['|',('section_id','=',section_id),('section_id','=',False), ('object_id.model', '=', 'crm.lead')]", context="{'object_name': 'crm.lead'}")
     }
-
-    def _get_default_section_id(self, cr, uid, context=None):
-        """ Gives default section by checking if present in the context """
-        section_id = self.pool.get('crm.lead')._resolve_section_id_from_context(cr, uid, context=context) or False
-        if not section_id:
-            section_id = self.pool.get('res.users').browse(cr, uid, uid, context).default_section_id.id or False
-        return section_id
-
-    _defaults = {
-        'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c),
-    }
-
-    def _prepare_invoice(self, cr, uid, order, lines, context=None):
-        invoice_vals = super(sale_order, self)._prepare_invoice(cr, uid, order, lines, context=context)
-        if order.section_id and order.section_id.id:
-            invoice_vals['section_id'] = order.section_id.id
-        return invoice_vals
-
-
-class crm_case_section(osv.osv):
-    _inherit = 'crm.case.section'
-
-    def _get_sale_orders_data(self, cr, uid, ids, field_name, arg, context=None):
-        obj = self.pool.get('sale.order')
-        res = dict.fromkeys(ids, False)
-        month_begin = date.today().replace(day=1)
-        date_begin = (month_begin - relativedelta.relativedelta(months=self._period_number - 1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
-        date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
-        for id in ids:
-            res[id] = dict()
-            created_domain = [('section_id', '=', id), ('state', '=', ['draft']), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)]
-            res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context)
-            validated_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'sent', 'cancel']), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)]
-            res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context)
-        return res
-
-    def _get_invoices_data(self, cr, uid, ids, field_name, arg, context=None):
-        obj = self.pool.get('account.invoice.report')
-        res = dict.fromkeys(ids, False)
-        month_begin = date.today().replace(day=1)
-        date_begin = (month_begin - relativedelta.relativedelta(months=self._period_number - 1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
-        date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
-        for id in ids:
-            created_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', date_begin), ('date', '<=', date_end)]
-            res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date'], 'price_total', 'date', context=context)
-        return res
-
-    _columns = {
-        'invoiced_forecast': fields.integer(string='Invoice Forecast',
-            help="Forecast of the invoice revenue for the current month. This is the amount the sales \n"
-                    "team should invoice this month. It is used to compute the progression ratio \n"
-                    " of the current and forecast revenue on the kanban view."),
-        'invoiced_target': fields.integer(string='Invoice Target',
-            help="Target of invoice revenue for the current month. This is the amount the sales \n"
-                    "team estimates to be able to invoice this month."),
-        'monthly_quoted': fields.function(_get_sale_orders_data,
-            type='string', readonly=True, multi='_get_sale_orders_data',
-            string='Rate of created quotation per duration'),
-        'monthly_confirmed': fields.function(_get_sale_orders_data,
-            type='string', readonly=True, multi='_get_sale_orders_data',
-            string='Rate of validate sales orders per duration'),
-        'monthly_invoiced': fields.function(_get_invoices_data,
-            type='string', readonly=True,
-            string='Rate of sent invoices per duration'),
-    }
-
-    def action_forecast(self, cr, uid, id, value, context=None):
-        return self.write(cr, uid, [id], {'invoiced_forecast': round(float(value))}, context=context)
-
-class sale_crm_lead(osv.Model):
-    _inherit = 'crm.lead'
-
-    def on_change_user(self, cr, uid, ids, user_id, context=None):
-        """ Override of on change user_id on lead/opportunity; when having sale
-            the new logic is :
-            - use user.default_section_id
-            - or fallback on previous behavior """
-        if user_id:
-            user = self.pool.get('res.users').browse(cr, uid, user_id, context=context)
-            if user.default_section_id and user.default_section_id.id:
-                return {'value': {'section_id': user.default_section_id.id}}
-        return super(sale_crm_lead, self).on_change_user(cr, uid, ids, user_id, context=context)
-
-
-class account_invoice(osv.osv):
-    _inherit = 'account.invoice'
-    _columns = {
-        'section_id': fields.many2one('crm.case.section', 'Sales Team'),
-    }
-    _defaults = {
-        'section_id': lambda self, cr, uid, c=None: self.pool.get('res.users').browse(cr, uid, uid, c).default_section_id.id or False,
-    }
-
+    
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index 88d7fe8..102eb94 100644 (file)
             <field name="inherit_id" ref="sale.view_order_form"/>
             <field name="arch" type="xml">
                 <field name="user_id" position="after">
-                    <field name="section_id" options="{'no_create': True}" groups="base.group_multi_salesteams"/>
                     <field name="categ_ids" widget="many2many_tags"/>
                 </field>
             </field>
         </record>
-
-        <record id="view_sales_order_filter_inherit" model="ir.ui.view">
-            <field name="name">sale.order.list.select</field>
-            <field name="model">sale.order</field>
-            <field name="inherit_id" ref="sale.view_sales_order_filter"/>
-            <field name="arch" type="xml">
-                <xpath expr="//filter[@name='my_sale_orders_filter']" position="after">
-                    <separator/>
-                    <filter string="My Sales Team(s)"
-                        icon="terp-personal+"
-                        domain="[('section_id.user_id','=',uid)]"
-                        help="My Sales Team(s)"  groups="base.group_multi_salesteams"/>
-                </xpath>
-                <xpath expr="//field[@name='user_id']" position="after">
-                    <field name="section_id" string="Sales Team" groups="base.group_multi_salesteams"/>
-                </xpath>
-            </field>
-        </record>
-
-        <!-- Update account invoice list view!-->
-        <record model="ir.ui.view" id="account_invoice_tree">
-            <field name="name">Account Invoice</field>
-            <field name="model">account.invoice</field>
-            <field name="inherit_id" ref="account.invoice_tree"/>
-            <field name="arch" type="xml">
-                <data>
-                    <xpath expr="//field[@name='user_id']" position="after">
-                        <field name="section_id" string="Sales Team" groups="base.group_multi_salesteams"/>
-                    </xpath>
-                </data>
-            </field>
-        </record>
-
-        <!-- Update account invoice search view!-->
-        <record id="account_invoice_groupby_inherit" model="ir.ui.view">
-            <field name="name">account.invoice.groupby</field>
-            <field name="model">account.invoice</field>
-            <field name="inherit_id" ref="account.view_account_invoice_filter"/>
-            <field name="arch" type="xml">
-                <xpath expr="//field[@name='user_id']" position="after">
-                    <field name="section_id"/>
-                </xpath>
-                <xpath expr="//group/filter[@string='Due Month']" position="after">
-                    <filter string="Sales Team" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
-                </xpath>
-            </field>
-        </record>
-
-        <!-- Update account invoice !-->
-        <record model="ir.ui.view" id="account_invoice_form">
-            <field name="name">Account Invoice</field>
-            <field name="model">account.invoice</field>
-            <field name="inherit_id" ref="account.invoice_form"/>
-            <field name="arch" type="xml">
-                <data>
-                    <xpath expr="//field[@name='user_id']" position="after">
-                        <field name="section_id" groups="base.group_multi_salesteams"/>
-                    </xpath>
-                </data>
-            </field>
-        </record>
-
-        <!-- Update user form !-->
-        <record model="ir.ui.view" id="res_user_form">
-            <field name="name">Users Preferences</field>
-            <field name="model">res.users</field>
-            <field name="inherit_id" ref="base.view_users_form"/>
-            <field name="arch" type="xml">
-                <data>
-                    <xpath expr="//field[@name='tz']" position="after">
-                        <field name="default_section_id"/>
-                    </xpath>
-                </data>
-            </field>
-        </record>
-
-        <!-- Update Preferences form !-->
-        <record id="view_users_form_preferences" model="ir.ui.view">
-            <field name="name">res.users.preferences.form</field>
-            <field name="model">res.users</field>
-            <field name="inherit_id" ref="base.view_users_form_simple_modif"/>
-            <field name="arch" type="xml">
-            <data>
-                    <xpath expr="//field[@name='company_id']" position="before">
-                        <field name="default_section_id" readonly="0"/>
-                    </xpath>
-                </data>
-            </field>
-        </record>
-
-        <!-- search by Salesteams -->
-
-        <record id="action_orders_salesteams" model="ir.actions.act_window">
-            <field name="name">Sales Orders</field>
-            <field name="type">ir.actions.act_window</field>
-            <field name="res_model">sale.order</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">tree,form,calendar,graph</field>
-            <field name="search_view_id" ref="sale.view_sales_order_filter"/>
-            <field name="domain">[('state','not in',('draft','sent','cancel'))]</field>
-            <field name="context">{
-                    'search_default_section_id': [active_id],
-                    'default_section_id': active_id,
-                }
-            </field>
-            <field name="help" type="html">
-              <p class="oe_view_nocontent_create">
-                Click to create a quotation that can be converted into a sales
-                order.
-              </p><p>
-                OpenERP will help you efficiently handle the complete sales flow:
-                quotation, sales order, delivery, invoicing and payment.
-              </p>
-            </field>
-        </record>
-
-        <record id="action_quotations_salesteams" model="ir.actions.act_window">
-            <field name="name">Quotations</field>
-            <field name="type">ir.actions.act_window</field>
-            <field name="res_model">sale.order</field>
-            <field name="view_type">form</field>
-            <field name="view_id" ref="sale.view_quotation_tree"/>
-            <field name="view_mode">tree,form,calendar,graph</field>
-            <field name="context">{
-                    'search_default_section_id': [active_id],
-                    'default_section_id': active_id,
-                    'show_address': 1,
-                }
-            </field>
-            <field name="domain">[('state','in',('draft','sent','cancel'))]</field>
-            <field name="search_view_id" ref="sale.view_sales_order_filter"/>
-            <field name="help" type="html">
-              <p class="oe_view_nocontent_create">
-                Click to create a quotation, the first step of a new sale.
-              </p><p>
-                OpenERP will help you handle efficiently the complete sale flow:
-                from the quotation to the sales order, the
-                delivery, the invoicing and the payment collection.
-              </p><p>
-                The social feature helps you organize discussions on each sales
-                order, and allow your customers to keep track of the evolution
-                of the sales order.
-              </p>
-            </field>
-        </record>
-
-        <record id="action_invoice_salesteams" model="ir.actions.act_window">
-            <field name="name">Invoices</field>
-            <field name="res_model">account.invoice</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">tree,form,calendar,graph</field>
-            <field name="view_id" ref="account.invoice_tree"/>
-            <field name="domain">[
-                ('state', 'not in', ['draft', 'cancel']),
-                ('type', '=', 'out_invoice')]</field>
-            <field name="context">{
-                    'search_default_section_id': [active_id],
-                    'default_section_id': active_id,
-                    'default_type':'out_invoice',
-                    'type':'out_invoice',
-                    'journal_type': 'sale',
-                }
-            </field>
-            <field name="search_view_id" ref="account.view_account_invoice_filter"/>
-        </record>
-
-        <record id="action_invoice_salesteams_view_tree" model="ir.actions.act_window.view">
-            <field name="sequence">1</field>
-            <field name="view_mode">tree</field>
-            <field name="act_window_id" ref="sale_crm.action_invoice_salesteams"/>
-        </record>
-
-        <record id="action_invoice_salesteams_view_form" model="ir.actions.act_window.view">
-            <field name="sequence">2</field>
-            <field name="view_mode">form</field>
-            <field name="view_id" ref="account.invoice_form"/>
-            <field name="act_window_id" ref="sale_crm.action_invoice_salesteams"/>
-        </record>
-
-        <record id="action_order_report_quotation_salesteam" model="ir.actions.act_window">
-            <field name="name">Quotations Analysis</field>
-            <field name="res_model">sale.report</field>
-            <field name="view_mode">graph</field>
-            <field name="domain">[('state','=','draft'),('section_id', '=', active_id)]</field>
-            <field name="context">{'search_default_order_month':1}</field>
-            <field name="help">This report performs analysis on your quotations. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application.</field>
-        </record>
-
-        <record id="action_order_report_so_salesteam" model="ir.actions.act_window">
-            <field name="name">Sales Analysis</field>
-            <field name="res_model">sale.report</field>
-            <field name="view_mode">graph</field>
-            <field name="domain">[('state','not in',('draft','sent','cancel')),('section_id', '=', active_id)]</field>
-            <field name="context">{'search_default_order_month':1}</field>
-            <field name="help">This report performs analysis on your sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application.</field>
-        </record>
-
-        <record id="action_account_invoice_report_salesteam" model="ir.actions.act_window">
-            <field name="name">Invoices Analysis</field>
-            <field name="res_model">account.invoice.report</field>
-            <field name="view_mode">graph</field>
-            <field name="domain">[('section_id', '=', active_id),('state', 'not in', ['draft', 'cancel'])]</field>
-            <field name="context">{'search_default_month':1}</field>
-            <field name="help">From this report, you can have an overview of the amount invoiced to your customer. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs.</field>
-        </record>
-
-        <record id="crm_case_section_salesteams_view_form" model="ir.ui.view">
-            <field name="name">crm.case.section.form</field>
-            <field name="model">crm.case.section</field>
-            <field name="inherit_id" ref="crm.crm_case_section_view_form"/>
-            <field name="arch" type="xml">
-            <data>
-                <xpath expr="//field[@name='code']" position="after">
-                    <field name="invoiced_target"/>
-                    <field name="invoiced_forecast"/>
-                </xpath>
-            </data>
-            </field>
-        </record>
-
-        <record id="crm_case_section_salesteams_view_kanban" model="ir.ui.view">
-            <field name="name">crm.case.section.kanban</field>
-            <field name="model">crm.case.section</field>
-            <field name="inherit_id" ref="crm.crm_case_section_salesteams_view_kanban"/>
-            <field name="arch" type="xml">
-            <data>
-                <xpath expr="//field[@name='name']" position="after">
-                    <field name="monthly_quoted"/>
-                    <field name="monthly_confirmed"/>
-                    <field name="monthly_invoiced"/>
-                    <field name="invoiced_forecast"/>
-                    <field name="invoiced_target"/>
-                </xpath>
-                <xpath expr="//div[@class='oe_salesteams_leads']" position="after">
-                    <div class="oe_salesteams_orders">
-                        <a name="%(action_orders_salesteams)d" type="action">Sales Orders</a>
-                        <a name="%(action_order_report_so_salesteam)d" type="action" class="oe_sparkline_bar_link">
-                            <field name="monthly_confirmed" widget="sparkline_bar" options="{'delayIn': '3000'}">
-                                Revenue of confirmed sales orders per month.<br/>Click to acces the Sales Analysis.
-                            </field>
-                        </a>
-                    </div>
-                </xpath>
-                <xpath expr="//div[@class='oe_salesteams_opportunities']" position="after">
-                    <div class="oe_salesteams_invoices" groups="account.group_account_invoice">
-                        <a name="%(action_invoice_salesteams)d" type="action">Invoices</a>
-                        <a name="%(action_account_invoice_report_salesteam)d" type="action" class="oe_sparkline_bar_link">
-                            <field name="monthly_invoiced" widget="sparkline_bar" options="{'delayIn': '3000'}">
-                                Revenue of sent invoices per month.<br/>Click to see a detailed analysis of invoices.
-                            </field>
-                        </a>
-                    </div>
-                    <div class="oe_salesteams_quotations">
-                        <a name="%(action_quotations_salesteams)d" type="action" class="oe_sparkline_bar_link">Quotations</a>
-                        <a name="%(action_order_report_quotation_salesteam)d" type="action" class="oe_sparkline_bar_link">
-                            <field name="monthly_quoted" widget="sparkline_bar" options="{'delayIn': '3000'}">
-                                Revenue of created quotations per month.<br/>Click to see a detailed analysis.
-                            </field>
-                        </a>
-                    </div>
-                </xpath>
-                <xpath expr="//div[@class='oe_clear']" position="after">
-                    <div class="oe_center" t-if="record.invoiced_target.raw_value">
-                        <field name="monthly_invoiced" widget="gauge" style="width:160px; height: 120px; cursor: pointer;"
-                            options="{'max_field': 'invoiced_target'}">Invoiced</field>
-                        <field name="invoiced_forecast" widget="gauge" style="width:160px; height: 120px; cursor: pointer;"
-                            options="{'max_field': 'invoiced_target', 'on_change': 'action_forecast'}">Forecast</field>
-                    </div>
-                    <div class="oe_center oe_salesteams_help" style="color:#bbbbbb;" t-if="!record.invoiced_target.raw_value">
-                        <br/>Define an invoicing target in the sales team settings to see the period's achievement and forecast at a glance.
-                    </div>
-                </xpath>
-            </data>
-            </field>
-        </record>
-    </data>
+      </data>
 </openerp>
diff --git a/addons/sale_team/__init__.py b/addons/sale_team/__init__.py
new file mode 100644 (file)
index 0000000..6bac200
--- /dev/null
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+import sale_team
+import sale_team_config
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/sale_team/__openerp__.py b/addons/sale_team/__openerp__.py
new file mode 100644 (file)
index 0000000..9c303ae
--- /dev/null
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+{
+    'name' : 'Sale Team',
+    'version' : '1.0',
+    'author' : 'OpenERP SA',
+    'category': 'hidden',
+    'sequence': 10,
+    'summary': 'Sales Team',
+    'description': """ """,
+    'website': 'http://www.openerp.com',
+    'depends' : ['base','web_kanban','calendar'],
+    'data': ['sale_team.xml','sale_team_security.xml','res_config_view.xml','ir.model.access.csv'],
+    'demo': [],
+    'installable': True,
+    'auto_install': True,
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/sale_team/ir.model.access.csv b/addons/sale_team/ir.model.access.csv
new file mode 100644 (file)
index 0000000..9817ad8
--- /dev/null
@@ -0,0 +1,5 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_crm_case_section,crm.case.section,model_crm_case_section,base.group_user,1,0,0,0
+access_crm_case_section_user,crm.case.section.user,model_crm_case_section,base.group_sale_salesman,1,0,0,0
+access_crm_case_section_manager,crm.case.section.manager,model_crm_case_section,base.group_sale_manager,1,1,1,1
+
diff --git a/addons/sale_team/res_config_view.xml b/addons/sale_team/res_config_view.xml
new file mode 100644 (file)
index 0000000..7723fda
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+
+        <record id="view_sale_config_settings" model="ir.ui.view">
+            <field name="name">crm settings</field>
+            <field name="model">sale.team.config.settings</field>
+            <field name="inherit_id" ref="base_setup.view_sale_config_settings"/>
+            <field name="arch" type="xml">
+                <data>
+                   <div name="config_sale" position="before">
+                    <separator string="Sales Teams"/>
+                    <group>
+                        <label for="id" string="Manage Sales Teams"/>
+                        <div>
+                            <div>
+                                <field name="group_multi_salesteams" class="oe_inline"/>
+                                <label for="group_multi_salesteams"/>
+                            </div>
+                            
+                        </div>
+                    </group>
+                   </div>
+                 </data></field>
+        </record>
+     </data>
+</openerp>
diff --git a/addons/sale_team/sale_team.py b/addons/sale_team/sale_team.py
new file mode 100644 (file)
index 0000000..3643a6d
--- /dev/null
@@ -0,0 +1,153 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+
+import calendar
+from datetime import date, datetime
+from dateutil import relativedelta
+
+from openerp import tools
+from openerp.osv import fields
+from openerp.osv import osv
+
+
+
+class crm_case_section(osv.osv):
+    _name = "crm.case.section"
+    _inherits = {'mail.alias': 'alias_id'}
+    _inherit = ['mail.thread', 'ir.needaction_mixin']
+    _description = "Sales Teams"
+    _order = "complete_name"
+    # number of periods for lead/opportunities/... tracking in salesteam kanban dashboard/kanban view
+    _period_number = 5
+
+    def get_full_name(self, cr, uid, ids, field_name, arg, context=None):
+        return dict(self.name_get(cr, uid, ids, context=context))
+        
+    def __get_bar_values(self, cr, uid, obj, domain, read_fields, value_field, groupby_field, context=None):
+        
+        """ Generic method to generate data for bar chart values using SparklineBarWidget.
+            This method performs obj.read_group(cr, uid, domain, read_fields, groupby_field).
+
+            :param obj: the target model (i.e. crm_lead)
+            :param domain: the domain applied to the read_group
+            :param list read_fields: the list of fields to read in the read_group
+            :param str value_field: the field used to compute the value of the bar slice
+            :param str groupby_field: the fields used to group
+
+            :return list section_result: a list of dicts: [
+                                                {   'value': (int) bar_column_value,
+                                                    'tootip': (str) bar_column_tooltip,
+                                                }
+                                            ]
+        """
+        month_begin = date.today().replace(day=1)
+        section_result = [{
+                          'value': 0,
+                          'tooltip': (month_begin + relativedelta.relativedelta(months=-i)).strftime('%B %Y'),
+                          } for i in range(self._period_number - 1, -1, -1)]
+        group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context)
+        pattern = tools.DEFAULT_SERVER_DATE_FORMAT if obj.fields_get(cr, uid, groupby_field)[groupby_field]['type'] == 'date' else tools.DEFAULT_SERVER_DATETIME_FORMAT
+        for group in group_obj:
+            group_begin_date = datetime.strptime(group['__domain'][0][2], pattern)
+            month_delta = relativedelta.relativedelta(month_begin, group_begin_date)
+            section_result[self._period_number - (month_delta.months + 1)] = {'value': group.get(value_field, 0), 'tooltip': group.get(groupby_field, 0)}
+        return section_result
+   
+    _columns = {
+        'name': fields.char('Sales Team', size=64, required=True, translate=True),
+        'complete_name': fields.function(get_full_name, type='char', size=256, readonly=True, store=True),
+        'code': fields.char('Code', size=8),
+        'active': fields.boolean('Active', help="If the active field is set to "\
+                        "true, it will allow you to hide the sales team without removing it."),
+        'change_responsible': fields.boolean('Reassign Escalated', help="When escalating to this team override the salesman with the team leader."),
+        'user_id': fields.many2one('res.users', 'Team Leader'),
+        'member_ids': fields.many2many('res.users', 'sale_member_rel', 'section_id', 'member_id', 'Team Members'),
+        'reply_to': fields.char('Reply-To', size=64, help="The email address put in the 'Reply-To' of all emails sent by OpenERP about cases in this sales team"),
+        'parent_id': fields.many2one('crm.case.section', 'Parent Team'),
+        'child_ids': fields.one2many('crm.case.section', 'parent_id', 'Child Teams'),
+        'resource_calendar_id': fields.many2one('resource.calendar', "Working Time", help="Used to compute open days"),
+        'note': fields.text('Description'),
+        'working_hours': fields.float('Working Hours', digits=(16, 2)),
+        'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True, help="The email address associated with this team. New emails received will automatically ""create new leads assigned to the team."),
+        'color': fields.integer('Color Index'),
+           }
+   
+    _defaults = {
+        'active': 1,
+    }
+
+    _sql_constraints = [
+        ('code_uniq', 'unique (code)', 'The code of the sales team must be unique !')
+    ]
+
+    _constraints = [
+        (osv.osv._check_recursion, 'Error ! You cannot create recursive Sales team.', ['parent_id'])
+    ]
+
+    def name_get(self, cr, uid, ids, context=None):
+        """Overrides orm name_get method"""
+        if not isinstance(ids, list):
+            ids = [ids]
+        res = []
+        if not ids:
+            return res
+        reads = self.read(cr, uid, ids, ['name', 'parent_id'], context)
+
+        for record in reads:
+            name = record['name']
+            if record['parent_id']:
+                name = record['parent_id'][1] + ' / ' + name
+            res.append((record['id'], name))
+        return res
+        
+    def create(self, cr, uid, vals, context=None):
+        if context is None:
+            context = {}
+        create_context = dict(context, alias_model_name=self._name, alias_parent_model_name=self._name)
+        section_id = super(crm_case_section, self).create(cr, uid, vals, context=create_context)
+        section = self.browse(cr, uid, section_id, context=context)
+        self.pool.get('mail.alias').write(cr, uid, [section.alias_id.id], {'alias_parent_thread_id': section_id, 'alias_defaults': {'section_id': section_id, 'type': 'lead'}}, context=context)
+        return section_id
+
+    def unlink(self, cr, uid, ids, context=None):
+        # Cascade-delete mail aliases as well, as they should not exist without the sales team.
+        mail_alias = self.pool.get('mail.alias')
+        alias_ids = [team.alias_id.id for team in self.browse(cr, uid, ids, context=context) if team.alias_id]
+        res = super(crm_case_section, self).unlink(cr, uid, ids, context=context)
+        mail_alias.unlink(cr, uid, alias_ids, context=context)
+        return res
+        
+class res_users(osv.Model):
+    _inherit = 'res.users'
+    _columns = {
+        'default_section_id': fields.many2one('crm.case.section', 'Default Sales Team'),
+    }
+
+    def __init__(self, pool, cr):
+        init_res = super(res_users, self).__init__(pool, cr)
+        # duplicate list to avoid modifying the original reference
+        self.SELF_WRITEABLE_FIELDS = list(self.SELF_WRITEABLE_FIELDS)
+        self.SELF_WRITEABLE_FIELDS.extend(['default_section_id'])
+        return init_res
+
+    
+      
\ No newline at end of file
diff --git a/addons/sale_team/sale_team.xml b/addons/sale_team/sale_team.xml
new file mode 100644 (file)
index 0000000..687919f
--- /dev/null
@@ -0,0 +1,232 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+  <data>
+
+ <!-- Update user form !-->
+        <record model="ir.ui.view" id="res_user_form">
+            <field name="name">Users Preferences</field>
+            <field name="model">res.users</field>
+            <field name="inherit_id" ref="base.view_users_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//field[@name='tz']" position="after">
+                        <field name="default_section_id"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+
+        <!-- Update Preferences form !-->
+        <record id="view_users_form_preferences" model="ir.ui.view">
+            <field name="name">res.users.preferences.form</field>
+            <field name="model">res.users</field>
+            <field name="inherit_id" ref="base.view_users_form_simple_modif"/>
+            <field name="arch" type="xml">
+            <data>
+                    <xpath expr="//field[@name='company_id']" position="before">
+                        <field name="default_section_id" readonly="0"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+
+        <!-- Case Sections Salesteams kanban view -->
+
+       <record model="ir.ui.view" id="crm_case_section_salesteams_view_kanban">
+            <field name="name">crm.case.section.kanban</field>
+            <field name="model">crm.case.section</field>
+            <field name="arch" type="xml">
+                <kanban version="7.0" class="oe_background_grey">
+                    <field name="name"/>
+                    <field name="user_id"/>
+                    <field name="member_ids"/>
+                    <field name="note"/>
+                    <field name="alias_id"/>
+                    <field name="color"/>
+                    <templates>
+                        <t t-name="kanban-box">
+                            <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click oe_kanban_crm_salesteams">
+                                <div class="oe_dropdown_toggle oe_dropdown_kanban">
+                                    <span class="oe_e">í</span>
+                                    <ul class="oe_dropdown_menu">
+                                        <li t-if="widget.view.is_action_enabled('edit')"><a type="edit">Sales Teams Settings</a></li>
+                                        <li t-if="widget.view.is_action_enabled('delete')"><a type="delete">Delete</a></li>
+                                        <li t-if="widget.view.is_action_enabled('edit')"><ul class="oe_kanban_colorpicker" data-field="color"/></li>
+                                    </ul>
+                                </div>
+                                <div class="oe_kanban_content">
+                                    <h4 class="oe_center"><field name="name"/></h4>
+                                   <div class="oe_kanban_alias oe_center" t-if="record.alias_id.value">
+                                        <small><span class="oe_e oe_e_alias" style="float: none;">%%</span><t t-raw="record.alias_id.raw_value[1]"/></small>
+                                    </div>
+                                    <div class="oe_clear"></div>
+                                    <div class="oe_kanban_salesteams_avatars">
+                                        <t t-foreach="record.member_ids.raw_value.slice(0,10)" t-as="member">
+                                            <img t-att-src="kanban_image('res.users', 'image_small', member)" t-att-data-member_id="member"/>
+                                        </t>
+                                    </div>
+                                </div>
+                            </div>
+                        </t>
+                    </templates>
+                </kanban>
+            </field>
+        </record>
+
+
+        
+        <!-- Case Sections Search view -->
+
+        <record id="crm_case_section_salesteams_search" model="ir.ui.view">
+            <field name="name">Case Sections - Search</field>
+            <field name="model">crm.case.section</field>
+            <field name="arch" type="xml">
+                <search string="Salesteams Search">
+                    <field name="name"/>
+                    <field name="parent_id"/>
+                    <field name="user_id"/>
+                    <field name="note"/>
+                    <field name="code"/>
+                    <filter name="personal" string="My Salesteams" domain="['|', ('member_ids', '=', uid), ('user_id', '=', uid)]"/>
+                    <group expand="0" string="Group By...">
+                        <filter string="Team Leader" domain="[]" context="{'group_by':'user_id'}"/>
+                        <filter string="Parent Sales Teams" domain="[]" context="{'group_by':'parent_id'}"/>
+                    </group>
+                </search>
+            </field>
+        </record>
+
+        <!-- Case Sections Action -->
+
+        <record id="crm_case_section_salesteams_act" model="ir.actions.act_window">
+            <field name="name">Sales Teams</field>
+            <field name="res_model">crm.case.section</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">kanban,tree,form</field>
+            <field name="context">{}</field>
+            <field name="view_id" ref="crm_case_section_salesteams_search"/>
+            <field name="help" type="html">
+                <p class="oe_view_nocontent_create">
+                    Click here to define a new sales team.
+                </p><p>
+                    Use sales team to organize your different salespersons or
+                    departments into separate teams. Each team will work in
+                    its own list of opportunities.
+                </p>
+            </field>
+        </record>
+
+        <!-- Case Sections Form View -->
+
+         <record id="crm_case_section_view_form" model="ir.ui.view">
+            <field name="name">crm.case.section.form</field>
+            <field name="model">crm.case.section</field>
+            <field name="arch" type="xml">
+                <form string="Sales Team" version="7.0">
+                    <sheet>
+                        <div class="oe_title">
+                            <label for="name" class="oe_edit_only" string="Sales team"/>
+                            <h1>
+                                <field name="name" string="Salesteam"/>
+                            </h1>
+                        </div>
+                        <group>
+                            <group>
+                                <field name="user_id" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_salesman_all_leads']}"/>
+                                <field name="code"/>
+                                <field name="parent_id"/>
+                                <field name="change_responsible"/>
+                                <field name="active"/>
+                            </group>
+                            <group>
+                                <label for="alias_name" string="Email Alias"
+                                        attrs="{'invisible': [('alias_domain', '=', False)]}"/>
+                                <div name="alias_def"
+                                        attrs="{'invisible': [('alias_domain', '=', False)]}">
+                                    <field name="alias_id" class="oe_read_only oe_inline"
+                                            string="Email Alias" required="0"/>
+                                    <div class="oe_edit_only oe_inline" name="edit_alias" style="display: inline;" >
+                                        <field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline" readonly="1"/>
+                                    </div>
+                                </div>
+                                <field name="alias_contact" class="oe_inline"
+                                        string="Accept Emails From"
+                                        attrs="{'invisible': [('alias_domain', '=', False)]}"/>
+                            </group>
+                        </group>
+                        <notebook colspan="4">
+                            <page string="Team Members">
+                                <field name="member_ids" widget="many2many_kanban">
+                                    <kanban quick_create="false" create="true">
+                                        <field name="name"/>
+                                        <templates>
+                                            <t t-name="kanban-box">
+                                                <div style="position: relative">
+                                                    <a t-if="! read_only_mode" type="delete" style="position: absolute; right: 0; padding: 4px; diplay: inline-block">X</a>
+                                                    <div class="oe_module_vignette">
+                                                        <div class="oe_module_desc">
+                                                            <field name="name"/>
+                                                        </div>
+                                                    </div>
+                                                </div>
+                                            </t>
+                                        </templates>
+                                    </kanban>
+                                </field>
+                            </page>
+                            <page string="Notes">
+                                <field name="note"/>
+                            </page>
+                        </notebook>
+                    </sheet>
+                    <div class="oe_chatter">
+                        <field name="message_follower_ids" widget="mail_followers" help="Follow this salesteam to automatically track the events associated to users of this team."/>
+                        <field name="message_ids" widget="mail_thread"/>
+                    </div>
+                </form>
+            </field>
+        </record>
+
+        <!-- Case Sections Tree View -->
+
+        <record id="crm_case_section_view_tree" model="ir.ui.view">
+            <field name="name">crm.case.section.tree</field>
+            <field name="model">crm.case.section</field>
+            <field name="field_parent">child_ids</field>
+            <field name="arch" type="xml">
+                <tree string="Sales Team">
+                    <field name="name"/>
+                    <field name="code"/>
+                    <field name="user_id"/>
+                </tree>
+            </field>
+        </record>
+
+        <!-- Case Sections Action -->
+
+        <!--<record model="ir.actions.act_window.view" id="action_crm_tag_kanban_view_salesteams_oppor11">
+            <field name="sequence" eval="0"/>
+            <field name="view_mode">kanban</field>
+            <field name="view_id" ref="crm_case_kanban_view_leads"/>
+            <field name="act_window_id" ref="crm_case_form_view_salesteams_opportunity"/>
+        </record>-->
+
+        <record id="crm_case_section_act" model="ir.actions.act_window">
+            <field name="name">Sales Teams</field>
+            <field name="res_model">crm.case.section</field>
+            <field name="view_type">form</field>
+            <field name="view_id" ref="crm_case_section_view_tree"/>
+            <field name="help" type="html">
+                <p class="oe_view_nocontent_create">
+                    Click here to define a new sales team.
+                </p><p>
+                    Use sales team to organize your different salespersons or
+                    departments into separate teams. Each team will work in
+                    its own list of opportunities.
+                </p>
+            </field>
+        </record>
+        
+    <menuitem id="sale_team.menu_sale_team_act" action="crm_case_section_salesteams_act" sequence="1" parent="base.menu_sales"/>
+  </data>
+</openerp>
\ No newline at end of file
diff --git a/addons/sale_team/sale_team_config.py b/addons/sale_team/sale_team_config.py
new file mode 100644 (file)
index 0000000..33a5e84
--- /dev/null
@@ -0,0 +1,45 @@
+
+from openerp import SUPERUSER_ID
+from openerp.osv import fields, osv
+
+
+class sala_team_configuration(osv.TransientModel):
+    _name = 'sale.team.config.settings'
+    _inherit = ['sale.config.settings', 'fetchmail.config.settings']
+
+    def set_group_multi_salesteams(self, cr, uid, ids, context=None):
+        """ This method is automatically called by res_config as it begins
+            with set. It is used to implement the 'one group or another'
+            behavior. We have to perform some group manipulation by hand
+            because in res_config.execute(), set_* methods are called
+            after group_*; therefore writing on an hidden res_config file
+            could not work.
+            If group_multi_salesteams is checked: remove group_mono_salesteams
+            from group_user, remove the users. Otherwise, just add
+            group_mono_salesteams in group_user.
+            The inverse logic about group_multi_salesteams is managed by the
+            normal behavior of 'group_multi_salesteams' field.
+        """
+        def ref(xml_id):
+            mod, xml = xml_id.split('.', 1)
+            return self.pool['ir.model.data'].get_object(cr, uid, mod, xml, context)
+
+        for obj in self.browse(cr, uid, ids, context=context):
+            config_group = ref('base.group_mono_salesteams')
+            base_group = ref('base.group_user')
+            if obj.group_multi_salesteams:
+                base_group.write({'implied_ids': [(3, config_group.id)]})
+                config_group.write({'users': [(3, u.id) for u in base_group.users]})
+            else:
+                base_group.write({'implied_ids': [(4, config_group.id)]})
+        return True
+
+    _columns = {
+       
+        'group_multi_salesteams': fields.boolean("Organize Sales activities into multiple Sales Teams",
+            implied_group='base.group_multi_salesteams',
+            help="""Allows you to use Sales Teams to manage your leads and opportunities."""),
+    }
+
+
+    
diff --git a/addons/sale_team/sale_team_security.xml b/addons/sale_team/sale_team_security.xml
new file mode 100644 (file)
index 0000000..ff8eef0
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+<data>
+    <record id="base.group_mono_salesteams" model="res.groups">
+        <field name="name">Do Not Use Sales Teams</field>
+        <field name="category_id" ref="base.module_category_hidden"/>
+    </record>
+    <record id="base.group_user" model="res.groups">
+        <field name="implied_ids" eval="[(4, ref('base.group_mono_salesteams'))]"/>
+    </record>
+
+    <record id="base.group_multi_salesteams" model="res.groups">
+        <field name="name">Manage Sales Teams</field>
+        <field name="category_id" ref="base.module_category_hidden"/>
+    </record>
+</data>
+</openerp>
+