Initial commit
This commit is contained in:
commit
05d9947ca3
12 files changed
+1867
No files matched your search
@@ -0,0 +1,16 @@
|
||||
Copyright (C) 2010 Open Labs Business Solutions.
|
||||
Copyright (C) 2010 Cédric Krier.
|
||||
Copyright (C) 2010 B2CK SPRL.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@@ -0,0 +1,33 @@
|
||||
Installing trytond_sale_opportunity
|
||||
===================================
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
* Python 2.4 or later (http://www.python.org/)
|
||||
* trytond (http://www.tryton.org/)
|
||||
* trytond_party (http://www.tryton.org/)
|
||||
* trytond_company (http://www.tryton.org/)
|
||||
* trytond_product (http://www.tryton.org/)
|
||||
* trytond_sale (http://www.tryton.org/)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Once you've downloaded and unpacked the trytond_sale_opportunity source
|
||||
release, enter the directory where the archive was unpacked, and run:
|
||||
|
||||
python setup.py install
|
||||
|
||||
Note that you may need administrator/root privileges for this step, as
|
||||
this command will by default attempt to install module to the Python
|
||||
site-packages directory on your system.
|
||||
|
||||
For advanced options, please refer to the easy_install and/or the distutils
|
||||
documentation:
|
||||
|
||||
http://peak.telecommunity.com/DevCenter/EasyInstall
|
||||
http://docs.python.org/inst/inst.html
|
||||
|
||||
To use without installation, extract the archive into ``trytond/modules`` with
|
||||
the directory name sale_opportunity.
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
include INSTALL
|
||||
include README
|
||||
include TODO
|
||||
include COPYRIGHT
|
||||
include CHANGELOG
|
||||
include LICENSE
|
||||
include *.xml
|
||||
include *.odt
|
||||
include *.csv
|
||||
include tests/*
|
||||
include doc/*
|
||||
@@ -0,0 +1,36 @@
|
||||
trytond_sale_opportunity
|
||||
========================
|
||||
|
||||
The sale_opportunity module of the Tryton application platform.
|
||||
See __tryton__.py
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
See INSTALL
|
||||
|
||||
Support
|
||||
-------
|
||||
|
||||
If you encounter any problems with Tryton, please don't hesitate to ask
|
||||
questions on the Tryton bug tracker, mailing list, wiki or IRC channel:
|
||||
|
||||
http://bugs.tryton.org/
|
||||
http://groups.tryton.org/
|
||||
http://wiki.tryton.org/
|
||||
irc://irc.freenode.net/tryton
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
See LICENSE
|
||||
|
||||
Copyright
|
||||
---------
|
||||
|
||||
See COPYRIGHT
|
||||
|
||||
|
||||
For more information please visit the Tryton web site:
|
||||
|
||||
http://www.tryton.org/
|
||||
@@ -0,0 +1,4 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
|
||||
from opportunity import *
|
||||
@@ -0,0 +1,31 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
{
|
||||
'name': 'Sales Leads and Opportunities',
|
||||
'version': '0.0.1',
|
||||
'author': 'Sharoon Thomas, Openlabs',
|
||||
'email': 'info@openlabs.co.in',
|
||||
'website': 'http://www.openlabs.co.in/',
|
||||
'description': '''Provides:
|
||||
- Leads
|
||||
- Leads to Opportunities conversion
|
||||
- Conversion Management
|
||||
- Record of lost leads
|
||||
- Opportunities to sale orders
|
||||
''',
|
||||
'depends': [
|
||||
'party',
|
||||
'company',
|
||||
'product',
|
||||
'sale',
|
||||
'account',
|
||||
'stock',
|
||||
'currency',
|
||||
],
|
||||
'xml': [
|
||||
'opportunity.xml',
|
||||
'party.xml',
|
||||
],
|
||||
'translation': [
|
||||
],
|
||||
}
|
||||
+846
@@ -0,0 +1,846 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
"Sales extension for managing leads and opportunities"
|
||||
import datetime
|
||||
from trytond.model import ModelView, ModelSQL, ModelWorkflow, fields
|
||||
from trytond.wizard import Wizard
|
||||
from trytond.backend import FIELDS
|
||||
from trytond.pyson import Equal, Eval, Not, In, If, Get, PYSONEncoder
|
||||
|
||||
STATES = [
|
||||
('lead', 'Lead'),
|
||||
('opportunity', 'Opportunity'),
|
||||
('converted', 'Converted'),
|
||||
('cancelled', 'Cancelled'),
|
||||
('lost', 'Lost'),
|
||||
]
|
||||
_STATES_START = {
|
||||
'readonly': Not(Equal(Eval('state'), 'lead')),
|
||||
}
|
||||
_STATES_STOP = {
|
||||
'readonly': In(Eval('state'), ['converted', 'lost', 'cancelled']),
|
||||
}
|
||||
|
||||
class SaleOpportunity(ModelWorkflow, ModelSQL, ModelView):
|
||||
'Sale Opportunity'
|
||||
_name = "sale.opportunity"
|
||||
_description = __doc__
|
||||
_history = True
|
||||
_rec_name = 'description'
|
||||
|
||||
party = fields.Many2One('party.party', 'Party', required=True, select=1,
|
||||
states=_STATES_STOP, depends=['state'])
|
||||
address = fields.Many2One('party.address', 'Address',
|
||||
domain=[('party', '=', Eval('party'))],
|
||||
select=2, depends=['party', 'state'],
|
||||
states=_STATES_STOP)
|
||||
company = fields.Many2One('company.company', 'Company', required=True,
|
||||
select=1, states=_STATES_STOP, domain=[
|
||||
('id', If(In('company', Eval('context', {})), '=', '!='),
|
||||
Get(Eval('context', {}), 'company', 0)),
|
||||
], on_change=['company'], depends=['state'])
|
||||
currency = fields.Function(fields.Many2One('currency.currency',
|
||||
'Currency'), 'get_currency')
|
||||
currency_digits = fields.Function(fields.Integer('Currency Digits'),
|
||||
'get_currency_digits')
|
||||
amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
|
||||
depends=['currency_digits'], help='Estimated revenue amount')
|
||||
warehouse = fields.Many2One('stock.location', 'Warehouse',
|
||||
domain=[('type', '=', 'warehouse')], states={
|
||||
'required': Equal(Eval('state'), 'converted'),
|
||||
'readonly': In(Eval('state'),
|
||||
['converted', 'lost', 'cancelled']),
|
||||
})
|
||||
payment_term = fields.Many2One('account.invoice.payment_term',
|
||||
'Payment Term', states={
|
||||
'required': Equal(Eval('state'), 'converted'),
|
||||
'readonly': In(Eval('state'),
|
||||
['converted', 'lost', 'cancelled']),
|
||||
})
|
||||
employee = fields.Many2One('company.employee', 'Employee', required=True,
|
||||
states=_STATES_STOP, depends=['state', 'company'],
|
||||
domain=[('company', '=', Eval('company'))])
|
||||
start_date = fields.Date('Start Date', required=True, select=1,
|
||||
states=_STATES_START, depends=['state'])
|
||||
end_date = fields.Date('End Date', select=2, readonly=True, states={
|
||||
'invisible': Not(In(Eval('state'),
|
||||
['converted', 'cancelled', 'lost'])),
|
||||
}, depends=['state'])
|
||||
description = fields.Char('Description', required=True,
|
||||
states=_STATES_STOP, depends=['state'])
|
||||
comment = fields.Text('Comment', states=_STATES_STOP)
|
||||
lines = fields.One2Many('sale.opportunity.line', 'opportunity', 'Lines',
|
||||
states=_STATES_STOP, depends=['state'])
|
||||
state = fields.Selection(STATES, 'State', required=True, select=1,
|
||||
sort=False, readonly=True)
|
||||
probability = fields.Integer('Conversion Probability',
|
||||
states={
|
||||
'readonly': Not(In(Eval('state'), ['opportunity', 'lead'])),
|
||||
}, depends=['state'], help="Percentage between 0 and 100")
|
||||
history = fields.One2Many('sale.opportunity.history', 'opportunity',
|
||||
'History', readonly=True)
|
||||
lost_reason = fields.Text('Reason for loss', states={
|
||||
'invisible': Not(Equal(Eval('state'), 'lost')),
|
||||
}, depends=['state'])
|
||||
sale = fields.Many2One('sale.sale', 'Sale', readonly=True, states={
|
||||
'invisible': Not(Equal(Eval('state'), 'converted')),
|
||||
}, depends=['state'])
|
||||
|
||||
def __init__(self):
|
||||
super(SaleOpportunity, self).__init__()
|
||||
self._order.insert(0, ('start_date', 'DESC'))
|
||||
self._sql_constraints += [
|
||||
('check_percentage',
|
||||
'CHECK(probability >= 0 AND probability <= 100)',
|
||||
'Probability must be between 0 and 100!')
|
||||
]
|
||||
self._rpc.update({
|
||||
'button_lead': True,
|
||||
})
|
||||
|
||||
def default_state(self, cursor, user, context=None):
|
||||
return 'lead'
|
||||
|
||||
def default_start_date(self, cursor, user, context=None):
|
||||
date_obj = self.pool.get('ir.date')
|
||||
return date_obj.today(cursor, user, context=context)
|
||||
|
||||
def default_probability(self, cursor, user, context=None):
|
||||
return 50
|
||||
|
||||
def default_company(self, cursor, user, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
return context.get('company', False)
|
||||
|
||||
def default_employee(self, cursor, user_id, context=None):
|
||||
user_obj = self.pool.get('res.user')
|
||||
employee_obj = self.pool.get('company.employee')
|
||||
|
||||
if context is None:
|
||||
context = {}
|
||||
employee_id = None
|
||||
if context.get('employee'):
|
||||
employee_id = context['employee']
|
||||
else:
|
||||
user = user_obj.browse(cursor, user_id, user_id, context=context)
|
||||
if user.employee:
|
||||
employee_id = user.employee.id
|
||||
if employee_id:
|
||||
return employee_id
|
||||
return False
|
||||
|
||||
def default_payment_term(self, cursor, user, context=None):
|
||||
payment_term_obj = self.pool.get('account.invoice.payment_term')
|
||||
payment_term_ids = payment_term_obj.search(cursor, user,
|
||||
self.payment_term.domain, context=context)
|
||||
if len(payment_term_ids) == 1:
|
||||
return payment_term_ids[0]
|
||||
return False
|
||||
|
||||
def default_warehouse(self, cursor, user, context=None):
|
||||
location_obj = self.pool.get('stock.location')
|
||||
location_ids = location_obj.search(cursor, user,
|
||||
self.warehouse.domain, context=context)
|
||||
if len(location_ids) == 1:
|
||||
return location_ids[0]
|
||||
return False
|
||||
|
||||
def get_currency(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for opportunity in self.browse(cursor, user, ids, context=context):
|
||||
res[opportunity.id] = opportunity.company.currency.id
|
||||
return res
|
||||
|
||||
def get_currency_digits(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for opportunity in self.browse(cursor, user, ids, context=context):
|
||||
res[opportunity.id] = opportunity.company.currency.digits
|
||||
return res
|
||||
|
||||
def on_change_company(self, cursor, user, values, context=None):
|
||||
company_obj = self.pool.get('company.company')
|
||||
|
||||
res = {}
|
||||
if values.get('company'):
|
||||
company = company_obj.browse(cursor, user, values['company'],
|
||||
context=context)
|
||||
res['currency'] = company.currency.id
|
||||
res['currency.rec_name'] = company.currency.rec_name
|
||||
res['currency_digits'] = company.currency.digits
|
||||
return res
|
||||
|
||||
def on_change_party(self, cursor, user, values, context=None):
|
||||
party_obj = self.pool.get('party.party')
|
||||
payment_term_obj = self.pool.get('account.invoice.payment_term')
|
||||
|
||||
res = {
|
||||
'payment_term': False,
|
||||
}
|
||||
if vals.get('party'):
|
||||
party = party_obj.browse(cursor, user, vals['party'],
|
||||
context=context)
|
||||
if party.payment_term:
|
||||
res['payment_term'] = party.payment_term.id
|
||||
if not res['payment_term']:
|
||||
res['payment_term'] = self.default_payment_term(cursor, user,
|
||||
context=context)
|
||||
if res['payment_term']:
|
||||
res['payment_term.rec_name'] = payment_term_obj.browse(cursor, user,
|
||||
res['payment_term'], context=context).rec_name
|
||||
return res
|
||||
|
||||
def set_end_date(self, cursor, user, opportunity_id, state, context=None):
|
||||
"""
|
||||
This will fill the end_date for a lead/opportunity and change the state
|
||||
|
||||
:param cursor: the database cursor
|
||||
:param user: the user id
|
||||
:param opportunity_id: the id of the opportunity
|
||||
:param state: the target state
|
||||
:param context: the context
|
||||
"""
|
||||
date_obj = self.pool.get('ir.date')
|
||||
self.write(cursor, user, opportunity_id, {
|
||||
'end_date': date_obj.today(cursor, user, context=context),
|
||||
'state': state,
|
||||
}, context=context)
|
||||
|
||||
def button_lead(self, cursor, user, ids, context=None):
|
||||
self.workflow_trigger_create(cursor, user, ids, context=context)
|
||||
return True
|
||||
|
||||
def _get_sale_line_opportunity_line(self, cursor, user, opportunity,
|
||||
context=None):
|
||||
'''
|
||||
Return sale line values for each opportunity line
|
||||
|
||||
:param cursor: the database cursor
|
||||
:param user: the user id
|
||||
:param opportunity: the BrowseRecord of opportunity
|
||||
:param context: the context
|
||||
|
||||
:return: a dictionary with opportunity line id as key
|
||||
and a dictionary of sale line values as value
|
||||
'''
|
||||
line_obj = self.pool.get('sale.opportunity.line')
|
||||
res = {}
|
||||
for line in opportunity.lines:
|
||||
val = line_obj.get_sale_line(cursor, user, line, context=context)
|
||||
if val:
|
||||
res[line.id] = val
|
||||
return res
|
||||
|
||||
def _get_sale_opportunity(self, cursor, user, opportunity, context=None):
|
||||
'''
|
||||
Return sale values for an opportunity
|
||||
|
||||
:param cursor: the database cursor
|
||||
:param user: the user id
|
||||
:param opportunity: the BrowseRecord of the opportunity
|
||||
:param context: the context
|
||||
|
||||
:return: a dictionary with sale fields as key and sale values as value
|
||||
'''
|
||||
res = {
|
||||
'description': opportunity.description,
|
||||
'party': opportunity.party.id,
|
||||
'payment_term': opportunity.payment_term.id,
|
||||
'company': opportunity.company.id,
|
||||
'warehouse': opportunity.warehouse.id,
|
||||
'invoice_address': opportunity.address and opportunity.address.id,
|
||||
'shipment_address': opportunity.address and opportunity.address.id,
|
||||
'currency': opportunity.company.currency.id,
|
||||
'comment': opportunity.comment,
|
||||
}
|
||||
return res
|
||||
|
||||
def create_sale(self, cursor, user, opportunity_id, context=None):
|
||||
'''
|
||||
Create a sale for the opportunity
|
||||
|
||||
:param cursor: the database cursor
|
||||
:param user: the user id
|
||||
:param opportunity_id: the id of the opportunity
|
||||
:param context: the context
|
||||
'''
|
||||
sale_obj = self.pool.get('sale.sale')
|
||||
sale_line_obj = self.pool.get('sale.line')
|
||||
line_obj = self.pool.get('sale.opportunity.line')
|
||||
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
opportunity = self.browse(cursor, user, opportunity_id,
|
||||
context=context)
|
||||
|
||||
values = self._get_sale_opportunity(cursor, user, opportunity,
|
||||
context=context)
|
||||
sale_line_values = self._get_sale_line_opportunity_line(cursor, user,
|
||||
opportunity, context=context)
|
||||
|
||||
ctx = context.copy()
|
||||
ctx['user'] = user
|
||||
sale_id = sale_obj.create(cursor, 0, values, context=ctx)
|
||||
|
||||
for line_id, values in sale_line_values.iteritems():
|
||||
values['sale'] = sale_id
|
||||
sale_line_id = sale_line_obj.create(cursor, 0, values,
|
||||
context=ctx)
|
||||
line_obj.write(cursor, user, line_id, {
|
||||
'sale_line': sale_line_id,
|
||||
}, context=context)
|
||||
|
||||
self.write(cursor, user, opportunity_id, {
|
||||
'sale': sale_id,
|
||||
}, context=context)
|
||||
return sale_id
|
||||
|
||||
SaleOpportunity()
|
||||
|
||||
|
||||
class SaleOpportunityLine(ModelSQL, ModelView):
|
||||
'Sale Opportunity Line'
|
||||
_name = "sale.opportunity.line"
|
||||
_description = __doc__
|
||||
_rec_name = "product"
|
||||
_history = True
|
||||
|
||||
opportunity = fields.Many2One('sale.opportunity', 'Opportunity')
|
||||
sequence = fields.Integer('Sequence')
|
||||
product = fields.Many2One('product.product', 'Product', required=True,
|
||||
domain=[('salable', '=', True)], on_change=['product', 'unit'])
|
||||
quantity = fields.Float('Quantity', required=True,
|
||||
digits=(16, Eval('unit_digits', 2)), depends=['unit_digits'])
|
||||
unit = fields.Many2One('product.uom', 'Unit', required=True)
|
||||
unit_digits = fields.Function(fields.Integer('Unit Digits',
|
||||
on_change_with=['unit']), 'get_unit_digits')
|
||||
sale_line = fields.Many2One('sale.line', 'Sale Line', readonly=True,
|
||||
states={
|
||||
'invisible': Not(Equal(Eval('_parent_opportunity.state'),
|
||||
'converted')),
|
||||
})
|
||||
|
||||
def __init__(self):
|
||||
super(SaleOpportunityLine, self).__init__()
|
||||
self._order.insert(0, ('sequence', 'ASC'))
|
||||
|
||||
def on_change_with_unit_digits(self, cursor, user, vals, context=None):
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
if vals.get('unit'):
|
||||
uom = uom_obj.browse(cursor, user, vals['unit'],
|
||||
context=context)
|
||||
return uom.digits
|
||||
return 2
|
||||
|
||||
def get_unit_digits(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for line in self.browse(cursor, user, ids, context=context):
|
||||
if line.unit:
|
||||
res[line.id] = line.unit.digits
|
||||
else:
|
||||
res[line.id] = 2
|
||||
return res
|
||||
|
||||
def on_change_product(self, cursor, user, vals, context=None):
|
||||
product_obj = self.pool.get('product.product')
|
||||
|
||||
if context is None:
|
||||
context = {}
|
||||
if not vals.get('product'):
|
||||
return {}
|
||||
res = {}
|
||||
|
||||
product = product_obj.browse(cursor, user, vals['product'],
|
||||
context=context)
|
||||
category = product.sale_uom.category
|
||||
if not vals.get('unit') \
|
||||
or vals.get('unit') not in [x.id for x in category.uoms]:
|
||||
res['unit'] = product.sale_uom.id
|
||||
res['unit.rec_name'] = product.sale_uom.rec_name
|
||||
res['unit_digits'] = product.sale_uom.digits
|
||||
return res
|
||||
|
||||
def get_sale_line(self, cursor, user, line, context=None):
|
||||
'''
|
||||
Return sale line values for opportunity line
|
||||
|
||||
:param cursor: the database cursor
|
||||
:param user: the user id
|
||||
:param line: the BrowseRecord of the line
|
||||
:param context: the context
|
||||
|
||||
:return: a dictionary with sale line fields as key
|
||||
and sale line values as value
|
||||
'''
|
||||
sale_line_obj = self.pool.get('sale.line')
|
||||
res = {
|
||||
'type': 'line',
|
||||
'quantity': line.quantity,
|
||||
'unit': line.unit.id,
|
||||
'product': line.product.id,
|
||||
}
|
||||
res.update(sale_line_obj.on_change_product(cursor, user, {
|
||||
'product': line.product.id,
|
||||
'unit': line.unit.id,
|
||||
'quantity': line.quantity,
|
||||
'_parent_sale.party': line.opportunity.party.id,
|
||||
'_parent_sale.currency': line.opportunity.company.currency.id,
|
||||
}, context=context))
|
||||
return res
|
||||
|
||||
SaleOpportunityLine()
|
||||
|
||||
|
||||
class One2ManyHistory(fields.One2Many):
|
||||
|
||||
def get(self, cursor, user, ids, model, name, values=None,
|
||||
context=None):
|
||||
opportunity2id = {}
|
||||
histories = model.browse(cursor, user, ids, context=context)
|
||||
opportunity2id = dict((history.opportunity.id, history.id) for
|
||||
history in histories)
|
||||
res = super(One2ManyHistory, self).get(cursor, user,
|
||||
opportunity2id.keys(), model, name, values=values,
|
||||
context=context)
|
||||
return dict((opportunity2id[i], j) for i, j in res.iteritems())
|
||||
|
||||
|
||||
class SaleOpportunityHistory(ModelSQL, ModelView):
|
||||
'Sale Opportunity History'
|
||||
_name = 'sale.opportunity.history'
|
||||
_description = __doc__
|
||||
|
||||
date = fields.DateTime('Change Date')
|
||||
opportunity = fields.Many2One('sale.opportunity', 'Sale Opportunity')
|
||||
user = fields.Many2One('res.user', 'User')
|
||||
party = fields.Many2One('party.party', 'Party', datetime_field='date')
|
||||
address = fields.Many2One('party.address', 'Address',
|
||||
datetime_field='date')
|
||||
company = fields.Many2One('company.company', 'Company',
|
||||
datetime_field='date')
|
||||
employee = fields.Many2One('company.employee', 'Employee',
|
||||
datetime_field='date')
|
||||
start_date = fields.Date('Start Date')
|
||||
end_date = fields.Date('End Date', states={
|
||||
'invisible': Not(In(Eval('state'),
|
||||
['converted', 'cancelled', 'lost'])),
|
||||
}, depends=['state'])
|
||||
description = fields.Char('Description')
|
||||
comment = fields.Text('Comment')
|
||||
lines = One2ManyHistory('sale.opportunity.line', 'opportunity', 'Lines',
|
||||
datetime_field='date')
|
||||
state = fields.Selection(STATES, 'State')
|
||||
probability = fields.Integer('Conversion Probability')
|
||||
lost_reason = fields.Text('Reason for loss', states={
|
||||
'invisible': Not(Equal(Eval('state'), 'lost')),
|
||||
}, depends=['state'])
|
||||
|
||||
|
||||
def __init__(self):
|
||||
super(SaleOpportunityHistory, self).__init__()
|
||||
self._order.insert(0, ('date', 'DESC'))
|
||||
|
||||
def _table_query_fields(self, context=None):
|
||||
return [
|
||||
'MIN(__id) AS id',
|
||||
'id AS opportunity',
|
||||
'MIN(COALESCE(write_date, create_date)) AS date',
|
||||
'COALESCE(write_uid, create_uid) AS user',
|
||||
] + [name for name, field in self._columns.iteritems()
|
||||
if name not in ('id', 'opportunity', 'date', 'user')
|
||||
and not hasattr(field, 'set')]
|
||||
|
||||
def _table_query_group(self, context=None):
|
||||
return [
|
||||
'id',
|
||||
'COALESCE(write_uid, create_uid)',
|
||||
] + [name for name, field in self._columns.iteritems()
|
||||
if name not in ('id', 'opportunity', 'date', 'user')
|
||||
and not hasattr(field, 'set')]
|
||||
|
||||
def table_query(self, context=None):
|
||||
opportunity_obj = self.pool.get('sale.opportunity')
|
||||
return (('SELECT ' + \
|
||||
(', '.join(self._table_query_fields(context=context))) + \
|
||||
' FROM "%s__history" '
|
||||
'GROUP BY ' + \
|
||||
(', '.join(self._table_query_group(context=context)))) % \
|
||||
opportunity_obj._table, [])
|
||||
|
||||
def read(self, cursor, user, ids, fields_names=None, context=None):
|
||||
res = super(SaleOpportunityHistory, self).read(cursor, user, ids,
|
||||
fields_names=fields_names, context=context)
|
||||
|
||||
# Remove microsecond from timestamp
|
||||
for values in res:
|
||||
if 'date' in values:
|
||||
values['date'] = values['date'].replace(microsecond=0)
|
||||
return res
|
||||
|
||||
SaleOpportunityHistory()
|
||||
|
||||
|
||||
class SaleOpportunityEmployee(ModelSQL, ModelView):
|
||||
'Sale Opportunity per Employee'
|
||||
_name = 'sale.opportunity_employee'
|
||||
_description = __doc__
|
||||
|
||||
employee = fields.Many2One('company.employee', 'Employee')
|
||||
number = fields.Integer('Number')
|
||||
converted = fields.Integer('Converted')
|
||||
conversion_rate = fields.Function(fields.Float('Conversion Rate',
|
||||
help='In %'), 'get_conversion_rate')
|
||||
lost = fields.Integer('Lost')
|
||||
company = fields.Many2One('company.company', 'Company')
|
||||
currency = fields.Function(fields.Many2One('currency.currency',
|
||||
'Currency'), 'get_currency')
|
||||
currency_digits = fields.Function(fields.Integer('Currency Digits'),
|
||||
'get_currency_digits')
|
||||
amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
|
||||
depends=['currency_digits'])
|
||||
converted_amount = fields.Numeric('Converted Amount',
|
||||
digits=(16, Eval('currency_digits', 2)),
|
||||
depends=['currency_digits'])
|
||||
conversion_amount_rate = fields.Function(fields.Float(
|
||||
'Conversion Amount Rate', help='In %'), 'get_conversion_amount_rate')
|
||||
|
||||
def _converted_state(self, context=None):
|
||||
return ['converted']
|
||||
|
||||
def _lost_state(self, context=None):
|
||||
return ['lost']
|
||||
|
||||
def table_query(self, context=None):
|
||||
opportunity_obj = self.pool.get('sale.opportunity')
|
||||
if context is None:
|
||||
context = {}
|
||||
clause = ' '
|
||||
args = [True]
|
||||
if context.get('start_date'):
|
||||
clause += 'AND start_date >= %s '
|
||||
args.append(context['start_date'])
|
||||
if context.get('end_date'):
|
||||
clause += 'AND start_date <= %s '
|
||||
args.append(context['end_date'])
|
||||
return ('SELECT DISTINCT(employee) AS id, '
|
||||
'MAX(create_uid) AS create_uid, '
|
||||
'MAX(create_date) AS create_date, '
|
||||
'MAX(write_uid) AS write_uid, '
|
||||
'MAX(write_date) AS write_date, '
|
||||
'employee, '
|
||||
'company, '
|
||||
'COUNT(1) AS number, '
|
||||
'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
|
||||
for x in self._converted_state(context=context)) + ') '
|
||||
'THEN 1 ELSE 0 END) AS converted, '
|
||||
'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
|
||||
for x in self._lost_state(context=context)) + ') '
|
||||
'THEN 1 ELSE 0 END) AS lost, '
|
||||
'SUM(amount) AS amount, '
|
||||
'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
|
||||
for x in self._converted_state(context=context)) + ') '
|
||||
'THEN amount ELSE 0 END) AS converted_amount '
|
||||
'FROM "' + opportunity_obj._table + '" '
|
||||
'WHERE %s ' \
|
||||
+ clause + \
|
||||
'GROUP BY employee, company', args)
|
||||
|
||||
def get_conversion_rate(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
if record.number:
|
||||
res[record.id] = float(record.converted) / record.number * 100.0
|
||||
else:
|
||||
res[record.id] = 0.0
|
||||
return res
|
||||
|
||||
def get_currency(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
res[record.id] = record.company.currency.id
|
||||
return res
|
||||
|
||||
def get_currency_digits(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
res[record.id] = record.company.currency.digits
|
||||
return res
|
||||
|
||||
def get_conversion_amount_rate(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
if record.amount:
|
||||
res[record.id] = (float(record.converted_amount) /
|
||||
float(record.amount) * 100.0)
|
||||
else:
|
||||
res[record.id] = 0.0
|
||||
return res
|
||||
|
||||
SaleOpportunityEmployee()
|
||||
|
||||
|
||||
class OpenSaleOpportunityEmployeeInit(ModelView):
|
||||
'Open Sale Opportunity per Employee Init'
|
||||
_name = 'sale.open_opportunity_employee.init'
|
||||
_description = __doc__
|
||||
start_date = fields.Date('Start Date')
|
||||
end_date = fields.Date('End Date')
|
||||
|
||||
OpenSaleOpportunityEmployeeInit()
|
||||
|
||||
|
||||
class OpenSaleOpportunityEmployee(Wizard):
|
||||
'Open Sale Opportunity per Employee'
|
||||
_name = 'sale.open_opportunity_employee'
|
||||
states = {
|
||||
'init': {
|
||||
'result': {
|
||||
'type': 'form',
|
||||
'object': 'sale.open_opportunity_employee.init',
|
||||
'state': [
|
||||
('end', 'Cancel', 'tryton-cancel'),
|
||||
('open', 'Open', 'tryton-ok', True),
|
||||
],
|
||||
},
|
||||
},
|
||||
'open': {
|
||||
'result': {
|
||||
'type': 'action',
|
||||
'action': '_action_open',
|
||||
'state': 'end',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def _action_open(self, cursor, user, data, context=None):
|
||||
model_data_obj = self.pool.get('ir.model.data')
|
||||
act_window_obj = self.pool.get('ir.action.act_window')
|
||||
act_window_id = model_data_obj.get_id(cursor, user, 'sale_opportunity',
|
||||
'act_opportunity_employee_form', context=context)
|
||||
res = act_window_obj.read(cursor, user, act_window_id, context=context)
|
||||
res['pyson_context'] = PYSONEncoder().encode({
|
||||
'start_date': data['form']['start_date'],
|
||||
'end_date': data['form']['end_date'],
|
||||
})
|
||||
return res
|
||||
|
||||
OpenSaleOpportunityEmployee()
|
||||
|
||||
|
||||
class SaleOpportunityMonthly(ModelSQL, ModelView):
|
||||
'Sale Opportunity per Month'
|
||||
_name = 'sale.opportunity_monthly'
|
||||
_description = __doc__
|
||||
|
||||
year = fields.Char('Year')
|
||||
month = fields.Integer('Month')
|
||||
year_month = fields.Function(fields.Char('Year-Month'),
|
||||
'get_year_month')
|
||||
number = fields.Integer('Number')
|
||||
converted = fields.Integer('Converted')
|
||||
conversion_rate = fields.Function(fields.Float('Conversion Rate',
|
||||
help='In %'), 'get_conversion_rate')
|
||||
lost = fields.Integer('Lost')
|
||||
company = fields.Many2One('company.company', 'Company')
|
||||
currency = fields.Function(fields.Many2One('currency.currency',
|
||||
'Currency'), 'get_currency')
|
||||
currency_digits = fields.Function(fields.Integer('Currency Digits'),
|
||||
'get_currency_digits')
|
||||
amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
|
||||
depends=['currency_digits'])
|
||||
converted_amount = fields.Numeric('Converted Amount',
|
||||
digits=(16, Eval('currency_digits', 2)),
|
||||
depends=['currency_digits'])
|
||||
conversion_amount_rate = fields.Function(fields.Float(
|
||||
'Conversion Amount Rate', help='In %'), 'get_conversion_amount_rate')
|
||||
|
||||
def __init__(self):
|
||||
super(SaleOpportunityMonthly, self).__init__()
|
||||
self._order.insert(0, ('year', 'DESC'))
|
||||
self._order.insert(1, ('month', 'DESC'))
|
||||
|
||||
def _converted_state(self, context=None):
|
||||
return ['converted']
|
||||
|
||||
def _lost_state(self, context=None):
|
||||
return ['lost']
|
||||
|
||||
def table_query(self, context=None):
|
||||
opportunity_obj = self.pool.get('sale.opportunity')
|
||||
type_id = FIELDS[self.id._type].sql_type(self.id)[0]
|
||||
type_year = FIELDS[self.year._type].sql_type(self.year)[0]
|
||||
return ('SELECT CAST(id AS ' + type_id + '), create_uid, create_date, '
|
||||
'write_uid, write_date, '
|
||||
'CAST(year AS ' + type_year + '), month, company, number, '
|
||||
'converted, lost, amount, converted_amount '
|
||||
'FROM ('
|
||||
'SELECT EXTRACT(MONTH FROM start_date) + '
|
||||
'EXTRACT(YEAR FROM start_date) * 100 AS id, '
|
||||
'MAX(create_uid) AS create_uid, '
|
||||
'MAX(create_date) AS create_date, '
|
||||
'MAX(write_uid) AS write_uid, '
|
||||
'MAX(write_date) AS write_date, '
|
||||
'EXTRACT(YEAR FROM start_date) AS year, '
|
||||
'EXTRACT(MONTH FROM start_date) AS month, '
|
||||
'company, '
|
||||
'COUNT(1) AS number, '
|
||||
'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
|
||||
for x in self._converted_state(context=context)) + ') '
|
||||
'THEN 1 ELSE 0 END) AS converted, '
|
||||
'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
|
||||
for x in self._lost_state(context=context)) + ') '
|
||||
'THEN 1 ELSE 0 END) AS lost, '
|
||||
'SUM(amount) AS amount, '
|
||||
'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
|
||||
for x in self._converted_state(context=context)) + ') '
|
||||
'THEN amount ELSE 0 END) AS converted_amount '
|
||||
'FROM "' + opportunity_obj._table + '" '
|
||||
'GROUP BY year, month, company) AS "' + self._table + '"',
|
||||
[])
|
||||
|
||||
def get_conversion_rate(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
if record.number:
|
||||
res[record.id] = float(record.converted) / record.number * 100.0
|
||||
else:
|
||||
res[record.id] = 0.0
|
||||
return res
|
||||
|
||||
def get_year_month(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
res[record.id] = '%s-%s' % (record.year, int(record.month))
|
||||
return res
|
||||
|
||||
def get_currency(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
res[record.id] = record.company.currency.id
|
||||
return res
|
||||
|
||||
def get_currency_digits(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
res[record.id] = record.company.currency.digits
|
||||
return res
|
||||
|
||||
def get_conversion_amount_rate(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
if record.amount:
|
||||
res[record.id] = (float(record.converted_amount) /
|
||||
float(record.amount) * 100.0)
|
||||
else:
|
||||
res[record.id] = 0.0
|
||||
return res
|
||||
|
||||
SaleOpportunityMonthly()
|
||||
|
||||
|
||||
class SaleOpportunityEmployeeMonthly(ModelSQL, ModelView):
|
||||
'Sale Opportunity per Employee per Month'
|
||||
_name = 'sale.opportunity_employee_monthly'
|
||||
_description = __doc__
|
||||
|
||||
year = fields.Char('Year')
|
||||
month = fields.Integer('Month')
|
||||
employee = fields.Many2One('company.employee', 'Employee')
|
||||
number = fields.Integer('Number')
|
||||
converted = fields.Integer('Converted')
|
||||
conversion_rate = fields.Function(fields.Float('Conversion Rate',
|
||||
help='In %'), 'get_conversion_rate')
|
||||
lost = fields.Integer('Lost')
|
||||
company = fields.Many2One('company.company', 'Company')
|
||||
currency = fields.Function(fields.Many2One('currency.currency',
|
||||
'Currency'), 'get_currency')
|
||||
currency_digits = fields.Function(fields.Integer('Currency Digits'),
|
||||
'get_currency_digits')
|
||||
amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
|
||||
depends=['currency_digits'])
|
||||
converted_amount = fields.Numeric('Converted Amount',
|
||||
digits=(16, Eval('currency_digits', 2)),
|
||||
depends=['currency_digits'])
|
||||
conversion_amount_rate = fields.Function(fields.Float(
|
||||
'Conversion Amount Rate', help='In %'), 'get_conversion_amount_rate')
|
||||
|
||||
def __init__(self):
|
||||
super(SaleOpportunityEmployeeMonthly, self).__init__()
|
||||
self._order.insert(0, ('year', 'DESC'))
|
||||
self._order.insert(1, ('month', 'DESC'))
|
||||
self._order.insert(2, ('employee', 'ASC'))
|
||||
|
||||
def _converted_state(self, context=None):
|
||||
return ['converted']
|
||||
|
||||
def _lost_state(self, context=None):
|
||||
return ['lost']
|
||||
|
||||
def table_query(self, context=None):
|
||||
opportunity_obj = self.pool.get('sale.opportunity')
|
||||
type_id = FIELDS[self.id._type].sql_type(self.id)[0]
|
||||
type_year = FIELDS[self.year._type].sql_type(self.year)[0]
|
||||
return ('SELECT CAST(id AS ' + type_id + '), create_uid, create_date, '
|
||||
'write_uid, write_date, '
|
||||
'CAST(year AS ' + type_year + '), month, employee, '
|
||||
'company, number, converted, lost, amount, converted_amount '
|
||||
'FROM ('
|
||||
'SELECT EXTRACT(MONTH FROM start_date) + '
|
||||
'EXTRACT(YEAR FROM start_date) * 100 + '
|
||||
'employee * 1000000 AS id, '
|
||||
'MAX(create_uid) AS create_uid, '
|
||||
'MAX(create_date) AS create_date, '
|
||||
'MAX(write_uid) AS write_uid, '
|
||||
'MAX(write_date) AS write_date, '
|
||||
'EXTRACT(YEAR FROM start_date) AS year, '
|
||||
'EXTRACT(MONTH FROM start_date) AS month, '
|
||||
'employee, '
|
||||
'company, '
|
||||
'COUNT(1) AS number, '
|
||||
'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
|
||||
for x in self._converted_state(context=context)) + ') '
|
||||
'THEN 1 ELSE 0 END) AS converted, '
|
||||
'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
|
||||
for x in self._lost_state(context=context)) + ') '
|
||||
'THEN 1 ELSE 0 END) AS lost, '
|
||||
'SUM(amount) AS amount, '
|
||||
'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
|
||||
for x in self._converted_state(context=context)) + ') '
|
||||
'THEN amount ELSE 0 END) AS converted_amount '
|
||||
'FROM "' + opportunity_obj._table + '" '
|
||||
'GROUP BY year, month, employee, company) '
|
||||
'AS "' + self._table + '"', [])
|
||||
|
||||
def get_conversion_rate(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
if record.number:
|
||||
res[record.id] = float(record.converted) / record.number * 100.0
|
||||
else:
|
||||
res[record.id] = 0.0
|
||||
return res
|
||||
|
||||
def get_currency(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
res[record.id] = record.company.currency.id
|
||||
return res
|
||||
|
||||
def get_currency_digits(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
res[record.id] = record.company.currency.digits
|
||||
return res
|
||||
|
||||
def get_conversion_amount_rate(self, cursor, user, ids, name, context=None):
|
||||
res = {}
|
||||
for record in self.browse(cursor, user, ids, context=context):
|
||||
if record.amount:
|
||||
res[record.id] = (float(record.converted_amount) /
|
||||
float(record.amount) * 100.0)
|
||||
else:
|
||||
res[record.id] = 0.0
|
||||
return res
|
||||
|
||||
SaleOpportunityEmployeeMonthly()
|
||||
+766
@@ -0,0 +1,766 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tryton>
|
||||
<data>
|
||||
<menuitem name="Reporting" parent="sale.menu_sale"
|
||||
id="menu_reporting" sequence="100"/>
|
||||
|
||||
<record model="res.group" id="group_opportunity">
|
||||
<field name="name">Sale Opportunity</field>
|
||||
</record>
|
||||
<record model="res.user" id="res.user_admin">
|
||||
<field name="groups" eval="[('add', ref('group_opportunity'))]"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="opportunity_view_form">
|
||||
<field name="model">sale.opportunity</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<form string="Sale Lead/Opportunity">
|
||||
<label name="party"/>
|
||||
<field name="party"/>
|
||||
<label name="address"/>
|
||||
<field name="address"/>
|
||||
<label name="amount"/>
|
||||
<group col="2" id="amount_currency">
|
||||
<field name="amount"/>
|
||||
<field name="currency"/>
|
||||
</group>
|
||||
<label name="probability"/>
|
||||
<field name="probability"/>
|
||||
<label name="description"/>
|
||||
<field name="description" colspan="3"/>
|
||||
<notebook colspan="4">
|
||||
<page string="Lead/Opportunity" id="opportunity">
|
||||
<label name="company"/>
|
||||
<field name="company"/>
|
||||
<label name="employee"/>
|
||||
<field name="employee"/>
|
||||
<label name="start_date"/>
|
||||
<field name="start_date"/>
|
||||
<label name="end_date"/>
|
||||
<field name="end_date"/>
|
||||
<label name="warehouse"/>
|
||||
<field name="warehouse"/>
|
||||
<label name="payment_term"/>
|
||||
<field name="payment_term"/>
|
||||
<label name="sale"/>
|
||||
<field name="sale"/>
|
||||
<separator name="comment" colspan="4"/>
|
||||
<field name="comment" colspan="4"/>
|
||||
<separator name="lost_reason" colspan="4"/>
|
||||
<field name="lost_reason" colspan="4"/>
|
||||
<group col="2" colspan="2" id="state">
|
||||
<label name="state"/>
|
||||
<field name="state"/>
|
||||
</group>
|
||||
<group col="6" colspan="2" id="buttons">
|
||||
<button name="cancelled" string="Cancel"
|
||||
states="{'invisible': Not(In(Eval('state'), ['lead', 'opportunity']))}"
|
||||
icon="tryton-cancel"/>
|
||||
<button name="lost_lead" string="Mark as Lost"
|
||||
states="{'invisible':Not(In(Eval('state'), ['lead','opportunity']))}"
|
||||
icon="tryton-cancel"/>
|
||||
<button name="set_as_lead" string="Set as Lead"
|
||||
states="{'invisible':Not(In(Eval('state'), ['opportunity']))}"
|
||||
icon="tryton-go-previous"/>
|
||||
<button name="convert_to_opportunity" string="Convert to Opportunity"
|
||||
states="{'invisible': Not(In(Eval('state'), ['lead']))}"
|
||||
icon="tryton-go-next"/>
|
||||
<button name="convert_to_sale" string="Convert to Sale"
|
||||
states="{'invisible': Not(In(Eval('state'), ['opportunity']))}"
|
||||
icon="tryton-go-next"/>
|
||||
<button name="button_lead" string="Reset as Lead"
|
||||
type="object"
|
||||
states="{'invisible': Not(In(Eval('state'), ['lost', 'cancelled']))}"
|
||||
icon="tryton-clear"/>
|
||||
</group>
|
||||
</page>
|
||||
<page name="lines">
|
||||
<field name="lines"/>
|
||||
</page>
|
||||
<page name="history">
|
||||
<field name="history"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="opportunity_view_tree">
|
||||
<field name="model">sale.opportunity</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<tree string="Sales Leads/Opportunities">
|
||||
<field name="start_date" select="2"/>
|
||||
<field name="end_date" select="2"/>
|
||||
<field name="description" select="2"/>
|
||||
<field name="party" select="1"/>
|
||||
<field name="amount" select="1"/>
|
||||
<field name="currency"/>
|
||||
<field name="state" select="1"/>
|
||||
<field name="probability" select="2"/>
|
||||
</tree>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_opportunity_form">
|
||||
<field name="name">Leads and Opportunities</field>
|
||||
<field name="res_model">sale.opportunity</field>
|
||||
<field name="view_type">form</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="opportunity_view_tree"/>
|
||||
<field name="act_window" ref="act_opportunity_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="opportunity_view_form"/>
|
||||
<field name="act_window" ref="act_opportunity_form"/>
|
||||
</record>
|
||||
<menuitem parent="sale.menu_sale" action="act_opportunity_form"
|
||||
id="menu_opportunity_form" sequence="5"/>
|
||||
|
||||
<record model="ir.action.act_window" id="act_opportunity_form_new">
|
||||
<field name="name">New Lead</field>
|
||||
<field name="res_model">sale.opportunity</field>
|
||||
<field name="view_type">form</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_new_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="opportunity_view_form"/>
|
||||
<field name="act_window" ref="act_opportunity_form_new"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_new_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="opportunity_view_tree"/>
|
||||
<field name="act_window" ref="act_opportunity_form_new"/>
|
||||
</record>
|
||||
<menuitem parent="menu_opportunity_form" action="act_opportunity_form_new"
|
||||
id="menu_opportunity_form_new" sequence="10"/>
|
||||
|
||||
<record model="ir.action.act_window" id="act_opportunity_form_lead">
|
||||
<field name="name">Opened Leads</field>
|
||||
<field name="res_model">sale.opportunity</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[("state", "=", "lead")]</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_lead_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="opportunity_view_tree"/>
|
||||
<field name="act_window" ref="act_opportunity_form_lead"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_lead_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="opportunity_view_form"/>
|
||||
<field name="act_window" ref="act_opportunity_form_lead"/>
|
||||
</record>
|
||||
<menuitem parent="menu_opportunity_form" action="act_opportunity_form_lead"
|
||||
id="menu_opportunity_form_lead" sequence="20"/>
|
||||
|
||||
<record model="ir.action.act_window" id="act_opportunity_form_opportunity">
|
||||
<field name="name">Opportunities</field>
|
||||
<field name="res_model">sale.opportunity</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[("state", "=", "opportunity")]</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_opportunity_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="opportunity_view_tree"/>
|
||||
<field name="act_window" ref="act_opportunity_form_opportunity"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_opportunity_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="opportunity_view_form"/>
|
||||
<field name="act_window" ref="act_opportunity_form_opportunity"/>
|
||||
</record>
|
||||
<menuitem parent="menu_opportunity_form" action="act_opportunity_form_opportunity"
|
||||
id="menu_opportunity_form_opportunity" sequence="30"/>
|
||||
|
||||
<record model="ir.action.act_window" id="act_opportunity_form_converted">
|
||||
<field name="name">Converted Opportunities</field>
|
||||
<field name="res_model">sale.opportunity</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[("state", "=", "converted")]</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_converted_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="opportunity_view_tree"/>
|
||||
<field name="act_window" ref="act_opportunity_form_converted"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_converted_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="opportunity_view_form"/>
|
||||
<field name="act_window" ref="act_opportunity_form_converted"/>
|
||||
</record>
|
||||
<menuitem parent="menu_opportunity_form" action="act_opportunity_form_converted"
|
||||
id="menu_opportunity_form_converted" sequence="40"/>
|
||||
|
||||
<record model="ir.action.act_window" id="act_opportunity_form_cancelled">
|
||||
<field name="name">Cancelled Leads and Opportunities</field>
|
||||
<field name="res_model">sale.opportunity</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[("state", "=", "cancelled")]</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_cancelled_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="opportunity_view_tree"/>
|
||||
<field name="act_window" ref="act_opportunity_form_cancelled"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_cancelled_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="opportunity_view_form"/>
|
||||
<field name="act_window" ref="act_opportunity_form_cancelled"/>
|
||||
</record>
|
||||
<menuitem parent="menu_opportunity_form" action="act_opportunity_form_cancelled"
|
||||
id="menu_opportunity_form_cancelled" sequence="50"/>
|
||||
|
||||
<record model="ir.action.act_window" id="act_opportunity_form_lost">
|
||||
<field name="name">Lost Leads and Opportunities</field>
|
||||
<field name="res_model">sale.opportunity</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[("state", "=", "lost")]</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_lost_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="opportunity_view_tree"/>
|
||||
<field name="act_window" ref="act_opportunity_form_lost"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_opportunity_form_lost_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="opportunity_view_form"/>
|
||||
<field name="act_window" ref="act_opportunity_form_lost"/>
|
||||
</record>
|
||||
<menuitem parent="menu_opportunity_form" action="act_opportunity_form_lost"
|
||||
id="menu_opportunity_form_lost" sequence="60"/>
|
||||
|
||||
<record model="ir.model.access" id="access_opportunity">
|
||||
<field name="model" search="[('model', '=', 'sale.opportunity')]"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_opportunity_oppotunity">
|
||||
<field name="model" search="[('model', '=', 'sale.opportunity')]"/>
|
||||
<field name="group" ref="group_opportunity"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_opportunity">
|
||||
<field name="model" search="[('model', '=', 'sale.opportunity')]"/>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_group_opportunity1">
|
||||
<field name="field" search="[('name', '=', 'company'), ('model.model', '=', 'sale.opportunity')]"/>
|
||||
<field name="operator">=</field>
|
||||
<field name="operand">User/Current Company</field>
|
||||
<field name="rule_group" ref="rule_group_opportunity"/>
|
||||
</record>
|
||||
|
||||
<record model="workflow" id="opportunity_workflow">
|
||||
<field name="name">Sales Opportunity Workflow</field>
|
||||
<field name="model">sale.opportunity</field>
|
||||
<field name="on_create" eval="True" />
|
||||
</record>
|
||||
|
||||
<record model="workflow.activity" id="opportunity_activity_lead">
|
||||
<field name="name">Lead</field>
|
||||
<field name="workflow" ref="opportunity_workflow"/>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'state': 'lead'})</field>
|
||||
<field name="flow_start" eval="True"/>
|
||||
</record>
|
||||
<record model="workflow.activity" id="opportunity_activity_opportunity">
|
||||
<field name="name">Opportunity</field>
|
||||
<field name="workflow" ref="opportunity_workflow"/>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'state': 'opportunity'})</field>
|
||||
</record>
|
||||
<record model="workflow.activity" id="opportunity_activity_converted">
|
||||
<field name="name">Converted</field>
|
||||
<field name="workflow" ref="opportunity_workflow"/>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">set_end_date('converted')
|
||||
create_sale()</field>
|
||||
<field name="flow_stop" eval="True"/>
|
||||
</record>
|
||||
<record model="workflow.activity" id="opportunity_activity_cancelled">
|
||||
<field name="name">Cancelled</field>
|
||||
<field name="workflow" ref="opportunity_workflow"/>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">set_end_date('cancelled')</field>
|
||||
<field name="flow_stop" eval="True"/>
|
||||
</record>
|
||||
<record model="workflow.activity" id="opportunity_activity_lost">
|
||||
<field name="name">Lost</field>
|
||||
<field name="workflow" ref="opportunity_workflow"/>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">set_end_date('lost')</field>
|
||||
<field name="flow_stop" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="workflow.transition" id="opportunity_transition_lead_opportunity">
|
||||
<field name="act_from" ref="opportunity_activity_lead"/>
|
||||
<field name="act_to" ref="opportunity_activity_opportunity"/>
|
||||
<field name="signal">convert_to_opportunity</field>
|
||||
<field name="group" ref="sale.group_sale"/>
|
||||
</record>
|
||||
<record model="workflow.transition" id="opportunity_transition_opportunity_converted">
|
||||
<field name="act_from" ref="opportunity_activity_opportunity"/>
|
||||
<field name="act_to" ref="opportunity_activity_converted"/>
|
||||
<field name="signal">convert_to_sale</field>
|
||||
<field name="group" ref="sale.group_sale"/>
|
||||
</record>
|
||||
<record model="workflow.transition" id="opportunity_transition_opportunity_lead">
|
||||
<field name="act_from" ref="opportunity_activity_opportunity"/>
|
||||
<field name="act_to" ref="opportunity_activity_lead"/>
|
||||
<field name="signal">set_as_lead</field>
|
||||
<field name="group" ref="sale.group_sale"/>
|
||||
</record>
|
||||
<record model="workflow.transition" id="opportunity_transition_cancelled_lead">
|
||||
<field name="act_from" ref="opportunity_activity_cancelled"/>
|
||||
<field name="act_to" ref="opportunity_activity_lead"/>
|
||||
<field name="signal">set_as_lead</field>
|
||||
<field name="group" ref="sale.group_sale"/>
|
||||
</record>
|
||||
<record model="workflow.transition" id="opportunity_transition_lead_lost">
|
||||
<field name="act_from" ref="opportunity_activity_lead"/>
|
||||
<field name="act_to" ref="opportunity_activity_lost"/>
|
||||
<field name="signal">lost_lead</field>
|
||||
<field name="group" ref="sale.group_sale"/>
|
||||
</record>
|
||||
<record model="workflow.transition" id="opportunity_transition_opportunity_lost">
|
||||
<field name="act_from" ref="opportunity_activity_opportunity"/>
|
||||
<field name="act_to" ref="opportunity_activity_lost"/>
|
||||
<field name="signal">lost_lead</field>
|
||||
<field name="group" ref="sale.group_sale"/>
|
||||
</record>
|
||||
<record model="workflow.transition" id="opportunity_transition_lead_cancelled">
|
||||
<field name="act_from" ref="opportunity_activity_lead"/>
|
||||
<field name="act_to" ref="opportunity_activity_cancelled"/>
|
||||
<field name="signal">cancelled</field>
|
||||
<field name="group" ref="sale.group_sale"/>
|
||||
</record>
|
||||
<record model="workflow.transition" id="opportunity_transition_opp_cancelled">
|
||||
<field name="act_from" ref="opportunity_activity_opportunity"/>
|
||||
<field name="act_to" ref="opportunity_activity_cancelled"/>
|
||||
<field name="signal">cancelled</field>
|
||||
<field name="group" ref="sale.group_sale"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="opportunity_line_view_form">
|
||||
<field name="model">sale.opportunity.line</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<form string="Sale Lead/Opportunity Line">
|
||||
<label name="opportunity"/>
|
||||
<field name="opportunity" colspan="3"/>
|
||||
<label name="product"/>
|
||||
<field name="product"/>
|
||||
<label name="sequence"/>
|
||||
<field name="sequence"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<label name="unit"/>
|
||||
<field name="unit"/>
|
||||
<label name="sale_line"/>
|
||||
<field name="sale_line"/>
|
||||
</form>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="opportunity_line_view_tree">
|
||||
<field name="model">sale.opportunity.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<tree string="Sales Leads/Opportunities Lines" fill="1" sequence="sequence">
|
||||
<field name="opportunity" select="1"/>
|
||||
<field name="product" select="1"/>
|
||||
<field name="quantity" select="2"/>
|
||||
<field name="unit" select="2"/>
|
||||
<field name="sequence" tree_invisible="1"/>
|
||||
</tree>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_opportunity_line">
|
||||
<field name="model" search="[('model', '=', 'sale.opportunity.line')]"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_opportunity_line_opportunity">
|
||||
<field name="model" search="[('model', '=', 'sale.opportunity.line')]"/>
|
||||
<field name="group" ref="group_opportunity"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="opportunity_history_view_form">
|
||||
<field name="model">sale.opportunity.history</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<form string="History">
|
||||
<label name="opportunity"/>
|
||||
<field name="opportunity" colspan="4"/>
|
||||
<label name="user"/>
|
||||
<field name="user"/>
|
||||
<label name="date"/>
|
||||
<field name="date"/>
|
||||
<label name="party"/>
|
||||
<field name="party"/>
|
||||
<label name="address"/>
|
||||
<field name="address"/>
|
||||
<label name="description"/>
|
||||
<field name="description"/>
|
||||
<label name="probability"/>
|
||||
<field name="probability"/>
|
||||
<notebook colspan="4">
|
||||
<page string="Lead/Opportunity" id="opportunity">
|
||||
<label name="company"/>
|
||||
<field name="company"/>
|
||||
<label name="employee"/>
|
||||
<field name="employee"/>
|
||||
<label name="start_date"/>
|
||||
<field name="start_date"/>
|
||||
<label name="end_date"/>
|
||||
<field name="end_date"/>
|
||||
<separator name="comment" colspan="4"/>
|
||||
<field name="comment" colspan="4"/>
|
||||
<separator name="lost_reason" colspan="4"/>
|
||||
<field name="lost_reason" colspan="4"/>
|
||||
<group col="2" colspan="2" id="state">
|
||||
<label name="state"/>
|
||||
<field name="state"/>
|
||||
</group>
|
||||
</page>
|
||||
<page name="lines">
|
||||
<field name="lines"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="opportunity_history_view_tree">
|
||||
<field name="model">sale.opportunity.history</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<tree string="History">
|
||||
<field name="date" select="1"/>
|
||||
<field name="user" select="1"/>
|
||||
<field name="start_date" select="2"/>
|
||||
<field name="end_date" select="2"/>
|
||||
<field name="description" select="2"/>
|
||||
<field name="party" select="2"/>
|
||||
<field name="state" select="1"/>
|
||||
<field name="probability" select="2"/>
|
||||
</tree>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_opportunity_history">
|
||||
<field name="model" search="[('model', '=', 'sale.opportunity.history')]"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_opportunity_history_opportunity">
|
||||
<field name="model" search="[('model', '=', 'sale.opportunity.history')]"/>
|
||||
<field name="group" ref="group_opportunity"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_opportunity_history">
|
||||
<field name="model" search="[('model', '=', 'sale.opportunity.history')]"/>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_group_opportunity_history1">
|
||||
<field name="field" search="[('name', '=', 'company'), ('model.model', '=', 'sale.opportunity.history')]"/>
|
||||
<field name="operator">=</field>
|
||||
<field name="operand">User/Current Company</field>
|
||||
<field name="rule_group" ref="rule_group_opportunity_history"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="opportunity_employee_view_tree">
|
||||
<field name="model">sale.opportunity_employee</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<tree string="Opportunities per Employee">
|
||||
<field name="employee" select="1"/>
|
||||
<field name="number" select="1"/>
|
||||
<field name="converted" select="2"/>
|
||||
<field name="lost" select="2"/>
|
||||
<field name="conversion_rate"/>
|
||||
<field name="amount" select="1"/>
|
||||
<field name="converted_amount" select="2"/>
|
||||
<field name="currency" select="2"/>
|
||||
<field name="conversion_amount_rate"/>
|
||||
</tree>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="opportunity_employee_view_graph1">
|
||||
<field name="model">sale.opportunity_employee</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<graph string="Opportunities per Employee">
|
||||
<x>
|
||||
<field name="employee"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="number"/>
|
||||
<field name="converted"/>
|
||||
<field name="lost"/>
|
||||
</y>
|
||||
</graph>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="opportunity_employee_view_graph2">
|
||||
<field name="model">sale.opportunity_employee</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<graph string="Opportunities per Employee">
|
||||
<x>
|
||||
<field name="employee"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="amount"/>
|
||||
<field name="converted_amount"/>
|
||||
</y>
|
||||
</graph>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_opportunity_employee_form">
|
||||
<field name="name">Opportunities per Employee</field>
|
||||
<field name="res_model">sale.opportunity_employee</field>
|
||||
<field name="view_type">form</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_opportunity_employee_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="opportunity_employee_view_tree"/>
|
||||
<field name="act_window" ref="act_opportunity_employee_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_opportunity_employee_form_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="opportunity_employee_view_graph1"/>
|
||||
<field name="act_window" ref="act_opportunity_employee_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_opportunity_employee_form_view3">
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="view" ref="opportunity_employee_view_graph2"/>
|
||||
<field name="act_window" ref="act_opportunity_employee_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_opportunity_employee">
|
||||
<field name="model" search="[('model', '=', 'sale.opportunity_employee')]"/>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_group_opportunity_employee1">
|
||||
<field name="field" search="[('name', '=', 'company'), ('model.model', '=', 'sale.opportunity_employee')]"/>
|
||||
<field name="operator">=</field>
|
||||
<field name="operand">User/Current Company</field>
|
||||
<field name="rule_group" ref="rule_group_opportunity_employee"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="open_sale_opportunity_employee_init_view_form">
|
||||
<field name="model">sale.open_opportunity_employee.init</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<form string="Sale Opportunities per Employee">
|
||||
<label name="start_date"/>
|
||||
<field name="start_date"/>
|
||||
<label name="end_date"/>
|
||||
<field name="end_date"/>
|
||||
</form>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="act_open_sale_opportunity_employee">
|
||||
<field name="name">Opportunities per Employee</field>
|
||||
<field name="wiz_name">sale.open_opportunity_employee</field>
|
||||
</record>
|
||||
<menuitem parent="menu_reporting" action="act_open_sale_opportunity_employee"
|
||||
icon="tryton-list" id="menu_open_sale_opportunity_employee"/>
|
||||
|
||||
<record model="ir.ui.view" id="opportunity_monthly_view_tree">
|
||||
<field name="model">sale.opportunity_monthly</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<tree string="Sale Opportunities per Month">
|
||||
<field name="year" select="1"/>
|
||||
<field name="month" select="1"/>
|
||||
<field name="number" select="2"/>
|
||||
<field name="converted" select="2"/>
|
||||
<field name="lost" select="2"/>
|
||||
<field name="conversion_rate"/>
|
||||
<field name="amount" select="2"/>
|
||||
<field name="converted_amount" select="2"/>
|
||||
<field name="currency"/>
|
||||
<field name="conversion_amount_rate"/>
|
||||
</tree>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="opportunity_monthly_view_graph1">
|
||||
<field name="model">sale.opportunity_monthly</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<graph string="Sale Opportunities per Month">
|
||||
<x>
|
||||
<field name="year_month"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="number"/>
|
||||
<field name="converted"/>
|
||||
<field name="lost"/>
|
||||
</y>
|
||||
</graph>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="opportunity_monthly_view_graph2">
|
||||
<field name="model">sale.opportunity_monthly</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<graph string="Sale Opportunities per Month">
|
||||
<x>
|
||||
<field name="year_month"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="amount"/>
|
||||
<field name="converted_amount"/>
|
||||
</y>
|
||||
</graph>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_opportunity_monthly_form">
|
||||
<field name="name">Opportunities per Month</field>
|
||||
<field name="res_model">sale.opportunity_monthly</field>
|
||||
<field name="view_type">form</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_opportunity_monthly_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="opportunity_monthly_view_tree"/>
|
||||
<field name="act_window" ref="act_opportunity_monthly_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_opportunity_monthly_form_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="opportunity_monthly_view_graph1"/>
|
||||
<field name="act_window" ref="act_opportunity_monthly_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_opportunity_monthly_form_view3">
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="view" ref="opportunity_monthly_view_graph2"/>
|
||||
<field name="act_window" ref="act_opportunity_monthly_form"/>
|
||||
</record>
|
||||
|
||||
<menuitem parent="menu_reporting" action="act_opportunity_monthly_form"
|
||||
id="menu_opportunity_monthly_form"/>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_opportunity_monthly">
|
||||
<field name="model" search="[('model', '=', 'sale.opportunity_monthly')]"/>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_group_opportunity_monthly1">
|
||||
<field name="field" search="[('name', '=', 'company'), ('model.model', '=', 'sale.opportunity_monthly')]"/>
|
||||
<field name="operator">=</field>
|
||||
<field name="operand">User/Current Company</field>
|
||||
<field name="rule_group" ref="rule_group_opportunity_monthly"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="opportunity_employee_monthly_view_tree">
|
||||
<field name="model">sale.opportunity_employee_monthly</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<tree string="Sale Opportunities per Month">
|
||||
<field name="year" select="1"/>
|
||||
<field name="month" select="1"/>
|
||||
<field name="employee" select="1"/>
|
||||
<field name="number" select="2"/>
|
||||
<field name="converted" select="2"/>
|
||||
<field name="lost" select="2"/>
|
||||
<field name="conversion_rate"/>
|
||||
<field name="amount" select="2"/>
|
||||
<field name="converted_amount" select="2"/>
|
||||
<field name="currency"/>
|
||||
<field name="conversion_amount_rate"/>
|
||||
</tree>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_opportunity_employee_monthly_form">
|
||||
<field name="name">Opportunities per Employee per Month</field>
|
||||
<field name="res_model">sale.opportunity_employee_monthly</field>
|
||||
<field name="view_type">form</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_opportunity_employee_monthly_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="opportunity_employee_monthly_view_tree"/>
|
||||
<field name="act_window" ref="act_opportunity_employee_monthly_form"/>
|
||||
</record>
|
||||
|
||||
<menuitem parent="menu_reporting"
|
||||
action="act_opportunity_employee_monthly_form"
|
||||
id="menu_opportunity_employee_monthly_form"/>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_opportunity_employee_monthly">
|
||||
<field name="model" search="[('model', '=', 'sale.opportunity_employee_monthly')]"/>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_group_opportunity_employee_monthly1">
|
||||
<field name="field" search="[('name', '=', 'company'), ('model.model', '=', 'sale.opportunity_employee_monthly')]"/>
|
||||
<field name="operator">=</field>
|
||||
<field name="operand">User/Current Company</field>
|
||||
<field name="rule_group" ref="rule_group_opportunity_employee_monthly"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</tryton>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.action.act_window" id="act_opportunity_form2">
|
||||
<field name="name">Sales Leads/Opportunities</field>
|
||||
<field name="res_model">sale.opportunity</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[("party", "=", Eval('active_id'))]</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword"
|
||||
id="act_open_sale_keyword1">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">party.party,0</field>
|
||||
<field name="action" ref="act_opportunity_form2"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</tryton>
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
|
||||
from setuptools import setup
|
||||
import re
|
||||
|
||||
info = eval(open('__tryton__.py').read())
|
||||
major_version, minor_version, _ = info.get('version', '0.0.1').split('.', 2)
|
||||
major_version = int(major_version)
|
||||
minor_version = int(minor_version)
|
||||
|
||||
requires = []
|
||||
for dep in info.get('depends', []):
|
||||
if not re.match(r'(ir|res|workflow|webdav)(\W|$)', dep):
|
||||
requires.append('trytond_%s >= %s.%s, < %s.%s' %
|
||||
(dep, major_version, minor_version, major_version,
|
||||
minor_version + 1))
|
||||
requires.append('trytond >= %s.%s, < %s.%s' %
|
||||
(major_version, minor_version, major_version, minor_version + 1))
|
||||
|
||||
setup(name='trytond_sale_opportunity',
|
||||
version=info.get('version', '0.0.1'),
|
||||
description=info.get('description', ''),
|
||||
author=info.get('author', ''),
|
||||
author_email=info.get('email', ''),
|
||||
url=info.get('website', ''),
|
||||
download_url="http://downloads.tryton.org/" + \
|
||||
info.get('version', '0.0.1').rsplit('.', 1)[0] + '/',
|
||||
package_dir={'trytond.modules.sale_opportunity': '.'},
|
||||
packages=[
|
||||
'trytond.modules.sale_opportunity',
|
||||
'trytond.modules.sale_opportunity.tests',
|
||||
],
|
||||
package_data={
|
||||
'trytond.modules.sale_opportunity': info.get('xml', []) \
|
||||
+ info.get('translation', []),
|
||||
},
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Environment :: Plugins',
|
||||
'Intended Audience :: Developers',
|
||||
'Intended Audience :: Financial and Insurance Industry',
|
||||
'Intended Audience :: Legal Industry',
|
||||
'License :: OSI Approved :: GNU General Public License (GPL)',
|
||||
'Natural Language :: English',
|
||||
'Natural Language :: French',
|
||||
'Natural Language :: German',
|
||||
'Natural Language :: Spanish',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Office/Business',
|
||||
],
|
||||
license='GPL-3',
|
||||
install_requires=requires,
|
||||
zip_safe=False,
|
||||
entry_points="""
|
||||
[trytond.modules]
|
||||
sale_opportunity = trytond.modules.sale_opportunity
|
||||
""",
|
||||
test_suite='tests',
|
||||
test_loader='trytond.test_loader:Loader',
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
|
||||
from test_sale_opportunity import suite
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
|
||||
import sys, os
|
||||
DIR = os.path.abspath(os.path.normpath(os.path.join(__file__,
|
||||
'..', '..', '..', '..', '..', 'trytond')))
|
||||
if os.path.isdir(DIR):
|
||||
sys.path.insert(0, os.path.dirname(DIR))
|
||||
|
||||
import unittest
|
||||
import trytond.tests.test_tryton
|
||||
from trytond.tests.test_tryton import test_view
|
||||
|
||||
|
||||
class SaleOpportunityTestCase(unittest.TestCase):
|
||||
'''
|
||||
Test SaleOpportunity module.
|
||||
'''
|
||||
|
||||
def setUp(self):
|
||||
trytond.tests.test_tryton.install_module('sale_opportunity')
|
||||
|
||||
def test0005views(self):
|
||||
'''
|
||||
Test views.
|
||||
'''
|
||||
self.assertRaises(Exception, test_view('sale_opportunity'))
|
||||
|
||||
def suite():
|
||||
suite = trytond.tests.test_tryton.suite()
|
||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(
|
||||
SaleOpportunityTestCase))
|
||||
return suite
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.TextTestRunner(verbosity=2).run(suite())
|
||||
Reference in new issue
Block a user