[MERGE] Sync with trunk
This commit is contained in:
commit
ae89496bbc
|
@ -0,0 +1,241 @@
|
|||
# German translation for openobject-addons
|
||||
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
|
||||
# This file is distributed under the same license as the openobject-addons package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
|
||||
"PO-Revision-Date: 2014-05-13 09:39+0000\n"
|
||||
"Last-Translator: Claudia Haida <claudia.haida@initos.com>\n"
|
||||
"Language-Team: German <de@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2014-05-14 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17002)\n"
|
||||
|
||||
#. module: account_test
|
||||
#: view:accounting.assert.test:0
|
||||
msgid ""
|
||||
"Code should always set a variable named `result` with the result of your "
|
||||
"test, that can be a list or\n"
|
||||
"a dictionary. If `result` is an empty list, it means that the test was "
|
||||
"succesful. Otherwise it will\n"
|
||||
"try to translate and print what is inside `result`.\n"
|
||||
"\n"
|
||||
"If the result of your test is a dictionary, you can set a variable named "
|
||||
"`column_order` to choose in\n"
|
||||
"what order you want to print `result`'s content.\n"
|
||||
"\n"
|
||||
"Should you need them, you can also use the following variables into your "
|
||||
"code:\n"
|
||||
" * cr: cursor to the database\n"
|
||||
" * uid: ID of the current user\n"
|
||||
"\n"
|
||||
"In any ways, the code must be legal python statements with correct "
|
||||
"indentation (if needed).\n"
|
||||
"\n"
|
||||
"Example: \n"
|
||||
" sql = '''SELECT id, name, ref, date\n"
|
||||
" FROM account_move_line \n"
|
||||
" WHERE account_id IN (SELECT id FROM account_account WHERE type "
|
||||
"= 'view')\n"
|
||||
" '''\n"
|
||||
" cr.execute(sql)\n"
|
||||
" result = cr.dictfetchall()"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,name:account_test.account_test_02
|
||||
msgid "Test 2: Opening a fiscal year"
|
||||
msgstr "Test2: Eröffnung eines Geschäftsjahres"
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,desc:account_test.account_test_05
|
||||
msgid ""
|
||||
"Check that reconciled invoice for Sales/Purchases has reconciled entries for "
|
||||
"Payable and Receivable Accounts"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,desc:account_test.account_test_03
|
||||
msgid ""
|
||||
"Check if movement lines are balanced and have the same date and period"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: field:accounting.assert.test,name:0
|
||||
msgid "Test Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: report:account.test.assert.print:0
|
||||
msgid "Accouting tests on"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,name:account_test.account_test_01
|
||||
msgid "Test 1: General balance"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,desc:account_test.account_test_06
|
||||
msgid "Check that paid/reconciled invoices are not in 'Open' state"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,desc:account_test.account_test_05_2
|
||||
msgid ""
|
||||
"Check that reconciled account moves, that define Payable and Receivable "
|
||||
"accounts, are belonging to reconciled invoices"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: view:accounting.assert.test:0
|
||||
msgid "Tests"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: field:accounting.assert.test,desc:0
|
||||
msgid "Test Description"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: view:accounting.assert.test:0
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,desc:account_test.account_test_06_1
|
||||
msgid "Check that there's no move for any account with « View » account type"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,name:account_test.account_test_08
|
||||
msgid "Test 9 : Accounts and partners on account moves"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:ir.actions.act_window,name:account_test.action_accounting_assert
|
||||
#: model:ir.actions.report.xml,name:account_test.account_assert_test_report
|
||||
#: model:ir.ui.menu,name:account_test.menu_action_license
|
||||
msgid "Accounting Tests"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: code:addons/account_test/report/account_test_report.py:74
|
||||
#, python-format
|
||||
msgid "The test was passed successfully"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: field:accounting.assert.test,active:0
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,name:account_test.account_test_06
|
||||
msgid "Test 6 : Invoices status"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:ir.model,name:account_test.model_accounting_assert_test
|
||||
msgid "accounting.assert.test"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,name:account_test.account_test_05
|
||||
msgid ""
|
||||
"Test 5.1 : Payable and Receivable accountant lines of reconciled invoices"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: field:accounting.assert.test,code_exec:0
|
||||
msgid "Python code"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,desc:account_test.account_test_07
|
||||
msgid ""
|
||||
"Check on bank statement that the Closing Balance = Starting Balance + sum of "
|
||||
"statement lines"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,name:account_test.account_test_07
|
||||
msgid "Test 8 : Closing balance on bank statements"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,name:account_test.account_test_03
|
||||
msgid "Test 3: Movement lines"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,name:account_test.account_test_05_2
|
||||
msgid "Test 5.2 : Reconcilied invoices and Payable/Receivable accounts"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: view:accounting.assert.test:0
|
||||
msgid "Expression"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,name:account_test.account_test_04
|
||||
msgid "Test 4: Totally reconciled mouvements"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,desc:account_test.account_test_04
|
||||
msgid "Check if the totally reconciled movements are balanced"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: field:accounting.assert.test,sequence:0
|
||||
msgid "Sequence"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,desc:account_test.account_test_02
|
||||
msgid ""
|
||||
"Check if the balance of the new opened fiscal year matches with last year's "
|
||||
"balance"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: view:accounting.assert.test:0
|
||||
msgid "Python Code"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:ir.actions.act_window,help:account_test.action_accounting_assert
|
||||
msgid ""
|
||||
"<p class=\"oe_view_nocontent_create\">\n"
|
||||
" Click to create Accounting Test.\n"
|
||||
" </p>\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,desc:account_test.account_test_01
|
||||
msgid "Check the balance: Debit sum = Credit sum"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,desc:account_test.account_test_08
|
||||
msgid "Check that general accounts and partners on account moves are active"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: model:accounting.assert.test,name:account_test.account_test_06_1
|
||||
msgid "Test 7: « View » account type"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_test
|
||||
#: view:accounting.assert.test:0
|
||||
msgid "Code Help"
|
||||
msgstr ""
|
|
@ -1,23 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Business Applications
|
||||
# Copyright (c) 2011-2014 OpenERP S.A. <http://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 pytz
|
||||
import re
|
||||
|
@ -188,7 +169,8 @@ class calendar_attendee(osv.Model):
|
|||
res = cal.serialize()
|
||||
return res
|
||||
|
||||
def _send_mail_to_attendees(self, cr, uid, ids, email_from=tools.config.get('email_from', False), template_xmlid='calendar_template_meeting_invitation', context=None):
|
||||
def _send_mail_to_attendees(self, cr, uid, ids, email_from=tools.config.get('email_from', False),
|
||||
template_xmlid='calendar_template_meeting_invitation', context=None):
|
||||
"""
|
||||
Send mail for event invitation to event attendees.
|
||||
@param email_from: email address for user sending the mail
|
||||
|
@ -273,7 +255,8 @@ class calendar_attendee(osv.Model):
|
|||
meeting_obj = self.pool['calendar.event']
|
||||
res = self.write(cr, uid, ids, {'state': 'accepted'}, context)
|
||||
for attendee in self.browse(cr, uid, ids, context=context):
|
||||
meeting_obj.message_post(cr, uid, attendee.event_id.id, body=_(("%s has accepted invitation") % (attendee.cn)), subtype="calendar.subtype_invitation", context=context)
|
||||
meeting_obj.message_post(cr, uid, attendee.event_id.id, body=_(("%s has accepted invitation") % (attendee.cn)),
|
||||
subtype="calendar.subtype_invitation", context=context)
|
||||
|
||||
return res
|
||||
|
||||
|
@ -1410,7 +1393,7 @@ class calendar_event(osv.Model):
|
|||
new_args.append(new_arg)
|
||||
|
||||
if not context.get('virtual_id', True):
|
||||
return super(calendar_event, self).search(cr, uid, new_args, offset=offset, limit=limit, order=order, context=context, count=count)
|
||||
return super(calendar_event, self).search(cr, uid, new_args, offset=offset, limit=limit, order=order, count=count, context=context)
|
||||
|
||||
# offset, limit, order and count must be treated separately as we may need to deal with virtual ids
|
||||
res = super(calendar_event, self).search(cr, uid, new_args, offset=0, limit=0, order=None, context=context, count=False)
|
||||
|
@ -1535,17 +1518,17 @@ class calendar_event(osv.Model):
|
|||
|
||||
if (values.get('start_date') or values.get('start_datetime', False)) and values.get('active', True):
|
||||
the_id = new_id or (ids and int(ids[0]))
|
||||
if the_id:
|
||||
if attendees_create:
|
||||
attendees_create = attendees_create[the_id]
|
||||
mail_to_ids = list(set(attendees_create['old_attendee_ids']) - set(attendees_create['removed_attendee_ids']))
|
||||
else:
|
||||
mail_to_ids = [att.id for att in self.browse(cr, uid, the_id, context=context).attendee_ids]
|
||||
|
||||
if attendees_create:
|
||||
attendees_create = attendees_create[the_id]
|
||||
mail_to_ids = list(set(attendees_create['old_attendee_ids']) - set(attendees_create['removed_attendee_ids']))
|
||||
else:
|
||||
mail_to_ids = [att.id for att in self.browse(cr, uid, the_id, context=context).attendee_ids]
|
||||
|
||||
if mail_to_ids:
|
||||
current_user = self.pool['res.users'].browse(cr, uid, uid, context=context)
|
||||
if self.pool['calendar.attendee']._send_mail_to_attendees(cr, uid, mail_to_ids, template_xmlid='calendar_template_meeting_changedate', email_from=current_user.email, context=context):
|
||||
self.message_post(cr, uid, the_id, body=_("A email has been send to specify that the date has been changed !"), subtype="calendar.subtype_invitation", context=context)
|
||||
if mail_to_ids:
|
||||
current_user = self.pool['res.users'].browse(cr, uid, uid, context=context)
|
||||
if self.pool['calendar.attendee']._send_mail_to_attendees(cr, uid, mail_to_ids, template_xmlid='calendar_template_meeting_changedate', email_from=current_user.email, context=context):
|
||||
self.message_post(cr, uid, the_id, body=_("A email has been send to specify that the date has been changed !"), subtype="calendar.subtype_invitation", context=context)
|
||||
return res or True and False
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
|
@ -1624,7 +1607,7 @@ class calendar_event(osv.Model):
|
|||
continue
|
||||
if r['class'] == 'private':
|
||||
for f in r.keys():
|
||||
if f not in ('id', 'allday', 'start', 'stop', 'duration', 'user_id', 'state', 'interval', 'count', 'recurrent_id_date'):
|
||||
if f not in ('id', 'allday', 'start', 'stop', 'duration', 'user_id', 'state', 'interval', 'count', 'recurrent_id_date', 'rrule'):
|
||||
if isinstance(r[f], list):
|
||||
r[f] = []
|
||||
else:
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
import crm
|
||||
import crm_segmentation
|
||||
import crm_lead
|
||||
import sales_team
|
||||
import calendar_event
|
||||
import crm_phonecall
|
||||
import report
|
||||
|
|
|
@ -51,13 +51,13 @@ Dashboard for CRM will include:
|
|||
'depends': [
|
||||
'base_action_rule',
|
||||
'base_setup',
|
||||
'sales_team',
|
||||
'mail',
|
||||
'email_template',
|
||||
'calendar',
|
||||
'resource',
|
||||
'board',
|
||||
'fetchmail',
|
||||
'web_kanban_sparkline',
|
||||
],
|
||||
'data': [
|
||||
'crm_data.xml',
|
||||
|
@ -91,8 +91,7 @@ Dashboard for CRM will include:
|
|||
'res_config_view.xml',
|
||||
'base_partner_merge_view.xml',
|
||||
|
||||
'crm_case_section_view.xml',
|
||||
'views/crm.xml',
|
||||
'sales_team_view.xml',
|
||||
],
|
||||
'demo': [
|
||||
'crm_demo.xml',
|
||||
|
|
|
@ -88,155 +88,6 @@ 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"
|
||||
_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
|
||||
monthly_open_leads: number of open lead during the last months
|
||||
monthly_planned_revenue: planned revenu of opportunities during the last months
|
||||
"""
|
||||
obj = self.pool.get('crm.lead')
|
||||
res = dict.fromkeys(ids, False)
|
||||
month_begin = date.today().replace(day=1)
|
||||
date_begin = month_begin - relativedelta.relativedelta(months=self._period_number - 1)
|
||||
date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1])
|
||||
lead_pre_domain = [('create_date', '>=', date_begin.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)),
|
||||
('create_date', '<=', date_end.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)),
|
||||
('type', '=', 'lead')]
|
||||
opp_pre_domain = [('date_deadline', '>=', date_begin.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||
('date_deadline', '<=', date_end.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||
('type', '=', 'opportunity')]
|
||||
for id in ids:
|
||||
res[id] = dict()
|
||||
lead_domain = lead_pre_domain + [('section_id', '=', id)]
|
||||
opp_domain = opp_pre_domain + [('section_id', '=', id)]
|
||||
res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date'], 'create_date_count', 'create_date', context=context)
|
||||
res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'date_deadline'], 'planned_revenue', 'date_deadline', context=context)
|
||||
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."),
|
||||
'use_opportunities': fields.boolean('Opportunities', help="Check this box to manage opportunities in this sales team."),
|
||||
'monthly_open_leads': fields.function(_get_opportunities_data,
|
||||
type="string", readonly=True, multi='_get_opportunities_data',
|
||||
string='Open Leads per Month'),
|
||||
'monthly_planned_revenue': fields.function(_get_opportunities_data,
|
||||
type="string", readonly=True, multi='_get_opportunities_data',
|
||||
string='Planned Revenue per Month')
|
||||
}
|
||||
|
||||
def _get_stage_common(self, cr, uid, context):
|
||||
ids = self.pool.get('crm.case.stage').search(cr, uid, [('case_default', '=', 1)], context=context)
|
||||
return ids
|
||||
|
||||
_defaults = {
|
||||
'active': 1,
|
||||
'stage_ids': _get_stage_common,
|
||||
'use_leads': True,
|
||||
'use_opportunities': 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"
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="code">sales_team = self.pool.get('ir.model.data').get_object(cr, uid, 'crm', 'section_sales_department')
|
||||
<field name="code">sales_team = self.pool.get('ir.model.data').get_object(cr, uid, 'sales_team', 'section_sales_department')
|
||||
object.write({'section_id': sales_team.id})
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -1,333 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- CRM lead search by Salesteams -->
|
||||
|
||||
<record model="ir.actions.act_window" id="crm_case_form_view_salesteams_lead">
|
||||
<field name="name">Leads</field>
|
||||
<field name="res_model">crm.lead</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">['|', ('type','=','lead'), ('type','=',False)]</field>
|
||||
<field name="view_id" ref="crm_case_tree_view_leads"/>
|
||||
<field name="search_view_id" ref="crm.view_crm_case_leads_filter"/>
|
||||
<field name="context">{
|
||||
'search_default_section_id': [active_id],
|
||||
'default_section_id': active_id,
|
||||
'default_type': 'lead',
|
||||
'stage_type': 'lead',
|
||||
}
|
||||
</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
Use leads if you need a qualification step before creating an
|
||||
opportunity or a customer. It can be a business card you received,
|
||||
a contact form filled in your website, or a file of unqualified
|
||||
prospects you import, etc.
|
||||
</p><p>
|
||||
Once qualified, the lead can be converted into a business
|
||||
opportunity and/or a new customer in your address book.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- CRM opportunity search by Salesteams -->
|
||||
|
||||
<record model="ir.actions.act_window" id="crm_case_form_view_salesteams_opportunity">
|
||||
<field name="name">Opportunities</field>
|
||||
<field name="res_model">crm.lead</field>
|
||||
<field name="view_mode">kanban,tree,graph,form,calendar</field>
|
||||
<field name="domain">[('type','=','opportunity')]</field>
|
||||
<field name="view_id" ref="crm.crm_case_kanban_view_leads"/>
|
||||
<field name="search_view_id" ref="crm.view_crm_case_opportunities_filter"/>
|
||||
<field name="context">{
|
||||
'search_default_section_id': [active_id],
|
||||
'default_section_id': active_id,
|
||||
'stage_type': 'opportunity',
|
||||
'default_type': 'opportunity',
|
||||
'default_user_id': uid,
|
||||
}
|
||||
</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
OpenERP helps you keep track of your sales pipeline to follow
|
||||
up potential sales and better forecast your future revenues.
|
||||
</p><p>
|
||||
You will be able to plan meetings and phone calls from
|
||||
opportunities, convert them into quotations, attach related
|
||||
documents, track all discussions, and much more.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_report_crm_lead_salesteam" model="ir.actions.act_window">
|
||||
<field name="name">Leads Analysis</field>
|
||||
<field name="res_model">crm.lead.report</field>
|
||||
<field name="context">{"search_default_month":1}</field>
|
||||
<field name="view_mode">graph</field>
|
||||
<field name="view_id" ref="crm.view_report_crm_lead_graph_two"/>
|
||||
<field name="domain">[('type','=', 'lead'),('section_id', '=', active_id)]</field>
|
||||
<field name="help">Leads Analysis allows you to check different CRM related information like the treatment delays or number of leads per state. You can sort out your leads analysis by different groups to get accurate grained analysis.</field>
|
||||
</record>
|
||||
|
||||
<record id="action_report_crm_opportunity_salesteam" model="ir.actions.act_window">
|
||||
<field name="name">Opportunities Analysis</field>
|
||||
<field name="res_model">crm.lead.report</field>
|
||||
<field name="view_mode">graph</field>
|
||||
<field name="view_id" ref="crm.view_report_crm_opportunity_graph"/>
|
||||
<field name="domain">[('type','=', 'opportunity'), ('section_id', '=', active_id)]</field>
|
||||
<field name="help">Opportunities Analysis gives you an instant access to your opportunities with information such as the expected revenue, planned cost, missed deadlines or the number of interactions per opportunity. This report is mainly used by the sales manager in order to do the periodic review with the teams of the sales pipeline.</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="use_leads"/>
|
||||
<field name="use_opportunities"/>
|
||||
<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" t-if="record.use_opportunities.raw_value">
|
||||
<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 Sales Teams" 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"/>
|
||||
<field name="use_opportunities" class="oe_inline"/><label for="use_opportunities"/>
|
||||
</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"/>
|
||||
</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 model="ir.actions.act_window.view" id="action_crm_tag_tree_view_salesteams_oppor11">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="crm_case_tree_view_oppor"/>
|
||||
<field name="act_window_id" ref="crm_case_form_view_salesteams_opportunity"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window.view" id="action_crm_tag_form_view_salesteams_oppor11">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="crm_case_form_view_oppor"/>
|
||||
<field name="act_window_id" ref="crm_case_form_view_salesteams_opportunity"/>
|
||||
</record>
|
||||
|
||||
<record id="crm_case_section_act_tree" model="ir.actions.act_window">
|
||||
<field name="name">Cases by Sales Team</field>
|
||||
<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"/>
|
||||
</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="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>
|
|
@ -26,34 +26,31 @@
|
|||
<field name="name">Email</field>
|
||||
</record>
|
||||
|
||||
<record model="crm.case.section" id="section_sales_department">
|
||||
<field name="name">Direct Sales</field>
|
||||
<field name="code">DM</field>
|
||||
<record model="crm.case.section" id="sales_team.section_sales_department">
|
||||
<field name="use_leads">True</field>
|
||||
<field name="alias_name">sales</field>
|
||||
<field name="member_ids" eval="[(4, ref('base.user_root'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Payment Mode -->
|
||||
|
||||
<record model="crm.payment.mode" id="payment_mode_1">
|
||||
<field name="name">Cash</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
|
||||
<record model="crm.payment.mode" id="payment_mode_2">
|
||||
<field name="name">Check</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
|
||||
<record model="crm.payment.mode" id="payment_mode_3">
|
||||
<field name="name">Credit Card</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
|
||||
<record model="crm.payment.mode" id="payment_mode_4">
|
||||
<field name="name">Demand Draft</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
|
||||
<!--default alias for leads-->
|
||||
|
|
|
@ -2,26 +2,6 @@
|
|||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="base.user_demo" model="res.users">
|
||||
<field name="groups_id" eval="[(4,ref('base.group_sale_salesman'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="crm.section_sales_department" model="crm.case.section">
|
||||
<field name="member_ids" eval="[(4, ref('base.user_demo'))]"/>
|
||||
</record>
|
||||
|
||||
<record model="crm.case.section" id="crm_case_section_1">
|
||||
<field name="name">Indirect Sales</field>
|
||||
<field name="code">IM</field>
|
||||
<field name="member_ids" eval="[(4, ref('base.user_root')),(4, ref('base.user_demo'))]"/>
|
||||
</record>
|
||||
|
||||
<record model="crm.case.section" id="crm_case_section_2">
|
||||
<field name="name">Marketing</field>
|
||||
<field name="code">SPD</field>
|
||||
<field name="member_ids" eval="[(4, ref('base.user_root')),(4, ref('base.user_demo'))]"/>
|
||||
</record>
|
||||
|
||||
<record model="crm.segmentation" id="crm_segmentation0">
|
||||
<field name="name">OpenERP partners</field>
|
||||
<field name="exclusif">True</field>
|
||||
|
|
|
@ -90,7 +90,10 @@ class crm_lead(format_address, osv.osv):
|
|||
|
||||
def _get_default_section_id(self, cr, uid, context=None):
|
||||
""" Gives default section by checking if present in the context """
|
||||
return self._resolve_section_id_from_context(cr, uid, context=context) or False
|
||||
section_id = self._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
|
||||
|
||||
def _get_default_stage_id(self, cr, uid, context=None):
|
||||
""" Gives default stage_id """
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<field name="type">opportunity</field>
|
||||
</record>
|
||||
|
||||
<record model="crm.case.section" id="section_sales_department">
|
||||
<record model="crm.case.section" id="sales_team.section_sales_department">
|
||||
<field name="stage_ids" eval="[ (4, ref('stage_lead1')), (4, ref('stage_lead2')),
|
||||
(4, ref('stage_lead3')), (4, ref('stage_lead4')),
|
||||
(4, ref('stage_lead5')), (4, ref('stage_lead6')),
|
||||
|
@ -71,76 +71,76 @@
|
|||
<!-- Crm campain -->
|
||||
<record model="crm.case.resource.type" id="type_lead1">
|
||||
<field name="name">Telesales</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
<record model="crm.case.resource.type" id="type_lead2">
|
||||
<field name="name">Email Campaign - Services</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
<record model="crm.case.resource.type" id="type_lead3">
|
||||
<field name="name">Email Campaign - Products</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
<record model="crm.case.resource.type" id="type_lead4">
|
||||
<field name="name">Twitter Ads</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
<record model="crm.case.resource.type" id="type_lead5">
|
||||
<field name="name">Google Adwords</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
<record model="crm.case.resource.type" id="type_lead6">
|
||||
<field name="name">Banner Ads</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
<record model="crm.case.resource.type" id="type_lead7">
|
||||
<field name="name">Television</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
<record model="crm.case.resource.type" id="type_lead8">
|
||||
<field name="name">Newsletter</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
|
||||
<!-- crm categories -->
|
||||
<record model="crm.case.categ" id="categ_oppor1">
|
||||
<field name="name">Product</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||
</record>
|
||||
<record model="crm.case.categ" id="categ_oppor2">
|
||||
<field name="name">Software</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||
</record>
|
||||
<record model="crm.case.categ" id="categ_oppor3">
|
||||
<field name="name">Services</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||
</record>
|
||||
<record model="crm.case.categ" id="categ_oppor4">
|
||||
<field name="name">Information</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||
</record>
|
||||
<record model="crm.case.categ" id="categ_oppor5">
|
||||
<field name="name">Design</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||
</record>
|
||||
<record model="crm.case.categ" id="categ_oppor6">
|
||||
<field name="name">Training</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||
</record>
|
||||
<record model="crm.case.categ" id="categ_oppor7">
|
||||
<field name="name">Consulting</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||
</record>
|
||||
<record model="crm.case.categ" id="categ_oppor8">
|
||||
<field name="name">Other</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||
</record>
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor6])]"/>
|
||||
<field name="channel_id" ref="crm_case_channel_email"/>
|
||||
<field name="priority">1</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="stage_lead1"/>
|
||||
<field name="description">Hello,
|
||||
|
@ -55,7 +55,7 @@ Could you please send me the details ?</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor2])]"/>
|
||||
<field name="channel_id" ref="crm_case_channel_website"/>
|
||||
<field name="priority">1</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="stage_lead1"/>
|
||||
</record>
|
||||
|
@ -84,7 +84,7 @@ Could you please send me the details ?</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor4])]"/>
|
||||
<field name="channel_id" ref=""/>
|
||||
<field name="priority">2</field>
|
||||
<field name="section_id" ref="crm_case_section_1"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="stage_lead1"/>
|
||||
</record>
|
||||
|
@ -105,7 +105,7 @@ Could you please send me the details ?</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor5])]"/>
|
||||
<field name="channel_id" ref=""/>
|
||||
<field name="priority">2</field>
|
||||
<field name="section_id" ref="crm_case_section_2"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_2"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="stage_lead6"/>
|
||||
</record>
|
||||
|
@ -130,7 +130,7 @@ Could you please send me the details ?</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor1])]"/>
|
||||
<field name="channel_id" ref="crm_case_channel_website"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="section_id" ref="crm_case_section_1"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field name="user_id" ref=""/>
|
||||
<field name="stage_id" ref="stage_lead1"/>
|
||||
<field name="description">Hi,
|
||||
|
@ -157,7 +157,7 @@ Contact: +1 813 494 5005</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor3,categ_oppor4])]"/>
|
||||
<field name="channel_id" ref=""/>
|
||||
<field name="priority">2</field>
|
||||
<field name="section_id" ref="crm_case_section_2"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_2"/>
|
||||
<field name="user_id" ref=""/>
|
||||
<field name="stage_id" ref="stage_lead1"/>
|
||||
</record>
|
||||
|
@ -175,7 +175,7 @@ Contact: +1 813 494 5005</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor4])]"/>
|
||||
<field name="channel_id" ref=""/>
|
||||
<field name="priority">0</field>
|
||||
<field name="section_id" ref="crm_case_section_2"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_2"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="stage_lead1"/>
|
||||
</record>
|
||||
|
@ -194,7 +194,7 @@ Contact: +1 813 494 5005</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor6,categ_oppor8])]"/>
|
||||
<field name="channel_id" ref=""/>
|
||||
<field name="priority">1</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="stage_lead1"/>
|
||||
</record>
|
||||
|
@ -213,7 +213,7 @@ Contact: +1 813 494 5005</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor7])]"/>
|
||||
<field name="channel_id" ref="crm_case_channel_phone"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="stage_lead1"/>
|
||||
</record>
|
||||
|
@ -231,7 +231,7 @@ Contact: +1 813 494 5005</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor1])]"/>
|
||||
<field name="channel_id" ref="crm_case_channel_email"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="section_id" ref="crm_case_section_2"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_2"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="stage_lead1"/>
|
||||
<field name="description">Hi,
|
||||
|
@ -254,7 +254,7 @@ Andrew</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor7])]"/>
|
||||
<field name="channel_id" ref="crm_case_channel_direct"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="section_id" ref="crm_case_section_1"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field name="user_id" ref=""/>
|
||||
<field name="stage_id" ref="stage_lead1"/>
|
||||
</record>
|
||||
|
@ -272,7 +272,7 @@ Andrew</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor1])]"/>
|
||||
<field name="channel_id" ref="crm_case_channel_website"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="user_id" ref=""/>
|
||||
<field name="stage_id" ref="stage_lead1"/>
|
||||
</record>
|
||||
|
@ -310,7 +310,7 @@ Andrew</field>
|
|||
<field eval="time.strftime('%Y-%m-25')" name="date_deadline"/>
|
||||
<field eval="time.strftime('%Y-%m-12')" name="date_action"/>
|
||||
<field name="title_action">Meeting for pricing information.</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="crm.stage_lead1"/>
|
||||
</record>
|
||||
|
@ -335,7 +335,7 @@ Andrew</field>
|
|||
<field eval="time.strftime('%Y-%m-23')" name="date_deadline"/>
|
||||
<field eval="time.strftime('%Y-%m-10')" name="date_action"/>
|
||||
<field name="title_action">Send Catalogue by Email</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
</record>
|
||||
|
@ -356,7 +356,7 @@ Andrew</field>
|
|||
<field eval="time.strftime('%Y-%m-12')" name="date_deadline"/>
|
||||
<field eval="time.strftime('%Y-%m-10')" name="date_action"/>
|
||||
<field name="title_action">Call to ask system requirement</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
<field name="date_closed" eval="(DateTime.today() - relativedelta(months=2)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
|
@ -383,7 +383,7 @@ Andrew</field>
|
|||
<field eval="time.strftime('%Y-%m-28')" name="date_deadline"/>
|
||||
<field eval="time.strftime('%Y-%m-1')" name="date_action"/>
|
||||
<field name="title_action">Convert to quote</field>
|
||||
<field name="section_id" ref="crm_case_section_1"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
<field name="date_closed" eval="(DateTime.today() - relativedelta(hours=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
|
@ -408,7 +408,7 @@ Andrew</field>
|
|||
<field eval="time.strftime('%Y-%m-8')" name="date_deadline"/>
|
||||
<field eval="time.strftime('%Y-%m-3')" name="date_action"/>
|
||||
<field name="title_action">Send price list regarding our interventions</field>
|
||||
<field name="section_id" ref="crm_case_section_1"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead4"/>
|
||||
<field name="date_closed" eval="(DateTime.today() - relativedelta(hours=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
|
@ -435,7 +435,7 @@ Andrew</field>
|
|||
<field eval="time.strftime('%Y-%m-13')" name="date_deadline"/>
|
||||
<field eval="time.strftime('%Y-%m-4')" name="date_action"/>
|
||||
<field name="title_action">Call to define real needs about training</field>
|
||||
<field name="section_id" ref="crm_case_section_1"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead4"/>
|
||||
<field eval="1" name="active"/>
|
||||
|
@ -462,7 +462,7 @@ Andrew</field>
|
|||
<field eval="time.strftime('%Y-%m-10')" name="date_deadline"/>
|
||||
<field eval="time.strftime('%Y-%m-5')" name="date_action"/>
|
||||
<field name="title_action">Ask for the good receprion of the proposition</field>
|
||||
<field name="section_id" ref="crm_case_section_1"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="crm.stage_lead4"/>
|
||||
<field name="date_closed" eval="(DateTime.today() - relativedelta(months=3)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
|
@ -479,7 +479,7 @@ Andrew</field>
|
|||
<field name="type_id" ref="type_lead3"/>
|
||||
<field name="categ_ids" eval="[(6, 0, [categ_oppor4,categ_oppor8])]"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="section_id" ref="crm_case_section_1"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead4"/>
|
||||
</record>
|
||||
|
@ -493,7 +493,7 @@ Andrew</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor7])]"/>
|
||||
<field name="channel_id" ref="crm_case_channel_phone"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="section_id" ref="crm_case_section_2"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_2"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="crm.stage_lead4"/>
|
||||
<field name="date_closed" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
|
@ -511,7 +511,7 @@ Andrew</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor3])]"/>
|
||||
<field name="channel_id" ref="crm_case_channel_email"/>
|
||||
<field name="priority">1</field>
|
||||
<field name="section_id" ref="crm_case_section_2"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_2"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="crm.stage_lead5"/>
|
||||
</record>
|
||||
|
@ -526,7 +526,7 @@ Andrew</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor3])]"/>
|
||||
<field name="channel_id" ref="crm_case_channel_direct"/>
|
||||
<field name="priority">0</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead5"/>
|
||||
<field name="date_closed" eval="(DateTime.today() - relativedelta(hours=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
|
@ -545,7 +545,7 @@ Andrew</field>
|
|||
<field name="channel_id" ref="crm_case_channel_website"/>
|
||||
<field name="priority">0</field>
|
||||
<field eval="time.strftime('%Y-%m-6')" name="date_deadline"/>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="crm.stage_lead5"/>
|
||||
<field name="date_closed" eval="(DateTime.today() - relativedelta(month=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
|
@ -568,7 +568,7 @@ Andrew</field>
|
|||
<field name="categ_ids" eval="[(6, 0, [categ_oppor4])]"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="title_action">Conf call with technical service</field>
|
||||
<field name="section_id" ref="crm_case_section_1"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="crm.stage_lead5"/>
|
||||
</record>
|
||||
|
@ -592,7 +592,7 @@ Andrew</field>
|
|||
<field eval="time.strftime('%Y-%m-23')" name="date_deadline"/>
|
||||
<field eval="time.strftime('%Y-%m-10')" name="date_action"/>
|
||||
<field name="title_action">Send Catalogue by Email</field>
|
||||
<field name="section_id" ref="crm_case_section_2"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_2"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead5"/>
|
||||
<!-- <field name="date_closed" eval="(DateTime.today() - relativedelta(hours=1)).strftime('%Y-%m-%d %H:%M')"/> -->
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
action="crm_case_category_act_leads_all"/>
|
||||
<menuitem name="Opportunities" id="menu_crm_opportunities" parent="base.menu_sales" sequence="4"
|
||||
action="crm_case_category_act_oppor11"
|
||||
groups="base.group_mono_salesteams,base.group_sale_manager"/>
|
||||
groups="base.group_sale_salesman,base.group_sale_manager"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!--
|
||||
CRM CASE STAGE
|
||||
-->
|
||||
|
||||
<!-- Stage Search view -->
|
||||
<record id="crm_lead_stage_search" model="ir.ui.view">
|
||||
<field name="name">Stage - Search</field>
|
||||
|
@ -570,12 +568,12 @@
|
|||
help="Opportunities that are assigned to me"/>
|
||||
<filter string="My Team(s)"
|
||||
domain="[('section_id.member_ids', 'in', [uid])]" context="{'invisible_section': False}"
|
||||
help="Opportunities that are assigned to any sales teams I am member of"/>
|
||||
help="Opportunities that are assigned to any sales teams I am member of" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
|
||||
<separator/>
|
||||
<group expand="0" string="Group By..." colspan="16">
|
||||
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Team" domain="[]" context="{'group_by':'section_id'}"/>
|
||||
<filter string="Team" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/>
|
||||
<filter string="Customer" help="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Country" domain="[]" context="{'group_by':'country_id'}"/>
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
-->
|
||||
<record model="crm.case.categ" id="categ_phone1">
|
||||
<field name="name">Inbound</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.phonecall')]" model="ir.model"/>
|
||||
|
||||
</record>
|
||||
<record model="crm.case.categ" id="categ_phone2">
|
||||
<field name="name">Outbound</field>
|
||||
<field name="section_id" ref="section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.phonecall')]" model="ir.model"/>
|
||||
</record>
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<field name="name">Left the message</field>
|
||||
<field name="state">done</field>
|
||||
<field name="partner_phone">+34 934 340 230</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field name="categ_id" ref="crm.categ_phone1"/>
|
||||
<field eval="2.3" name="duration"/>
|
||||
|
@ -24,7 +24,7 @@
|
|||
<field name="name">Need more information on the proposed deal</field>
|
||||
<field name="state">done</field>
|
||||
<field name="partner_phone">+44 121 690 4596</field>
|
||||
<field name="section_id" ref="crm.crm_case_section_1"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field name="categ_id" ref="crm.categ_phone1"/>
|
||||
<field eval="1.5" name="duration"/>
|
||||
|
@ -37,7 +37,7 @@
|
|||
<field name="name">Ask for convenient time of meeting</field>
|
||||
<field name="state">open</field>
|
||||
<field name="partner_phone">+1 786 525 0724</field>
|
||||
<field name="section_id" ref="crm.crm_case_section_2"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_2"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field name="categ_id" ref="crm.categ_phone2"/>
|
||||
<field eval="5.0" name="duration"/>
|
||||
|
@ -50,7 +50,7 @@
|
|||
<field name="state">done</field>
|
||||
<field name="partner_phone">(077) 582-4035</field>
|
||||
<field name="partner_mobile">(077) 341-3591</field>
|
||||
<field name="section_id" ref="crm.crm_case_section_2"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_2"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field name="categ_id" ref="crm.categ_phone1"/>
|
||||
<field eval="5.45" name="duration"/>
|
||||
|
@ -63,7 +63,7 @@
|
|||
<field name="name">More information on the proposed deal</field>
|
||||
<field name="state">pending</field>
|
||||
<field name="partner_phone">+1 312 349 2324</field>
|
||||
<field name="section_id" ref="crm.crm_case_section_1"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field name="categ_id" ref="crm.categ_phone1"/>
|
||||
<field eval="2.08" name="duration"/>
|
||||
|
@ -74,7 +74,7 @@
|
|||
<field name="name">Proposal for discount offer</field>
|
||||
<field name="state">open</field>
|
||||
<field name="partner_phone">+34 230 953 485</field>
|
||||
<field name="section_id" ref="crm.crm_case_section_2"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_2"/>
|
||||
<field eval="time.strftime('%Y-%m-28 14:15:30')" name="date"/>
|
||||
<field name="categ_id" ref="crm.categ_phone2"/>
|
||||
<field eval="8.56" name="duration"/>
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
</group>
|
||||
<group expand="1" string="Group By...">
|
||||
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}" />
|
||||
<filter string="Sales Team" domain="[]" context="{'group_by':'section_id'}" />
|
||||
<filter string="Sales Team" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Partner" context="{'group_by':'partner_id'}" />
|
||||
<filter string="Country" context="{'group_by':'country_id'}" />
|
||||
<filter string="Company" domain="[]" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
</group>
|
||||
<group expand="1" string="Group By...">
|
||||
<filter string="Salesperson" name="Salesperson" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}" />
|
||||
<filter string="Sales Team" icon="terp-personal+" domain="[]" context="{'group_by':'section_id'}" />
|
||||
<filter string="Sales Team" icon="terp-personal+" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Partner" icon="terp-partner" context="{'group_by':'partner_id'}" />
|
||||
<filter string="Priority" icon="terp-rating-rated" domain="[]" context="{'group_by':'priority'}" />
|
||||
<filter string="Category" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'categ_id'}" />
|
||||
|
|
|
@ -27,33 +27,6 @@ class crm_configuration(osv.TransientModel):
|
|||
_name = 'sale.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_fund_raising': fields.boolean("Manage Fund Raising",
|
||||
implied_group='crm.group_fund_raising',
|
||||
|
@ -64,9 +37,6 @@ class crm_configuration(osv.TransientModel):
|
|||
'module_crm_helpdesk': fields.boolean("Manage Helpdesk and Support",
|
||||
help='Allows you to communicate with Customer, process Customer query, and provide better help and support.\n'
|
||||
'-This installs the module crm_helpdesk.'),
|
||||
'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."""),
|
||||
'alias_prefix': fields.char('Default Alias Name for Leads'),
|
||||
'alias_domain' : fields.char('Alias Domain'),
|
||||
'group_scheduled_calls': fields.boolean("Schedule calls to manage call center",
|
||||
|
|
|
@ -32,17 +32,6 @@
|
|||
</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">
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import calendar
|
||||
from datetime import date
|
||||
from dateutil import relativedelta
|
||||
|
||||
from openerp import tools
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
|
||||
class crm_case_section(osv.Model):
|
||||
_inherit = 'crm.case.section'
|
||||
_inherits = {'mail.alias': 'alias_id'}
|
||||
|
||||
def _get_opportunities_data(self, cr, uid, ids, field_name, arg, context=None):
|
||||
""" Get opportunities-related data for salesteam kanban view
|
||||
monthly_open_leads: number of open lead during the last months
|
||||
monthly_planned_revenue: planned revenu of opportunities during the last months
|
||||
"""
|
||||
obj = self.pool.get('crm.lead')
|
||||
res = dict.fromkeys(ids, False)
|
||||
month_begin = date.today().replace(day=1)
|
||||
date_begin = month_begin - relativedelta.relativedelta(months=self._period_number - 1)
|
||||
date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1])
|
||||
lead_pre_domain = [('create_date', '>=', date_begin.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)),
|
||||
('create_date', '<=', date_end.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)),
|
||||
('type', '=', 'lead')]
|
||||
opp_pre_domain = [('date_deadline', '>=', date_begin.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||
('date_deadline', '<=', date_end.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||
('type', '=', 'opportunity')]
|
||||
for id in ids:
|
||||
res[id] = dict()
|
||||
lead_domain = lead_pre_domain + [('section_id', '=', id)]
|
||||
opp_domain = opp_pre_domain + [('section_id', '=', id)]
|
||||
res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date'], 'create_date_count', 'create_date', context=context)
|
||||
res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'date_deadline'], 'planned_revenue', 'date_deadline', context=context)
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'resource_calendar_id': fields.many2one('resource.calendar', "Working Time", help="Used to compute open days"),
|
||||
'stage_ids': fields.many2many('crm.case.stage', 'section_stage_rel', 'section_id', 'stage_id', 'Stages'),
|
||||
'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."),
|
||||
'use_opportunities': fields.boolean('Opportunities', help="Check this box to manage opportunities in this sales team."),
|
||||
'monthly_open_leads': fields.function(_get_opportunities_data,
|
||||
type="string", readonly=True, multi='_get_opportunities_data',
|
||||
string='Open Leads per Month'),
|
||||
'monthly_planned_revenue': fields.function(_get_opportunities_data,
|
||||
type="string", readonly=True, multi='_get_opportunities_data',
|
||||
string='Planned Revenue per Month'),
|
||||
'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."),
|
||||
}
|
||||
|
||||
def _auto_init(self, cr, context=None):
|
||||
"""Installation hook to create aliases for all lead and avoid constraint errors."""
|
||||
return self.pool.get('mail.alias').migrate_to_alias(cr, self._name, self._table, super(crm_case_section, self)._auto_init,
|
||||
'crm.lead', self._columns['alias_id'], 'name', alias_prefix='Lead+', alias_defaults={}, context=context)
|
||||
|
||||
def _get_stage_common(self, cr, uid, context):
|
||||
ids = self.pool.get('crm.case.stage').search(cr, uid, [('case_default', '=', 1)], context=context)
|
||||
return ids
|
||||
|
||||
_defaults = {
|
||||
'stage_ids': _get_stage_common,
|
||||
'use_leads': True,
|
||||
'use_opportunities': True,
|
||||
}
|
||||
|
||||
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
|
|
@ -0,0 +1,185 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- CRM lead search by Salesteams -->
|
||||
|
||||
<record model="ir.actions.act_window" id="crm_case_form_view_salesteams_lead">
|
||||
<field name="name">Leads</field>
|
||||
<field name="res_model">crm.lead</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">['|', ('type','=','lead'), ('type','=',False)]</field>
|
||||
<field name="view_id" ref="crm_case_tree_view_leads"/>
|
||||
<field name="search_view_id" ref="crm.view_crm_case_leads_filter"/>
|
||||
<field name="context">{
|
||||
'search_default_section_id': [active_id],
|
||||
'default_section_id': active_id,
|
||||
'default_type': 'lead',
|
||||
'stage_type': 'lead',
|
||||
}
|
||||
</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
Use leads if you need a qualification step before creating an
|
||||
opportunity or a customer. It can be a business card you received,
|
||||
a contact form filled in your website, or a file of unqualified
|
||||
prospects you import, etc.
|
||||
</p><p>
|
||||
Once qualified, the lead can be converted into a business
|
||||
opportunity and/or a new customer in your address book.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- CRM opportunity search by Salesteams -->
|
||||
|
||||
<record model="ir.actions.act_window" id="crm_case_form_view_salesteams_opportunity">
|
||||
<field name="name">Opportunities</field>
|
||||
<field name="res_model">crm.lead</field>
|
||||
<field name="view_mode">kanban,tree,graph,form,calendar</field>
|
||||
<field name="domain">[('type','=','opportunity')]</field>
|
||||
<field name="view_id" ref="crm.crm_case_kanban_view_leads"/>
|
||||
<field name="search_view_id" ref="crm.view_crm_case_opportunities_filter"/>
|
||||
<field name="context">{
|
||||
'search_default_section_id': [active_id],
|
||||
'default_section_id': active_id,
|
||||
'stage_type': 'opportunity',
|
||||
'default_type': 'opportunity',
|
||||
'default_user_id': uid,
|
||||
}
|
||||
</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
OpenERP helps you keep track of your sales pipeline to follow
|
||||
up potential sales and better forecast your future revenues.
|
||||
</p><p>
|
||||
You will be able to plan meetings and phone calls from
|
||||
opportunities, convert them into quotations, attach related
|
||||
documents, track all discussions, and much more.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_report_crm_lead_salesteam" model="ir.actions.act_window">
|
||||
<field name="name">Leads Analysis</field>
|
||||
<field name="res_model">crm.lead.report</field>
|
||||
<field name="context">{"search_default_month":1}</field>
|
||||
<field name="view_mode">graph</field>
|
||||
<field name="view_id" ref="crm.view_report_crm_lead_graph_two"/>
|
||||
<field name="domain">[('type','=', 'lead'),('section_id', '=', active_id)]</field>
|
||||
<field name="help">Leads Analysis allows you to check different CRM related information like the treatment delays or number of leads per state. You can sort out your leads analysis by different groups to get accurate grained analysis.</field>
|
||||
</record>
|
||||
|
||||
<record id="action_report_crm_opportunity_salesteam" model="ir.actions.act_window">
|
||||
<field name="name">Opportunities Analysis</field>
|
||||
<field name="res_model">crm.lead.report</field>
|
||||
<field name="view_mode">graph</field>
|
||||
<field name="view_id" ref="crm.view_report_crm_opportunity_graph"/>
|
||||
<field name="domain">[('type','=', 'opportunity'), ('section_id', '=', active_id)]</field>
|
||||
<field name="help">Opportunities Analysis gives you an instant access to your opportunities with information such as the expected revenue, planned cost, missed deadlines or the number of interactions per opportunity. This report is mainly used by the sales manager in order to do the periodic review with the teams of the sales pipeline.</field>
|
||||
</record>
|
||||
|
||||
<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="sales_team.crm_case_section_salesteams_view_kanban"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//templates" position="before">
|
||||
<field name="alias_id"/>
|
||||
<field name="use_leads"/>
|
||||
<field name="use_opportunities"/>
|
||||
<field name="monthly_open_leads"/>
|
||||
<field name="monthly_planned_revenue"/>
|
||||
</xpath>
|
||||
<xpath expr="//h4[@name='name']" position="after">
|
||||
<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" t-if="record.use_opportunities.raw_value">
|
||||
<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>
|
||||
|
||||
<record model="ir.ui.view" id="sales_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="sales_team.crm_case_section_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@name='options_active']" position="inside">
|
||||
<field name="use_leads"/><label for="use_leads" string="Leads"/>
|
||||
<field name="use_opportunities" class="oe_inline"/><label for="use_opportunities"/>
|
||||
</xpath>
|
||||
<xpath expr="//group/field[@name='active']" position="inside">
|
||||
<group>
|
||||
<label for="alias_name" string="Email Alias"
|
||||
attrs="p'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>
|
||||
</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>
|
||||
|
||||
<!-- 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 model="ir.actions.act_window.view" id="action_crm_tag_tree_view_salesteams_oppor11">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="crm_case_tree_view_oppor"/>
|
||||
<field name="act_window_id" ref="crm_case_form_view_salesteams_opportunity"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window.view" id="action_crm_tag_form_view_salesteams_oppor11">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="crm_case_form_view_oppor"/>
|
||||
<field name="act_window_id" ref="crm_case_form_view_salesteams_opportunity"/>
|
||||
</record>
|
||||
|
||||
<record id="crm_case_section_act_tree" model="ir.actions.act_window">
|
||||
<field name="name">Cases by Sales Team</field>
|
||||
<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="sales_team.crm_case_section_view_tree"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -24,19 +24,6 @@
|
|||
<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"/>
|
||||
|
|
|
@ -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
|
||||
access_crm_case_channel_user,crm.case.channel user,model_crm_case_channel,base.group_sale_salesman,1,0,0,0
|
||||
access_crm_case_channel_manager,crm.case.channel manager,model_crm_case_channel,base.group_sale_manager,1,1,1,1
|
||||
access_crm_case_section,crm.case.section,model_crm_case_section,base.group_user,1,0,0,0
|
||||
access_crm_case_categ,crm.case.categ,model_crm_case_categ,base.group_sale_salesman,1,1,1,0
|
||||
access_crm_lead_manager,crm.lead.manager,model_crm_lead,base.group_sale_manager,1,1,1,1
|
||||
access_crm_phonecall_manager,crm.phonecall.manager,model_crm_phonecall,base.group_sale_manager,1,1,1,1
|
||||
access_crm_case_categ,crm.case.categ,model_crm_case_categ,base.group_user,1,0,0,0
|
||||
access_crm_lead,crm.lead,model_crm_lead,base.group_sale_salesman,1,1,1,0
|
||||
access_crm_phonecall,crm.phonecall,model_crm_phonecall,base.group_sale_salesman,1,1,1,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
|
||||
access_crm_case_stage,crm.case.stage,model_crm_case_stage,,1,0,0,0
|
||||
access_crm_case_stage_manager,crm.case.stage,model_crm_case_stage,base.group_sale_manager,1,1,1,1
|
||||
access_crm_case_resource_type_user,crm_case_resource_type user,model_crm_case_resource_type,base.group_sale_salesman,1,1,1,0
|
||||
|
|
|
|
@ -1,2 +0,0 @@
|
|||
crm.css: crm.sass
|
||||
sass --trace -t expanded crm.sass:crm.css
|
|
@ -5,7 +5,7 @@
|
|||
uid: 'crm_res_users_salesmanager'
|
||||
-
|
||||
!python {model: crm.lead}: |
|
||||
section_id = self.pool.get('crm.case.section').create(cr, uid, {'name': "Phone Marketing", 'parent_id': ref("crm.crm_case_section_2")})
|
||||
section_id = self.pool.get('crm.case.section').create(cr, uid, {'name': "Phone Marketing", 'parent_id': ref("sales_team.crm_case_section_2")})
|
||||
self.write(cr, uid, [ref("crm_case_1")], {'section_id': section_id})
|
||||
-
|
||||
Salesman check unqualified lead .
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
name: 'Test lead new'
|
||||
partner_id: base.res_partner_1
|
||||
description: This is the description of the test new lead.
|
||||
section_id: crm.section_sales_department
|
||||
section_id: sales_team.section_sales_department
|
||||
-
|
||||
I check default stage of lead.
|
||||
-
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
-
|
||||
!python {model: crm.lead2opportunity.partner.mass}: |
|
||||
context.update({'active_model': 'crm.lead', 'active_ids': [ref("crm_case_13"), ref("crm_case_2")], 'active_id': ref("crm_case_13")})
|
||||
id = self.create(cr, uid, {'user_ids': [(6, 0, [ref('base.user_root')])], 'section_id': ref('crm.section_sales_department')}, context=context)
|
||||
id = self.create(cr, uid, {'user_ids': [(6, 0, [ref('base.user_root')])], 'section_id': ref('sales_team.section_sales_department')}, context=context)
|
||||
self.mass_convert(cr, uid, [id], context=context)
|
||||
-
|
||||
Now I check first lead converted on opportunity.
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
-
|
||||
!python {model: crm.lead2opportunity.partner.mass}: |
|
||||
context.update({'active_model': 'crm.lead', 'active_ids': [ref("test_crm_lead_01"), ref("test_crm_lead_02"), ref("test_crm_lead_03"), ref("test_crm_lead_04"), ref("test_crm_lead_05"), ref("test_crm_lead_06")], 'active_id': ref("test_crm_lead_01")})
|
||||
id = self.create(cr, uid, {'user_ids': [(6, 0, [ref('test_res_user_01'), ref('test_res_user_02'), ref('test_res_user_03'), ref('test_res_user_04')])], 'section_id': ref('crm.section_sales_department'), 'deduplicate': False, 'force_assignation': True}, context=context)
|
||||
id = self.create(cr, uid, {'user_ids': [(6, 0, [ref('test_res_user_01'), ref('test_res_user_02'), ref('test_res_user_03'), ref('test_res_user_04')])], 'section_id': ref('sales_team.section_sales_department'), 'deduplicate': False, 'force_assignation': True}, context=context)
|
||||
self.mass_convert(cr, uid, [id], context=context)
|
||||
-
|
||||
The leads should now be opps with a salesman and a salesteam. Also, salesmen should have been assigned following a round-robin method.
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- vim:fdn=3:
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="assets_backend" name="crm assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/crm/static/src/css/crm.css"/>
|
||||
<script type="text/javascript" src="/crm/static/src/js/crm_case_section.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
|
@ -12,7 +12,7 @@
|
|||
</group>
|
||||
<group string="Assign opportunities to">
|
||||
<field name="user_id" class="oe_inline" on_change="on_change_user(user_id, section_id, context)"/>
|
||||
<field name="section_id" class="oe_inline"/>
|
||||
<field name="section_id" class="oe_inline" groups="base.group_multi_salesteams"/>
|
||||
</group>
|
||||
<group string="Opportunities">
|
||||
<field name="opportunity_ids" attrs="{'invisible': [('name', '!=', 'merge')]}" nolabel="1">
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<form string="Merge Leads/Opportunities" version="7.0">
|
||||
<group string="Assign opportunities to">
|
||||
<field name="user_id" class="oe_inline" on_change="on_change_user(user_id, section_id, context)"/>
|
||||
<field name="section_id" class="oe_inline"/>
|
||||
<field name="section_id" class="oe_inline" groups="base.group_multi_salesteams"/>
|
||||
</group>
|
||||
<group string="Select Leads/Opportunities">
|
||||
<field name="opportunity_ids" nolabel="1">
|
||||
|
|
|
@ -8,19 +8,19 @@
|
|||
|
||||
<record model="crm.case.categ" id="categ_claim1">
|
||||
<field name="name">Factual Claims</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
|
||||
</record>
|
||||
|
||||
<record model="crm.case.categ" id="categ_claim2">
|
||||
<field name="name">Value Claims</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
|
||||
</record>
|
||||
|
||||
<record model="crm.case.categ" id="categ_claim3">
|
||||
<field name="name">Policy Claims</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
|
||||
</record>
|
||||
|
||||
|
@ -30,12 +30,12 @@
|
|||
|
||||
<record model="crm.case.resource.type" id="type_claim1">
|
||||
<field name="name">Corrective</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
|
||||
<record model="crm.case.resource.type" id="type_claim2">
|
||||
<field name="name">Preventive</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<field eval=""1"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field eval=""Problem with the delivery of goods"" name="name"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="categ_id" ref="crm_claim.categ_claim1"/>
|
||||
<field name="stage_id" ref="crm_claim.stage_claim1"/>
|
||||
<field eval=""(769) 703-274"" name="partner_phone"/>
|
||||
|
@ -24,7 +24,7 @@
|
|||
<field eval=""0"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field eval=""Damaged Products"" name="name"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="categ_id" ref="crm_claim.categ_claim2"/>
|
||||
<field name="stage_id" ref="crm_claim.stage_claim5"/>
|
||||
<field eval=""(956) 293-2595"" name="partner_phone"/>
|
||||
|
@ -36,7 +36,7 @@
|
|||
<field eval=""2"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field eval=""Document related problems"" name="name"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="categ_id" ref="crm_claim.categ_claim3"/>
|
||||
<field name="stage_id" ref="crm_claim.stage_claim2"/>
|
||||
<field eval=""(079) 681-2139"" name="partner_phone"/>
|
||||
|
@ -49,7 +49,7 @@
|
|||
<field eval=""1"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field eval=""Product quality not maintained"" name="name"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="categ_id" ref="crm_claim.categ_claim1"/>
|
||||
<field name="stage_id" ref="crm_claim.stage_claim5"/>
|
||||
<field eval=""(514) 698-4118"" name="partner_phone"/>
|
||||
|
@ -61,7 +61,7 @@
|
|||
<field eval=""1"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field eval=""Some products missing"" name="name"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="categ_id" ref="crm_claim.categ_claim3"/>
|
||||
<field name="stage_id" ref="crm_claim.stage_claim3"/>
|
||||
<field eval=""(855) 924-4364"" name="partner_phone"/>
|
||||
|
@ -72,7 +72,7 @@
|
|||
<field eval=""1"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field eval=""Problem with the delivery of assignments"" name="name"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="time.strftime('%Y-%m-28 14:15:30')" name="date"/>
|
||||
<field name="categ_id" ref="crm_claim.categ_claim1"/>
|
||||
<field name="stage_id" ref="crm_claim.stage_claim5"/>
|
||||
|
@ -85,7 +85,7 @@
|
|||
<field eval=""1"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field eval=""Documents unclear"" name="name"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="time.strftime('%Y-%m-19 13:01:05')" name="date"/>
|
||||
<field name="categ_id" ref="crm_claim.categ_claim3"/>
|
||||
<field name="stage_id" ref="crm_claim.stage_claim2"/>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
</group>
|
||||
<group expand="1" string="Group By...">
|
||||
<filter string="Salesperson" name="Salesperson" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}" />
|
||||
<filter string="Sales Team" icon="terp-personal+" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Sales Team" icon="terp-personal+" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Partner" name="partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}" />
|
||||
<filter string="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}" />
|
||||
<filter string="Priority" icon="terp-rating-rated" domain="[]" context="{'group_by':'priority'}" />
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="priority">1</field>
|
||||
<field name="state">draft</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="date" eval="time.strftime('%Y-%m-04 11:10:36')"/>
|
||||
<field name="name">Download old version of OpenERP? </field>
|
||||
<field eval=""Is there any link to download old versions of OpenERP?"" name="description"/>
|
||||
|
@ -19,7 +19,7 @@
|
|||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="priority">1</field>
|
||||
<field name="state">draft</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="date" eval="time.strftime('%Y-%m-12 11:12:09')"/>
|
||||
<field name="name">Can not able to connect to Server</field>
|
||||
<field eval=""I am not able to connect Server, Can anyone help?"" name="description"/>
|
||||
|
@ -31,7 +31,7 @@
|
|||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="state">draft</field>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="date" eval="time.strftime('%Y-%m-12 11:13:10')"/>
|
||||
<field name="name">Documentation for CRM?</field>
|
||||
<field eval=""Can anyone give link of document for CRM"" name="description"/>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<field name="categ_ids" eval="[(6, 0, [ref('crm.categ_oppor1')])]"/>
|
||||
<field name="channel_id" ref="crm.crm_case_channel_email"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="section_id" ref="crm.crm_case_section_2"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_2"/>
|
||||
<field name="user_id" ref=""/>
|
||||
<field name="stage_id" ref="crm.stage_lead1"/>
|
||||
<field name="description">Hi,
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<field name="type_id" invisible="1"/>
|
||||
<field name="referred" invisible="1"/>
|
||||
<field name="channel_id" invisible="1"/>
|
||||
<field name="section_id" invisible="context.get('invisible_section', True)" />
|
||||
<field name="section_id" invisible="context.get('invisible_section', True)" groups="base.group_multi_salesteams"/>
|
||||
|
||||
<button string="I'm interested" name="case_interested" icon="gtk-index" type="object"/>
|
||||
<button string="I'm not interested" name="case_disinterested" icon="gtk-close" type="object"/>
|
||||
|
@ -84,7 +84,7 @@
|
|||
<field name="type_id" invisible="1"/>
|
||||
<field name="planned_revenue" sum="Expected Revenues"/>
|
||||
<field name="probability" widget="progressbar" avg="Avg. of Probability"/>
|
||||
<field name="section_id" invisible="context.get('invisible_section', True)" />
|
||||
<field name="section_id" groups="base.group_multi_salesteams" invisible="context.get('invisible_section', True)" />
|
||||
<field name="priority" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
|
|
|
@ -27,9 +27,9 @@
|
|||
domain="[]" context="{'group_by':'user_id'}" />
|
||||
<filter string="Partner" icon="terp-partner" context="{'group_by':'partner_assigned_id'}" />
|
||||
<filter string="Country" icon="terp-go-home" context="{'group_by':'country_id'}" />
|
||||
<filter string="Section" icon="terp-personal+"
|
||||
<filter string="Sales Team" icon="terp-personal+"
|
||||
domain="[]"
|
||||
context="{'group_by':'section_id'}" />
|
||||
context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Grade" name="group_grade" icon="terp-stock_symbol-selection"
|
||||
domain="[]" context="{'group_by':'grade_id'}" />
|
||||
<filter string="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/>
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
<filter string="Salesperson" name="user" icon="terp-personal"
|
||||
domain="[]" context="{'group_by':'user_id'}" />
|
||||
<filter string="Country" icon="terp-go-home" name="group_country" context="{'group_by':'country_id'}" />
|
||||
<filter string="Section" icon="terp-personal+"
|
||||
domain="[]" context="{'group_by':'section_id'}" />
|
||||
<filter string="Sales Team" icon="terp-personal+"
|
||||
domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Activation" name="group_activation" icon="terp-stock_symbol-selection"
|
||||
domain="[]" context="{'group_by':'activation'}" />
|
||||
<filter string="Grade" name="group_grade" icon="terp-stock_symbol-selection"
|
||||
|
|
|
@ -243,7 +243,7 @@
|
|||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">ir.attachment</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">kanban,form</field>
|
||||
<field name="view_mode">kanban,tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new document.
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
# Tamil translation for openobject-addons
|
||||
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
|
||||
# This file is distributed under the same license as the openobject-addons package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
|
||||
"PO-Revision-Date: 2014-05-13 08:27+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Tamil <ta@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2014-05-14 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17002)\n"
|
||||
|
||||
#. module: edi
|
||||
#. openerp-web
|
||||
#: code:addons/edi/static/src/js/edi.js:67
|
||||
#, python-format
|
||||
msgid "Reason:"
|
||||
msgstr ""
|
||||
|
||||
#. module: edi
|
||||
#. openerp-web
|
||||
#: code:addons/edi/static/src/js/edi.js:60
|
||||
#, python-format
|
||||
msgid "The document has been successfully imported!"
|
||||
msgstr ""
|
||||
|
||||
#. module: edi
|
||||
#. openerp-web
|
||||
#: code:addons/edi/static/src/js/edi.js:65
|
||||
#, python-format
|
||||
msgid "Sorry, the document could not be imported."
|
||||
msgstr ""
|
||||
|
||||
#. module: edi
|
||||
#: model:ir.model,name:edi.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr ""
|
||||
|
||||
#. module: edi
|
||||
#: model:ir.model,name:edi.model_res_currency
|
||||
msgid "Currency"
|
||||
msgstr ""
|
||||
|
||||
#. module: edi
|
||||
#. openerp-web
|
||||
#: code:addons/edi/static/src/js/edi.js:71
|
||||
#, python-format
|
||||
msgid "Document Import Notification"
|
||||
msgstr ""
|
||||
|
||||
#. module: edi
|
||||
#: code:addons/edi/models/edi.py:130
|
||||
#, python-format
|
||||
msgid "Missing application."
|
||||
msgstr ""
|
||||
|
||||
#. module: edi
|
||||
#: code:addons/edi/models/edi.py:131
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The document you are trying to import requires the OpenERP `%s` application. "
|
||||
"You can install it by connecting as the administrator and opening the "
|
||||
"configuration assistant."
|
||||
msgstr ""
|
||||
|
||||
#. module: edi
|
||||
#: code:addons/edi/models/edi.py:47
|
||||
#, python-format
|
||||
msgid "'%s' is an invalid external ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: edi
|
||||
#: model:ir.model,name:edi.model_res_partner
|
||||
msgid "Partner"
|
||||
msgstr ""
|
||||
|
||||
#. module: edi
|
||||
#: model:ir.model,name:edi.model_edi_edi
|
||||
msgid "EDI Subsystem"
|
||||
msgstr ""
|
|
@ -1,28 +1,12 @@
|
|||
# -*- 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 openerp
|
||||
from openerp.http import request
|
||||
from openerp.osv import osv
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.tools.translate import _
|
||||
from openerp.addons.web.http import request
|
||||
from datetime import datetime
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
|
||||
import werkzeug.urls
|
||||
import urllib2
|
||||
|
@ -31,6 +15,7 @@ import simplejson
|
|||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class google_service(osv.osv_memory):
|
||||
_name = 'google.service'
|
||||
|
||||
|
@ -48,7 +33,8 @@ class google_service(osv.osv_memory):
|
|||
req = urllib2.Request("https://accounts.google.com/o/oauth2/token", data, headers)
|
||||
content = urllib2.urlopen(req).read()
|
||||
except urllib2.HTTPError:
|
||||
raise self.pool.get('res.config.settings').get_config_warning(cr, _("Something went wrong during your token generation. Maybe your Authorization Code is invalid or already expired"), context=context)
|
||||
error_msg = "Something went wrong during your token generation. Maybe your Authorization Code is invalid or already expired"
|
||||
raise self.pool.get('res.config.settings').get_config_warning(cr, _(error_msg), context=context)
|
||||
|
||||
content = simplejson.loads(content)
|
||||
return content.get('refresh_token')
|
||||
|
@ -65,8 +51,8 @@ class google_service(osv.osv_memory):
|
|||
uri = 'https://accounts.google.com/o/oauth2/auth?%s' % werkzeug.url_encode(params)
|
||||
return uri
|
||||
|
||||
#If no scope is passed, we use service by default to get a default scope
|
||||
def _get_authorize_uri(self, cr, uid, from_url, service, scope = False, context=None):
|
||||
# If no scope is passed, we use service by default to get a default scope
|
||||
def _get_authorize_uri(self, cr, uid, from_url, service, scope=False, context=None):
|
||||
""" This method return the url needed to allow this instance of OpenErp to access to the scope of gmail specified as parameters """
|
||||
state_obj = dict(d=cr.dbname, s=service, f=from_url)
|
||||
|
||||
|
@ -76,11 +62,11 @@ class google_service(osv.osv_memory):
|
|||
params = {
|
||||
'response_type': 'code',
|
||||
'client_id': client_id,
|
||||
'state' : simplejson.dumps(state_obj),
|
||||
'state': simplejson.dumps(state_obj),
|
||||
'scope': scope or 'https://www.googleapis.com/auth/%s' % (service,),
|
||||
'redirect_uri': base_url + '/google_account/authentication',
|
||||
'approval_prompt':'force',
|
||||
'access_type':'offline'
|
||||
'approval_prompt': 'force',
|
||||
'access_type': 'offline'
|
||||
}
|
||||
|
||||
uri = self.get_uri_oauth(a='auth') + "?%s" % werkzeug.url_encode(params)
|
||||
|
@ -96,25 +82,24 @@ class google_service(osv.osv_memory):
|
|||
'code': authorize_code,
|
||||
'client_id': client_id,
|
||||
'client_secret': client_secret,
|
||||
'grant_type' : 'authorization_code',
|
||||
'grant_type': 'authorization_code',
|
||||
'redirect_uri': base_url + '/google_account/authentication'
|
||||
}
|
||||
|
||||
headers = {"content-type": "application/x-www-form-urlencoded"}
|
||||
|
||||
try:
|
||||
uri = self.get_uri_oauth(a='token')
|
||||
data = werkzeug.url_encode(params)
|
||||
req = urllib2.Request(self.get_uri_oauth(a='token'), data, headers)
|
||||
|
||||
content = urllib2.urlopen(req).read()
|
||||
res = simplejson.loads(content)
|
||||
except urllib2.HTTPError,e:
|
||||
raise self.pool.get('res.config.settings').get_config_warning(cr, _("Something went wrong during your token generation. Maybe your Authorization Code is invalid"), context=context)
|
||||
st, res = self._do_request(cr, uid, uri, params=data, headers=headers, type='POST', preuri='', context=context)
|
||||
except urllib2.HTTPError:
|
||||
error_msg = "Something went wrong during your token generation. Maybe your Authorization Code is invalid"
|
||||
raise self.pool.get('res.config.settings').get_config_warning(cr, _(error_msg), context=context)
|
||||
return res
|
||||
|
||||
def _refresh_google_token_json(self, cr, uid, refresh_token, service, context=None): #exchange_AUTHORIZATION vs Token (service = calendar)
|
||||
def _refresh_google_token_json(self, cr, uid, refresh_token, service, context=None): # exchange_AUTHORIZATION vs Token (service = calendar)
|
||||
res = False
|
||||
base_url = self.get_base_url(cr, uid, context)
|
||||
client_id = self.get_client_id(cr, uid, service, context)
|
||||
client_secret = self.get_client_secret(cr, uid, service, context)
|
||||
|
||||
|
@ -122,60 +107,79 @@ class google_service(osv.osv_memory):
|
|||
'refresh_token': refresh_token,
|
||||
'client_id': client_id,
|
||||
'client_secret': client_secret,
|
||||
'grant_type' : 'refresh_token'
|
||||
'grant_type': 'refresh_token',
|
||||
}
|
||||
|
||||
headers = {"content-type": "application/x-www-form-urlencoded"}
|
||||
|
||||
try:
|
||||
data = werkzeug.url_encode(params)
|
||||
req = urllib2.Request(self.get_uri_oauth(a='token'), data, headers)
|
||||
content = urllib2.urlopen(req).read()
|
||||
res = simplejson.loads(content)
|
||||
except urllib2.HTTPError:
|
||||
raise self.pool.get('res.config.settings').get_config_warning(cr, _("Something went wrong during your token generation. Maybe your Authorization Code is invalid or already expired"), context=context)
|
||||
uri = self.get_uri_oauth(a='token')
|
||||
|
||||
data = werkzeug.url_encode(params)
|
||||
st, res = self._do_request(cr, uid, uri, params=data, headers=headers, type='POST', preuri='', context=context)
|
||||
except urllib2.HTTPError, e:
|
||||
if e.code == 400: # invalid grant
|
||||
registry = openerp.modules.registry.RegistryManager.get(request.session.db)
|
||||
with registry.cursor() as cur:
|
||||
self.pool['res.users'].write(cur, uid, [uid], {'google_%s_rtoken' % service: False}, context=context)
|
||||
error_key = simplejson.loads(e.read()).get("error", "nc")
|
||||
_logger.exception("Bad google request : %s !" % error_key)
|
||||
error_msg = "Something went wrong during your token generation. Maybe your Authorization Code is invalid or already expired [%s]" % error_key
|
||||
raise self.pool.get('res.config.settings').get_config_warning(cr, _(error_msg), context=context)
|
||||
return res
|
||||
|
||||
def _do_request(self, cr, uid, uri, params={}, headers={}, type='POST', preuri="https://www.googleapis.com", context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
def _do_request(self,cr,uid,uri,params={},headers={},type='POST', context=None):
|
||||
_logger.debug("Uri: %s - Type : %s - Headers: %s - Params : %s !" % (uri,type,headers,werkzeug.url_encode(params) if type =='GET' else params))
|
||||
res = False
|
||||
""" Return a tuple ('HTTP_CODE', 'HTTP_RESPONSE') """
|
||||
_logger.debug("Uri: %s - Type : %s - Headers: %s - Params : %s !" % (uri, type, headers, werkzeug.url_encode(params) if type == 'GET' else params))
|
||||
|
||||
status = 418
|
||||
response = ""
|
||||
try:
|
||||
if type.upper() == 'GET' or type.upper() == 'DELETE':
|
||||
data = werkzeug.url_encode(params)
|
||||
req = urllib2.Request(self.get_uri_api() + uri + "?" + data)
|
||||
elif type.upper() == 'POST' or type.upper() == 'PATCH' or type.upper() == 'PUT':
|
||||
req = urllib2.Request(self.get_uri_api() + uri, params, headers)
|
||||
req = urllib2.Request(preuri + uri + "?" + data)
|
||||
elif type.upper() == 'POST' or type.upper() == 'PATCH' or type.upper() == 'PUT':
|
||||
req = urllib2.Request(preuri + uri, params, headers)
|
||||
else:
|
||||
raise ('Method not supported [%s] not in [GET, POST, PUT, PATCH or DELETE]!' % (type))
|
||||
req.get_method = lambda: type.upper()
|
||||
|
||||
request = urllib2.urlopen(req)
|
||||
status = request.getcode()
|
||||
|
||||
if request.getcode() == 204: #No content returned, (ex: POST calendar/event/clear)
|
||||
res = True
|
||||
elif request.getcode() == 404: #Page not found
|
||||
res = False
|
||||
if int(status) in (204, 404): # Page not found, no response
|
||||
response = False
|
||||
else:
|
||||
content=request.read()
|
||||
res = simplejson.loads(content)
|
||||
except urllib2.HTTPError,e:
|
||||
content = request.read()
|
||||
response = simplejson.loads(content)
|
||||
|
||||
if context.get('ask_time'):
|
||||
try:
|
||||
date = datetime.strptime(request.headers.get('date'), "%a, %d %b %Y %H:%M:%S %Z")
|
||||
except:
|
||||
date = datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
context['ask_time'] = date
|
||||
except urllib2.HTTPError, e:
|
||||
if e.code in (400, 401, 410):
|
||||
raise e
|
||||
|
||||
_logger.exception("Bad google request : %s !" % e.read())
|
||||
raise self.pool.get('res.config.settings').get_config_warning(cr, _("Something went wrong with your request to google"), context=context)
|
||||
return res
|
||||
return (status, response)
|
||||
|
||||
def get_base_url(self, cr, uid, context=None):
|
||||
return self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url',default='http://www.openerp.com?NoBaseUrl',context=context)
|
||||
return self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://www.openerp.com?NoBaseUrl', context=context)
|
||||
|
||||
def get_client_id(self, cr, uid, service, context=None):
|
||||
return self.pool.get('ir.config_parameter').get_param(cr, uid, 'google_%s_client_id' % (service,),default=False,context=context)
|
||||
return self.pool.get('ir.config_parameter').get_param(cr, uid, 'google_%s_client_id' % (service,), default=False, context=context)
|
||||
|
||||
def get_client_secret(self, cr, uid, service, context=None):
|
||||
return self.pool.get('ir.config_parameter').get_param(cr, uid, 'google_%s_client_secret' % (service,),default=False,context=context)
|
||||
return self.pool.get('ir.config_parameter').get_param(cr, uid, 'google_%s_client_secret' % (service,), default=False, context=context)
|
||||
|
||||
def get_uri_oauth(self,a=''): #a = optional action
|
||||
def get_uri_oauth(self, a=''): # a = optional action
|
||||
return "https://accounts.google.com/o/oauth2/%s" % (a,)
|
||||
|
||||
def get_uri_api(self):
|
||||
|
|
|
@ -25,20 +25,20 @@
|
|||
'version': '1.0',
|
||||
'category': 'Tools',
|
||||
'description': """
|
||||
The module adds the possibility to synchronize Google Calendar with OpenERP
|
||||
The module adds the possibility to synchronize Google Calendar with OpenERP
|
||||
========================================
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'website': 'http://www.openerp.com',
|
||||
'depends': ['google_account','calendar'],
|
||||
'depends': ['google_account', 'calendar'],
|
||||
'qweb': ['static/src/xml/*.xml'],
|
||||
'data': [
|
||||
'res_config_view.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'views/google_calendar.xml',
|
||||
'views/res_users.xml',
|
||||
],
|
||||
'demo': [],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,51 +1,60 @@
|
|||
import simplejson
|
||||
import urllib
|
||||
import openerp
|
||||
import openerp.addons.web.http as http
|
||||
from openerp.addons.web.http import request
|
||||
import openerp.addons.web.controllers.main as webmain
|
||||
from openerp.addons.web.http import SessionExpiredException
|
||||
from werkzeug.exceptions import BadRequest
|
||||
import werkzeug.utils
|
||||
|
||||
|
||||
class google_calendar_controller(http.Controller):
|
||||
|
||||
|
||||
@http.route('/google_calendar/sync_data', type='json', auth='user')
|
||||
def sync_data(self, arch, fields, model,**kw):
|
||||
"""
|
||||
def sync_data(self, arch, fields, model, **kw):
|
||||
"""
|
||||
This route/function is called when we want to synchronize openERP calendar with Google Calendar
|
||||
Function return a dictionary with the status : need_config_from_admin, need_auth, need_refresh, success if not calendar_event
|
||||
The dictionary may contains an url, to allow OpenERP Client to redirect user on this URL for authorization for example
|
||||
The dictionary may contains an url, to allow OpenERP Client to redirect user on this URL for authorization for example
|
||||
"""
|
||||
|
||||
|
||||
if model == 'calendar.event':
|
||||
gs_obj = request.registry['google.service']
|
||||
gc_obj = request.registry['google.calendar']
|
||||
|
||||
|
||||
# Checking that admin have already configured Google API for google synchronization !
|
||||
client_id = gs_obj.get_client_id(request.cr, request.uid,'calendar',context=kw.get('local_context'))
|
||||
client_id = gs_obj.get_client_id(request.cr, request.uid, 'calendar', context=kw.get('local_context'))
|
||||
|
||||
if not client_id or client_id == '':
|
||||
action = ''
|
||||
if gc_obj.can_authorize_google(request.cr,request.uid):
|
||||
dummy, action = request.registry.get('ir.model.data').get_object_reference(request.cr, request.uid, 'google_calendar', 'action_config_settings_google_calendar')
|
||||
|
||||
if gc_obj.can_authorize_google(request.cr, request.uid):
|
||||
dummy, action = request.registry.get('ir.model.data').get_object_reference(request.cr, request.uid,
|
||||
'google_calendar', 'action_config_settings_google_calendar')
|
||||
|
||||
return {
|
||||
"status" : "need_config_from_admin",
|
||||
"url" : '',
|
||||
"action" : action
|
||||
}
|
||||
|
||||
"status": "need_config_from_admin",
|
||||
"url": '',
|
||||
"action": action
|
||||
}
|
||||
|
||||
# Checking that user have already accepted OpenERP to access his calendar !
|
||||
if gc_obj.need_authorize(request.cr, request.uid,context=kw.get('local_context')):
|
||||
url = gc_obj.authorize_google_uri(request.cr, request.uid, from_url=kw.get('fromurl'), context=kw.get('local_context'))
|
||||
if gc_obj.need_authorize(request.cr, request.uid, context=kw.get('local_context')):
|
||||
url = gc_obj.authorize_google_uri(request.cr, request.uid, from_url=kw.get('fromurl'), context=kw.get('local_context'))
|
||||
return {
|
||||
"status" : "need_auth",
|
||||
"url" : url
|
||||
}
|
||||
|
||||
"status": "need_auth",
|
||||
"url": url
|
||||
}
|
||||
|
||||
# If App authorized, and user access accepted, We launch the synchronization
|
||||
return gc_obj.synchronize_events(request.cr, request.uid, [], kw.get('local_context'))
|
||||
|
||||
return { "status" : "success" }
|
||||
|
||||
return gc_obj.synchronize_events(request.cr, request.uid, [], context=kw.get('local_context'))
|
||||
|
||||
return {"status": "success"}
|
||||
|
||||
@http.route('/google_calendar/remove_references', type='json', auth='user')
|
||||
def remove_references(self, model, **kw):
|
||||
"""
|
||||
This route/function is called when we want to remove all the references between one calendar OpenERP and one Google Calendar
|
||||
"""
|
||||
status = "NOP"
|
||||
if model == 'calendar.event':
|
||||
gc_obj = request.registry['google.calendar']
|
||||
# Checking that user have already accepted OpenERP to access his calendar !
|
||||
if gc_obj.remove_references(request.cr, request.uid, context=kw.get('local_context')):
|
||||
status = "OK"
|
||||
else:
|
||||
status = "KO"
|
||||
return {"status": status}
|
||||
|
|
|
@ -1,31 +1,15 @@
|
|||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2012 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import operator
|
||||
import simplejson
|
||||
import urllib
|
||||
import urllib2
|
||||
|
||||
import openerp
|
||||
from openerp import tools
|
||||
from openerp import SUPERUSER_ID
|
||||
|
||||
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from openerp.tools.translate import _
|
||||
from openerp.http import request
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil import parser
|
||||
import pytz
|
||||
|
@ -35,6 +19,13 @@ import logging
|
|||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def status_response(status, substr=False):
|
||||
if substr:
|
||||
return int(str(status)[0])
|
||||
else:
|
||||
return status_response(status, substr=True) == 2
|
||||
|
||||
|
||||
class Meta(type):
|
||||
""" This Meta class allow to define class as a structure, and so instancied variable
|
||||
in __init__ to avoid to have side effect alike 'static' variable """
|
||||
|
@ -90,7 +81,7 @@ class SyncEvent(object):
|
|||
def __getitem__(self, key):
|
||||
return getattr(self, key)
|
||||
|
||||
def compute_OP(self):
|
||||
def compute_OP(self, modeFull=True):
|
||||
#If event are already in Gmail and in OpenERP
|
||||
if self.OE.found and self.GG.found:
|
||||
#If the event has been deleted from one side, we delete on other side !
|
||||
|
@ -120,7 +111,6 @@ class SyncEvent(object):
|
|||
else:
|
||||
if not self.OE.synchro or self.OE.synchro.split('.')[0] < self.OE.update.split('.')[0]:
|
||||
self.OP = Update('OE', 'Event already updated by another user, but not synchro with my google calendar')
|
||||
#import ipdb; ipdb.set_trace();
|
||||
else:
|
||||
self.OP = NothingToDo("", 'Not update needed')
|
||||
else:
|
||||
|
@ -128,11 +118,13 @@ class SyncEvent(object):
|
|||
|
||||
# New in openERP... Create on create_events of synchronize function
|
||||
elif self.OE.found and not self.GG.found:
|
||||
#Has been deleted from gmail
|
||||
if self.OE.status:
|
||||
self.OP = Delete('OE', 'Removed from GOOGLE')
|
||||
self.OP = Delete('OE', 'Update or delete from GOOGLE')
|
||||
else:
|
||||
self.OP = NothingToDo("", "Already Deleted in gmail and unlinked in OpenERP")
|
||||
if not modeFull:
|
||||
self.OP = Delete('GG', 'Deleted from OpenERP, need to delete it from Gmail if already created')
|
||||
else:
|
||||
self.OP = NothingToDo("", "Already Deleted in gmail and unlinked in OpenERP")
|
||||
elif self.GG.found and not self.OE.found:
|
||||
tmpSrc = 'GG'
|
||||
if not self.GG.status and not self.GG.isInstance:
|
||||
|
@ -151,11 +143,11 @@ class SyncEvent(object):
|
|||
return self.__repr__()
|
||||
|
||||
def __repr__(self):
|
||||
myPrint = "---- A SYNC EVENT ---"
|
||||
myPrint = "\n\n---- A SYNC EVENT ---"
|
||||
myPrint += "\n ID OE: %s " % (self.OE.event and self.OE.event.id)
|
||||
myPrint += "\n ID GG: %s " % (self.GG.event and self.GG.event.get('id', False))
|
||||
myPrint += "\n Name OE: %s " % (self.OE.event and self.OE.event.name)
|
||||
myPrint += "\n Name GG: %s " % (self.GG.event and self.GG.event.get('summary', False))
|
||||
myPrint += "\n Name OE: %s " % (self.OE.event and self.OE.event.name.encode('utf8'))
|
||||
myPrint += "\n Name GG: %s " % (self.GG.event and self.GG.event.get('summary', '').encode('utf8'))
|
||||
myPrint += "\n Found OE:%5s vs GG: %5s" % (self.OE.found, self.GG.found)
|
||||
myPrint += "\n Recurrence OE:%5s vs GG: %5s" % (self.OE.isRecurrence, self.GG.isRecurrence)
|
||||
myPrint += "\n Instance OE:%5s vs GG: %5s" % (self.OE.isInstance, self.GG.isInstance)
|
||||
|
@ -207,15 +199,15 @@ class google_calendar(osv.AbstractModel):
|
|||
STR_SERVICE = 'calendar'
|
||||
_name = 'google.%s' % STR_SERVICE
|
||||
|
||||
def generate_data(self, cr, uid, event, context=None):
|
||||
def generate_data(self, cr, uid, event, isCreating=False, context=None):
|
||||
if event.allday:
|
||||
start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T').split('T')[0]
|
||||
end_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(hours=event.duration), context=context).isoformat('T').split('T')[0]
|
||||
start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.start, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T').split('T')[0]
|
||||
final_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.start, tools.DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(hours=event.duration) + timedelta(days=isCreating and 1 or 0), context=context).isoformat('T').split('T')[0]
|
||||
type = 'date'
|
||||
vstype = 'dateTime'
|
||||
else:
|
||||
start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
|
||||
end_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date_deadline, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
|
||||
start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.start, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
|
||||
final_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.stop, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
|
||||
type = 'dateTime'
|
||||
vstype = 'date'
|
||||
attendee_list = []
|
||||
|
@ -235,7 +227,7 @@ class google_calendar(osv.AbstractModel):
|
|||
'timeZone': 'UTC'
|
||||
},
|
||||
"end": {
|
||||
type: end_date,
|
||||
type: final_date,
|
||||
vstype: None,
|
||||
'timeZone': 'UTC'
|
||||
},
|
||||
|
@ -256,10 +248,9 @@ class google_calendar(osv.AbstractModel):
|
|||
|
||||
def create_an_event(self, cr, uid, event, context=None):
|
||||
gs_pool = self.pool['google.service']
|
||||
data = self.generate_data(cr, uid, event, isCreating=True, context=context)
|
||||
|
||||
data = self.generate_data(cr, uid, event, context=context)
|
||||
|
||||
url = "/calendar/v3/calendars/%s/events?fields=%s&access_token=%s" % ('primary', urllib.quote('id,updated'), self.get_token(cr, uid, context))
|
||||
url = "/calendar/v3/calendars/%s/events?fields=%s&access_token=%s" % ('primary', urllib2.quote('id,updated'), self.get_token(cr, uid, context))
|
||||
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
||||
data_json = simplejson.dumps(data)
|
||||
|
||||
|
@ -276,38 +267,89 @@ class google_calendar(osv.AbstractModel):
|
|||
|
||||
return gs_pool._do_request(cr, uid, url, params, headers, type='DELETE', context=context)
|
||||
|
||||
def get_event_dict(self, cr, uid, token=False, nextPageToken=False, context=None):
|
||||
def get_calendar_primary_id(self, cr, uid, context=None):
|
||||
params = {
|
||||
'fields': 'id',
|
||||
'access_token': self.get_token(cr, uid, context)
|
||||
}
|
||||
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
||||
|
||||
url = "/calendar/v3/calendars/primary"
|
||||
|
||||
try:
|
||||
st, content = self.pool['google.service']._do_request(cr, uid, url, params, headers, type='GET', context=context)
|
||||
except Exception, e:
|
||||
|
||||
if (e.code == 401): # Token invalid / Acces unauthorized
|
||||
error_msg = "Your token is invalid or has been revoked !"
|
||||
|
||||
registry = openerp.modules.registry.RegistryManager.get(request.session.db)
|
||||
with registry.cursor() as cur:
|
||||
self.pool['res.users'].write(cur, uid, [uid], {'google_calendar_token': False, 'google_calendar_token_validity': False}, context=context)
|
||||
|
||||
raise self.pool.get('res.config.settings').get_config_warning(cr, _(error_msg), context=context)
|
||||
raise
|
||||
|
||||
return status_response(st) and content['id'] or False
|
||||
|
||||
def get_event_synchro_dict(self, cr, uid, lastSync=False, token=False, nextPageToken=False, context=None):
|
||||
if not token:
|
||||
token = self.get_token(cr, uid, context)
|
||||
|
||||
gs_pool = self.pool['google.service']
|
||||
|
||||
params = {
|
||||
'fields': 'items,nextPageToken',
|
||||
'access_token': token,
|
||||
'maxResults': 1000,
|
||||
'timeMin': self.get_start_time_to_synchro(cr, uid, context=context).strftime("%Y-%m-%dT%H:%M:%S.%fz"),
|
||||
#'timeMin': self.get_minTime(cr, uid, context=context).strftime("%Y-%m-%dT%H:%M:%S.%fz"),
|
||||
}
|
||||
|
||||
if lastSync:
|
||||
params['updatedMin'] = lastSync.strftime("%Y-%m-%dT%H:%M:%S.%fz")
|
||||
params['showDeleted'] = True
|
||||
else:
|
||||
params['timeMin'] = self.get_minTime(cr, uid, context=context).strftime("%Y-%m-%dT%H:%M:%S.%fz")
|
||||
|
||||
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
||||
|
||||
url = "/calendar/v3/calendars/%s/events" % 'primary'
|
||||
if nextPageToken:
|
||||
params['pageToken'] = nextPageToken
|
||||
|
||||
content = gs_pool._do_request(cr, uid, url, params, headers, type='GET', context=context)
|
||||
status, content = self.pool['google.service']._do_request(cr, uid, url, params, headers, type='GET', context=context)
|
||||
|
||||
google_events_dict = {}
|
||||
|
||||
for google_event in content['items']:
|
||||
google_events_dict[google_event['id']] = google_event
|
||||
|
||||
if content.get('nextPageToken', False):
|
||||
google_events_dict.update(self.get_event_dict(cr, uid, token, content['nextPageToken'], context=context))
|
||||
if content.get('nextPageToken'):
|
||||
google_events_dict.update(
|
||||
self.get_event_synchro_dict(cr, uid, lastSync=lastSync, token=token, nextPageToken=content['nextPageToken'], context=context)
|
||||
)
|
||||
|
||||
return google_events_dict
|
||||
|
||||
def get_one_event_synchro(self, cr, uid, google_id, context=None):
|
||||
token = self.get_token(cr, uid, context)
|
||||
|
||||
params = {
|
||||
'access_token': token,
|
||||
'maxResults': 1000,
|
||||
'showDeleted': True,
|
||||
}
|
||||
|
||||
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
||||
|
||||
url = "/calendar/v3/calendars/%s/events/%s" % ('primary', google_id)
|
||||
try:
|
||||
status, content = self.pool['google.service']._do_request(cr, uid, url, params, headers, type='GET', context=context)
|
||||
except:
|
||||
_logger.info("Calendar Synchro - In except of get_one_event_synchro")
|
||||
pass
|
||||
|
||||
return status_response(status) and content or False
|
||||
|
||||
def update_to_google(self, cr, uid, oe_event, google_event, context):
|
||||
calendar_event = self.pool['calendar.event']
|
||||
gs_pool = self.pool['google.service']
|
||||
|
||||
url = "/calendar/v3/calendars/%s/events/%s?fields=%s&access_token=%s" % ('primary', google_event['id'], 'id,updated', self.get_token(cr, uid, context))
|
||||
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
||||
|
@ -315,7 +357,7 @@ class google_calendar(osv.AbstractModel):
|
|||
data['sequence'] = google_event.get('sequence', 0)
|
||||
data_json = simplejson.dumps(data)
|
||||
|
||||
content = gs_pool._do_request(cr, uid, url, data_json, headers, type='PATCH', context=context)
|
||||
status, content = self.pool['google.service']._do_request(cr, uid, url, data_json, headers, type='PATCH', context=context)
|
||||
|
||||
update_date = datetime.strptime(content['updated'], "%Y-%m-%dT%H:%M:%S.%fz")
|
||||
calendar_event.write(cr, uid, [oe_event.id], {'oe_update_date': update_date})
|
||||
|
@ -324,15 +366,13 @@ class google_calendar(osv.AbstractModel):
|
|||
self.pool['calendar.attendee'].write(cr, uid, [context['curr_attendee']], {'oe_synchro_date': update_date}, context)
|
||||
|
||||
def update_an_event(self, cr, uid, event, context=None):
|
||||
gs_pool = self.pool['google.service']
|
||||
|
||||
data = self.generate_data(cr, uid, event, context=context)
|
||||
|
||||
url = "/calendar/v3/calendars/%s/events/%s" % ('primary', event.google_internal_event_id)
|
||||
headers = {}
|
||||
data['access_token'] = self.get_token(cr, uid, context)
|
||||
|
||||
response = gs_pool._do_request(cr, uid, url, data, headers, type='GET', context=context)
|
||||
status, response = self.pool['google.service']._do_request(cr, uid, url, data, headers, type='GET', context=context)
|
||||
#TO_CHECK : , if http fail, no event, do DELETE ?
|
||||
return response
|
||||
|
||||
|
@ -379,7 +419,12 @@ class google_calendar(osv.AbstractModel):
|
|||
if self.get_need_synchro_attendee(cr, uid, context=context):
|
||||
attendee_id = res_partner_obj.search(cr, uid, [('email', '=', google_attendee['email'])], context=context)
|
||||
if not attendee_id:
|
||||
attendee_id = [res_partner_obj.create(cr, uid, {'email': google_attendee['email'], 'customer': False, 'name': google_attendee.get("displayName", False) or google_attendee['email']}, context=context)]
|
||||
data = {
|
||||
'email': google_attendee['email'],
|
||||
'customer': False,
|
||||
'name': google_attendee.get("displayName", False) or google_attendee['email']
|
||||
}
|
||||
attendee_id = [res_partner_obj.create(cr, uid, data, context=context)]
|
||||
attendee = res_partner_obj.read(cr, uid, attendee_id[0], ['email'], context=context)
|
||||
partner_record.append((4, attendee.get('id')))
|
||||
attendee['partner_id'] = attendee.pop('id')
|
||||
|
@ -388,26 +433,25 @@ class google_calendar(osv.AbstractModel):
|
|||
|
||||
UTC = pytz.timezone('UTC')
|
||||
if single_event_dict.get('start') and single_event_dict.get('end'): # If not cancelled
|
||||
|
||||
if single_event_dict['start'].get('dateTime', False) and single_event_dict['end'].get('dateTime', False):
|
||||
date = parser.parse(single_event_dict['start']['dateTime'])
|
||||
date_deadline = parser.parse(single_event_dict['end']['dateTime'])
|
||||
delta = date_deadline.astimezone(UTC) - date.astimezone(UTC)
|
||||
stop = parser.parse(single_event_dict['end']['dateTime'])
|
||||
date = str(date.astimezone(UTC))[:-6]
|
||||
date_deadline = str(date_deadline.astimezone(UTC))[:-6]
|
||||
stop = str(stop.astimezone(UTC))[:-6]
|
||||
allday = False
|
||||
else:
|
||||
date = (single_event_dict['start']['date'] + ' 00:00:00')
|
||||
date_deadline = (single_event_dict['end']['date'] + ' 00:00:00')
|
||||
d_start = datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
|
||||
d_end = datetime.strptime(date_deadline, "%Y-%m-%d %H:%M:%S")
|
||||
delta = (d_end - d_start)
|
||||
date = (single_event_dict['start']['date'])
|
||||
stop = (single_event_dict['end']['date'])
|
||||
d_end = datetime.strptime(stop, DEFAULT_SERVER_DATE_FORMAT)
|
||||
allday = True
|
||||
d_end = d_end + timedelta(days=-1)
|
||||
stop = d_end.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
|
||||
result['duration'] = (delta.seconds / 60) / 60.0 + delta.days * 24
|
||||
update_date = datetime.strptime(single_event_dict['updated'], "%Y-%m-%dT%H:%M:%S.%fz")
|
||||
result.update({
|
||||
'date': date,
|
||||
'date_deadline': date_deadline,
|
||||
'start': date,
|
||||
'stop': stop,
|
||||
'allday': allday
|
||||
})
|
||||
result.update({
|
||||
|
@ -419,7 +463,6 @@ class google_calendar(osv.AbstractModel):
|
|||
'location': single_event_dict.get('location', False),
|
||||
'class': single_event_dict.get('visibility', 'public'),
|
||||
'oe_update_date': update_date,
|
||||
# 'google_internal_event_id': single_event_dict.get('id',False),
|
||||
})
|
||||
|
||||
if single_event_dict.get("recurrence", False):
|
||||
|
@ -431,7 +474,6 @@ class google_calendar(osv.AbstractModel):
|
|||
elif type == "copy":
|
||||
result['recurrence'] = True
|
||||
res = calendar_event.write(cr, uid, [event['id']], result, context=context)
|
||||
|
||||
elif type == "create":
|
||||
res = calendar_event.create(cr, uid, result, context=context)
|
||||
|
||||
|
@ -439,18 +481,81 @@ class google_calendar(osv.AbstractModel):
|
|||
self.pool['calendar.attendee'].write(cr, uid, [context['curr_attendee']], {'oe_synchro_date': update_date, 'google_internal_event_id': single_event_dict.get('id', False)}, context)
|
||||
return res
|
||||
|
||||
def synchronize_events(self, cr, uid, ids, context=None):
|
||||
# Create all new events from OpenERP into Gmail, if that is not recurrent event
|
||||
self.create_new_events(cr, uid, context=context)
|
||||
self.bind_recurring_events_to_google(cr, uid, context)
|
||||
res = self.update_events(cr, uid, context)
|
||||
def remove_references(self, cr, uid, context=None):
|
||||
current_user = self.pool['res.users'].browse(cr, uid, uid, context=context)
|
||||
reset_data = {
|
||||
'google_calendar_rtoken': False,
|
||||
'google_calendar_token': False,
|
||||
'google_calendar_token_validity': False,
|
||||
'google_calendar_last_sync_date': False,
|
||||
'google_calendar_cal_id': False,
|
||||
}
|
||||
|
||||
all_my_attendees = self.pool['calendar.attendee'].search(cr, uid, [('partner_id', '=', current_user.partner_id.id)], context=context)
|
||||
self.pool['calendar.attendee'].write(cr, uid, all_my_attendees, {'oe_synchro_date': False, 'google_internal_event_id': False}, context=context)
|
||||
current_user.write(reset_data, context=context)
|
||||
return True
|
||||
|
||||
def synchronize_events(self, cr, uid, ids, lastSync=True, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
# def isValidSync(syncToken):
|
||||
# gs_pool = self.pool['google.service']
|
||||
# params = {
|
||||
# 'maxResults': 1,
|
||||
# 'fields': 'id',
|
||||
# 'access_token': self.get_token(cr, uid, context),
|
||||
# 'syncToken': syncToken,
|
||||
# }
|
||||
# url = "/calendar/v3/calendars/primary/events"
|
||||
# status, response = gs_pool._do_request(cr, uid, url, params, type='GET', context=context)
|
||||
# return int(status) != 410
|
||||
|
||||
current_user = self.pool['res.users'].browse(cr, uid, uid, context=context)
|
||||
|
||||
context_with_time = dict(context.copy(), ask_time=True)
|
||||
current_google = self.get_calendar_primary_id(cr, uid, context=context_with_time)
|
||||
|
||||
if current_user.google_calendar_cal_id:
|
||||
if current_google != current_user.google_calendar_cal_id:
|
||||
return {
|
||||
"status": "need_reset",
|
||||
"info": {
|
||||
"old_name": current_user.google_calendar_cal_id,
|
||||
"new_name": current_google
|
||||
},
|
||||
"url": ''
|
||||
}
|
||||
|
||||
if lastSync and self.get_last_sync_date(cr, uid, context=context) and not self.get_disable_since_synchro(cr, uid, context=context):
|
||||
lastSync = self.get_last_sync_date(cr, uid, context)
|
||||
_logger.info("Calendar Synchro - MODE SINCE_MODIFIED : %s !" % lastSync.strftime(DEFAULT_SERVER_DATETIME_FORMAT))
|
||||
else:
|
||||
lastSync = False
|
||||
_logger.info("Calendar Synchro - MODE FULL SYNCHRO FORCED")
|
||||
else:
|
||||
current_user.write({'google_calendar_cal_id': current_google}, context=context)
|
||||
lastSync = False
|
||||
_logger.info("Calendar Synchro - MODE FULL SYNCHRO - NEW CAL ID")
|
||||
|
||||
new_ids = []
|
||||
new_ids += self.create_new_events(cr, uid, context=context)
|
||||
new_ids += self.bind_recurring_events_to_google(cr, uid, context)
|
||||
|
||||
res = self.update_events(cr, uid, lastSync, context)
|
||||
|
||||
current_user.write({'google_calendar_last_sync_date': context_with_time.get('ask_time')}, context=context)
|
||||
return {
|
||||
"status": res and "need_refresh" or "no_new_event_form_google",
|
||||
"url": ''
|
||||
}
|
||||
|
||||
def create_new_events(self, cr, uid, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
new_ids = []
|
||||
ev_obj = self.pool['calendar.event']
|
||||
att_obj = self.pool['calendar.attendee']
|
||||
user_obj = self.pool['res.users']
|
||||
|
@ -458,32 +563,43 @@ class google_calendar(osv.AbstractModel):
|
|||
|
||||
context_norecurrent = context.copy()
|
||||
context_norecurrent['virtual_id'] = False
|
||||
|
||||
my_att_ids = att_obj.search(cr, uid, [('partner_id', '=', myPartnerID),
|
||||
('google_internal_event_id', '=', False),
|
||||
'|',
|
||||
('event_id.date_deadline', '>', self.get_start_time_to_synchro(cr, uid, context).strftime("%Y-%m-%d %H:%M:%S")),
|
||||
('event_id.end_date', '>', self.get_start_time_to_synchro(cr, uid, context).strftime("%Y-%m-%d %H:%M:%S")),
|
||||
('event_id.stop', '>', self.get_minTime(cr, uid, context=context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||
('event_id.final_date', '>', self.get_minTime(cr, uid, context=context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||
], context=context_norecurrent)
|
||||
|
||||
for att in att_obj.browse(cr, uid, my_att_ids, context=context):
|
||||
if not att.event_id.recurrent_id or att.event_id.recurrent_id == 0:
|
||||
response = self.create_an_event(cr, uid, att.event_id, context=context)
|
||||
update_date = datetime.strptime(response['updated'], "%Y-%m-%dT%H:%M:%S.%fz")
|
||||
ev_obj.write(cr, uid, att.event_id.id, {'oe_update_date': update_date})
|
||||
att_obj.write(cr, uid, [att.id], {'google_internal_event_id': response['id'], 'oe_synchro_date': update_date})
|
||||
cr.commit()
|
||||
st, response = self.create_an_event(cr, uid, att.event_id, context=context)
|
||||
if status_response(st):
|
||||
update_date = datetime.strptime(response['updated'], "%Y-%m-%dT%H:%M:%S.%fz")
|
||||
ev_obj.write(cr, uid, att.event_id.id, {'oe_update_date': update_date})
|
||||
new_ids.append(response['id'])
|
||||
att_obj.write(cr, uid, [att.id], {'google_internal_event_id': response['id'], 'oe_synchro_date': update_date})
|
||||
cr.commit()
|
||||
else:
|
||||
_logger.warning("Impossible to create event %s. [%s]" % (att.event_id.id, st))
|
||||
_logger.warning("Response : %s" % response)
|
||||
return new_ids
|
||||
|
||||
def bind_recurring_events_to_google(self, cr, uid, context):
|
||||
def get_context_no_virtual(self, context):
|
||||
context_norecurrent = context.copy()
|
||||
context_norecurrent['virtual_id'] = False
|
||||
context_norecurrent['active_test'] = False
|
||||
return context_norecurrent
|
||||
|
||||
def bind_recurring_events_to_google(self, cr, uid, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
new_ids = []
|
||||
ev_obj = self.pool['calendar.event']
|
||||
att_obj = self.pool['calendar.attendee']
|
||||
user_obj = self.pool['res.users']
|
||||
myPartnerID = user_obj.browse(cr, uid, uid, context=context).partner_id.id
|
||||
|
||||
context_norecurrent = context.copy()
|
||||
context_norecurrent['virtual_id'] = False
|
||||
context_norecurrent['active_test'] = False
|
||||
|
||||
context_norecurrent = self.get_context_no_virtual(context)
|
||||
my_att_ids = att_obj.search(cr, uid, [('partner_id', '=', myPartnerID), ('google_internal_event_id', '=', False)], context=context_norecurrent)
|
||||
|
||||
for att in att_obj.browse(cr, uid, my_att_ids, context=context):
|
||||
|
@ -500,11 +616,20 @@ class google_calendar(osv.AbstractModel):
|
|||
|
||||
if new_google_internal_event_id:
|
||||
#TODO WARNING, NEED TO CHECK THAT EVENT and ALL instance NOT DELETE IN GMAIL BEFORE !
|
||||
self.update_recurrent_event_exclu(cr, uid, new_google_internal_event_id, source_attendee_record.google_internal_event_id, att.event_id, context=context)
|
||||
att_obj.write(cr, uid, [att.id], {'google_internal_event_id': new_google_internal_event_id}, context=context)
|
||||
cr.commit()
|
||||
try:
|
||||
st, response = self.update_recurrent_event_exclu(cr, uid, new_google_internal_event_id, source_attendee_record.google_internal_event_id, att.event_id, context=context)
|
||||
if status_response(st):
|
||||
att_obj.write(cr, uid, [att.id], {'google_internal_event_id': new_google_internal_event_id}, context=context)
|
||||
new_ids.append(new_google_internal_event_id)
|
||||
cr.commit()
|
||||
else:
|
||||
_logger.warning("Impossible to create event %s. [%s]" % (att.event_id.id, st))
|
||||
_logger.warning("Response : %s" % response)
|
||||
except:
|
||||
pass
|
||||
return new_ids
|
||||
|
||||
def update_events(self, cr, uid, context=None):
|
||||
def update_events(self, cr, uid, lastSync=False, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
|
@ -512,20 +637,64 @@ class google_calendar(osv.AbstractModel):
|
|||
user_obj = self.pool['res.users']
|
||||
att_obj = self.pool['calendar.attendee']
|
||||
myPartnerID = user_obj.browse(cr, uid, uid, context=context).partner_id.id
|
||||
context_novirtual = self.get_context_no_virtual(context)
|
||||
|
||||
context_novirtual = context.copy()
|
||||
context_novirtual['virtual_id'] = False
|
||||
context_novirtual['active_test'] = False
|
||||
if lastSync:
|
||||
try:
|
||||
all_event_from_google = self.get_event_synchro_dict(cr, uid, lastSync=lastSync, context=context)
|
||||
except urllib2.HTTPError, e:
|
||||
if e.code == 410: # GONE, Google is lost.
|
||||
# we need to force the rollback from this cursor, because it locks my res_users but I need to write in this tuple before to raise.
|
||||
cr.rollback()
|
||||
registry = openerp.modules.registry.RegistryManager.get(request.session.db)
|
||||
with registry.cursor() as cur:
|
||||
self.pool['res.users'].write(cur, uid, [uid], {'google_calendar_last_sync_date': False}, context=context)
|
||||
error_key = simplejson.loads(e.read())
|
||||
error_key = error_key.get('error', {}).get('message', 'nc')
|
||||
error_msg = "Google are lost... the next synchro will be a full synchro. \n\n %s" % error_key
|
||||
raise self.pool.get('res.config.settings').get_config_warning(cr, _(error_msg), context=context)
|
||||
|
||||
all_event_from_google = self.get_event_dict(cr, uid, context=context)
|
||||
my_google_att_ids = att_obj.search(cr, uid, [
|
||||
('partner_id', '=', myPartnerID),
|
||||
('google_internal_event_id', 'in', all_event_from_google.keys())
|
||||
], context=context_novirtual)
|
||||
|
||||
my_openerp_att_ids = att_obj.search(cr, uid, [
|
||||
('partner_id', '=', myPartnerID),
|
||||
('event_id.oe_update_date', '>', lastSync and lastSync.strftime(DEFAULT_SERVER_DATETIME_FORMAT) or self.get_minTime(cr, uid, context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||
('google_internal_event_id', '!=', False),
|
||||
], context=context_novirtual)
|
||||
|
||||
my_openerp_googleinternal_ids = att_obj.read(cr, uid, my_openerp_att_ids, ['google_internal_event_id', 'event_id'], context=context_novirtual)
|
||||
|
||||
if self.get_print_log(cr, uid, context=context):
|
||||
_logger.info("Calendar Synchro - \n\nUPDATE IN GOOGLE\n%s\n\nRETRIEVE FROM OE\n%s\n\nUPDATE IN OE\n%s\n\nRETRIEVE FROM GG\n%s\n\n" % (all_event_from_google, my_google_att_ids, my_openerp_att_ids, my_openerp_googleinternal_ids))
|
||||
|
||||
for giid in my_openerp_googleinternal_ids:
|
||||
active = True # if not sure, we request google
|
||||
if giid.get('event_id'):
|
||||
active = calendar_event.browse(cr, uid, int(giid.get('event_id')[0]), context=context_novirtual).active
|
||||
|
||||
if giid.get('google_internal_event_id') and not all_event_from_google.get(giid.get('google_internal_event_id')) and active:
|
||||
one_event = self.get_one_event_synchro(cr, uid, giid.get('google_internal_event_id'), context=context)
|
||||
if one_event:
|
||||
all_event_from_google[one_event['id']] = one_event
|
||||
|
||||
my_att_ids = list(set(my_google_att_ids + my_openerp_att_ids))
|
||||
|
||||
else:
|
||||
domain = [
|
||||
('partner_id', '=', myPartnerID),
|
||||
('google_internal_event_id', '!=', False),
|
||||
'|',
|
||||
('event_id.stop', '>', self.get_minTime(cr, uid, context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||
('event_id.final_date', '>', self.get_minTime(cr, uid, context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||
]
|
||||
|
||||
# Select all events from OpenERP which have been already synchronized in gmail
|
||||
my_att_ids = att_obj.search(cr, uid, domain, context=context_novirtual)
|
||||
all_event_from_google = self.get_event_synchro_dict(cr, uid, lastSync=False, context=context)
|
||||
|
||||
# Select all events from OpenERP which have been already synchronized in gmail
|
||||
my_att_ids = att_obj.search(cr, uid, [('partner_id', '=', myPartnerID),
|
||||
('google_internal_event_id', '!=', False),
|
||||
'|',
|
||||
('event_id.date_deadline', '>', self.get_start_time_to_synchro(cr, uid, context).strftime("%Y-%m-%d %H:%M:%S")),
|
||||
('event_id.end_date', '>', self.get_start_time_to_synchro(cr, uid, context).strftime("%Y-%m-%d %H:%M:%S")),
|
||||
], context=context_novirtual)
|
||||
event_to_synchronize = {}
|
||||
for att in att_obj.browse(cr, uid, my_att_ids, context=context):
|
||||
event = att.event_id
|
||||
|
@ -576,8 +745,10 @@ class google_calendar(osv.AbstractModel):
|
|||
######################
|
||||
for base_event in event_to_synchronize:
|
||||
for current_event in event_to_synchronize[base_event]:
|
||||
event_to_synchronize[base_event][current_event].compute_OP()
|
||||
#print event_to_synchronize[base_event]
|
||||
event_to_synchronize[base_event][current_event].compute_OP(modeFull=not lastSync)
|
||||
if self.get_print_log(cr, uid, context=context):
|
||||
if not isinstance(event_to_synchronize[base_event][current_event].OP, NothingToDo):
|
||||
_logger.info(event_to_synchronize[base_event])
|
||||
|
||||
######################
|
||||
# DO ACTION #
|
||||
|
@ -615,24 +786,35 @@ class google_calendar(osv.AbstractModel):
|
|||
if actSrc == 'OE':
|
||||
self.delete_an_event(cr, uid, current_event[0], context=context)
|
||||
elif actSrc == 'GG':
|
||||
new_google_event_id = event.GG.event['id'].split('_')[1]
|
||||
if 'T' in new_google_event_id:
|
||||
new_google_event_id = new_google_event_id.replace('T', '')[:-1]
|
||||
else:
|
||||
new_google_event_id = new_google_event_id + "000000"
|
||||
new_google_event_id = event.GG.event['id'].split('_')[1]
|
||||
if 'T' in new_google_event_id:
|
||||
new_google_event_id = new_google_event_id.replace('T', '')[:-1]
|
||||
else:
|
||||
new_google_event_id = new_google_event_id + "000000"
|
||||
|
||||
if event.GG.status:
|
||||
parent_event = {}
|
||||
parent_event['id'] = "%s-%s" % (event_to_synchronize[base_event][0][1].OE.event_id, new_google_event_id)
|
||||
res = self.update_from_google(cr, uid, parent_event, event.GG.event, "copy", context)
|
||||
else:
|
||||
if event_to_synchronize[base_event][0][1].OE.event_id:
|
||||
parent_oe_id = event_to_synchronize[base_event][0][1].OE.event_id
|
||||
calendar_event.unlink(cr, uid, "%s-%s" % (parent_oe_id, new_google_event_id), can_be_deleted=True, context=context)
|
||||
if event.GG.status:
|
||||
parent_event = {}
|
||||
if not event_to_synchronize[base_event][0][1].OE.event_id:
|
||||
event_to_synchronize[base_event][0][1].OE.event_id = att_obj.search_read(cr, uid, [('google_internal_event_id', '=', event.GG.event['id'].split('_')[0])], ['event_id'], context=context_novirtual)[0].get('event_id')[0]
|
||||
|
||||
parent_event['id'] = "%s-%s" % (event_to_synchronize[base_event][0][1].OE.event_id, new_google_event_id)
|
||||
res = self.update_from_google(cr, uid, parent_event, event.GG.event, "copy", context)
|
||||
else:
|
||||
parent_oe_id = event_to_synchronize[base_event][0][1].OE.event_id
|
||||
calendar_event.unlink(cr, uid, "%s-%s" % (parent_oe_id, new_google_event_id), can_be_deleted=True, context=context)
|
||||
|
||||
elif isinstance(actToDo, Delete):
|
||||
if actSrc == 'GG':
|
||||
self.delete_an_event(cr, uid, current_event[0], context=context)
|
||||
try:
|
||||
self.delete_an_event(cr, uid, current_event[0], context=context)
|
||||
except Exception, e:
|
||||
error = simplejson.loads(e.read())
|
||||
error_nr = error.get('error', {}).get('code')
|
||||
# if already deleted from gmail or never created
|
||||
if error_nr in (404, 410,):
|
||||
pass
|
||||
else:
|
||||
raise e
|
||||
elif actSrc == 'OE':
|
||||
calendar_event.unlink(cr, uid, event.OE.event_id, can_be_deleted=False, context=context)
|
||||
return True
|
||||
|
@ -655,7 +837,7 @@ class google_calendar(osv.AbstractModel):
|
|||
|
||||
url = "/calendar/v3/calendars/%s/events/%s" % ('primary', instance_id)
|
||||
|
||||
content = gs_pool._do_request(cr, uid, url, params, headers, type='GET', context=context)
|
||||
st, content = gs_pool._do_request(cr, uid, url, params, headers, type='GET', context=context)
|
||||
return content.get('sequence', 0)
|
||||
#################################
|
||||
## MANAGE CONNEXION TO GMAIL ##
|
||||
|
@ -663,13 +845,16 @@ class google_calendar(osv.AbstractModel):
|
|||
|
||||
def get_token(self, cr, uid, context=None):
|
||||
current_user = self.pool['res.users'].browse(cr, uid, uid, context=context)
|
||||
|
||||
if datetime.strptime(current_user.google_calendar_token_validity.split('.')[0], "%Y-%m-%d %H:%M:%S") < (datetime.now() + timedelta(minutes=1)):
|
||||
if not current_user.google_calendar_token_validity or \
|
||||
datetime.strptime(current_user.google_calendar_token_validity.split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT) < (datetime.now() + timedelta(minutes=1)):
|
||||
self.do_refresh_token(cr, uid, context=context)
|
||||
current_user.refresh()
|
||||
|
||||
return current_user.google_calendar_token
|
||||
|
||||
def get_last_sync_date(self, cr, uid, context=None):
|
||||
current_user = self.pool['res.users'].browse(cr, uid, uid, context=context)
|
||||
return current_user.google_calendar_last_sync_date and datetime.strptime(current_user.google_calendar_last_sync_date, DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(minutes=0) or False
|
||||
|
||||
def do_refresh_token(self, cr, uid, context=None):
|
||||
current_user = self.pool['res.users'].browse(cr, uid, uid, context=context)
|
||||
gs_pool = self.pool['google.service']
|
||||
|
@ -707,14 +892,18 @@ class google_calendar(osv.AbstractModel):
|
|||
vals['google_%s_token' % self.STR_SERVICE] = all_token.get('access_token')
|
||||
self.pool['res.users'].write(cr, SUPERUSER_ID, uid, vals, context=context)
|
||||
|
||||
def get_start_time_to_synchro(self, cr, uid, context=None):
|
||||
# WILL BE AN IR CONFIG PARAMETER - beginning from SAAS4
|
||||
number_of_week = 13
|
||||
def get_minTime(self, cr, uid, context=None):
|
||||
number_of_week = self.pool['ir.config_parameter'].get_param(cr, uid, 'calendar.week_synchro', default=13)
|
||||
return datetime.now() - timedelta(weeks=number_of_week)
|
||||
|
||||
def get_need_synchro_attendee(self, cr, uid, context=None):
|
||||
# WILL BE AN IR CONFIG PARAMETER - beginning from SAAS4
|
||||
return True
|
||||
return self.pool['ir.config_parameter'].get_param(cr, uid, 'calendar.block_synchro_attendee', default=True)
|
||||
|
||||
def get_disable_since_synchro(self, cr, uid, context=None):
|
||||
return self.pool['ir.config_parameter'].get_param(cr, uid, 'calendar.block_since_synchro', default=False)
|
||||
|
||||
def get_print_log(self, cr, uid, context=None):
|
||||
return self.pool['ir.config_parameter'].get_param(cr, uid, 'calendar.debug_print', default=False)
|
||||
|
||||
|
||||
class res_users(osv.Model):
|
||||
|
@ -724,16 +913,22 @@ class res_users(osv.Model):
|
|||
'google_calendar_rtoken': fields.char('Refresh Token'),
|
||||
'google_calendar_token': fields.char('User token'),
|
||||
'google_calendar_token_validity': fields.datetime('Token Validity'),
|
||||
'google_calendar_last_sync_date': fields.datetime('Last synchro date'),
|
||||
'google_calendar_cal_id': fields.char('Calendar ID', help='Last Calendar ID who has been synchronized. If it is changed, we remove \
|
||||
all links between GoogleID and OpenERP Google Internal ID')
|
||||
}
|
||||
|
||||
|
||||
class calendar_event(osv.Model):
|
||||
_inherit = "calendar.event"
|
||||
|
||||
def get_fields_need_update_google(self, cr, uid, context=None):
|
||||
return ['name', 'description', 'allday', 'date', 'date_end', 'stop', 'attendee_ids', 'location', 'class', 'active']
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
sync_fields = set(['name', 'description', 'date', 'date_closed', 'date_deadline', 'attendee_ids', 'location', 'class'])
|
||||
sync_fields = set(self.get_fields_need_update_google(cr, uid, context))
|
||||
if (set(vals.keys()) & sync_fields) and 'oe_update_date' not in vals.keys() and 'NewMeeting' not in context:
|
||||
vals['oe_update_date'] = datetime.now()
|
||||
|
||||
|
@ -762,7 +957,7 @@ class calendar_attendee(osv.Model):
|
|||
_inherit = 'calendar.attendee'
|
||||
|
||||
_columns = {
|
||||
'google_internal_event_id': fields.char('Google Calendar Event Id', size=256),
|
||||
'google_internal_event_id': fields.char('Google Calendar Event Id'),
|
||||
'oe_synchro_date': fields.datetime('OpenERP Synchro Date'),
|
||||
}
|
||||
_sql_constraints = [('google_id_uniq', 'unique(google_internal_event_id,partner_id,event_id)', 'Google ID should be unique!')]
|
||||
|
|
|
@ -11,38 +11,54 @@ openerp.google_calendar = function(instance) {
|
|||
});
|
||||
return this._super(r);
|
||||
},
|
||||
sync_calendar: function(res,button) {
|
||||
sync_calendar: function(res, button) {
|
||||
var self = this;
|
||||
var context = instance.web.pyeval.eval('context');
|
||||
//$('div.oe_cal_sync_button').hide();
|
||||
$('div.oe_cal_sync_button').prop('disabled',true);
|
||||
|
||||
$('div.oe_cal_sync_button').prop('disabled', true);
|
||||
|
||||
self.rpc('/google_calendar/sync_data', {
|
||||
arch: res.arch,
|
||||
fields: res.fields,
|
||||
model:res.model,
|
||||
model: res.model,
|
||||
fromurl: window.location.href,
|
||||
local_context:context
|
||||
local_context: context
|
||||
}).done(function(o) {
|
||||
if (o.status == "need_auth") {
|
||||
if (o.status === "need_auth") {
|
||||
alert(_t("You will be redirected on gmail to authorize your OpenErp to access your calendar !"));
|
||||
instance.web.redirect(o.url);
|
||||
}
|
||||
else if (o.status == "need_config_from_admin") {
|
||||
|
||||
if (!_.isUndefined(o.action) && parseInt(o.action)) {
|
||||
if (confirm(_t("An admin need to configure Google Synchronization before to use it, do you want to configure it now ? !"))) {
|
||||
self.do_action(o.action);
|
||||
else if (o.status === "need_config_from_admin"){
|
||||
if (!_.isUndefined(o.action) && parseInt(o.action)){
|
||||
if (confirm(_t("An admin need to configure Google Synchronization before to use it, do you want to configure it now ? !"))){
|
||||
self.do_action(o.action);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else{
|
||||
alert(_t("An admin need to configure Google Synchronization before to use it !"));
|
||||
}
|
||||
}
|
||||
else if (o.status == "need_refresh"){
|
||||
else if (o.status === "need_refresh"){
|
||||
self.$calendar.fullCalendar('refetchEvents');
|
||||
}
|
||||
else if (o.status === "need_reset"){
|
||||
if (confirm(_t("The account that you are trying to synchronize (" + o.info.new_name + "), is not the same that the last one used \
|
||||
(" + o.info.old_name + "! )" + "\r\n\r\nDo you want remove all references from the old account ?"))){
|
||||
|
||||
self.rpc('/google_calendar/remove_references', {
|
||||
model:res.model,
|
||||
local_context:context
|
||||
}).done(function(o) {
|
||||
if (o.status === "OK") {
|
||||
alert(_t("All old references have been deleted. You can now restart the synchronization"));
|
||||
}
|
||||
else if (o.status === "KO") {
|
||||
alert(_t("An error has occured when we was removing all old references. Please retry or contact your administrator."));
|
||||
}
|
||||
//else NOP
|
||||
});
|
||||
}
|
||||
}
|
||||
}).always(function(o) { $('div.oe_cal_sync_button').prop('disabled',false); });
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="view_users_form" model="ir.ui.view">
|
||||
<field name="name">res.users.form</field>
|
||||
<field name="model">res.users</field>
|
||||
<field name="inherit_id" ref="base.view_users_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<notebook colspan="4" position="inside">
|
||||
<page string="Calendar">
|
||||
<group>
|
||||
<field name="google_calendar_rtoken"/>
|
||||
<field name="google_calendar_token"/>
|
||||
<field name="google_calendar_token_validity"/>
|
||||
<field name="google_calendar_last_sync_date"/>
|
||||
<field name="google_calendar_cal_id"/>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
@ -115,7 +115,7 @@
|
|||
</field>
|
||||
</record>
|
||||
<record id="base.action_attachment" model="ir.actions.act_window">
|
||||
<field name="view_mode">kanban,form</field>
|
||||
<field name="view_mode">kanban,tree,form</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
|
|
@ -214,7 +214,7 @@ class project(osv.osv):
|
|||
'res_model': 'ir.attachment',
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_id': False,
|
||||
'view_mode': 'kanban,form',
|
||||
'view_mode': 'kanban,tree,form',
|
||||
'view_type': 'form',
|
||||
'limit': 80,
|
||||
'context': "{'default_res_model': '%s','default_res_id': %d}" % (self._name, res_id)
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
<field name="priority">0</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_id" ref="base.res_partner_2"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field name="project_id" ref="project.project_project_1"/>
|
||||
<field eval="15.0" name="duration"/>
|
||||
|
@ -54,7 +54,7 @@
|
|||
<field name="priority">1</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_id" ref="base.res_partner_1"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="3.5" name="duration"/>
|
||||
<field name="name">Program not giving proper output</field>
|
||||
|
@ -66,7 +66,7 @@
|
|||
<field eval="time.strftime('%Y-%m-18 14:30:00')" name="date"/>
|
||||
<field name="priority">0</field>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="2.3" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_5"/>
|
||||
|
@ -82,7 +82,7 @@
|
|||
<field name="priority">1</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_id" ref="base.res_partner_14"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="4.0" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_4"/>
|
||||
|
@ -95,7 +95,7 @@
|
|||
<field name="priority">1</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_id" ref="base.res_partner_13"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="1.0" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_4"/>
|
||||
|
@ -108,7 +108,7 @@
|
|||
<field name="priority">1</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_id" ref="base.res_partner_5"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="4.0" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_4"/>
|
||||
|
@ -124,7 +124,7 @@
|
|||
<field name="priority">2</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_id" ref="base.res_partner_6"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="2.0" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_2"/>
|
||||
|
@ -137,7 +137,7 @@
|
|||
<field name="priority">2</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_id" ref="base.res_partner_6"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="7.3" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_2"/>
|
||||
|
@ -162,7 +162,7 @@
|
|||
<field name="priority">2</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_id" ref="base.res_partner_2"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="13.0" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_2"/>
|
||||
|
@ -175,7 +175,7 @@
|
|||
<field name="priority">0</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_id" ref="base.res_partner_8"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="3.2" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_3"/>
|
||||
|
@ -188,7 +188,7 @@
|
|||
<field name="priority">1</field>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="partner_id" ref="base.res_partner_9"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="3.0" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_3"/>
|
||||
|
@ -202,7 +202,7 @@
|
|||
<field name="priority">1</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_id" ref="base.res_partner_10"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="2.0" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_2"/>
|
||||
|
@ -216,7 +216,7 @@
|
|||
<field name="priority">1</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_id" ref="base.res_partner_6"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="2.45" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_4"/>
|
||||
|
@ -229,7 +229,7 @@
|
|||
<field name="priority">0</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_id" ref="base.res_partner_11"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="15.0" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_4"/>
|
||||
|
@ -242,7 +242,7 @@
|
|||
<field name="priority">2</field>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="partner_id" ref="base.res_partner_11"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field eval="1" name="active"/>
|
||||
<field eval="06.15" name="duration"/>
|
||||
<field name="project_id" ref="project.project_project_4"/>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<group expand="1" string="Group By...">
|
||||
<filter string="Assigned to" name="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}" />
|
||||
<filter string="Contact" icon="terp-partner" context="{'group_by':'partner_id'}" />
|
||||
<filter string="Sale Team" icon="terp-personal+" domain="[]" context="{'group_by':'section_id'}" />
|
||||
<filter string="Sale Team" icon="terp-personal+" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Project" name="project" icon="terp-folder-violet" context="{'group_by':'project_id'}" />
|
||||
<filter string="Task" icon="terp-stock_align_left_24" domain="[]" context="{'group_by':'task_id'}"/>
|
||||
<filter string="Version" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'version_id'}"/>
|
||||
|
|
|
@ -19,15 +19,10 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Init Sales
|
||||
#----------------------------------------------------------
|
||||
|
||||
import sale
|
||||
import sales_team
|
||||
import res_partner
|
||||
import wizard
|
||||
import report
|
||||
import edi
|
||||
import res_config
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -59,7 +59,7 @@ The Dashboard for the Sales Manager will include
|
|||
'author': 'OpenERP SA',
|
||||
'website': 'http://www.openerp.com',
|
||||
'images': ['images/Sale_order_line_to_invoice.jpeg','images/sale_order.jpeg','images/sales_analysis.jpeg'],
|
||||
'depends': ['account_voucher', 'procurement', 'report'],
|
||||
'depends': ['sales_team','account_voucher', 'procurement', 'report'],
|
||||
'data': [
|
||||
'wizard/sale_make_invoice_advance.xml',
|
||||
'wizard/sale_line_invoice.xml',
|
||||
|
@ -71,6 +71,7 @@ The Dashboard for the Sales Manager will include
|
|||
'sale_report.xml',
|
||||
'sale_data.xml',
|
||||
'sale_view.xml',
|
||||
'sales_team_view.xml',
|
||||
'res_partner_view.xml',
|
||||
'report/sale_report_view.xml',
|
||||
'edi/sale_order_action_data.xml',
|
||||
|
|
|
@ -15,7 +15,7 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2014-05-13 05:59+0000\n"
|
||||
"X-Launchpad-Export-Date: 2014-05-14 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 17002)\n"
|
||||
|
||||
#. module: sale
|
||||
|
|
|
@ -20,5 +20,5 @@
|
|||
##############################################################################
|
||||
|
||||
import sale_report
|
||||
|
||||
import invoice_report
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -53,6 +53,7 @@ class sale_report(osv.osv):
|
|||
], 'Order Status', readonly=True),
|
||||
'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', readonly=True),
|
||||
'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
|
||||
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
|
||||
}
|
||||
_order = 'date desc'
|
||||
|
||||
|
@ -73,7 +74,8 @@ class sale_report(osv.osv):
|
|||
s.state,
|
||||
t.categ_id as categ_id,
|
||||
s.pricelist_id as pricelist_id,
|
||||
s.project_id as analytic_account_id
|
||||
s.project_id as analytic_account_id,
|
||||
s.section_id as section_id
|
||||
"""
|
||||
return select_str
|
||||
|
||||
|
@ -101,7 +103,8 @@ class sale_report(osv.osv):
|
|||
s.company_id,
|
||||
s.state,
|
||||
s.pricelist_id,
|
||||
s.project_id
|
||||
s.project_id,
|
||||
s.section_id
|
||||
"""
|
||||
return group_by_str
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
</group>
|
||||
<group expand="1" string="Group By...">
|
||||
<filter string="Salesperson" icon="terp-personal" name="User" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Sales Team" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Partner" icon="terp-partner" name="Customer" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Product" icon="terp-accessories-archiver" context="{'group_by':'product_id','set_visible':True}"/>
|
||||
<filter string="Reference Unit of Measure" icon="terp-mrp" context="{'group_by':'product_uom'}"/>
|
||||
|
|
|
@ -163,6 +163,28 @@ class sale_order(osv.osv):
|
|||
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._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
|
||||
|
||||
def _resolve_section_id_from_context(self, cr, uid, context=None):
|
||||
""" Returns ID of section based on the value of 'section_id'
|
||||
context key, or None if it cannot be resolved to a single
|
||||
Sales Team.
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
if type(context.get('default_section_id')) in (int, long):
|
||||
return context.get('default_section_id')
|
||||
if isinstance(context.get('default_section_id'), basestring):
|
||||
section_ids = self.pool.get('crm.case.section').name_search(cr, uid, name=context['default_section_id'], context=context)
|
||||
if len(section_ids) == 1:
|
||||
return int(section_ids[0][0])
|
||||
return None
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Order Reference', size=64, required=True,
|
||||
readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, select=True),
|
||||
|
@ -228,7 +250,9 @@ 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'),
|
||||
'procurement_group_id': fields.many2one('procurement.group', 'Procurement group'),
|
||||
|
||||
}
|
||||
_defaults = {
|
||||
'date_order': fields.datetime.now,
|
||||
|
@ -239,7 +263,8 @@ class sale_order(osv.osv):
|
|||
'name': lambda obj, cr, uid, context: '/',
|
||||
'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!'),
|
||||
|
@ -374,7 +399,8 @@ 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
|
||||
|
@ -1189,6 +1215,36 @@ class mail_compose_message(osv.Model):
|
|||
class account_invoice(osv.Model):
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
def _get_default_section_id(self, cr, uid, context=None):
|
||||
""" Gives default section by checking if present in the context """
|
||||
section_id = self._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
|
||||
|
||||
def _resolve_section_id_from_context(self, cr, uid, context=None):
|
||||
""" Returns ID of section based on the value of 'section_id'
|
||||
context key, or None if it cannot be resolved to a single
|
||||
Sales Team.
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
if type(context.get('default_section_id')) in (int, long):
|
||||
return context.get('default_section_id')
|
||||
if isinstance(context.get('default_section_id'), basestring):
|
||||
section_ids = self.pool.get('crm.case.section').name_search(cr, uid, name=context['default_section_id'], context=context)
|
||||
if len(section_ids) == 1:
|
||||
return int(section_ids[0][0])
|
||||
return None
|
||||
|
||||
_columns = {
|
||||
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'section_id': lambda self, cr, uid, c=None: self._get_default_section_id(cr, uid, context=c)
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<field name="body"><![CDATA[<p>This application lets you create and send quotations and process your sales orders; from delivery to invoicing.</p>
|
||||
<p>If you need to manage your sales pipeline (leads, opportunities, phonecalls), the <i>CRM</i> application may be useful. Use the Settings menu to install it.</p>]]></field>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- Sale-related subtypes for messaging / Chatter -->
|
||||
<record id="mt_order_sent" model="mail.message.subtype">
|
||||
<field name="name">Quotation sent</field>
|
||||
|
@ -44,5 +44,21 @@
|
|||
<field name="description">Quotation confirmed</field>
|
||||
</record>
|
||||
|
||||
<!-- Salesteam-related subtypes for messaging / Chatter -->
|
||||
<record id="mt_salesteam_order_sent" model="mail.message.subtype">
|
||||
<field name="name">Quotation Send</field>
|
||||
<field name="sequence">20</field>
|
||||
<field name="res_model">crm.case.section</field>
|
||||
<field name="parent_id" eval="ref('sale.mt_order_sent')"/>
|
||||
<field name="relation_field">section_id</field>
|
||||
</record>
|
||||
<record id="mt_salesteam_order_confirmed" model="mail.message.subtype">
|
||||
<field name="name">Sales Order Confirmed</field>
|
||||
<field name="sequence">21</field>
|
||||
<field name="res_model">crm.case.section</field>
|
||||
<field name="parent_id" eval="ref('sale.mt_order_confirmed')"/>
|
||||
<field name="relation_field">section_id</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -2,12 +2,24 @@
|
|||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<record model="crm.case.section" id="sales_team.section_sales_department">
|
||||
<field name="invoiced_forecast">52700</field>
|
||||
<field name="invoiced_target">60000</field>
|
||||
</record>
|
||||
|
||||
<record model="crm.case.section" id="sales_team.crm_case_section_1">
|
||||
<field name="invoiced_forecast">36000</field>
|
||||
<field name="invoiced_target">40000</field>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_1" model="sale.order">
|
||||
<field name="partner_id" ref="base.res_partner_2"/>
|
||||
<field name="partner_invoice_id" ref="base.res_partner_2"/>
|
||||
<field name="partner_shipping_id" ref="base.res_partner_2"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="date_order" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_line_1" model="sale.order.line">
|
||||
|
@ -47,6 +59,8 @@
|
|||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="order_policy">manual</field>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="date_order" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_line_4" model="sale.order.line">
|
||||
|
@ -76,6 +90,7 @@
|
|||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="order_policy">manual</field>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_line_6" model="sale.order.line">
|
||||
|
@ -104,6 +119,7 @@
|
|||
<field name="partner_shipping_id" ref="base.res_partner_address_25"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_line_8" model="sale.order.line">
|
||||
|
@ -152,6 +168,8 @@
|
|||
<field name="partner_shipping_id" ref="base.res_partner_2"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field name="date_order" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_line_12" model="sale.order.line">
|
||||
|
@ -190,6 +208,7 @@
|
|||
<field name="partner_shipping_id" ref="base.res_partner_18"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_line_15" model="sale.order.line">
|
||||
|
@ -209,6 +228,8 @@
|
|||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="order_policy">manual</field>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="date_confirm" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_line_16" model="sale.order.line">
|
||||
|
@ -259,6 +280,7 @@
|
|||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="order_policy">manual</field>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_line_20" model="sale.order.line">
|
||||
|
@ -335,5 +357,86 @@ Thanks!</field>
|
|||
<field eval="[(4, ref('base.group_sale_salesman'))]" name="groups_id"/>
|
||||
</record>
|
||||
|
||||
<!-- coming from sale_crm -->
|
||||
<record id="test_crm_sale_invoice_1" model="account.invoice">
|
||||
<field name="currency_id" ref="base.EUR"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="partner_id" ref="base.res_partner_1"/>
|
||||
<field name="journal_id" ref="account.sales_journal"/>
|
||||
<field name="section_id" ref="sales_team.section_sales_department"/>
|
||||
<field name="state">draft</field>
|
||||
<field name="type">in_invoice</field>
|
||||
<field name="account_id" ref="account.a_recv"/>
|
||||
<field name="name">Test invoice 1</field>
|
||||
</record>
|
||||
<record id="test_crm_sale_invoice_1_line_1" model="account.invoice.line">
|
||||
<field name="name">Basic computer with Dvorak keyboard and left-handed mouse</field>
|
||||
<field name="invoice_id" ref="test_crm_sale_invoice_1"/>
|
||||
<field name="price_unit">250</field>
|
||||
<field name="quantity">1</field>
|
||||
<field name="account_id" ref="account.a_sale"/>
|
||||
</record>
|
||||
<record id="test_crm_sale_invoice_1_line_2" model="account.invoice.line">
|
||||
<field name="name">Little server with raid 1 and 512ECC ram</field>
|
||||
<field name="invoice_id" ref="test_crm_sale_invoice_1"/>
|
||||
<field name="price_unit">800</field>
|
||||
<field name="quantity">2</field>
|
||||
<field name="account_id" ref="account.a_sale"/>
|
||||
</record>
|
||||
<record id="test_crm_sale_invoice_1_line_2" model="account.invoice.line">
|
||||
<field name="name">Server with raid 10 and 2048ECC ram</field>
|
||||
<field name="invoice_id" ref="test_crm_sale_invoice_1"/>
|
||||
<field name="price_unit">4800</field>
|
||||
<field name="quantity">4</field>
|
||||
<field name="account_id" ref="account.a_sale"/>
|
||||
</record>
|
||||
<workflow action="invoice_open" model="account.invoice" ref="test_crm_sale_invoice_1"/>
|
||||
<function model="account.invoice" name="pay_and_reconcile">
|
||||
<!-- ids = --> <value eval="[ref('test_crm_sale_invoice_1')]"/>
|
||||
<!-- pay_amount = --> <value eval="1850"/>
|
||||
<!-- pay_account_id = --> <value eval="ref('account.cash')"/>
|
||||
<!-- period_id = --> <value eval="ref('account.period_' + str(int(time.strftime('%m'))))"/>
|
||||
<!-- pay_journal_id = --> <value eval="ref('account.bank_journal')"/>
|
||||
<!-- writeoff_acc_id = --> <value eval="ref('account.cash')"/>
|
||||
<!-- writeoff_period_id = --> <value eval="ref('account.period_' + str(int(time.strftime('%m'))))"/>
|
||||
<!-- writeoff_journal_id = --> <value eval="ref('account.bank_journal')"/>
|
||||
<!-- context = --> <value eval="{}"/>
|
||||
<!-- name = --> <value eval="str('Payment from ASUStek')"/>
|
||||
</function>
|
||||
|
||||
<!-- Invoice for Indirect Marketing -->
|
||||
|
||||
<record id="test_crm_sale_invoice_2" model="account.invoice">
|
||||
<field name="currency_id" ref="base.EUR"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="partner_id" ref="base.res_partner_1"/>
|
||||
<field name="journal_id" ref="account.sales_journal"/>
|
||||
<field name="section_id" ref="sales_team.crm_case_section_1"/>
|
||||
<field name="state">draft</field>
|
||||
<field name="type">out_invoice</field>
|
||||
<field name="account_id" ref="account.a_recv"/>
|
||||
<field name="name">Test invoice 1</field>
|
||||
</record>
|
||||
<record id="test_crm_sale_invoice_2_line_1" model="account.invoice.line">
|
||||
<field name="name">Basic formation with Dvorak</field>
|
||||
<field name="invoice_id" ref="test_crm_sale_invoice_2"/>
|
||||
<field name="price_unit">500</field>
|
||||
<field name="quantity">1</field>
|
||||
<field name="account_id" ref="account.a_sale"/>
|
||||
</record>
|
||||
<workflow action="invoice_open" model="account.invoice" ref="test_crm_sale_invoice_2"/>
|
||||
<function model="account.invoice" name="pay_and_reconcile">
|
||||
<!-- ids = --> <value eval="[ref('test_crm_sale_invoice_2')]"/>
|
||||
<!-- pay_amount = --> <value eval="500"/>
|
||||
<!-- pay_account_id = --> <value eval="ref('account.cash')"/>
|
||||
<!-- period_id = --> <value eval="ref('account.period_' + str(int(time.strftime('%m'))))"/>
|
||||
<!-- pay_journal_id = --> <value eval="ref('account.bank_journal')"/>
|
||||
<!-- writeoff_acc_id = --> <value eval="ref('account.cash')"/>
|
||||
<!-- writeoff_period_id = --> <value eval="ref('account.period_' + str(int(time.strftime('%m'))))"/>
|
||||
<!-- writeoff_journal_id = --> <value eval="ref('account.bank_journal')"/>
|
||||
<!-- context = --> <value eval="{}"/>
|
||||
<!-- name = --> <value eval="str('Payment from ASUStek')"/>
|
||||
</function>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -201,6 +201,7 @@
|
|||
<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">
|
||||
|
@ -255,12 +256,14 @@
|
|||
<filter string="My" domain="[('user_id','=',uid)]" help="My Sales Orders" icon="terp-personal" name="my_sale_orders_filter"/>
|
||||
<field name="partner_id" operator="child_of"/>
|
||||
<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>
|
||||
|
@ -519,5 +522,163 @@
|
|||
</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" groups="base.group_multi_salesteams"/>
|
||||
</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>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import calendar
|
||||
from datetime import date
|
||||
from dateutil import relativedelta
|
||||
|
||||
from openerp import tools
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
|
||||
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 = {
|
||||
'use_quotations': fields.boolean('Opportunities', help="Check this box to manage quotations in this sales team."),
|
||||
'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'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'use_quotations': True,
|
||||
}
|
||||
|
||||
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:
|
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<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="sales_team.crm_case_section_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//div[@name='options_active']" position="inside">
|
||||
<field name="use_quotations" class="oe_inline"/><label for="use_quotations"/>
|
||||
</xpath>
|
||||
<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="sales_team.crm_case_section_salesteams_view_kanban"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//field[@name='name']" position="after">
|
||||
<field name="use_quotations"/>
|
||||
<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" t-if="record.use_quotations.raw_value">
|
||||
<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 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 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>
|
|
@ -21,6 +21,5 @@
|
|||
|
||||
import wizard
|
||||
import sale_crm
|
||||
import report
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -41,12 +41,10 @@ modules.
|
|||
'data': [
|
||||
'wizard/crm_make_sale_view.xml',
|
||||
'sale_crm_view.xml',
|
||||
'sale_crm_data.xml',
|
||||
'security/sale_crm_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'report/sale_report_view.xml',
|
||||
],
|
||||
'demo': ['sale_crm_demo.xml'],
|
||||
'demo': [],
|
||||
'test': ['test/sale_crm.yml'],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
|
|
|
@ -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:
|
|
@ -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>
|
|
@ -26,122 +26,9 @@ 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', 'Tags', \
|
||||
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 = {
|
||||
'use_quotations': fields.boolean('Opportunities', help="Check this box to manage quotations in this sales team."),
|
||||
'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'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'use_quotations': True,
|
||||
}
|
||||
|
||||
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:
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- Salesteam-related subtypes for messaging / Chatter -->
|
||||
<record id="mt_salesteam_order_sent" model="mail.message.subtype">
|
||||
<field name="name">Quotation Send</field>
|
||||
<field name="sequence">20</field>
|
||||
<field name="res_model">crm.case.section</field>
|
||||
<field name="parent_id" eval="ref('sale.mt_order_sent')"/>
|
||||
<field name="relation_field">section_id</field>
|
||||
</record>
|
||||
<record id="mt_salesteam_order_confirmed" model="mail.message.subtype">
|
||||
<field name="name">Sales Order Confirmed</field>
|
||||
<field name="sequence">21</field>
|
||||
<field name="res_model">crm.case.section</field>
|
||||
<field name="parent_id" eval="ref('sale.mt_order_confirmed')"/>
|
||||
<field name="relation_field">section_id</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -1,130 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record model="crm.case.section" id="crm.section_sales_department">
|
||||
<field name="invoiced_forecast">52700</field>
|
||||
<field name="invoiced_target">60000</field>
|
||||
</record>
|
||||
|
||||
<record model="crm.case.section" id="crm.crm_case_section_1">
|
||||
<field name="name">Indirect Sales</field>
|
||||
<field name="code">IM</field>
|
||||
<field name="invoiced_forecast">36000</field>
|
||||
<field name="invoiced_target">40000</field>
|
||||
</record>
|
||||
|
||||
<!-- Invoice for Direct Marketing -->
|
||||
|
||||
<record id="test_crm_sale_invoice_1" model="account.invoice">
|
||||
<field name="currency_id" ref="base.EUR"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="partner_id" ref="base.res_partner_1"/>
|
||||
<field name="journal_id" ref="account.sales_journal"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="state">draft</field>
|
||||
<field name="type">in_invoice</field>
|
||||
<field name="account_id" ref="account.a_recv"/>
|
||||
<field name="name">Test invoice 1</field>
|
||||
</record>
|
||||
<record id="test_crm_sale_invoice_1_line_1" model="account.invoice.line">
|
||||
<field name="name">Basic computer with Dvorak keyboard and left-handed mouse</field>
|
||||
<field name="invoice_id" ref="test_crm_sale_invoice_1"/>
|
||||
<field name="price_unit">250</field>
|
||||
<field name="quantity">1</field>
|
||||
<field name="account_id" ref="account.a_sale"/>
|
||||
</record>
|
||||
<record id="test_crm_sale_invoice_1_line_2" model="account.invoice.line">
|
||||
<field name="name">Little server with raid 1 and 512ECC ram</field>
|
||||
<field name="invoice_id" ref="test_crm_sale_invoice_1"/>
|
||||
<field name="price_unit">800</field>
|
||||
<field name="quantity">2</field>
|
||||
<field name="account_id" ref="account.a_sale"/>
|
||||
</record>
|
||||
<record id="test_crm_sale_invoice_1_line_2" model="account.invoice.line">
|
||||
<field name="name">Server with raid 10 and 2048ECC ram</field>
|
||||
<field name="invoice_id" ref="test_crm_sale_invoice_1"/>
|
||||
<field name="price_unit">4800</field>
|
||||
<field name="quantity">4</field>
|
||||
<field name="account_id" ref="account.a_sale"/>
|
||||
</record>
|
||||
<workflow action="invoice_open" model="account.invoice" ref="test_crm_sale_invoice_1"/>
|
||||
<function model="account.invoice" name="pay_and_reconcile">
|
||||
<!-- ids = --> <value eval="[ref('test_crm_sale_invoice_1')]"/>
|
||||
<!-- pay_amount = --> <value eval="1850"/>
|
||||
<!-- pay_account_id = --> <value eval="ref('account.cash')"/>
|
||||
<!-- period_id = --> <value eval="ref('account.period_' + str(int(time.strftime('%m'))))"/>
|
||||
<!-- pay_journal_id = --> <value eval="ref('account.bank_journal')"/>
|
||||
<!-- writeoff_acc_id = --> <value eval="ref('account.cash')"/>
|
||||
<!-- writeoff_period_id = --> <value eval="ref('account.period_' + str(int(time.strftime('%m'))))"/>
|
||||
<!-- writeoff_journal_id = --> <value eval="ref('account.bank_journal')"/>
|
||||
<!-- context = --> <value eval="{}"/>
|
||||
<!-- name = --> <value eval="str('Payment from ASUStek')"/>
|
||||
</function>
|
||||
|
||||
<!-- Invoice for Indirect Marketing -->
|
||||
|
||||
<record id="test_crm_sale_invoice_2" model="account.invoice">
|
||||
<field name="currency_id" ref="base.EUR"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="partner_id" ref="base.res_partner_1"/>
|
||||
<field name="journal_id" ref="account.sales_journal"/>
|
||||
<field name="section_id" ref="crm.crm_case_section_1"/>
|
||||
<field name="state">draft</field>
|
||||
<field name="type">out_invoice</field>
|
||||
<field name="account_id" ref="account.a_recv"/>
|
||||
<field name="name">Test invoice 1</field>
|
||||
</record>
|
||||
<record id="test_crm_sale_invoice_2_line_1" model="account.invoice.line">
|
||||
<field name="name">Basic formation with Dvorak</field>
|
||||
<field name="invoice_id" ref="test_crm_sale_invoice_2"/>
|
||||
<field name="price_unit">500</field>
|
||||
<field name="quantity">1</field>
|
||||
<field name="account_id" ref="account.a_sale"/>
|
||||
</record>
|
||||
<workflow action="invoice_open" model="account.invoice" ref="test_crm_sale_invoice_2"/>
|
||||
<function model="account.invoice" name="pay_and_reconcile">
|
||||
<!-- ids = --> <value eval="[ref('test_crm_sale_invoice_2')]"/>
|
||||
<!-- pay_amount = --> <value eval="500"/>
|
||||
<!-- pay_account_id = --> <value eval="ref('account.cash')"/>
|
||||
<!-- period_id = --> <value eval="ref('account.period_' + str(int(time.strftime('%m'))))"/>
|
||||
<!-- pay_journal_id = --> <value eval="ref('account.bank_journal')"/>
|
||||
<!-- writeoff_acc_id = --> <value eval="ref('account.cash')"/>
|
||||
<!-- writeoff_period_id = --> <value eval="ref('account.period_' + str(int(time.strftime('%m'))))"/>
|
||||
<!-- writeoff_journal_id = --> <value eval="ref('account.bank_journal')"/>
|
||||
<!-- context = --> <value eval="{}"/>
|
||||
<!-- name = --> <value eval="str('Payment from ASUStek')"/>
|
||||
</function>
|
||||
|
||||
|
||||
<record id="sale.sale_order_1" model="sale.order">
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="date_order" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
<record id="sale.sale_order_2" model="sale.order">
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="date_order" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
<record id="sale.sale_order_3" model="sale.order">
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
</record>
|
||||
<record id="sale.sale_order_4" model="sale.order">
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
</record>
|
||||
<record id="sale.sale_order_5" model="sale.order">
|
||||
<field name="section_id" ref="crm.crm_case_section_1"/>
|
||||
<field name="date_order" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
<record id="sale.sale_order_6" model="sale.order">
|
||||
<field name="section_id" ref="crm.crm_case_section_1"/>
|
||||
</record>
|
||||
<record id="sale.sale_order_7" model="sale.order">
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="date_confirm" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
<record id="sale.sale_order_8" model="sale.order">
|
||||
<field name="section_id" ref="crm.crm_case_section_1"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -27,289 +27,9 @@
|
|||
<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="//div[@name='options_active']" position="inside">
|
||||
<field name="use_quotations" class="oe_inline"/><label for="use_quotations"/>
|
||||
</xpath>
|
||||
<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="use_quotations"/>
|
||||
<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" t-if="record.use_quotations.raw_value">
|
||||
<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>
|
||||
</openerp>
|
||||
|
|
|
@ -18,9 +18,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import sales_crm_account_invoice_report
|
||||
import sale_report
|
||||
import sales_team
|
||||
import res_config
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# -*- 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': 'Sales Management',
|
||||
'summary': 'Sales Team',
|
||||
'description': """
|
||||
Using this application you can manage Sales Team with CRM and/or Sales
|
||||
=======================================================================
|
||||
""",
|
||||
'website': 'http://www.openerp.com',
|
||||
'depends': ['base','mail','web_kanban_sparkline',],
|
||||
'data': ['security/sales_team_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'res_config_view.xml',
|
||||
'sales_team_data.xml',
|
||||
'sales_team.xml',],
|
||||
'demo': ['sales_team_demo.xml'],
|
||||
'css': ['static/src/css/sales_team.css'],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
}
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,41 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
|
||||
class sales_team_configuration(osv.TransientModel):
|
||||
_name = 'sale.config.settings'
|
||||
_inherit = ['sale.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."""),
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?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.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>
|
||||
<menuitem id="base.menu_sale_config" name="Sales" parent="base.menu_config"
|
||||
sequence="1" action="base_setup.action_sale_config"/>
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,130 @@
|
|||
# -*- 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
from datetime import date, datetime
|
||||
from dateutil import relativedelta
|
||||
from openerp import tools
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
|
||||
class crm_case_section(osv.osv):
|
||||
_name = "crm.case.section"
|
||||
_inherit = ['mail.thread', 'ir.needaction_mixin']
|
||||
_description = "Sales Teams"
|
||||
_order = "complete_name"
|
||||
_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'),
|
||||
'note': fields.text('Description'),
|
||||
'working_hours': fields.float('Working Hours', digits=(16, 2)),
|
||||
'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
|
||||
|
||||
|
||||
class res_partner(osv.Model):
|
||||
_inherit = 'res.partner'
|
||||
_columns = {
|
||||
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
|
||||
}
|
||||
|
||||
|
||||
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
|
|
@ -0,0 +1,211 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<menuitem name="Sales"
|
||||
id="base.menu_base_partner"
|
||||
groups="base.group_sale_salesman,base.group_sale_manager"/>
|
||||
<!-- 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 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="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="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" 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" name="name"><field name="name"/></h4>
|
||||
<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">
|
||||
<filter name="personal" string="My Salesteams" domain="['|', ('member_ids', '=', uid), ('user_id', '=', uid)]"/>
|
||||
<field name="name"/>
|
||||
<field name="parent_id"/>
|
||||
<field name="user_id"/>
|
||||
<field name="note"/>
|
||||
<field name="code"/>
|
||||
<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 name="options_active"></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>
|
||||
<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>
|
||||
|
||||
<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="sales_team.menu_sales_team_act" action="crm_case_section_salesteams_act" sequence="1" parent="base.menu_sales" groups="base.group_multi_salesteams"/>
|
||||
|
||||
<!-- add css / js -->
|
||||
<template id="assets_backend" name="sales_team assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/sales_team/static/src/css/sales_team.css"/>
|
||||
<script type="text/javascript" src="/sales_team/static/src/js/sales_team.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
<record model="crm.case.section" id="section_sales_department">
|
||||
<field name="name">Direct Sales</field>
|
||||
<field name="code">DM</field>
|
||||
<field name="member_ids" eval="[(4, ref('base.user_root'))]"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
<record id="base.user_demo" model="res.users">
|
||||
<field name="groups_id" eval="[(4,ref('base.group_sale_salesman'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="section_sales_department" model="crm.case.section">
|
||||
<field name="member_ids" eval="[(4, ref('base.user_demo'))]"/>
|
||||
</record>
|
||||
<record model="crm.case.section" id="crm_case_section_1">
|
||||
<field name="name">Indirect Sales</field>
|
||||
<field name="code">IM</field>
|
||||
<field name="member_ids" eval="[(4, ref('base.user_root')),(4, ref('base.user_demo'))]"/>
|
||||
</record>
|
||||
|
||||
<record model="crm.case.section" id="crm_case_section_2">
|
||||
<field name="name">Marketing</field>
|
||||
<field name="code">SPD</field>
|
||||
<field name="member_ids" eval="[(4, ref('base.user_root')),(4, ref('base.user_demo'))]"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,4 @@
|
|||
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
|
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="0">
|
||||
<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>
|
|
@ -0,0 +1,2 @@
|
|||
sales_team.css: sales_team.sass
|
||||
sass --trace -t expanded sales_team.sass:sales_team.css
|
|
@ -26,7 +26,7 @@ class WebsiteDoc(http.Controller):
|
|||
}
|
||||
return request.website.render("website_forum_doc.documentation", value)
|
||||
|
||||
@http.route(['''/forum/how-to/<model("forum.documentation.toc"):toc>/<model("forum.post", "[('documentation_toc_id','=',toc)]"):post>'''], type='http', auth="public", website=True, multilang=True)
|
||||
@http.route(['''/forum/how-to/<model("forum.documentation.toc"):toc>/<model("forum.post", "[('documentation_toc_id','=',toc[0])]"):post>'''], type='http', auth="public", website=True, multilang=True)
|
||||
def post(self, toc, post, **kwargs):
|
||||
# TODO: implement a redirect instead of crash
|
||||
assert post.documentation_toc_id.id == toc.id, "Wrong post!"
|
||||
|
|
|
@ -639,6 +639,16 @@ class website_sale(http.Controller):
|
|||
# cancel the quotation
|
||||
sale_order_obj.action_cancel(cr, SUPERUSER_ID, [order.id], context=request.context)
|
||||
|
||||
# send the email
|
||||
if email_act and email_act.get('context'):
|
||||
composer_values = {}
|
||||
email_ctx = email_act['context']
|
||||
public_id = request.website.user_id.id
|
||||
if uid == public_id:
|
||||
composer_values['email_from'] = request.website.user_id.company_id.email
|
||||
composer_id = request.registry['mail.compose.message'].create(cr, SUPERUSER_ID, composer_values, context=email_ctx)
|
||||
request.registry['mail.compose.message'].send_mail(cr, SUPERUSER_ID, [composer_id], context=email_ctx)
|
||||
|
||||
# clean context and session, then redirect to the confirmation page
|
||||
request.website.sale_reset(context=context)
|
||||
|
||||
|
|
Loading…
Reference in New Issue