From d1016c8728778cf59ccde810f03ebcdd2bd1ffd9 Mon Sep 17 00:00:00 2001
From: Vijaykumar Baladaniya
Date: Mon, 10 Dec 2012 11:13:34 +0530
Subject: [PATCH 01/72] [IMP] add a class to preference_save button.
bzr revid: vba@tinyerp.com-20121210054334-8r3ohu4324aopyzu
---
openerp/addons/base/res/res_users_view.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/openerp/addons/base/res/res_users_view.xml b/openerp/addons/base/res/res_users_view.xml
index b5c88e4c24b..0ff1c9d7cba 100644
--- a/openerp/addons/base/res/res_users_view.xml
+++ b/openerp/addons/base/res/res_users_view.xml
@@ -257,7 +257,7 @@
From 259054160791c25f44a4ea88b444557a3fea6850 Mon Sep 17 00:00:00 2001
From: Numerigraphe - Lionel Sausin
Date: Fri, 18 Jan 2013 12:33:09 +0100
Subject: [PATCH 02/72] [IMP] Document the description fields on Products, and
assume that invoices must get either the Purchase Description or the Sale
Description.
lp bug: https://launchpad.net/bugs/1101146 fixed
bzr revid: ls@numerigraphe.fr-20130118113309-vpq65omrvges1th8
---
addons/account/account_invoice.py | 7 +++++--
addons/product/product.py | 11 ++++++++---
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py
index c9cf90789cb..cb91943b66e 100644
--- a/addons/account/account_invoice.py
+++ b/addons/account/account_invoice.py
@@ -1454,8 +1454,13 @@ class account_invoice_line(osv.osv):
if type in ('out_invoice', 'out_refund'):
taxes = res.taxes_id and res.taxes_id or (a and self.pool.get('account.account').browse(cr, uid, a, context=context).tax_ids or False)
+ if res.description_sale:
+ result['name'] += '\n'+res.description_sale
else:
taxes = res.supplier_taxes_id and res.supplier_taxes_id or (a and self.pool.get('account.account').browse(cr, uid, a, context=context).tax_ids or False)
+ if res.description_purchase:
+ result['name'] += '\n'+res.description_purchase
+
tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes)
if type in ('in_invoice', 'in_refund'):
@@ -1465,8 +1470,6 @@ class account_invoice_line(osv.osv):
result['name'] = res.partner_ref
result['uos_id'] = uom_id or res.uom_id.id
- if res.description:
- result['name'] += '\n'+res.description
domain = {'uos_id':[('category_id','=',res.uom_id.category_id.id)]}
diff --git a/addons/product/product.py b/addons/product/product.py
index 1cacb58df23..fad975d3cb3 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -296,9 +296,14 @@ class product_template(osv.osv):
_columns = {
'name': fields.char('Name', size=128, required=True, translate=True, select=True),
'product_manager': fields.many2one('res.users','Product Manager'),
- 'description': fields.text('Description',translate=True),
- 'description_purchase': fields.text('Purchase Description',translate=True),
- 'description_sale': fields.text('Sale Description',translate=True),
+ 'description': fields.text('Description',translate=True,
+ help="A precise description of the Product, used only for internal information purposes."),
+ 'description_purchase': fields.text('Purchase Description',translate=True,
+ help="A description of the Product that you want to communicate to your customers. "
+ "This description will be copied to every Sale Order, Delivery Order and Customer Invoice/Refund"),
+ 'description_sale': fields.text('Sale Description',translate=True,
+ help="A description of the Product that you want to communicate to your suppliers. "
+ "This description will be copied to every Purchase Order, Reception and Supplier Invoice/Refund."),
'type': fields.selection([('consu', 'Consumable'),('service','Service')], 'Product Type', required=True, help="Consumable are product where you don't manage stock, a service is a non-material product provided by a company or an individual."),
'produce_delay': fields.float('Manufacturing Lead Time', help="Average delay in days to produce this product. In the case of multi-level BOM, the manufacturing lead times of the components will be added."),
'rental': fields.boolean('Can be Rent'),
From 03b7ffd0b5112baec72a7cd18fe1541ff26191f9 Mon Sep 17 00:00:00 2001
From: "Ravish(OpenERP)" <>
Date: Thu, 24 Jan 2013 15:20:52 +0100
Subject: [PATCH 03/72] [FIX] borrow an additionnal fix from
lp:~openerp-dev/openobject-addons/trunk-bug-1101146-rmu
bzr revid: ls@numerigraphe.fr-20130124142052-ob4xxtzh88r1javw
---
addons/account/account_invoice.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py
index cb91943b66e..fd950ad19e2 100644
--- a/addons/account/account_invoice.py
+++ b/addons/account/account_invoice.py
@@ -1440,6 +1440,7 @@ class account_invoice_line(osv.osv):
result = {}
res = self.pool.get('product.product').browse(cr, uid, product, context=context)
+ result['name'] = res.partner_ref
if type in ('out_invoice','out_refund'):
a = res.property_account_income.id
if not a:
@@ -1467,7 +1468,6 @@ class account_invoice_line(osv.osv):
result.update( {'price_unit': price_unit or res.standard_price,'invoice_line_tax_id': tax_id} )
else:
result.update({'price_unit': res.list_price, 'invoice_line_tax_id': tax_id})
- result['name'] = res.partner_ref
result['uos_id'] = uom_id or res.uom_id.id
From c4670af9d8d182685bb2b7538e86ca9cf9548672 Mon Sep 17 00:00:00 2001
From: "Ravish(OpenERP)" <>
Date: Thu, 24 Jan 2013 15:41:56 +0100
Subject: [PATCH 04/72] [FIX] purchase/sale description helps are inverted -
borrowed from lp:~openerp-dev/openobject-addons/trunk-bug-1101146-rmu
bzr revid: ls@numerigraphe.fr-20130124144156-sguv4wtpk5c20zea
---
addons/product/product.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/addons/product/product.py b/addons/product/product.py
index fad975d3cb3..fbe89490036 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -299,11 +299,11 @@ class product_template(osv.osv):
'description': fields.text('Description',translate=True,
help="A precise description of the Product, used only for internal information purposes."),
'description_purchase': fields.text('Purchase Description',translate=True,
- help="A description of the Product that you want to communicate to your customers. "
- "This description will be copied to every Sale Order, Delivery Order and Customer Invoice/Refund"),
- 'description_sale': fields.text('Sale Description',translate=True,
help="A description of the Product that you want to communicate to your suppliers. "
"This description will be copied to every Purchase Order, Reception and Supplier Invoice/Refund."),
+ 'description_sale': fields.text('Sale Description',translate=True,
+ help="A description of the Product that you want to communicate to your customers. "
+ "This description will be copied to every Sale Order, Delivery Order and Customer Invoice/Refund"),
'type': fields.selection([('consu', 'Consumable'),('service','Service')], 'Product Type', required=True, help="Consumable are product where you don't manage stock, a service is a non-material product provided by a company or an individual."),
'produce_delay': fields.float('Manufacturing Lead Time', help="Average delay in days to produce this product. In the case of multi-level BOM, the manufacturing lead times of the components will be added."),
'rental': fields.boolean('Can be Rent'),
From c377140650596536bc3490dc5c11bc82cf927c34 Mon Sep 17 00:00:00 2001
From: Guewen Baconnier
Date: Thu, 7 Mar 2013 09:58:26 +0100
Subject: [PATCH 05/72] [FIX] add SQL constraints only for models stored in
database (not on AbstractModel)
lp bug: https://launchpad.net/bugs/1151703 fixed
bzr revid: guewen.baconnier@camptocamp.com-20130307085826-omhoc3clix41zydl
---
openerp/osv/orm.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py
index 96f56ee6826..7f8f671dfb0 100644
--- a/openerp/osv/orm.py
+++ b/openerp/osv/orm.py
@@ -3204,7 +3204,8 @@ class BaseModel(object):
cr.commit() # start a new transaction
- self._add_sql_constraints(cr)
+ if getattr(self, '_auto', True):
+ self._add_sql_constraints(cr)
if create:
self._execute_sql(cr)
From 363c4ab2a955e19f09d63f3f489b4ad523165d96 Mon Sep 17 00:00:00 2001
From: "Dharmraj Zala (OpenERP Trainee)"
Date: Fri, 5 Apr 2013 12:45:22 +0530
Subject: [PATCH 06/72] [FIX]Improved field int res_user_view.xml tree view
lp bug: https://launchpad.net/bugs/1164561 fixed
bzr revid: dizzy.zala@gmail.com-20130405071522-xmpa556spxe95i5b
---
openerp/addons/base/res/res_users_view.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/openerp/addons/base/res/res_users_view.xml b/openerp/addons/base/res/res_users_view.xml
index daa25806349..ac452f495e8 100644
--- a/openerp/addons/base/res/res_users_view.xml
+++ b/openerp/addons/base/res/res_users_view.xml
@@ -189,7 +189,7 @@
-
+
From 84ac4daf198938675cf54dee206269e2c67e5f25 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Fri, 12 Apr 2013 18:17:30 +0200
Subject: [PATCH 07/72] [IMP] crm_case_section: add js libraries JustGage
bzr revid: chm@openerp.com-20130412161730-lrx5tqr6wn784glq
---
addons/crm/__openerp__.py | 4 +-
addons/crm/crm_case_section_view.xml | 12 +-
.../static/lib/sparkline/jquery.sparkline.js | 3047 +++++++++++++++++
.../lib/sparkline/jquery.sparkline.min.js | 5 +
addons/crm/static/src/css/crm.css | 9 +-
addons/crm/static/src/js/crm.js | 11 -
addons/crm/static/src/js/crm_case_section.js | 70 +
addons/project_timesheet/__openerp__.py | 2 +-
addons/sale_crm/sale_crm.py | 3 +
addons/sale_crm/sale_crm_view.xml | 6 +-
10 files changed, 3146 insertions(+), 23 deletions(-)
create mode 100644 addons/crm/static/lib/sparkline/jquery.sparkline.js
create mode 100644 addons/crm/static/lib/sparkline/jquery.sparkline.min.js
delete mode 100644 addons/crm/static/src/js/crm.js
create mode 100644 addons/crm/static/src/js/crm_case_section.js
diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py
index 072624a7d98..577a51f4784 100644
--- a/addons/crm/__openerp__.py
+++ b/addons/crm/__openerp__.py
@@ -120,7 +120,9 @@ Dashboard for CRM will include:
'static/src/css/crm.css'
],
'js': [
- 'static/src/js/crm.js'
+ 'static/src/js/crm_case_section.js',
+ 'static/lib/sparkline/jquery.sparkline.min.js',
+ 'static/lib/justgage.js',
],
'installable': True,
'application': True,
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index c788872b47e..1c812b23685 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -83,7 +83,7 @@
-
+
í
-
-
diff --git a/addons/crm/static/lib/sparkline/jquery.sparkline.js b/addons/crm/static/lib/sparkline/jquery.sparkline.js
new file mode 100644
index 00000000000..c003923e03b
--- /dev/null
+++ b/addons/crm/static/lib/sparkline/jquery.sparkline.js
@@ -0,0 +1,3047 @@
+/**
+*
+* jquery.sparkline.js
+*
+* v2.1.1
+* (c) Splunk, Inc
+* Contact: Gareth Watts (gareth@splunk.com)
+* http://omnipotent.net/jquery.sparkline/
+*
+* Generates inline sparkline charts from data supplied either to the method
+* or inline in HTML
+*
+* Compatible with Internet Explorer 6.0+ and modern browsers equipped with the canvas tag
+* (Firefox 2.0+, Safari, Opera, etc)
+*
+* License: New BSD License
+*
+* Copyright (c) 2012, Splunk Inc.
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification,
+* are permitted provided that the following conditions are met:
+*
+* * Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+* * Neither the name of Splunk Inc nor the names of its contributors may
+* be used to endorse or promote products derived from this software without
+* specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+* SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+*
+* Usage:
+* $(selector).sparkline(values, options)
+*
+* If values is undefined or set to 'html' then the data values are read from the specified tag:
+*
Sparkline: 1,4,6,6,8,5,3,5
+* $('.sparkline').sparkline();
+* There must be no spaces in the enclosed data set
+*
+* Otherwise values must be an array of numbers or null values
+*
Sparkline: This text replaced if the browser is compatible
+* $('#sparkline1').sparkline([1,4,6,6,8,5,3,5])
+* $('#sparkline2').sparkline([1,4,6,null,null,5,3,5])
+*
+* Values can also be specified in an HTML comment, or as a values attribute:
+*
Sparkline:
+*
Sparkline:
+* $('.sparkline').sparkline();
+*
+* For line charts, x values can also be specified:
+*
Sparkline: 1:1,2.7:4,3.4:6,5:6,6:8,8.7:5,9:3,10:5
+* $('#sparkline1').sparkline([ [1,1], [2.7,4], [3.4,6], [5,6], [6,8], [8.7,5], [9,3], [10,5] ])
+*
+* By default, options should be passed in as teh second argument to the sparkline function:
+* $('.sparkline').sparkline([1,2,3,4], {type: 'bar'})
+*
+* Options can also be set by passing them on the tag itself. This feature is disabled by default though
+* as there's a slight performance overhead:
+* $('.sparkline').sparkline([1,2,3,4], {enableTagOptions: true})
+*
Sparkline: loading
+* Prefix all options supplied as tag attribute with "spark" (configurable by setting tagOptionPrefix)
+*
+* Supported options:
+* lineColor - Color of the line used for the chart
+* fillColor - Color used to fill in the chart - Set to '' or false for a transparent chart
+* width - Width of the chart - Defaults to 3 times the number of values in pixels
+* height - Height of the chart - Defaults to the height of the containing element
+* chartRangeMin - Specify the minimum value to use for the Y range of the chart - Defaults to the minimum value supplied
+* chartRangeMax - Specify the maximum value to use for the Y range of the chart - Defaults to the maximum value supplied
+* chartRangeClip - Clip out of range values to the max/min specified by chartRangeMin and chartRangeMax
+* chartRangeMinX - Specify the minimum value to use for the X range of the chart - Defaults to the minimum value supplied
+* chartRangeMaxX - Specify the maximum value to use for the X range of the chart - Defaults to the maximum value supplied
+* composite - If true then don't erase any existing chart attached to the tag, but draw
+* another chart over the top - Note that width and height are ignored if an
+* existing chart is detected.
+* tagValuesAttribute - Name of tag attribute to check for data values - Defaults to 'values'
+* enableTagOptions - Whether to check tags for sparkline options
+* tagOptionPrefix - Prefix used for options supplied as tag attributes - Defaults to 'spark'
+* disableHiddenCheck - If set to true, then the plugin will assume that charts will never be drawn into a
+* hidden dom element, avoding a browser reflow
+* disableInteraction - If set to true then all mouseover/click interaction behaviour will be disabled,
+* making the plugin perform much like it did in 1.x
+* disableTooltips - If set to true then tooltips will be disabled - Defaults to false (tooltips enabled)
+* disableHighlight - If set to true then highlighting of selected chart elements on mouseover will be disabled
+* defaults to false (highlights enabled)
+* highlightLighten - Factor to lighten/darken highlighted chart values by - Defaults to 1.4 for a 40% increase
+* tooltipContainer - Specify which DOM element the tooltip should be rendered into - defaults to document.body
+* tooltipClassname - Optional CSS classname to apply to tooltips - If not specified then a default style will be applied
+* tooltipOffsetX - How many pixels away from the mouse pointer to render the tooltip on the X axis
+* tooltipOffsetY - How many pixels away from the mouse pointer to render the tooltip on the r axis
+* tooltipFormatter - Optional callback that allows you to override the HTML displayed in the tooltip
+* callback is given arguments of (sparkline, options, fields)
+* tooltipChartTitle - If specified then the tooltip uses the string specified by this setting as a title
+* tooltipFormat - A format string or SPFormat object (or an array thereof for multiple entries)
+* to control the format of the tooltip
+* tooltipPrefix - A string to prepend to each field displayed in a tooltip
+* tooltipSuffix - A string to append to each field displayed in a tooltip
+* tooltipSkipNull - If true then null values will not have a tooltip displayed (defaults to true)
+* tooltipValueLookups - An object or range map to map field values to tooltip strings
+* (eg. to map -1 to "Lost", 0 to "Draw", and 1 to "Win")
+* numberFormatter - Optional callback for formatting numbers in tooltips
+* numberDigitGroupSep - Character to use for group separator in numbers "1,234" - Defaults to ","
+* numberDecimalMark - Character to use for the decimal point when formatting numbers - Defaults to "."
+* numberDigitGroupCount - Number of digits between group separator - Defaults to 3
+*
+* There are 7 types of sparkline, selected by supplying a "type" option of 'line' (default),
+* 'bar', 'tristate', 'bullet', 'discrete', 'pie' or 'box'
+* line - Line chart. Options:
+* spotColor - Set to '' to not end each line in a circular spot
+* minSpotColor - If set, color of spot at minimum value
+* maxSpotColor - If set, color of spot at maximum value
+* spotRadius - Radius in pixels
+* lineWidth - Width of line in pixels
+* normalRangeMin
+* normalRangeMax - If set draws a filled horizontal bar between these two values marking the "normal"
+* or expected range of values
+* normalRangeColor - Color to use for the above bar
+* drawNormalOnTop - Draw the normal range above the chart fill color if true
+* defaultPixelsPerValue - Defaults to 3 pixels of width for each value in the chart
+* highlightSpotColor - The color to use for drawing a highlight spot on mouseover - Set to null to disable
+* highlightLineColor - The color to use for drawing a highlight line on mouseover - Set to null to disable
+* valueSpots - Specify which points to draw spots on, and in which color. Accepts a range map
+*
+* bar - Bar chart. Options:
+* barColor - Color of bars for postive values
+* negBarColor - Color of bars for negative values
+* zeroColor - Color of bars with zero values
+* nullColor - Color of bars with null values - Defaults to omitting the bar entirely
+* barWidth - Width of bars in pixels
+* colorMap - Optional mappnig of values to colors to override the *BarColor values above
+* can be an Array of values to control the color of individual bars or a range map
+* to specify colors for individual ranges of values
+* barSpacing - Gap between bars in pixels
+* zeroAxis - Centers the y-axis around zero if true
+*
+* tristate - Charts values of win (>0), lose (<0) or draw (=0)
+* posBarColor - Color of win values
+* negBarColor - Color of lose values
+* zeroBarColor - Color of draw values
+* barWidth - Width of bars in pixels
+* barSpacing - Gap between bars in pixels
+* colorMap - Optional mappnig of values to colors to override the *BarColor values above
+* can be an Array of values to control the color of individual bars or a range map
+* to specify colors for individual ranges of values
+*
+* discrete - Options:
+* lineHeight - Height of each line in pixels - Defaults to 30% of the graph height
+* thesholdValue - Values less than this value will be drawn using thresholdColor instead of lineColor
+* thresholdColor
+*
+* bullet - Values for bullet graphs msut be in the order: target, performance, range1, range2, range3, ...
+* options:
+* targetColor - The color of the vertical target marker
+* targetWidth - The width of the target marker in pixels
+* performanceColor - The color of the performance measure horizontal bar
+* rangeColors - Colors to use for each qualitative range background color
+*
+* pie - Pie chart. Options:
+* sliceColors - An array of colors to use for pie slices
+* offset - Angle in degrees to offset the first slice - Try -90 or +90
+* borderWidth - Width of border to draw around the pie chart, in pixels - Defaults to 0 (no border)
+* borderColor - Color to use for the pie chart border - Defaults to #000
+*
+* box - Box plot. Options:
+* raw - Set to true to supply pre-computed plot points as values
+* values should be: low_outlier, low_whisker, q1, median, q3, high_whisker, high_outlier
+* When set to false you can supply any number of values and the box plot will
+* be computed for you. Default is false.
+* showOutliers - Set to true (default) to display outliers as circles
+* outlierIQR - Interquartile range used to determine outliers. Default 1.5
+* boxLineColor - Outline color of the box
+* boxFillColor - Fill color for the box
+* whiskerColor - Line color used for whiskers
+* outlierLineColor - Outline color of outlier circles
+* outlierFillColor - Fill color of the outlier circles
+* spotRadius - Radius of outlier circles
+* medianColor - Line color of the median line
+* target - Draw a target cross hair at the supplied value (default undefined)
+*
+*
+*
+* Examples:
+* $('#sparkline1').sparkline(myvalues, { lineColor: '#f00', fillColor: false });
+* $('.barsparks').sparkline('html', { type:'bar', height:'40px', barWidth:5 });
+* $('#tristate').sparkline([1,1,-1,1,0,0,-1], { type:'tristate' }):
+* $('#discrete').sparkline([1,3,4,5,5,3,4,5], { type:'discrete' });
+* $('#bullet').sparkline([10,12,12,9,7], { type:'bullet' });
+* $('#pie').sparkline([1,1,2], { type:'pie' });
+*/
+
+/*jslint regexp: true, browser: true, jquery: true, white: true, nomen: false, plusplus: false, maxerr: 500, indent: 4 */
+
+(function(factory) {
+ if(typeof define === 'function' && define.amd) {
+ define(['jquery'], factory);
+ }
+ else {
+ factory(jQuery);
+ }
+}
+(function($) {
+ 'use strict';
+
+ var UNSET_OPTION = {},
+ getDefaults, createClass, SPFormat, clipval, quartile, normalizeValue, normalizeValues,
+ remove, isNumber, all, sum, addCSS, ensureArray, formatNumber, RangeMap,
+ MouseHandler, Tooltip, barHighlightMixin,
+ line, bar, tristate, discrete, bullet, pie, box, defaultStyles, initStyles,
+ VShape, VCanvas_base, VCanvas_canvas, VCanvas_vml, pending, shapeCount = 0;
+
+ /**
+ * Default configuration settings
+ */
+ getDefaults = function () {
+ return {
+ // Settings common to most/all chart types
+ common: {
+ type: 'line',
+ lineColor: '#00f',
+ fillColor: '#cdf',
+ defaultPixelsPerValue: 3,
+ width: 'auto',
+ height: 'auto',
+ composite: false,
+ tagValuesAttribute: 'values',
+ tagOptionsPrefix: 'spark',
+ enableTagOptions: false,
+ enableHighlight: true,
+ highlightLighten: 1.4,
+ tooltipSkipNull: true,
+ tooltipPrefix: '',
+ tooltipSuffix: '',
+ disableHiddenCheck: false,
+ numberFormatter: false,
+ numberDigitGroupCount: 3,
+ numberDigitGroupSep: ',',
+ numberDecimalMark: '.',
+ disableTooltips: false,
+ disableInteraction: false
+ },
+ // Defaults for line charts
+ line: {
+ spotColor: '#f80',
+ highlightSpotColor: '#5f5',
+ highlightLineColor: '#f22',
+ spotRadius: 1.5,
+ minSpotColor: '#f80',
+ maxSpotColor: '#f80',
+ lineWidth: 1,
+ normalRangeMin: undefined,
+ normalRangeMax: undefined,
+ normalRangeColor: '#ccc',
+ drawNormalOnTop: false,
+ chartRangeMin: undefined,
+ chartRangeMax: undefined,
+ chartRangeMinX: undefined,
+ chartRangeMaxX: undefined,
+ tooltipFormat: new SPFormat('
● {{prefix}}{{y}}{{suffix}}')
+ },
+ // Defaults for bar charts
+ bar: {
+ barColor: '#3366cc',
+ negBarColor: '#f44',
+ stackedBarColor: ['#3366cc', '#dc3912', '#ff9900', '#109618', '#66aa00',
+ '#dd4477', '#0099c6', '#990099'],
+ zeroColor: undefined,
+ nullColor: undefined,
+ zeroAxis: true,
+ barWidth: 4,
+ barSpacing: 1,
+ chartRangeMax: undefined,
+ chartRangeMin: undefined,
+ chartRangeClip: false,
+ colorMap: undefined,
+ tooltipFormat: new SPFormat('
● {{prefix}}{{value}}{{suffix}}')
+ },
+ // Defaults for tristate charts
+ tristate: {
+ barWidth: 4,
+ barSpacing: 1,
+ posBarColor: '#6f6',
+ negBarColor: '#f44',
+ zeroBarColor: '#999',
+ colorMap: {},
+ tooltipFormat: new SPFormat('
● {{value:map}}'),
+ tooltipValueLookups: { map: { '-1': 'Loss', '0': 'Draw', '1': 'Win' } }
+ },
+ // Defaults for discrete charts
+ discrete: {
+ lineHeight: 'auto',
+ thresholdColor: undefined,
+ thresholdValue: 0,
+ chartRangeMax: undefined,
+ chartRangeMin: undefined,
+ chartRangeClip: false,
+ tooltipFormat: new SPFormat('{{prefix}}{{value}}{{suffix}}')
+ },
+ // Defaults for bullet charts
+ bullet: {
+ targetColor: '#f33',
+ targetWidth: 3, // width of the target bar in pixels
+ performanceColor: '#33f',
+ rangeColors: ['#d3dafe', '#a8b6ff', '#7f94ff'],
+ base: undefined, // set this to a number to change the base start number
+ tooltipFormat: new SPFormat('{{fieldkey:fields}} - {{value}}'),
+ tooltipValueLookups: { fields: {r: 'Range', p: 'Performance', t: 'Target'} }
+ },
+ // Defaults for pie charts
+ pie: {
+ offset: 0,
+ sliceColors: ['#3366cc', '#dc3912', '#ff9900', '#109618', '#66aa00',
+ '#dd4477', '#0099c6', '#990099'],
+ borderWidth: 0,
+ borderColor: '#000',
+ tooltipFormat: new SPFormat('
● {{value}} ({{percent.1}}%)')
+ },
+ // Defaults for box plots
+ box: {
+ raw: false,
+ boxLineColor: '#000',
+ boxFillColor: '#cdf',
+ whiskerColor: '#000',
+ outlierLineColor: '#333',
+ outlierFillColor: '#fff',
+ medianColor: '#f00',
+ showOutliers: true,
+ outlierIQR: 1.5,
+ spotRadius: 1.5,
+ target: undefined,
+ targetColor: '#4a2',
+ chartRangeMax: undefined,
+ chartRangeMin: undefined,
+ tooltipFormat: new SPFormat('{{field:fields}}: {{value}}'),
+ tooltipFormatFieldlistKey: 'field',
+ tooltipValueLookups: { fields: { lq: 'Lower Quartile', med: 'Median',
+ uq: 'Upper Quartile', lo: 'Left Outlier', ro: 'Right Outlier',
+ lw: 'Left Whisker', rw: 'Right Whisker'} }
+ }
+ };
+ };
+
+ // You can have tooltips use a css class other than jqstooltip by specifying tooltipClassname
+ defaultStyles = '.jqstooltip { ' +
+ 'position: absolute;' +
+ 'left: 0px;' +
+ 'top: 0px;' +
+ 'visibility: hidden;' +
+ 'background: rgb(0, 0, 0) transparent;' +
+ 'background-color: rgba(0,0,0,0.6);' +
+ 'filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);' +
+ '-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";' +
+ 'color: white;' +
+ 'font: 10px arial, san serif;' +
+ 'text-align: left;' +
+ 'white-space: nowrap;' +
+ 'padding: 5px;' +
+ 'border: 1px solid white;' +
+ 'z-index: 10000;' +
+ '}' +
+ '.jqsfield { ' +
+ 'color: white;' +
+ 'font: 10px arial, san serif;' +
+ 'text-align: left;' +
+ '}';
+
+ /**
+ * Utilities
+ */
+
+ createClass = function (/* [baseclass, [mixin, ...]], definition */) {
+ var Class, args;
+ Class = function () {
+ this.init.apply(this, arguments);
+ };
+ if (arguments.length > 1) {
+ if (arguments[0]) {
+ Class.prototype = $.extend(new arguments[0](), arguments[arguments.length - 1]);
+ Class._super = arguments[0].prototype;
+ } else {
+ Class.prototype = arguments[arguments.length - 1];
+ }
+ if (arguments.length > 2) {
+ args = Array.prototype.slice.call(arguments, 1, -1);
+ args.unshift(Class.prototype);
+ $.extend.apply($, args);
+ }
+ } else {
+ Class.prototype = arguments[0];
+ }
+ Class.prototype.cls = Class;
+ return Class;
+ };
+
+ /**
+ * Wraps a format string for tooltips
+ * {{x}}
+ * {{x.2}
+ * {{x:months}}
+ */
+ $.SPFormatClass = SPFormat = createClass({
+ fre: /\{\{([\w.]+?)(:(.+?))?\}\}/g,
+ precre: /(\w+)\.(\d+)/,
+
+ init: function (format, fclass) {
+ this.format = format;
+ this.fclass = fclass;
+ },
+
+ render: function (fieldset, lookups, options) {
+ var self = this,
+ fields = fieldset,
+ match, token, lookupkey, fieldvalue, prec;
+ return this.format.replace(this.fre, function () {
+ var lookup;
+ token = arguments[1];
+ lookupkey = arguments[3];
+ match = self.precre.exec(token);
+ if (match) {
+ prec = match[2];
+ token = match[1];
+ } else {
+ prec = false;
+ }
+ fieldvalue = fields[token];
+ if (fieldvalue === undefined) {
+ return '';
+ }
+ if (lookupkey && lookups && lookups[lookupkey]) {
+ lookup = lookups[lookupkey];
+ if (lookup.get) { // RangeMap
+ return lookups[lookupkey].get(fieldvalue) || fieldvalue;
+ } else {
+ return lookups[lookupkey][fieldvalue] || fieldvalue;
+ }
+ }
+ if (isNumber(fieldvalue)) {
+ if (options.get('numberFormatter')) {
+ fieldvalue = options.get('numberFormatter')(fieldvalue);
+ } else {
+ fieldvalue = formatNumber(fieldvalue, prec,
+ options.get('numberDigitGroupCount'),
+ options.get('numberDigitGroupSep'),
+ options.get('numberDecimalMark'));
+ }
+ }
+ return fieldvalue;
+ });
+ }
+ });
+
+ // convience method to avoid needing the new operator
+ $.spformat = function(format, fclass) {
+ return new SPFormat(format, fclass);
+ };
+
+ clipval = function (val, min, max) {
+ if (val < min) {
+ return min;
+ }
+ if (val > max) {
+ return max;
+ }
+ return val;
+ };
+
+ quartile = function (values, q) {
+ var vl;
+ if (q === 2) {
+ vl = Math.floor(values.length / 2);
+ return values.length % 2 ? values[vl] : (values[vl-1] + values[vl]) / 2;
+ } else {
+ if (values.length % 2 ) { // odd
+ vl = (values.length * q + q) / 4;
+ return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl-1];
+ } else { //even
+ vl = (values.length * q + 2) / 4;
+ return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl-1];
+
+ }
+ }
+ };
+
+ normalizeValue = function (val) {
+ var nf;
+ switch (val) {
+ case 'undefined':
+ val = undefined;
+ break;
+ case 'null':
+ val = null;
+ break;
+ case 'true':
+ val = true;
+ break;
+ case 'false':
+ val = false;
+ break;
+ default:
+ nf = parseFloat(val);
+ if (val == nf) {
+ val = nf;
+ }
+ }
+ return val;
+ };
+
+ normalizeValues = function (vals) {
+ var i, result = [];
+ for (i = vals.length; i--;) {
+ result[i] = normalizeValue(vals[i]);
+ }
+ return result;
+ };
+
+ remove = function (vals, filter) {
+ var i, vl, result = [];
+ for (i = 0, vl = vals.length; i < vl; i++) {
+ if (vals[i] !== filter) {
+ result.push(vals[i]);
+ }
+ }
+ return result;
+ };
+
+ isNumber = function (num) {
+ return !isNaN(parseFloat(num)) && isFinite(num);
+ };
+
+ formatNumber = function (num, prec, groupsize, groupsep, decsep) {
+ var p, i;
+ num = (prec === false ? parseFloat(num).toString() : num.toFixed(prec)).split('');
+ p = (p = $.inArray('.', num)) < 0 ? num.length : p;
+ if (p < num.length) {
+ num[p] = decsep;
+ }
+ for (i = p - groupsize; i > 0; i -= groupsize) {
+ num.splice(i, 0, groupsep);
+ }
+ return num.join('');
+ };
+
+ // determine if all values of an array match a value
+ // returns true if the array is empty
+ all = function (val, arr, ignoreNull) {
+ var i;
+ for (i = arr.length; i--; ) {
+ if (ignoreNull && arr[i] === null) continue;
+ if (arr[i] !== val) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ // sums the numeric values in an array, ignoring other values
+ sum = function (vals) {
+ var total = 0, i;
+ for (i = vals.length; i--;) {
+ total += typeof vals[i] === 'number' ? vals[i] : 0;
+ }
+ return total;
+ };
+
+ ensureArray = function (val) {
+ return $.isArray(val) ? val : [val];
+ };
+
+ // http://paulirish.com/2008/bookmarklet-inject-new-css-rules/
+ addCSS = function(css) {
+ var tag;
+ //if ('\v' == 'v') /* ie only */ {
+ if (document.createStyleSheet) {
+ document.createStyleSheet().cssText = css;
+ } else {
+ tag = document.createElement('style');
+ tag.type = 'text/css';
+ document.getElementsByTagName('head')[0].appendChild(tag);
+ tag[(typeof document.body.style.WebkitAppearance == 'string') /* webkit only */ ? 'innerText' : 'innerHTML'] = css;
+ }
+ };
+
+ // Provide a cross-browser interface to a few simple drawing primitives
+ $.fn.simpledraw = function (width, height, useExisting, interact) {
+ var target, mhandler;
+ if (useExisting && (target = this.data('_jqs_vcanvas'))) {
+ return target;
+ }
+ if (width === undefined) {
+ width = $(this).innerWidth();
+ }
+ if (height === undefined) {
+ height = $(this).innerHeight();
+ }
+ if ($.fn.sparkline.hasCanvas) {
+ target = new VCanvas_canvas(width, height, this, interact);
+ } else if ($.fn.sparkline.hasVML) {
+ target = new VCanvas_vml(width, height, this);
+ } else {
+ return false;
+ }
+ mhandler = $(this).data('_jqs_mhandler');
+ if (mhandler) {
+ mhandler.registerCanvas(target);
+ }
+ return target;
+ };
+
+ $.fn.cleardraw = function () {
+ var target = this.data('_jqs_vcanvas');
+ if (target) {
+ target.reset();
+ }
+ };
+
+ $.RangeMapClass = RangeMap = createClass({
+ init: function (map) {
+ var key, range, rangelist = [];
+ for (key in map) {
+ if (map.hasOwnProperty(key) && typeof key === 'string' && key.indexOf(':') > -1) {
+ range = key.split(':');
+ range[0] = range[0].length === 0 ? -Infinity : parseFloat(range[0]);
+ range[1] = range[1].length === 0 ? Infinity : parseFloat(range[1]);
+ range[2] = map[key];
+ rangelist.push(range);
+ }
+ }
+ this.map = map;
+ this.rangelist = rangelist || false;
+ },
+
+ get: function (value) {
+ var rangelist = this.rangelist,
+ i, range, result;
+ if ((result = this.map[value]) !== undefined) {
+ return result;
+ }
+ if (rangelist) {
+ for (i = rangelist.length; i--;) {
+ range = rangelist[i];
+ if (range[0] <= value && range[1] >= value) {
+ return range[2];
+ }
+ }
+ }
+ return undefined;
+ }
+ });
+
+ // Convenience function
+ $.range_map = function(map) {
+ return new RangeMap(map);
+ };
+
+ MouseHandler = createClass({
+ init: function (el, options) {
+ var $el = $(el);
+ this.$el = $el;
+ this.options = options;
+ this.currentPageX = 0;
+ this.currentPageY = 0;
+ this.el = el;
+ this.splist = [];
+ this.tooltip = null;
+ this.over = false;
+ this.displayTooltips = !options.get('disableTooltips');
+ this.highlightEnabled = !options.get('disableHighlight');
+ },
+
+ registerSparkline: function (sp) {
+ this.splist.push(sp);
+ if (this.over) {
+ this.updateDisplay();
+ }
+ },
+
+ registerCanvas: function (canvas) {
+ var $canvas = $(canvas.canvas);
+ this.canvas = canvas;
+ this.$canvas = $canvas;
+ $canvas.mouseenter($.proxy(this.mouseenter, this));
+ $canvas.mouseleave($.proxy(this.mouseleave, this));
+ $canvas.click($.proxy(this.mouseclick, this));
+ },
+
+ reset: function (removeTooltip) {
+ this.splist = [];
+ if (this.tooltip && removeTooltip) {
+ this.tooltip.remove();
+ this.tooltip = undefined;
+ }
+ },
+
+ mouseclick: function (e) {
+ var clickEvent = $.Event('sparklineClick');
+ clickEvent.originalEvent = e;
+ clickEvent.sparklines = this.splist;
+ this.$el.trigger(clickEvent);
+ },
+
+ mouseenter: function (e) {
+ $(document.body).unbind('mousemove.jqs');
+ $(document.body).bind('mousemove.jqs', $.proxy(this.mousemove, this));
+ this.over = true;
+ this.currentPageX = e.pageX;
+ this.currentPageY = e.pageY;
+ this.currentEl = e.target;
+ if (!this.tooltip && this.displayTooltips) {
+ this.tooltip = new Tooltip(this.options);
+ this.tooltip.updatePosition(e.pageX, e.pageY);
+ }
+ this.updateDisplay();
+ },
+
+ mouseleave: function () {
+ $(document.body).unbind('mousemove.jqs');
+ var splist = this.splist,
+ spcount = splist.length,
+ needsRefresh = false,
+ sp, i;
+ this.over = false;
+ this.currentEl = null;
+
+ if (this.tooltip) {
+ this.tooltip.remove();
+ this.tooltip = null;
+ }
+
+ for (i = 0; i < spcount; i++) {
+ sp = splist[i];
+ if (sp.clearRegionHighlight()) {
+ needsRefresh = true;
+ }
+ }
+
+ if (needsRefresh) {
+ this.canvas.render();
+ }
+ },
+
+ mousemove: function (e) {
+ this.currentPageX = e.pageX;
+ this.currentPageY = e.pageY;
+ this.currentEl = e.target;
+ if (this.tooltip) {
+ this.tooltip.updatePosition(e.pageX, e.pageY);
+ }
+ this.updateDisplay();
+ },
+
+ updateDisplay: function () {
+ var splist = this.splist,
+ spcount = splist.length,
+ needsRefresh = false,
+ offset = this.$canvas.offset(),
+ localX = this.currentPageX - offset.left,
+ localY = this.currentPageY - offset.top,
+ tooltiphtml, sp, i, result, changeEvent;
+ if (!this.over) {
+ return;
+ }
+ for (i = 0; i < spcount; i++) {
+ sp = splist[i];
+ result = sp.setRegionHighlight(this.currentEl, localX, localY);
+ if (result) {
+ needsRefresh = true;
+ }
+ }
+ if (needsRefresh) {
+ changeEvent = $.Event('sparklineRegionChange');
+ changeEvent.sparklines = this.splist;
+ this.$el.trigger(changeEvent);
+ if (this.tooltip) {
+ tooltiphtml = '';
+ for (i = 0; i < spcount; i++) {
+ sp = splist[i];
+ tooltiphtml += sp.getCurrentRegionTooltip();
+ }
+ this.tooltip.setContent(tooltiphtml);
+ }
+ if (!this.disableHighlight) {
+ this.canvas.render();
+ }
+ }
+ if (result === null) {
+ this.mouseleave();
+ }
+ }
+ });
+
+
+ Tooltip = createClass({
+ sizeStyle: 'position: static !important;' +
+ 'display: block !important;' +
+ 'visibility: hidden !important;' +
+ 'float: left !important;',
+
+ init: function (options) {
+ var tooltipClassname = options.get('tooltipClassname', 'jqstooltip'),
+ sizetipStyle = this.sizeStyle,
+ offset;
+ this.container = options.get('tooltipContainer') || document.body;
+ this.tooltipOffsetX = options.get('tooltipOffsetX', 10);
+ this.tooltipOffsetY = options.get('tooltipOffsetY', 12);
+ // remove any previous lingering tooltip
+ $('#jqssizetip').remove();
+ $('#jqstooltip').remove();
+ this.sizetip = $('
', {
+ id: 'jqssizetip',
+ style: sizetipStyle,
+ 'class': tooltipClassname
+ });
+ this.tooltip = $('
', {
+ id: 'jqstooltip',
+ 'class': tooltipClassname
+ }).appendTo(this.container);
+ // account for the container's location
+ offset = this.tooltip.offset();
+ this.offsetLeft = offset.left;
+ this.offsetTop = offset.top;
+ this.hidden = true;
+ $(window).unbind('resize.jqs scroll.jqs');
+ $(window).bind('resize.jqs scroll.jqs', $.proxy(this.updateWindowDims, this));
+ this.updateWindowDims();
+ },
+
+ updateWindowDims: function () {
+ this.scrollTop = $(window).scrollTop();
+ this.scrollLeft = $(window).scrollLeft();
+ this.scrollRight = this.scrollLeft + $(window).width();
+ this.updatePosition();
+ },
+
+ getSize: function (content) {
+ this.sizetip.html(content).appendTo(this.container);
+ this.width = this.sizetip.width() + 1;
+ this.height = this.sizetip.height();
+ this.sizetip.remove();
+ },
+
+ setContent: function (content) {
+ if (!content) {
+ this.tooltip.css('visibility', 'hidden');
+ this.hidden = true;
+ return;
+ }
+ this.getSize(content);
+ this.tooltip.html(content)
+ .css({
+ 'width': this.width,
+ 'height': this.height,
+ 'visibility': 'visible'
+ });
+ if (this.hidden) {
+ this.hidden = false;
+ this.updatePosition();
+ }
+ },
+
+ updatePosition: function (x, y) {
+ if (x === undefined) {
+ if (this.mousex === undefined) {
+ return;
+ }
+ x = this.mousex - this.offsetLeft;
+ y = this.mousey - this.offsetTop;
+
+ } else {
+ this.mousex = x = x - this.offsetLeft;
+ this.mousey = y = y - this.offsetTop;
+ }
+ if (!this.height || !this.width || this.hidden) {
+ return;
+ }
+
+ y -= this.height + this.tooltipOffsetY;
+ x += this.tooltipOffsetX;
+
+ if (y < this.scrollTop) {
+ y = this.scrollTop;
+ }
+ if (x < this.scrollLeft) {
+ x = this.scrollLeft;
+ } else if (x + this.width > this.scrollRight) {
+ x = this.scrollRight - this.width;
+ }
+
+ this.tooltip.css({
+ 'left': x,
+ 'top': y
+ });
+ },
+
+ remove: function () {
+ this.tooltip.remove();
+ this.sizetip.remove();
+ this.sizetip = this.tooltip = undefined;
+ $(window).unbind('resize.jqs scroll.jqs');
+ }
+ });
+
+ initStyles = function() {
+ addCSS(defaultStyles);
+ };
+
+ $(initStyles);
+
+ pending = [];
+ $.fn.sparkline = function (userValues, userOptions) {
+ return this.each(function () {
+ var options = new $.fn.sparkline.options(this, userOptions),
+ $this = $(this),
+ render, i;
+ render = function () {
+ var values, width, height, tmp, mhandler, sp, vals;
+ if (userValues === 'html' || userValues === undefined) {
+ vals = this.getAttribute(options.get('tagValuesAttribute'));
+ if (vals === undefined || vals === null) {
+ vals = $this.html();
+ }
+ values = vals.replace(/(^\s*\s*$)|\s+/g, '').split(',');
+ } else {
+ values = userValues;
+ }
+
+ width = options.get('width') === 'auto' ? values.length * options.get('defaultPixelsPerValue') : options.get('width');
+ if (options.get('height') === 'auto') {
+ if (!options.get('composite') || !$.data(this, '_jqs_vcanvas')) {
+ // must be a better way to get the line height
+ tmp = document.createElement('span');
+ tmp.innerHTML = 'a';
+ $this.html(tmp);
+ height = $(tmp).innerHeight() || $(tmp).height();
+ $(tmp).remove();
+ tmp = null;
+ }
+ } else {
+ height = options.get('height');
+ }
+
+ if (!options.get('disableInteraction')) {
+ mhandler = $.data(this, '_jqs_mhandler');
+ if (!mhandler) {
+ mhandler = new MouseHandler(this, options);
+ $.data(this, '_jqs_mhandler', mhandler);
+ } else if (!options.get('composite')) {
+ mhandler.reset();
+ }
+ } else {
+ mhandler = false;
+ }
+
+ if (options.get('composite') && !$.data(this, '_jqs_vcanvas')) {
+ if (!$.data(this, '_jqs_errnotify')) {
+ alert('Attempted to attach a composite sparkline to an element with no existing sparkline');
+ $.data(this, '_jqs_errnotify', true);
+ }
+ return;
+ }
+
+ sp = new $.fn.sparkline[options.get('type')](this, values, options, width, height);
+
+ sp.render();
+
+ if (mhandler) {
+ mhandler.registerSparkline(sp);
+ }
+ };
+ // jQuery 1.3.0 completely changed the meaning of :hidden :-/
+ if (($(this).html() && !options.get('disableHiddenCheck') && $(this).is(':hidden')) || ($.fn.jquery < '1.3.0' && $(this).parents().is(':hidden')) || !$(this).parents('body').length) {
+ if (!options.get('composite') && $.data(this, '_jqs_pending')) {
+ // remove any existing references to the element
+ for (i = pending.length; i; i--) {
+ if (pending[i - 1][0] == this) {
+ pending.splice(i - 1, 1);
+ }
+ }
+ }
+ pending.push([this, render]);
+ $.data(this, '_jqs_pending', true);
+ } else {
+ render.call(this);
+ }
+ });
+ };
+
+ $.fn.sparkline.defaults = getDefaults();
+
+
+ $.sparkline_display_visible = function () {
+ var el, i, pl;
+ var done = [];
+ for (i = 0, pl = pending.length; i < pl; i++) {
+ el = pending[i][0];
+ if ($(el).is(':visible') && !$(el).parents().is(':hidden')) {
+ pending[i][1].call(el);
+ $.data(pending[i][0], '_jqs_pending', false);
+ done.push(i);
+ } else if (!$(el).closest('html').length && !$.data(el, '_jqs_pending')) {
+ // element has been inserted and removed from the DOM
+ // If it was not yet inserted into the dom then the .data request
+ // will return true.
+ // removing from the dom causes the data to be removed.
+ $.data(pending[i][0], '_jqs_pending', false);
+ done.push(i);
+ }
+ }
+ for (i = done.length; i; i--) {
+ pending.splice(done[i - 1], 1);
+ }
+ };
+
+
+ /**
+ * User option handler
+ */
+ $.fn.sparkline.options = createClass({
+ init: function (tag, userOptions) {
+ var extendedOptions, defaults, base, tagOptionType;
+ this.userOptions = userOptions = userOptions || {};
+ this.tag = tag;
+ this.tagValCache = {};
+ defaults = $.fn.sparkline.defaults;
+ base = defaults.common;
+ this.tagOptionsPrefix = userOptions.enableTagOptions && (userOptions.tagOptionsPrefix || base.tagOptionsPrefix);
+
+ tagOptionType = this.getTagSetting('type');
+ if (tagOptionType === UNSET_OPTION) {
+ extendedOptions = defaults[userOptions.type || base.type];
+ } else {
+ extendedOptions = defaults[tagOptionType];
+ }
+ this.mergedOptions = $.extend({}, base, extendedOptions, userOptions);
+ },
+
+
+ getTagSetting: function (key) {
+ var prefix = this.tagOptionsPrefix,
+ val, i, pairs, keyval;
+ if (prefix === false || prefix === undefined) {
+ return UNSET_OPTION;
+ }
+ if (this.tagValCache.hasOwnProperty(key)) {
+ val = this.tagValCache.key;
+ } else {
+ val = this.tag.getAttribute(prefix + key);
+ if (val === undefined || val === null) {
+ val = UNSET_OPTION;
+ } else if (val.substr(0, 1) === '[') {
+ val = val.substr(1, val.length - 2).split(',');
+ for (i = val.length; i--;) {
+ val[i] = normalizeValue(val[i].replace(/(^\s*)|(\s*$)/g, ''));
+ }
+ } else if (val.substr(0, 1) === '{') {
+ pairs = val.substr(1, val.length - 2).split(',');
+ val = {};
+ for (i = pairs.length; i--;) {
+ keyval = pairs[i].split(':', 2);
+ val[keyval[0].replace(/(^\s*)|(\s*$)/g, '')] = normalizeValue(keyval[1].replace(/(^\s*)|(\s*$)/g, ''));
+ }
+ } else {
+ val = normalizeValue(val);
+ }
+ this.tagValCache.key = val;
+ }
+ return val;
+ },
+
+ get: function (key, defaultval) {
+ var tagOption = this.getTagSetting(key),
+ result;
+ if (tagOption !== UNSET_OPTION) {
+ return tagOption;
+ }
+ return (result = this.mergedOptions[key]) === undefined ? defaultval : result;
+ }
+ });
+
+
+ $.fn.sparkline._base = createClass({
+ disabled: false,
+
+ init: function (el, values, options, width, height) {
+ this.el = el;
+ this.$el = $(el);
+ this.values = values;
+ this.options = options;
+ this.width = width;
+ this.height = height;
+ this.currentRegion = undefined;
+ },
+
+ /**
+ * Setup the canvas
+ */
+ initTarget: function () {
+ var interactive = !this.options.get('disableInteraction');
+ if (!(this.target = this.$el.simpledraw(this.width, this.height, this.options.get('composite'), interactive))) {
+ this.disabled = true;
+ } else {
+ this.canvasWidth = this.target.pixelWidth;
+ this.canvasHeight = this.target.pixelHeight;
+ }
+ },
+
+ /**
+ * Actually render the chart to the canvas
+ */
+ render: function () {
+ if (this.disabled) {
+ this.el.innerHTML = '';
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * Return a region id for a given x/y co-ordinate
+ */
+ getRegion: function (x, y) {
+ },
+
+ /**
+ * Highlight an item based on the moused-over x,y co-ordinate
+ */
+ setRegionHighlight: function (el, x, y) {
+ var currentRegion = this.currentRegion,
+ highlightEnabled = !this.options.get('disableHighlight'),
+ newRegion;
+ if (x > this.canvasWidth || y > this.canvasHeight || x < 0 || y < 0) {
+ return null;
+ }
+ newRegion = this.getRegion(el, x, y);
+ if (currentRegion !== newRegion) {
+ if (currentRegion !== undefined && highlightEnabled) {
+ this.removeHighlight();
+ }
+ this.currentRegion = newRegion;
+ if (newRegion !== undefined && highlightEnabled) {
+ this.renderHighlight();
+ }
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Reset any currently highlighted item
+ */
+ clearRegionHighlight: function () {
+ if (this.currentRegion !== undefined) {
+ this.removeHighlight();
+ this.currentRegion = undefined;
+ return true;
+ }
+ return false;
+ },
+
+ renderHighlight: function () {
+ this.changeHighlight(true);
+ },
+
+ removeHighlight: function () {
+ this.changeHighlight(false);
+ },
+
+ changeHighlight: function (highlight) {},
+
+ /**
+ * Fetch the HTML to display as a tooltip
+ */
+ getCurrentRegionTooltip: function () {
+ var options = this.options,
+ header = '',
+ entries = [],
+ fields, formats, formatlen, fclass, text, i,
+ showFields, showFieldsKey, newFields, fv,
+ formatter, format, fieldlen, j;
+ if (this.currentRegion === undefined) {
+ return '';
+ }
+ fields = this.getCurrentRegionFields();
+ formatter = options.get('tooltipFormatter');
+ if (formatter) {
+ return formatter(this, options, fields);
+ }
+ if (options.get('tooltipChartTitle')) {
+ header += '
' + options.get('tooltipChartTitle') + '
\n';
+ }
+ formats = this.options.get('tooltipFormat');
+ if (!formats) {
+ return '';
+ }
+ if (!$.isArray(formats)) {
+ formats = [formats];
+ }
+ if (!$.isArray(fields)) {
+ fields = [fields];
+ }
+ showFields = this.options.get('tooltipFormatFieldlist');
+ showFieldsKey = this.options.get('tooltipFormatFieldlistKey');
+ if (showFields && showFieldsKey) {
+ // user-selected ordering of fields
+ newFields = [];
+ for (i = fields.length; i--;) {
+ fv = fields[i][showFieldsKey];
+ if ((j = $.inArray(fv, showFields)) != -1) {
+ newFields[j] = fields[i];
+ }
+ }
+ fields = newFields;
+ }
+ formatlen = formats.length;
+ fieldlen = fields.length;
+ for (i = 0; i < formatlen; i++) {
+ format = formats[i];
+ if (typeof format === 'string') {
+ format = new SPFormat(format);
+ }
+ fclass = format.fclass || 'jqsfield';
+ for (j = 0; j < fieldlen; j++) {
+ if (!fields[j].isNull || !options.get('tooltipSkipNull')) {
+ $.extend(fields[j], {
+ prefix: options.get('tooltipPrefix'),
+ suffix: options.get('tooltipSuffix')
+ });
+ text = format.render(fields[j], options.get('tooltipValueLookups'), options);
+ entries.push('
' + text + '
');
+ }
+ }
+ }
+ if (entries.length) {
+ return header + entries.join('\n');
+ }
+ return '';
+ },
+
+ getCurrentRegionFields: function () {},
+
+ calcHighlightColor: function (color, options) {
+ var highlightColor = options.get('highlightColor'),
+ lighten = options.get('highlightLighten'),
+ parse, mult, rgbnew, i;
+ if (highlightColor) {
+ return highlightColor;
+ }
+ if (lighten) {
+ // extract RGB values
+ parse = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(color) || /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(color);
+ if (parse) {
+ rgbnew = [];
+ mult = color.length === 4 ? 16 : 1;
+ for (i = 0; i < 3; i++) {
+ rgbnew[i] = clipval(Math.round(parseInt(parse[i + 1], 16) * mult * lighten), 0, 255);
+ }
+ return 'rgb(' + rgbnew.join(',') + ')';
+ }
+
+ }
+ return color;
+ }
+
+ });
+
+ barHighlightMixin = {
+ changeHighlight: function (highlight) {
+ var currentRegion = this.currentRegion,
+ target = this.target,
+ shapeids = this.regionShapes[currentRegion],
+ newShapes;
+ // will be null if the region value was null
+ if (shapeids) {
+ newShapes = this.renderRegion(currentRegion, highlight);
+ if ($.isArray(newShapes) || $.isArray(shapeids)) {
+ target.replaceWithShapes(shapeids, newShapes);
+ this.regionShapes[currentRegion] = $.map(newShapes, function (newShape) {
+ return newShape.id;
+ });
+ } else {
+ target.replaceWithShape(shapeids, newShapes);
+ this.regionShapes[currentRegion] = newShapes.id;
+ }
+ }
+ },
+
+ render: function () {
+ var values = this.values,
+ target = this.target,
+ regionShapes = this.regionShapes,
+ shapes, ids, i, j;
+
+ if (!this.cls._super.render.call(this)) {
+ return;
+ }
+ for (i = values.length; i--;) {
+ shapes = this.renderRegion(i);
+ if (shapes) {
+ if ($.isArray(shapes)) {
+ ids = [];
+ for (j = shapes.length; j--;) {
+ shapes[j].append();
+ ids.push(shapes[j].id);
+ }
+ regionShapes[i] = ids;
+ } else {
+ shapes.append();
+ regionShapes[i] = shapes.id; // store just the shapeid
+ }
+ } else {
+ // null value
+ regionShapes[i] = null;
+ }
+ }
+ target.render();
+ }
+ };
+
+ /**
+ * Line charts
+ */
+ $.fn.sparkline.line = line = createClass($.fn.sparkline._base, {
+ type: 'line',
+
+ init: function (el, values, options, width, height) {
+ line._super.init.call(this, el, values, options, width, height);
+ this.vertices = [];
+ this.regionMap = [];
+ this.xvalues = [];
+ this.yvalues = [];
+ this.yminmax = [];
+ this.hightlightSpotId = null;
+ this.lastShapeId = null;
+ this.initTarget();
+ },
+
+ getRegion: function (el, x, y) {
+ var i,
+ regionMap = this.regionMap; // maps regions to value positions
+ for (i = regionMap.length; i--;) {
+ if (regionMap[i] !== null && x >= regionMap[i][0] && x <= regionMap[i][1]) {
+ return regionMap[i][2];
+ }
+ }
+ return undefined;
+ },
+
+ getCurrentRegionFields: function () {
+ var currentRegion = this.currentRegion;
+ return {
+ isNull: this.yvalues[currentRegion] === null,
+ x: this.xvalues[currentRegion],
+ y: this.yvalues[currentRegion],
+ color: this.options.get('lineColor'),
+ fillColor: this.options.get('fillColor'),
+ offset: currentRegion
+ };
+ },
+
+ renderHighlight: function () {
+ var currentRegion = this.currentRegion,
+ target = this.target,
+ vertex = this.vertices[currentRegion],
+ options = this.options,
+ spotRadius = options.get('spotRadius'),
+ highlightSpotColor = options.get('highlightSpotColor'),
+ highlightLineColor = options.get('highlightLineColor'),
+ highlightSpot, highlightLine;
+
+ if (!vertex) {
+ return;
+ }
+ if (spotRadius && highlightSpotColor) {
+ highlightSpot = target.drawCircle(vertex[0], vertex[1],
+ spotRadius, undefined, highlightSpotColor);
+ this.highlightSpotId = highlightSpot.id;
+ target.insertAfterShape(this.lastShapeId, highlightSpot);
+ }
+ if (highlightLineColor) {
+ highlightLine = target.drawLine(vertex[0], this.canvasTop, vertex[0],
+ this.canvasTop + this.canvasHeight, highlightLineColor);
+ this.highlightLineId = highlightLine.id;
+ target.insertAfterShape(this.lastShapeId, highlightLine);
+ }
+ },
+
+ removeHighlight: function () {
+ var target = this.target;
+ if (this.highlightSpotId) {
+ target.removeShapeId(this.highlightSpotId);
+ this.highlightSpotId = null;
+ }
+ if (this.highlightLineId) {
+ target.removeShapeId(this.highlightLineId);
+ this.highlightLineId = null;
+ }
+ },
+
+ scanValues: function () {
+ var values = this.values,
+ valcount = values.length,
+ xvalues = this.xvalues,
+ yvalues = this.yvalues,
+ yminmax = this.yminmax,
+ i, val, isStr, isArray, sp;
+ for (i = 0; i < valcount; i++) {
+ val = values[i];
+ isStr = typeof(values[i]) === 'string';
+ isArray = typeof(values[i]) === 'object' && values[i] instanceof Array;
+ sp = isStr && values[i].split(':');
+ if (isStr && sp.length === 2) { // x:y
+ xvalues.push(Number(sp[0]));
+ yvalues.push(Number(sp[1]));
+ yminmax.push(Number(sp[1]));
+ } else if (isArray) {
+ xvalues.push(val[0]);
+ yvalues.push(val[1]);
+ yminmax.push(val[1]);
+ } else {
+ xvalues.push(i);
+ if (values[i] === null || values[i] === 'null') {
+ yvalues.push(null);
+ } else {
+ yvalues.push(Number(val));
+ yminmax.push(Number(val));
+ }
+ }
+ }
+ if (this.options.get('xvalues')) {
+ xvalues = this.options.get('xvalues');
+ }
+
+ this.maxy = this.maxyorg = Math.max.apply(Math, yminmax);
+ this.miny = this.minyorg = Math.min.apply(Math, yminmax);
+
+ this.maxx = Math.max.apply(Math, xvalues);
+ this.minx = Math.min.apply(Math, xvalues);
+
+ this.xvalues = xvalues;
+ this.yvalues = yvalues;
+ this.yminmax = yminmax;
+
+ },
+
+ processRangeOptions: function () {
+ var options = this.options,
+ normalRangeMin = options.get('normalRangeMin'),
+ normalRangeMax = options.get('normalRangeMax');
+
+ if (normalRangeMin !== undefined) {
+ if (normalRangeMin < this.miny) {
+ this.miny = normalRangeMin;
+ }
+ if (normalRangeMax > this.maxy) {
+ this.maxy = normalRangeMax;
+ }
+ }
+ if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < this.miny)) {
+ this.miny = options.get('chartRangeMin');
+ }
+ if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > this.maxy)) {
+ this.maxy = options.get('chartRangeMax');
+ }
+ if (options.get('chartRangeMinX') !== undefined && (options.get('chartRangeClipX') || options.get('chartRangeMinX') < this.minx)) {
+ this.minx = options.get('chartRangeMinX');
+ }
+ if (options.get('chartRangeMaxX') !== undefined && (options.get('chartRangeClipX') || options.get('chartRangeMaxX') > this.maxx)) {
+ this.maxx = options.get('chartRangeMaxX');
+ }
+
+ },
+
+ drawNormalRange: function (canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey) {
+ var normalRangeMin = this.options.get('normalRangeMin'),
+ normalRangeMax = this.options.get('normalRangeMax'),
+ ytop = canvasTop + Math.round(canvasHeight - (canvasHeight * ((normalRangeMax - this.miny) / rangey))),
+ height = Math.round((canvasHeight * (normalRangeMax - normalRangeMin)) / rangey);
+ this.target.drawRect(canvasLeft, ytop, canvasWidth, height, undefined, this.options.get('normalRangeColor')).append();
+ },
+
+ render: function () {
+ var options = this.options,
+ target = this.target,
+ canvasWidth = this.canvasWidth,
+ canvasHeight = this.canvasHeight,
+ vertices = this.vertices,
+ spotRadius = options.get('spotRadius'),
+ regionMap = this.regionMap,
+ rangex, rangey, yvallast,
+ canvasTop, canvasLeft,
+ vertex, path, paths, x, y, xnext, xpos, xposnext,
+ last, next, yvalcount, lineShapes, fillShapes, plen,
+ valueSpots, hlSpotsEnabled, color, xvalues, yvalues, i;
+
+ if (!line._super.render.call(this)) {
+ return;
+ }
+
+ this.scanValues();
+ this.processRangeOptions();
+
+ xvalues = this.xvalues;
+ yvalues = this.yvalues;
+
+ if (!this.yminmax.length || this.yvalues.length < 2) {
+ // empty or all null valuess
+ return;
+ }
+
+ canvasTop = canvasLeft = 0;
+
+ rangex = this.maxx - this.minx === 0 ? 1 : this.maxx - this.minx;
+ rangey = this.maxy - this.miny === 0 ? 1 : this.maxy - this.miny;
+ yvallast = this.yvalues.length - 1;
+
+ if (spotRadius && (canvasWidth < (spotRadius * 4) || canvasHeight < (spotRadius * 4))) {
+ spotRadius = 0;
+ }
+ if (spotRadius) {
+ // adjust the canvas size as required so that spots will fit
+ hlSpotsEnabled = options.get('highlightSpotColor') && !options.get('disableInteraction');
+ if (hlSpotsEnabled || options.get('minSpotColor') || (options.get('spotColor') && yvalues[yvallast] === this.miny)) {
+ canvasHeight -= Math.ceil(spotRadius);
+ }
+ if (hlSpotsEnabled || options.get('maxSpotColor') || (options.get('spotColor') && yvalues[yvallast] === this.maxy)) {
+ canvasHeight -= Math.ceil(spotRadius);
+ canvasTop += Math.ceil(spotRadius);
+ }
+ if (hlSpotsEnabled ||
+ ((options.get('minSpotColor') || options.get('maxSpotColor')) && (yvalues[0] === this.miny || yvalues[0] === this.maxy))) {
+ canvasLeft += Math.ceil(spotRadius);
+ canvasWidth -= Math.ceil(spotRadius);
+ }
+ if (hlSpotsEnabled || options.get('spotColor') ||
+ (options.get('minSpotColor') || options.get('maxSpotColor') &&
+ (yvalues[yvallast] === this.miny || yvalues[yvallast] === this.maxy))) {
+ canvasWidth -= Math.ceil(spotRadius);
+ }
+ }
+
+
+ canvasHeight--;
+
+ if (options.get('normalRangeMin') !== undefined && !options.get('drawNormalOnTop')) {
+ this.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey);
+ }
+
+ path = [];
+ paths = [path];
+ last = next = null;
+ yvalcount = yvalues.length;
+ for (i = 0; i < yvalcount; i++) {
+ x = xvalues[i];
+ xnext = xvalues[i + 1];
+ y = yvalues[i];
+ xpos = canvasLeft + Math.round((x - this.minx) * (canvasWidth / rangex));
+ xposnext = i < yvalcount - 1 ? canvasLeft + Math.round((xnext - this.minx) * (canvasWidth / rangex)) : canvasWidth;
+ next = xpos + ((xposnext - xpos) / 2);
+ regionMap[i] = [last || 0, next, i];
+ last = next;
+ if (y === null) {
+ if (i) {
+ if (yvalues[i - 1] !== null) {
+ path = [];
+ paths.push(path);
+ }
+ vertices.push(null);
+ }
+ } else {
+ if (y < this.miny) {
+ y = this.miny;
+ }
+ if (y > this.maxy) {
+ y = this.maxy;
+ }
+ if (!path.length) {
+ // previous value was null
+ path.push([xpos, canvasTop + canvasHeight]);
+ }
+ vertex = [xpos, canvasTop + Math.round(canvasHeight - (canvasHeight * ((y - this.miny) / rangey)))];
+ path.push(vertex);
+ vertices.push(vertex);
+ }
+ }
+
+ lineShapes = [];
+ fillShapes = [];
+ plen = paths.length;
+ for (i = 0; i < plen; i++) {
+ path = paths[i];
+ if (path.length) {
+ if (options.get('fillColor')) {
+ path.push([path[path.length - 1][0], (canvasTop + canvasHeight)]);
+ fillShapes.push(path.slice(0));
+ path.pop();
+ }
+ // if there's only a single point in this path, then we want to display it
+ // as a vertical line which means we keep path[0] as is
+ if (path.length > 2) {
+ // else we want the first value
+ path[0] = [path[0][0], path[1][1]];
+ }
+ lineShapes.push(path);
+ }
+ }
+
+ // draw the fill first, then optionally the normal range, then the line on top of that
+ plen = fillShapes.length;
+ for (i = 0; i < plen; i++) {
+ target.drawShape(fillShapes[i],
+ options.get('fillColor'), options.get('fillColor')).append();
+ }
+
+ if (options.get('normalRangeMin') !== undefined && options.get('drawNormalOnTop')) {
+ this.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey);
+ }
+
+ plen = lineShapes.length;
+ for (i = 0; i < plen; i++) {
+ target.drawShape(lineShapes[i], options.get('lineColor'), undefined,
+ options.get('lineWidth')).append();
+ }
+
+ if (spotRadius && options.get('valueSpots')) {
+ valueSpots = options.get('valueSpots');
+ if (valueSpots.get === undefined) {
+ valueSpots = new RangeMap(valueSpots);
+ }
+ for (i = 0; i < yvalcount; i++) {
+ color = valueSpots.get(yvalues[i]);
+ if (color) {
+ target.drawCircle(canvasLeft + Math.round((xvalues[i] - this.minx) * (canvasWidth / rangex)),
+ canvasTop + Math.round(canvasHeight - (canvasHeight * ((yvalues[i] - this.miny) / rangey))),
+ spotRadius, undefined,
+ color).append();
+ }
+ }
+
+ }
+ if (spotRadius && options.get('spotColor') && yvalues[yvallast] !== null) {
+ target.drawCircle(canvasLeft + Math.round((xvalues[xvalues.length - 1] - this.minx) * (canvasWidth / rangex)),
+ canvasTop + Math.round(canvasHeight - (canvasHeight * ((yvalues[yvallast] - this.miny) / rangey))),
+ spotRadius, undefined,
+ options.get('spotColor')).append();
+ }
+ if (this.maxy !== this.minyorg) {
+ if (spotRadius && options.get('minSpotColor')) {
+ x = xvalues[$.inArray(this.minyorg, yvalues)];
+ target.drawCircle(canvasLeft + Math.round((x - this.minx) * (canvasWidth / rangex)),
+ canvasTop + Math.round(canvasHeight - (canvasHeight * ((this.minyorg - this.miny) / rangey))),
+ spotRadius, undefined,
+ options.get('minSpotColor')).append();
+ }
+ if (spotRadius && options.get('maxSpotColor')) {
+ x = xvalues[$.inArray(this.maxyorg, yvalues)];
+ target.drawCircle(canvasLeft + Math.round((x - this.minx) * (canvasWidth / rangex)),
+ canvasTop + Math.round(canvasHeight - (canvasHeight * ((this.maxyorg - this.miny) / rangey))),
+ spotRadius, undefined,
+ options.get('maxSpotColor')).append();
+ }
+ }
+
+ this.lastShapeId = target.getLastShapeId();
+ this.canvasTop = canvasTop;
+ target.render();
+ }
+ });
+
+ /**
+ * Bar charts
+ */
+ $.fn.sparkline.bar = bar = createClass($.fn.sparkline._base, barHighlightMixin, {
+ type: 'bar',
+
+ init: function (el, values, options, width, height) {
+ var barWidth = parseInt(options.get('barWidth'), 10),
+ barSpacing = parseInt(options.get('barSpacing'), 10),
+ chartRangeMin = options.get('chartRangeMin'),
+ chartRangeMax = options.get('chartRangeMax'),
+ chartRangeClip = options.get('chartRangeClip'),
+ stackMin = Infinity,
+ stackMax = -Infinity,
+ isStackString, groupMin, groupMax, stackRanges,
+ numValues, i, vlen, range, zeroAxis, xaxisOffset, min, max, clipMin, clipMax,
+ stacked, vlist, j, slen, svals, val, yoffset, yMaxCalc, canvasHeightEf;
+ bar._super.init.call(this, el, values, options, width, height);
+
+ // scan values to determine whether to stack bars
+ for (i = 0, vlen = values.length; i < vlen; i++) {
+ val = values[i];
+ isStackString = typeof(val) === 'string' && val.indexOf(':') > -1;
+ if (isStackString || $.isArray(val)) {
+ stacked = true;
+ if (isStackString) {
+ val = values[i] = normalizeValues(val.split(':'));
+ }
+ val = remove(val, null); // min/max will treat null as zero
+ groupMin = Math.min.apply(Math, val);
+ groupMax = Math.max.apply(Math, val);
+ if (groupMin < stackMin) {
+ stackMin = groupMin;
+ }
+ if (groupMax > stackMax) {
+ stackMax = groupMax;
+ }
+ }
+ }
+
+ this.stacked = stacked;
+ this.regionShapes = {};
+ this.barWidth = barWidth;
+ this.barSpacing = barSpacing;
+ this.totalBarWidth = barWidth + barSpacing;
+ this.width = width = (values.length * barWidth) + ((values.length - 1) * barSpacing);
+
+ this.initTarget();
+
+ if (chartRangeClip) {
+ clipMin = chartRangeMin === undefined ? -Infinity : chartRangeMin;
+ clipMax = chartRangeMax === undefined ? Infinity : chartRangeMax;
+ }
+
+ numValues = [];
+ stackRanges = stacked ? [] : numValues;
+ var stackTotals = [];
+ var stackRangesNeg = [];
+ for (i = 0, vlen = values.length; i < vlen; i++) {
+ if (stacked) {
+ vlist = values[i];
+ values[i] = svals = [];
+ stackTotals[i] = 0;
+ stackRanges[i] = stackRangesNeg[i] = 0;
+ for (j = 0, slen = vlist.length; j < slen; j++) {
+ val = svals[j] = chartRangeClip ? clipval(vlist[j], clipMin, clipMax) : vlist[j];
+ if (val !== null) {
+ if (val > 0) {
+ stackTotals[i] += val;
+ }
+ if (stackMin < 0 && stackMax > 0) {
+ if (val < 0) {
+ stackRangesNeg[i] += Math.abs(val);
+ } else {
+ stackRanges[i] += val;
+ }
+ } else {
+ stackRanges[i] += Math.abs(val - (val < 0 ? stackMax : stackMin));
+ }
+ numValues.push(val);
+ }
+ }
+ } else {
+ val = chartRangeClip ? clipval(values[i], clipMin, clipMax) : values[i];
+ val = values[i] = normalizeValue(val);
+ if (val !== null) {
+ numValues.push(val);
+ }
+ }
+ }
+ this.max = max = Math.max.apply(Math, numValues);
+ this.min = min = Math.min.apply(Math, numValues);
+ this.stackMax = stackMax = stacked ? Math.max.apply(Math, stackTotals) : max;
+ this.stackMin = stackMin = stacked ? Math.min.apply(Math, numValues) : min;
+
+ if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < min)) {
+ min = options.get('chartRangeMin');
+ }
+ if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > max)) {
+ max = options.get('chartRangeMax');
+ }
+
+ this.zeroAxis = zeroAxis = options.get('zeroAxis', true);
+ if (min <= 0 && max >= 0 && zeroAxis) {
+ xaxisOffset = 0;
+ } else if (zeroAxis == false) {
+ xaxisOffset = min;
+ } else if (min > 0) {
+ xaxisOffset = min;
+ } else {
+ xaxisOffset = max;
+ }
+ this.xaxisOffset = xaxisOffset;
+
+ range = stacked ? (Math.max.apply(Math, stackRanges) + Math.max.apply(Math, stackRangesNeg)) : max - min;
+
+ // as we plot zero/min values a single pixel line, we add a pixel to all other
+ // values - Reduce the effective canvas size to suit
+ this.canvasHeightEf = (zeroAxis && min < 0) ? this.canvasHeight - 2 : this.canvasHeight - 1;
+
+ if (min < xaxisOffset) {
+ yMaxCalc = (stacked && max >= 0) ? stackMax : max;
+ yoffset = (yMaxCalc - xaxisOffset) / range * this.canvasHeight;
+ if (yoffset !== Math.ceil(yoffset)) {
+ this.canvasHeightEf -= 2;
+ yoffset = Math.ceil(yoffset);
+ }
+ } else {
+ yoffset = this.canvasHeight;
+ }
+ this.yoffset = yoffset;
+
+ if ($.isArray(options.get('colorMap'))) {
+ this.colorMapByIndex = options.get('colorMap');
+ this.colorMapByValue = null;
+ } else {
+ this.colorMapByIndex = null;
+ this.colorMapByValue = options.get('colorMap');
+ if (this.colorMapByValue && this.colorMapByValue.get === undefined) {
+ this.colorMapByValue = new RangeMap(this.colorMapByValue);
+ }
+ }
+
+ this.range = range;
+ },
+
+ getRegion: function (el, x, y) {
+ var result = Math.floor(x / this.totalBarWidth);
+ return (result < 0 || result >= this.values.length) ? undefined : result;
+ },
+
+ getCurrentRegionFields: function () {
+ var currentRegion = this.currentRegion,
+ values = ensureArray(this.values[currentRegion]),
+ result = [],
+ value, i;
+ for (i = values.length; i--;) {
+ value = values[i];
+ result.push({
+ isNull: value === null,
+ value: value,
+ color: this.calcColor(i, value, currentRegion),
+ offset: currentRegion
+ });
+ }
+ return result;
+ },
+
+ calcColor: function (stacknum, value, valuenum) {
+ var colorMapByIndex = this.colorMapByIndex,
+ colorMapByValue = this.colorMapByValue,
+ options = this.options,
+ color, newColor;
+ if (this.stacked) {
+ color = options.get('stackedBarColor');
+ } else {
+ color = (value < 0) ? options.get('negBarColor') : options.get('barColor');
+ }
+ if (value === 0 && options.get('zeroColor') !== undefined) {
+ color = options.get('zeroColor');
+ }
+ if (colorMapByValue && (newColor = colorMapByValue.get(value))) {
+ color = newColor;
+ } else if (colorMapByIndex && colorMapByIndex.length > valuenum) {
+ color = colorMapByIndex[valuenum];
+ }
+ return $.isArray(color) ? color[stacknum % color.length] : color;
+ },
+
+ /**
+ * Render bar(s) for a region
+ */
+ renderRegion: function (valuenum, highlight) {
+ var vals = this.values[valuenum],
+ options = this.options,
+ xaxisOffset = this.xaxisOffset,
+ result = [],
+ range = this.range,
+ stacked = this.stacked,
+ target = this.target,
+ x = valuenum * this.totalBarWidth,
+ canvasHeightEf = this.canvasHeightEf,
+ yoffset = this.yoffset,
+ y, height, color, isNull, yoffsetNeg, i, valcount, val, minPlotted, allMin;
+
+ vals = $.isArray(vals) ? vals : [vals];
+ valcount = vals.length;
+ val = vals[0];
+ isNull = all(null, vals);
+ allMin = all(xaxisOffset, vals, true);
+
+ if (isNull) {
+ if (options.get('nullColor')) {
+ color = highlight ? options.get('nullColor') : this.calcHighlightColor(options.get('nullColor'), options);
+ y = (yoffset > 0) ? yoffset - 1 : yoffset;
+ return target.drawRect(x, y, this.barWidth - 1, 0, color, color);
+ } else {
+ return undefined;
+ }
+ }
+ yoffsetNeg = yoffset;
+ for (i = 0; i < valcount; i++) {
+ val = vals[i];
+
+ if (stacked && val === xaxisOffset) {
+ if (!allMin || minPlotted) {
+ continue;
+ }
+ minPlotted = true;
+ }
+
+ if (range > 0) {
+ height = Math.floor(canvasHeightEf * ((Math.abs(val - xaxisOffset) / range))) + 1;
+ } else {
+ height = 1;
+ }
+ if (val < xaxisOffset || (val === xaxisOffset && yoffset === 0)) {
+ y = yoffsetNeg;
+ yoffsetNeg += height;
+ } else {
+ y = yoffset - height;
+ yoffset -= height;
+ }
+ color = this.calcColor(i, val, valuenum);
+ if (highlight) {
+ color = this.calcHighlightColor(color, options);
+ }
+ result.push(target.drawRect(x, y, this.barWidth - 1, height - 1, color, color));
+ }
+ if (result.length === 1) {
+ return result[0];
+ }
+ return result;
+ }
+ });
+
+ /**
+ * Tristate charts
+ */
+ $.fn.sparkline.tristate = tristate = createClass($.fn.sparkline._base, barHighlightMixin, {
+ type: 'tristate',
+
+ init: function (el, values, options, width, height) {
+ var barWidth = parseInt(options.get('barWidth'), 10),
+ barSpacing = parseInt(options.get('barSpacing'), 10);
+ tristate._super.init.call(this, el, values, options, width, height);
+
+ this.regionShapes = {};
+ this.barWidth = barWidth;
+ this.barSpacing = barSpacing;
+ this.totalBarWidth = barWidth + barSpacing;
+ this.values = $.map(values, Number);
+ this.width = width = (values.length * barWidth) + ((values.length - 1) * barSpacing);
+
+ if ($.isArray(options.get('colorMap'))) {
+ this.colorMapByIndex = options.get('colorMap');
+ this.colorMapByValue = null;
+ } else {
+ this.colorMapByIndex = null;
+ this.colorMapByValue = options.get('colorMap');
+ if (this.colorMapByValue && this.colorMapByValue.get === undefined) {
+ this.colorMapByValue = new RangeMap(this.colorMapByValue);
+ }
+ }
+ this.initTarget();
+ },
+
+ getRegion: function (el, x, y) {
+ return Math.floor(x / this.totalBarWidth);
+ },
+
+ getCurrentRegionFields: function () {
+ var currentRegion = this.currentRegion;
+ return {
+ isNull: this.values[currentRegion] === undefined,
+ value: this.values[currentRegion],
+ color: this.calcColor(this.values[currentRegion], currentRegion),
+ offset: currentRegion
+ };
+ },
+
+ calcColor: function (value, valuenum) {
+ var values = this.values,
+ options = this.options,
+ colorMapByIndex = this.colorMapByIndex,
+ colorMapByValue = this.colorMapByValue,
+ color, newColor;
+
+ if (colorMapByValue && (newColor = colorMapByValue.get(value))) {
+ color = newColor;
+ } else if (colorMapByIndex && colorMapByIndex.length > valuenum) {
+ color = colorMapByIndex[valuenum];
+ } else if (values[valuenum] < 0) {
+ color = options.get('negBarColor');
+ } else if (values[valuenum] > 0) {
+ color = options.get('posBarColor');
+ } else {
+ color = options.get('zeroBarColor');
+ }
+ return color;
+ },
+
+ renderRegion: function (valuenum, highlight) {
+ var values = this.values,
+ options = this.options,
+ target = this.target,
+ canvasHeight, height, halfHeight,
+ x, y, color;
+
+ canvasHeight = target.pixelHeight;
+ halfHeight = Math.round(canvasHeight / 2);
+
+ x = valuenum * this.totalBarWidth;
+ if (values[valuenum] < 0) {
+ y = halfHeight;
+ height = halfHeight - 1;
+ } else if (values[valuenum] > 0) {
+ y = 0;
+ height = halfHeight - 1;
+ } else {
+ y = halfHeight - 1;
+ height = 2;
+ }
+ color = this.calcColor(values[valuenum], valuenum);
+ if (color === null) {
+ return;
+ }
+ if (highlight) {
+ color = this.calcHighlightColor(color, options);
+ }
+ return target.drawRect(x, y, this.barWidth - 1, height - 1, color, color);
+ }
+ });
+
+ /**
+ * Discrete charts
+ */
+ $.fn.sparkline.discrete = discrete = createClass($.fn.sparkline._base, barHighlightMixin, {
+ type: 'discrete',
+
+ init: function (el, values, options, width, height) {
+ discrete._super.init.call(this, el, values, options, width, height);
+
+ this.regionShapes = {};
+ this.values = values = $.map(values, Number);
+ this.min = Math.min.apply(Math, values);
+ this.max = Math.max.apply(Math, values);
+ this.range = this.max - this.min;
+ this.width = width = options.get('width') === 'auto' ? values.length * 2 : this.width;
+ this.interval = Math.floor(width / values.length);
+ this.itemWidth = width / values.length;
+ if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < this.min)) {
+ this.min = options.get('chartRangeMin');
+ }
+ if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > this.max)) {
+ this.max = options.get('chartRangeMax');
+ }
+ this.initTarget();
+ if (this.target) {
+ this.lineHeight = options.get('lineHeight') === 'auto' ? Math.round(this.canvasHeight * 0.3) : options.get('lineHeight');
+ }
+ },
+
+ getRegion: function (el, x, y) {
+ return Math.floor(x / this.itemWidth);
+ },
+
+ getCurrentRegionFields: function () {
+ var currentRegion = this.currentRegion;
+ return {
+ isNull: this.values[currentRegion] === undefined,
+ value: this.values[currentRegion],
+ offset: currentRegion
+ };
+ },
+
+ renderRegion: function (valuenum, highlight) {
+ var values = this.values,
+ options = this.options,
+ min = this.min,
+ max = this.max,
+ range = this.range,
+ interval = this.interval,
+ target = this.target,
+ canvasHeight = this.canvasHeight,
+ lineHeight = this.lineHeight,
+ pheight = canvasHeight - lineHeight,
+ ytop, val, color, x;
+
+ val = clipval(values[valuenum], min, max);
+ x = valuenum * interval;
+ ytop = Math.round(pheight - pheight * ((val - min) / range));
+ color = (options.get('thresholdColor') && val < options.get('thresholdValue')) ? options.get('thresholdColor') : options.get('lineColor');
+ if (highlight) {
+ color = this.calcHighlightColor(color, options);
+ }
+ return target.drawLine(x, ytop, x, ytop + lineHeight, color);
+ }
+ });
+
+ /**
+ * Bullet charts
+ */
+ $.fn.sparkline.bullet = bullet = createClass($.fn.sparkline._base, {
+ type: 'bullet',
+
+ init: function (el, values, options, width, height) {
+ var min, max, vals;
+ bullet._super.init.call(this, el, values, options, width, height);
+
+ // values: target, performance, range1, range2, range3
+ this.values = values = normalizeValues(values);
+ // target or performance could be null
+ vals = values.slice();
+ vals[0] = vals[0] === null ? vals[2] : vals[0];
+ vals[1] = values[1] === null ? vals[2] : vals[1];
+ min = Math.min.apply(Math, values);
+ max = Math.max.apply(Math, values);
+ if (options.get('base') === undefined) {
+ min = min < 0 ? min : 0;
+ } else {
+ min = options.get('base');
+ }
+ this.min = min;
+ this.max = max;
+ this.range = max - min;
+ this.shapes = {};
+ this.valueShapes = {};
+ this.regiondata = {};
+ this.width = width = options.get('width') === 'auto' ? '4.0em' : width;
+ this.target = this.$el.simpledraw(width, height, options.get('composite'));
+ if (!values.length) {
+ this.disabled = true;
+ }
+ this.initTarget();
+ },
+
+ getRegion: function (el, x, y) {
+ var shapeid = this.target.getShapeAt(el, x, y);
+ return (shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined;
+ },
+
+ getCurrentRegionFields: function () {
+ var currentRegion = this.currentRegion;
+ return {
+ fieldkey: currentRegion.substr(0, 1),
+ value: this.values[currentRegion.substr(1)],
+ region: currentRegion
+ };
+ },
+
+ changeHighlight: function (highlight) {
+ var currentRegion = this.currentRegion,
+ shapeid = this.valueShapes[currentRegion],
+ shape;
+ delete this.shapes[shapeid];
+ switch (currentRegion.substr(0, 1)) {
+ case 'r':
+ shape = this.renderRange(currentRegion.substr(1), highlight);
+ break;
+ case 'p':
+ shape = this.renderPerformance(highlight);
+ break;
+ case 't':
+ shape = this.renderTarget(highlight);
+ break;
+ }
+ this.valueShapes[currentRegion] = shape.id;
+ this.shapes[shape.id] = currentRegion;
+ this.target.replaceWithShape(shapeid, shape);
+ },
+
+ renderRange: function (rn, highlight) {
+ var rangeval = this.values[rn],
+ rangewidth = Math.round(this.canvasWidth * ((rangeval - this.min) / this.range)),
+ color = this.options.get('rangeColors')[rn - 2];
+ if (highlight) {
+ color = this.calcHighlightColor(color, this.options);
+ }
+ return this.target.drawRect(0, 0, rangewidth - 1, this.canvasHeight - 1, color, color);
+ },
+
+ renderPerformance: function (highlight) {
+ var perfval = this.values[1],
+ perfwidth = Math.round(this.canvasWidth * ((perfval - this.min) / this.range)),
+ color = this.options.get('performanceColor');
+ if (highlight) {
+ color = this.calcHighlightColor(color, this.options);
+ }
+ return this.target.drawRect(0, Math.round(this.canvasHeight * 0.3), perfwidth - 1,
+ Math.round(this.canvasHeight * 0.4) - 1, color, color);
+ },
+
+ renderTarget: function (highlight) {
+ var targetval = this.values[0],
+ x = Math.round(this.canvasWidth * ((targetval - this.min) / this.range) - (this.options.get('targetWidth') / 2)),
+ targettop = Math.round(this.canvasHeight * 0.10),
+ targetheight = this.canvasHeight - (targettop * 2),
+ color = this.options.get('targetColor');
+ if (highlight) {
+ color = this.calcHighlightColor(color, this.options);
+ }
+ return this.target.drawRect(x, targettop, this.options.get('targetWidth') - 1, targetheight - 1, color, color);
+ },
+
+ render: function () {
+ var vlen = this.values.length,
+ target = this.target,
+ i, shape;
+ if (!bullet._super.render.call(this)) {
+ return;
+ }
+ for (i = 2; i < vlen; i++) {
+ shape = this.renderRange(i).append();
+ this.shapes[shape.id] = 'r' + i;
+ this.valueShapes['r' + i] = shape.id;
+ }
+ if (this.values[1] !== null) {
+ shape = this.renderPerformance().append();
+ this.shapes[shape.id] = 'p1';
+ this.valueShapes.p1 = shape.id;
+ }
+ if (this.values[0] !== null) {
+ shape = this.renderTarget().append();
+ this.shapes[shape.id] = 't0';
+ this.valueShapes.t0 = shape.id;
+ }
+ target.render();
+ }
+ });
+
+ /**
+ * Pie charts
+ */
+ $.fn.sparkline.pie = pie = createClass($.fn.sparkline._base, {
+ type: 'pie',
+
+ init: function (el, values, options, width, height) {
+ var total = 0, i;
+
+ pie._super.init.call(this, el, values, options, width, height);
+
+ this.shapes = {}; // map shape ids to value offsets
+ this.valueShapes = {}; // maps value offsets to shape ids
+ this.values = values = $.map(values, Number);
+
+ if (options.get('width') === 'auto') {
+ this.width = this.height;
+ }
+
+ if (values.length > 0) {
+ for (i = values.length; i--;) {
+ total += values[i];
+ }
+ }
+ this.total = total;
+ this.initTarget();
+ this.radius = Math.floor(Math.min(this.canvasWidth, this.canvasHeight) / 2);
+ },
+
+ getRegion: function (el, x, y) {
+ var shapeid = this.target.getShapeAt(el, x, y);
+ return (shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined;
+ },
+
+ getCurrentRegionFields: function () {
+ var currentRegion = this.currentRegion;
+ return {
+ isNull: this.values[currentRegion] === undefined,
+ value: this.values[currentRegion],
+ percent: this.values[currentRegion] / this.total * 100,
+ color: this.options.get('sliceColors')[currentRegion % this.options.get('sliceColors').length],
+ offset: currentRegion
+ };
+ },
+
+ changeHighlight: function (highlight) {
+ var currentRegion = this.currentRegion,
+ newslice = this.renderSlice(currentRegion, highlight),
+ shapeid = this.valueShapes[currentRegion];
+ delete this.shapes[shapeid];
+ this.target.replaceWithShape(shapeid, newslice);
+ this.valueShapes[currentRegion] = newslice.id;
+ this.shapes[newslice.id] = currentRegion;
+ },
+
+ renderSlice: function (valuenum, highlight) {
+ var target = this.target,
+ options = this.options,
+ radius = this.radius,
+ borderWidth = options.get('borderWidth'),
+ offset = options.get('offset'),
+ circle = 2 * Math.PI,
+ values = this.values,
+ total = this.total,
+ next = offset ? (2*Math.PI)*(offset/360) : 0,
+ start, end, i, vlen, color;
+
+ vlen = values.length;
+ for (i = 0; i < vlen; i++) {
+ start = next;
+ end = next;
+ if (total > 0) { // avoid divide by zero
+ end = next + (circle * (values[i] / total));
+ }
+ if (valuenum === i) {
+ color = options.get('sliceColors')[i % options.get('sliceColors').length];
+ if (highlight) {
+ color = this.calcHighlightColor(color, options);
+ }
+
+ return target.drawPieSlice(radius, radius, radius - borderWidth, start, end, undefined, color);
+ }
+ next = end;
+ }
+ },
+
+ render: function () {
+ var target = this.target,
+ values = this.values,
+ options = this.options,
+ radius = this.radius,
+ borderWidth = options.get('borderWidth'),
+ shape, i;
+
+ if (!pie._super.render.call(this)) {
+ return;
+ }
+ if (borderWidth) {
+ target.drawCircle(radius, radius, Math.floor(radius - (borderWidth / 2)),
+ options.get('borderColor'), undefined, borderWidth).append();
+ }
+ for (i = values.length; i--;) {
+ if (values[i]) { // don't render zero values
+ shape = this.renderSlice(i).append();
+ this.valueShapes[i] = shape.id; // store just the shapeid
+ this.shapes[shape.id] = i;
+ }
+ }
+ target.render();
+ }
+ });
+
+ /**
+ * Box plots
+ */
+ $.fn.sparkline.box = box = createClass($.fn.sparkline._base, {
+ type: 'box',
+
+ init: function (el, values, options, width, height) {
+ box._super.init.call(this, el, values, options, width, height);
+ this.values = $.map(values, Number);
+ this.width = options.get('width') === 'auto' ? '4.0em' : width;
+ this.initTarget();
+ if (!this.values.length) {
+ this.disabled = 1;
+ }
+ },
+
+ /**
+ * Simulate a single region
+ */
+ getRegion: function () {
+ return 1;
+ },
+
+ getCurrentRegionFields: function () {
+ var result = [
+ { field: 'lq', value: this.quartiles[0] },
+ { field: 'med', value: this.quartiles[1] },
+ { field: 'uq', value: this.quartiles[2] }
+ ];
+ if (this.loutlier !== undefined) {
+ result.push({ field: 'lo', value: this.loutlier});
+ }
+ if (this.routlier !== undefined) {
+ result.push({ field: 'ro', value: this.routlier});
+ }
+ if (this.lwhisker !== undefined) {
+ result.push({ field: 'lw', value: this.lwhisker});
+ }
+ if (this.rwhisker !== undefined) {
+ result.push({ field: 'rw', value: this.rwhisker});
+ }
+ return result;
+ },
+
+ render: function () {
+ var target = this.target,
+ values = this.values,
+ vlen = values.length,
+ options = this.options,
+ canvasWidth = this.canvasWidth,
+ canvasHeight = this.canvasHeight,
+ minValue = options.get('chartRangeMin') === undefined ? Math.min.apply(Math, values) : options.get('chartRangeMin'),
+ maxValue = options.get('chartRangeMax') === undefined ? Math.max.apply(Math, values) : options.get('chartRangeMax'),
+ canvasLeft = 0,
+ lwhisker, loutlier, iqr, q1, q2, q3, rwhisker, routlier, i,
+ size, unitSize;
+
+ if (!box._super.render.call(this)) {
+ return;
+ }
+
+ if (options.get('raw')) {
+ if (options.get('showOutliers') && values.length > 5) {
+ loutlier = values[0];
+ lwhisker = values[1];
+ q1 = values[2];
+ q2 = values[3];
+ q3 = values[4];
+ rwhisker = values[5];
+ routlier = values[6];
+ } else {
+ lwhisker = values[0];
+ q1 = values[1];
+ q2 = values[2];
+ q3 = values[3];
+ rwhisker = values[4];
+ }
+ } else {
+ values.sort(function (a, b) { return a - b; });
+ q1 = quartile(values, 1);
+ q2 = quartile(values, 2);
+ q3 = quartile(values, 3);
+ iqr = q3 - q1;
+ if (options.get('showOutliers')) {
+ lwhisker = rwhisker = undefined;
+ for (i = 0; i < vlen; i++) {
+ if (lwhisker === undefined && values[i] > q1 - (iqr * options.get('outlierIQR'))) {
+ lwhisker = values[i];
+ }
+ if (values[i] < q3 + (iqr * options.get('outlierIQR'))) {
+ rwhisker = values[i];
+ }
+ }
+ loutlier = values[0];
+ routlier = values[vlen - 1];
+ } else {
+ lwhisker = values[0];
+ rwhisker = values[vlen - 1];
+ }
+ }
+ this.quartiles = [q1, q2, q3];
+ this.lwhisker = lwhisker;
+ this.rwhisker = rwhisker;
+ this.loutlier = loutlier;
+ this.routlier = routlier;
+
+ unitSize = canvasWidth / (maxValue - minValue + 1);
+ if (options.get('showOutliers')) {
+ canvasLeft = Math.ceil(options.get('spotRadius'));
+ canvasWidth -= 2 * Math.ceil(options.get('spotRadius'));
+ unitSize = canvasWidth / (maxValue - minValue + 1);
+ if (loutlier < lwhisker) {
+ target.drawCircle((loutlier - minValue) * unitSize + canvasLeft,
+ canvasHeight / 2,
+ options.get('spotRadius'),
+ options.get('outlierLineColor'),
+ options.get('outlierFillColor')).append();
+ }
+ if (routlier > rwhisker) {
+ target.drawCircle((routlier - minValue) * unitSize + canvasLeft,
+ canvasHeight / 2,
+ options.get('spotRadius'),
+ options.get('outlierLineColor'),
+ options.get('outlierFillColor')).append();
+ }
+ }
+
+ // box
+ target.drawRect(
+ Math.round((q1 - minValue) * unitSize + canvasLeft),
+ Math.round(canvasHeight * 0.1),
+ Math.round((q3 - q1) * unitSize),
+ Math.round(canvasHeight * 0.8),
+ options.get('boxLineColor'),
+ options.get('boxFillColor')).append();
+ // left whisker
+ target.drawLine(
+ Math.round((lwhisker - minValue) * unitSize + canvasLeft),
+ Math.round(canvasHeight / 2),
+ Math.round((q1 - minValue) * unitSize + canvasLeft),
+ Math.round(canvasHeight / 2),
+ options.get('lineColor')).append();
+ target.drawLine(
+ Math.round((lwhisker - minValue) * unitSize + canvasLeft),
+ Math.round(canvasHeight / 4),
+ Math.round((lwhisker - minValue) * unitSize + canvasLeft),
+ Math.round(canvasHeight - canvasHeight / 4),
+ options.get('whiskerColor')).append();
+ // right whisker
+ target.drawLine(Math.round((rwhisker - minValue) * unitSize + canvasLeft),
+ Math.round(canvasHeight / 2),
+ Math.round((q3 - minValue) * unitSize + canvasLeft),
+ Math.round(canvasHeight / 2),
+ options.get('lineColor')).append();
+ target.drawLine(
+ Math.round((rwhisker - minValue) * unitSize + canvasLeft),
+ Math.round(canvasHeight / 4),
+ Math.round((rwhisker - minValue) * unitSize + canvasLeft),
+ Math.round(canvasHeight - canvasHeight / 4),
+ options.get('whiskerColor')).append();
+ // median line
+ target.drawLine(
+ Math.round((q2 - minValue) * unitSize + canvasLeft),
+ Math.round(canvasHeight * 0.1),
+ Math.round((q2 - minValue) * unitSize + canvasLeft),
+ Math.round(canvasHeight * 0.9),
+ options.get('medianColor')).append();
+ if (options.get('target')) {
+ size = Math.ceil(options.get('spotRadius'));
+ target.drawLine(
+ Math.round((options.get('target') - minValue) * unitSize + canvasLeft),
+ Math.round((canvasHeight / 2) - size),
+ Math.round((options.get('target') - minValue) * unitSize + canvasLeft),
+ Math.round((canvasHeight / 2) + size),
+ options.get('targetColor')).append();
+ target.drawLine(
+ Math.round((options.get('target') - minValue) * unitSize + canvasLeft - size),
+ Math.round(canvasHeight / 2),
+ Math.round((options.get('target') - minValue) * unitSize + canvasLeft + size),
+ Math.round(canvasHeight / 2),
+ options.get('targetColor')).append();
+ }
+ target.render();
+ }
+ });
+
+ // Setup a very simple "virtual canvas" to make drawing the few shapes we need easier
+ // This is accessible as $(foo).simpledraw()
+
+ // Detect browser renderer support
+ (function() {
+ if (document.namespaces && !document.namespaces.v) {
+ $.fn.sparkline.hasVML = true;
+ document.namespaces.add('v', 'urn:schemas-microsoft-com:vml', '#default#VML');
+ } else {
+ $.fn.sparkline.hasVML = false;
+ }
+
+ var el = document.createElement('canvas');
+ $.fn.sparkline.hasCanvas = !!(el.getContext && el.getContext('2d'));
+
+ })()
+
+ VShape = createClass({
+ init: function (target, id, type, args) {
+ this.target = target;
+ this.id = id;
+ this.type = type;
+ this.args = args;
+ },
+ append: function () {
+ this.target.appendShape(this);
+ return this;
+ }
+ });
+
+ VCanvas_base = createClass({
+ _pxregex: /(\d+)(px)?\s*$/i,
+
+ init: function (width, height, target) {
+ if (!width) {
+ return;
+ }
+ this.width = width;
+ this.height = height;
+ this.target = target;
+ this.lastShapeId = null;
+ if (target[0]) {
+ target = target[0];
+ }
+ $.data(target, '_jqs_vcanvas', this);
+ },
+
+ drawLine: function (x1, y1, x2, y2, lineColor, lineWidth) {
+ return this.drawShape([[x1, y1], [x2, y2]], lineColor, lineWidth);
+ },
+
+ drawShape: function (path, lineColor, fillColor, lineWidth) {
+ return this._genShape('Shape', [path, lineColor, fillColor, lineWidth]);
+ },
+
+ drawCircle: function (x, y, radius, lineColor, fillColor, lineWidth) {
+ return this._genShape('Circle', [x, y, radius, lineColor, fillColor, lineWidth]);
+ },
+
+ drawPieSlice: function (x, y, radius, startAngle, endAngle, lineColor, fillColor) {
+ return this._genShape('PieSlice', [x, y, radius, startAngle, endAngle, lineColor, fillColor]);
+ },
+
+ drawRect: function (x, y, width, height, lineColor, fillColor) {
+ return this._genShape('Rect', [x, y, width, height, lineColor, fillColor]);
+ },
+
+ getElement: function () {
+ return this.canvas;
+ },
+
+ /**
+ * Return the most recently inserted shape id
+ */
+ getLastShapeId: function () {
+ return this.lastShapeId;
+ },
+
+ /**
+ * Clear and reset the canvas
+ */
+ reset: function () {
+ alert('reset not implemented');
+ },
+
+ _insert: function (el, target) {
+ $(target).html(el);
+ },
+
+ /**
+ * Calculate the pixel dimensions of the canvas
+ */
+ _calculatePixelDims: function (width, height, canvas) {
+ // XXX This should probably be a configurable option
+ var match;
+ match = this._pxregex.exec(height);
+ if (match) {
+ this.pixelHeight = match[1];
+ } else {
+ this.pixelHeight = $(canvas).height();
+ }
+ match = this._pxregex.exec(width);
+ if (match) {
+ this.pixelWidth = match[1];
+ } else {
+ this.pixelWidth = $(canvas).width();
+ }
+ },
+
+ /**
+ * Generate a shape object and id for later rendering
+ */
+ _genShape: function (shapetype, shapeargs) {
+ var id = shapeCount++;
+ shapeargs.unshift(id);
+ return new VShape(this, id, shapetype, shapeargs);
+ },
+
+ /**
+ * Add a shape to the end of the render queue
+ */
+ appendShape: function (shape) {
+ alert('appendShape not implemented');
+ },
+
+ /**
+ * Replace one shape with another
+ */
+ replaceWithShape: function (shapeid, shape) {
+ alert('replaceWithShape not implemented');
+ },
+
+ /**
+ * Insert one shape after another in the render queue
+ */
+ insertAfterShape: function (shapeid, shape) {
+ alert('insertAfterShape not implemented');
+ },
+
+ /**
+ * Remove a shape from the queue
+ */
+ removeShapeId: function (shapeid) {
+ alert('removeShapeId not implemented');
+ },
+
+ /**
+ * Find a shape at the specified x/y co-ordinates
+ */
+ getShapeAt: function (el, x, y) {
+ alert('getShapeAt not implemented');
+ },
+
+ /**
+ * Render all queued shapes onto the canvas
+ */
+ render: function () {
+ alert('render not implemented');
+ }
+ });
+
+ VCanvas_canvas = createClass(VCanvas_base, {
+ init: function (width, height, target, interact) {
+ VCanvas_canvas._super.init.call(this, width, height, target);
+ this.canvas = document.createElement('canvas');
+ if (target[0]) {
+ target = target[0];
+ }
+ $.data(target, '_jqs_vcanvas', this);
+ $(this.canvas).css({ display: 'inline-block', width: width, height: height, verticalAlign: 'top' });
+ this._insert(this.canvas, target);
+ this._calculatePixelDims(width, height, this.canvas);
+ this.canvas.width = this.pixelWidth;
+ this.canvas.height = this.pixelHeight;
+ this.interact = interact;
+ this.shapes = {};
+ this.shapeseq = [];
+ this.currentTargetShapeId = undefined;
+ $(this.canvas).css({width: this.pixelWidth, height: this.pixelHeight});
+ },
+
+ _getContext: function (lineColor, fillColor, lineWidth) {
+ var context = this.canvas.getContext('2d');
+ if (lineColor !== undefined) {
+ context.strokeStyle = lineColor;
+ }
+ context.lineWidth = lineWidth === undefined ? 1 : lineWidth;
+ if (fillColor !== undefined) {
+ context.fillStyle = fillColor;
+ }
+ return context;
+ },
+
+ reset: function () {
+ var context = this._getContext();
+ context.clearRect(0, 0, this.pixelWidth, this.pixelHeight);
+ this.shapes = {};
+ this.shapeseq = [];
+ this.currentTargetShapeId = undefined;
+ },
+
+ _drawShape: function (shapeid, path, lineColor, fillColor, lineWidth) {
+ var context = this._getContext(lineColor, fillColor, lineWidth),
+ i, plen;
+ context.beginPath();
+ context.moveTo(path[0][0] + 0.5, path[0][1] + 0.5);
+ for (i = 1, plen = path.length; i < plen; i++) {
+ context.lineTo(path[i][0] + 0.5, path[i][1] + 0.5); // the 0.5 offset gives us crisp pixel-width lines
+ }
+ if (lineColor !== undefined) {
+ context.stroke();
+ }
+ if (fillColor !== undefined) {
+ context.fill();
+ }
+ if (this.targetX !== undefined && this.targetY !== undefined &&
+ context.isPointInPath(this.targetX, this.targetY)) {
+ this.currentTargetShapeId = shapeid;
+ }
+ },
+
+ _drawCircle: function (shapeid, x, y, radius, lineColor, fillColor, lineWidth) {
+ var context = this._getContext(lineColor, fillColor, lineWidth);
+ context.beginPath();
+ context.arc(x, y, radius, 0, 2 * Math.PI, false);
+ if (this.targetX !== undefined && this.targetY !== undefined &&
+ context.isPointInPath(this.targetX, this.targetY)) {
+ this.currentTargetShapeId = shapeid;
+ }
+ if (lineColor !== undefined) {
+ context.stroke();
+ }
+ if (fillColor !== undefined) {
+ context.fill();
+ }
+ },
+
+ _drawPieSlice: function (shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) {
+ var context = this._getContext(lineColor, fillColor);
+ context.beginPath();
+ context.moveTo(x, y);
+ context.arc(x, y, radius, startAngle, endAngle, false);
+ context.lineTo(x, y);
+ context.closePath();
+ if (lineColor !== undefined) {
+ context.stroke();
+ }
+ if (fillColor) {
+ context.fill();
+ }
+ if (this.targetX !== undefined && this.targetY !== undefined &&
+ context.isPointInPath(this.targetX, this.targetY)) {
+ this.currentTargetShapeId = shapeid;
+ }
+ },
+
+ _drawRect: function (shapeid, x, y, width, height, lineColor, fillColor) {
+ return this._drawShape(shapeid, [[x, y], [x + width, y], [x + width, y + height], [x, y + height], [x, y]], lineColor, fillColor);
+ },
+
+ appendShape: function (shape) {
+ this.shapes[shape.id] = shape;
+ this.shapeseq.push(shape.id);
+ this.lastShapeId = shape.id;
+ return shape.id;
+ },
+
+ replaceWithShape: function (shapeid, shape) {
+ var shapeseq = this.shapeseq,
+ i;
+ this.shapes[shape.id] = shape;
+ for (i = shapeseq.length; i--;) {
+ if (shapeseq[i] == shapeid) {
+ shapeseq[i] = shape.id;
+ }
+ }
+ delete this.shapes[shapeid];
+ },
+
+ replaceWithShapes: function (shapeids, shapes) {
+ var shapeseq = this.shapeseq,
+ shapemap = {},
+ sid, i, first;
+
+ for (i = shapeids.length; i--;) {
+ shapemap[shapeids[i]] = true;
+ }
+ for (i = shapeseq.length; i--;) {
+ sid = shapeseq[i];
+ if (shapemap[sid]) {
+ shapeseq.splice(i, 1);
+ delete this.shapes[sid];
+ first = i;
+ }
+ }
+ for (i = shapes.length; i--;) {
+ shapeseq.splice(first, 0, shapes[i].id);
+ this.shapes[shapes[i].id] = shapes[i];
+ }
+
+ },
+
+ insertAfterShape: function (shapeid, shape) {
+ var shapeseq = this.shapeseq,
+ i;
+ for (i = shapeseq.length; i--;) {
+ if (shapeseq[i] === shapeid) {
+ shapeseq.splice(i + 1, 0, shape.id);
+ this.shapes[shape.id] = shape;
+ return;
+ }
+ }
+ },
+
+ removeShapeId: function (shapeid) {
+ var shapeseq = this.shapeseq,
+ i;
+ for (i = shapeseq.length; i--;) {
+ if (shapeseq[i] === shapeid) {
+ shapeseq.splice(i, 1);
+ break;
+ }
+ }
+ delete this.shapes[shapeid];
+ },
+
+ getShapeAt: function (el, x, y) {
+ this.targetX = x;
+ this.targetY = y;
+ this.render();
+ return this.currentTargetShapeId;
+ },
+
+ render: function () {
+ var shapeseq = this.shapeseq,
+ shapes = this.shapes,
+ shapeCount = shapeseq.length,
+ context = this._getContext(),
+ shapeid, shape, i;
+ context.clearRect(0, 0, this.pixelWidth, this.pixelHeight);
+ for (i = 0; i < shapeCount; i++) {
+ shapeid = shapeseq[i];
+ shape = shapes[shapeid];
+ this['_draw' + shape.type].apply(this, shape.args);
+ }
+ if (!this.interact) {
+ // not interactive so no need to keep the shapes array
+ this.shapes = {};
+ this.shapeseq = [];
+ }
+ }
+
+ });
+
+ VCanvas_vml = createClass(VCanvas_base, {
+ init: function (width, height, target) {
+ var groupel;
+ VCanvas_vml._super.init.call(this, width, height, target);
+ if (target[0]) {
+ target = target[0];
+ }
+ $.data(target, '_jqs_vcanvas', this);
+ this.canvas = document.createElement('span');
+ $(this.canvas).css({ display: 'inline-block', position: 'relative', overflow: 'hidden', width: width, height: height, margin: '0px', padding: '0px', verticalAlign: 'top'});
+ this._insert(this.canvas, target);
+ this._calculatePixelDims(width, height, this.canvas);
+ this.canvas.width = this.pixelWidth;
+ this.canvas.height = this.pixelHeight;
+ groupel = '
';
+ this.canvas.insertAdjacentHTML('beforeEnd', groupel);
+ this.group = $(this.canvas).children()[0];
+ this.rendered = false;
+ this.prerender = '';
+ },
+
+ _drawShape: function (shapeid, path, lineColor, fillColor, lineWidth) {
+ var vpath = [],
+ initial, stroke, fill, closed, vel, plen, i;
+ for (i = 0, plen = path.length; i < plen; i++) {
+ vpath[i] = '' + (path[i][0]) + ',' + (path[i][1]);
+ }
+ initial = vpath.splice(0, 1);
+ lineWidth = lineWidth === undefined ? 1 : lineWidth;
+ stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="' + lineWidth + 'px" strokeColor="' + lineColor + '" ';
+ fill = fillColor === undefined ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" ';
+ closed = vpath[0] === vpath[vpath.length - 1] ? 'x ' : '';
+ vel = '
' +
+ ' ';
+ return vel;
+ },
+
+ _drawCircle: function (shapeid, x, y, radius, lineColor, fillColor, lineWidth) {
+ var stroke, fill, vel;
+ x -= radius;
+ y -= radius;
+ stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="' + lineWidth + 'px" strokeColor="' + lineColor + '" ';
+ fill = fillColor === undefined ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" ';
+ vel = '
';
+ return vel;
+
+ },
+
+ _drawPieSlice: function (shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) {
+ var vpath, startx, starty, endx, endy, stroke, fill, vel;
+ if (startAngle === endAngle) {
+ return ''; // VML seems to have problem when start angle equals end angle.
+ }
+ if ((endAngle - startAngle) === (2 * Math.PI)) {
+ startAngle = 0.0; // VML seems to have a problem when drawing a full circle that doesn't start 0
+ endAngle = (2 * Math.PI);
+ }
+
+ startx = x + Math.round(Math.cos(startAngle) * radius);
+ starty = y + Math.round(Math.sin(startAngle) * radius);
+ endx = x + Math.round(Math.cos(endAngle) * radius);
+ endy = y + Math.round(Math.sin(endAngle) * radius);
+
+ if (startx === endx && starty === endy) {
+ if ((endAngle - startAngle) < Math.PI) {
+ // Prevent very small slices from being mistaken as a whole pie
+ return '';
+ }
+ // essentially going to be the entire circle, so ignore startAngle
+ startx = endx = x + radius;
+ starty = endy = y;
+ }
+
+ if (startx === endx && starty === endy && (endAngle - startAngle) < Math.PI) {
+ return '';
+ }
+
+ vpath = [x - radius, y - radius, x + radius, y + radius, startx, starty, endx, endy];
+ stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="1px" strokeColor="' + lineColor + '" ';
+ fill = fillColor === undefined ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" ';
+ vel = '
' +
+ ' ';
+ return vel;
+ },
+
+ _drawRect: function (shapeid, x, y, width, height, lineColor, fillColor) {
+ return this._drawShape(shapeid, [[x, y], [x, y + height], [x + width, y + height], [x + width, y], [x, y]], lineColor, fillColor);
+ },
+
+ reset: function () {
+ this.group.innerHTML = '';
+ },
+
+ appendShape: function (shape) {
+ var vel = this['_draw' + shape.type].apply(this, shape.args);
+ if (this.rendered) {
+ this.group.insertAdjacentHTML('beforeEnd', vel);
+ } else {
+ this.prerender += vel;
+ }
+ this.lastShapeId = shape.id;
+ return shape.id;
+ },
+
+ replaceWithShape: function (shapeid, shape) {
+ var existing = $('#jqsshape' + shapeid),
+ vel = this['_draw' + shape.type].apply(this, shape.args);
+ existing[0].outerHTML = vel;
+ },
+
+ replaceWithShapes: function (shapeids, shapes) {
+ // replace the first shapeid with all the new shapes then toast the remaining old shapes
+ var existing = $('#jqsshape' + shapeids[0]),
+ replace = '',
+ slen = shapes.length,
+ i;
+ for (i = 0; i < slen; i++) {
+ replace += this['_draw' + shapes[i].type].apply(this, shapes[i].args);
+ }
+ existing[0].outerHTML = replace;
+ for (i = 1; i < shapeids.length; i++) {
+ $('#jqsshape' + shapeids[i]).remove();
+ }
+ },
+
+ insertAfterShape: function (shapeid, shape) {
+ var existing = $('#jqsshape' + shapeid),
+ vel = this['_draw' + shape.type].apply(this, shape.args);
+ existing[0].insertAdjacentHTML('afterEnd', vel);
+ },
+
+ removeShapeId: function (shapeid) {
+ var existing = $('#jqsshape' + shapeid);
+ this.group.removeChild(existing[0]);
+ },
+
+ getShapeAt: function (el, x, y) {
+ var shapeid = el.id.substr(8);
+ return shapeid;
+ },
+
+ render: function () {
+ if (!this.rendered) {
+ // batch the intial render into a single repaint
+ this.group.innerHTML = this.prerender;
+ this.rendered = true;
+ }
+ }
+ });
+
+}));
diff --git a/addons/crm/static/lib/sparkline/jquery.sparkline.min.js b/addons/crm/static/lib/sparkline/jquery.sparkline.min.js
new file mode 100644
index 00000000000..74187063f45
--- /dev/null
+++ b/addons/crm/static/lib/sparkline/jquery.sparkline.min.js
@@ -0,0 +1,5 @@
+/* jquery.sparkline 2.1.1 - http://omnipotent.net/jquery.sparkline/
+** Licensed under the New BSD License - see above site for details */
+
+(function(a){typeof define=="function"&&define.amd?define(["jquery"],a):a(jQuery)})(function(a){"use strict";var b={},c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I=0;c=function(){return{common:{type:"line",lineColor:"#00f",fillColor:"#cdf",defaultPixelsPerValue:3,width:"auto",height:"auto",composite:!1,tagValuesAttribute:"values",tagOptionsPrefix:"spark",enableTagOptions:!1,enableHighlight:!0,highlightLighten:1.4,tooltipSkipNull:!0,tooltipPrefix:"",tooltipSuffix:"",disableHiddenCheck:!1,numberFormatter:!1,numberDigitGroupCount:3,numberDigitGroupSep:",",numberDecimalMark:".",disableTooltips:!1,disableInteraction:!1},line:{spotColor:"#f80",highlightSpotColor:"#5f5",highlightLineColor:"#f22",spotRadius:1.5,minSpotColor:"#f80",maxSpotColor:"#f80",lineWidth:1,normalRangeMin:undefined,normalRangeMax:undefined,normalRangeColor:"#ccc",drawNormalOnTop:!1,chartRangeMin:undefined,chartRangeMax:undefined,chartRangeMinX:undefined,chartRangeMaxX:undefined,tooltipFormat:new e('
● {{prefix}}{{y}}{{suffix}}')},bar:{barColor:"#3366cc",negBarColor:"#f44",stackedBarColor:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],zeroColor:undefined,nullColor:undefined,zeroAxis:!0,barWidth:4,barSpacing:1,chartRangeMax:undefined,chartRangeMin:undefined,chartRangeClip:!1,colorMap:undefined,tooltipFormat:new e('
● {{prefix}}{{value}}{{suffix}}')},tristate:{barWidth:4,barSpacing:1,posBarColor:"#6f6",negBarColor:"#f44",zeroBarColor:"#999",colorMap:{},tooltipFormat:new e('
● {{value:map}}'),tooltipValueLookups:{map:{"-1":"Loss",0:"Draw",1:"Win"}}},discrete:{lineHeight:"auto",thresholdColor:undefined,thresholdValue:0,chartRangeMax:undefined,chartRangeMin:undefined,chartRangeClip:!1,tooltipFormat:new e("{{prefix}}{{value}}{{suffix}}")},bullet:{targetColor:"#f33",targetWidth:3,performanceColor:"#33f",rangeColors:["#d3dafe","#a8b6ff","#7f94ff"],base:undefined,tooltipFormat:new e("{{fieldkey:fields}} - {{value}}"),tooltipValueLookups:{fields:{r:"Range",p:"Performance",t:"Target"}}},pie:{offset:0,sliceColors:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],borderWidth:0,borderColor:"#000",tooltipFormat:new e('
● {{value}} ({{percent.1}}%)')},box:{raw:!1,boxLineColor:"#000",boxFillColor:"#cdf",whiskerColor:"#000",outlierLineColor:"#333",outlierFillColor:"#fff",medianColor:"#f00",showOutliers:!0,outlierIQR:1.5,spotRadius:1.5,target:undefined,targetColor:"#4a2",chartRangeMax:undefined,chartRangeMin:undefined,tooltipFormat:new e("{{field:fields}}: {{value}}"),tooltipFormatFieldlistKey:"field",tooltipValueLookups:{fields:{lq:"Lower Quartile",med:"Median",uq:"Upper Quartile",lo:"Left Outlier",ro:"Right Outlier",lw:"Left Whisker",rw:"Right Whisker"}}}}},B='.jqstooltip { position: absolute;left: 0px;top: 0px;visibility: hidden;background: rgb(0, 0, 0) transparent;background-color: rgba(0,0,0,0.6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";color: white;font: 10px arial, san serif;text-align: left;white-space: nowrap;padding: 5px;border: 1px solid white;z-index: 10000;}.jqsfield { color: white;font: 10px arial, san serif;text-align: left;}',d=function(){var b,c;return b=function(){this.init.apply(this,arguments)},arguments.length>1?(arguments[0]?(b.prototype=a.extend(new arguments[0],arguments[arguments.length-1]),b._super=arguments[0].prototype):b.prototype=arguments[arguments.length-1],arguments.length>2&&(c=Array.prototype.slice.call(arguments,1,-1),c.unshift(b.prototype),a.extend.apply(a,c))):b.prototype=arguments[0],b.prototype.cls=b,b},a.SPFormatClass=e=d({fre:/\{\{([\w.]+?)(:(.+?))?\}\}/g,precre:/(\w+)\.(\d+)/,init:function(a,b){this.format=a,this.fclass=b},render:function(a,b,c){var d=this,e=a,f,g,h,i,j;return this.format.replace(this.fre,function(){var a;return g=arguments[1],h=arguments[3],f=d.precre.exec(g),f?(j=f[2],g=f[1]):j=!1,i=e[g],i===undefined?"":h&&b&&b[h]?(a=b[h],a.get?b[h].get(i)||i:b[h][i]||i):(k(i)&&(c.get("numberFormatter")?i=c.get("numberFormatter")(i):i=p(i,j,c.get("numberDigitGroupCount"),c.get("numberDigitGroupSep"),c.get("numberDecimalMark"))),i)})}}),a.spformat=function(a,b){return new e(a,b)},f=function(a,b,c){return a
c?c:a},g=function(a,b){var c;return b===2?(c=Math.floor(a.length/2),a.length%2?a[c]:(a[c-1]+a[c])/2):a.length%2?(c=(a.length*b+b)/4,c%1?(a[Math.floor(c)]+a[Math.floor(c)-1])/2:a[c-1]):(c=(a.length*b+2)/4,c%1?(a[Math.floor(c)]+a[Math.floor(c)-1])/2:a[c-1])},h=function(a){var b;switch(a){case"undefined":a=undefined;break;case"null":a=null;break;case"true":a=!0;break;case"false":a=!1;break;default:b=parseFloat(a),a==b&&(a=b)}return a},i=function(a){var b,c=[];for(b=a.length;b--;)c[b]=h(a[b]);return c},j=function(a,b){var c,d,e=[];for(c=0,d=a.length;c0;h-=d)b.splice(h,0,e);return b.join("")},l=function(a,b,c){var d;for(d=b.length;d--;){if(c&&b[d]===null)continue;if(b[d]!==a)return!1}return!0},m=function(a){var b=0,c;for(c=a.length;c--;)b+=typeof a[c]=="number"?a[c]:0;return b},o=function(b){return a.isArray(b)?b:[b]},n=function(a){var b;document.createStyleSheet?document.createStyleSheet().cssText=a:(b=document.createElement("style"),b.type="text/css",document.getElementsByTagName("head")[0].appendChild(b),b[typeof document.body.style.WebkitAppearance=="string"?"innerText":"innerHTML"]=a)},a.fn.simpledraw=function(b,c,d,e){var f,g;if(d&&(f=this.data("_jqs_vcanvas")))return f;b===undefined&&(b=a(this).innerWidth()),c===undefined&&(c=a(this).innerHeight());if(a.fn.sparkline.hasCanvas)f=new F(b,c,this,e);else{if(!a.fn.sparkline.hasVML)return!1;f=new G(b,c,this)}return g=a(this).data("_jqs_mhandler"),g&&g.registerCanvas(f),f},a.fn.cleardraw=function(){var a=this.data("_jqs_vcanvas");a&&a.reset()},a.RangeMapClass=q=d({init:function(a){var b,c,d=[];for(b in a)a.hasOwnProperty(b)&&typeof b=="string"&&b.indexOf(":")>-1&&(c=b.split(":"),c[0]=c[0].length===0?-Infinity:parseFloat(c[0]),c[1]=c[1].length===0?Infinity:parseFloat(c[1]),c[2]=a[b],d.push(c));this.map=a,this.rangelist=d||!1},get:function(a){var b=this.rangelist,c,d,e;if((e=this.map[a])!==undefined)return e;if(b)for(c=b.length;c--;){d=b[c];if(d[0]<=a&&d[1]>=a)return d[2]}return undefined}}),a.range_map=function(a){return new q(a)},r=d({init:function(b,c){var d=a(b);this.$el=d,this.options=c,this.currentPageX=0,this.currentPageY=0,this.el=b,this.splist=[],this.tooltip=null,this.over=!1,this.displayTooltips=!c.get("disableTooltips"),this.highlightEnabled=!c.get("disableHighlight")},registerSparkline:function(a){this.splist.push(a),this.over&&this.updateDisplay()},registerCanvas:function(b){var c=a(b.canvas);this.canvas=b,this.$canvas=c,c.mouseenter(a.proxy(this.mouseenter,this)),c.mouseleave(a.proxy(this.mouseleave,this)),c.click(a.proxy(this.mouseclick,this))},reset:function(a){this.splist=[],this.tooltip&&a&&(this.tooltip.remove(),this.tooltip=undefined)},mouseclick:function(b){var c=a.Event("sparklineClick");c.originalEvent=b,c.sparklines=this.splist,this.$el.trigger(c)},mouseenter:function(b){a(document.body).unbind("mousemove.jqs"),a(document.body).bind("mousemove.jqs",a.proxy(this.mousemove,this)),this.over=!0,this.currentPageX=b.pageX,this.currentPageY=b.pageY,this.currentEl=b.target,!this.tooltip&&this.displayTooltips&&(this.tooltip=new s(this.options),this.tooltip.updatePosition(b.pageX,b.pageY)),this.updateDisplay()},mouseleave:function(){a(document.body).unbind("mousemove.jqs");var b=this.splist,c=b.length,d=!1,e,f;this.over=!1,this.currentEl=null,this.tooltip&&(this.tooltip.remove(),this.tooltip=null);for(f=0;f",{id:"jqssizetip",style:d,"class":c}),this.tooltip=a("
",{id:"jqstooltip","class":c}).appendTo(this.container),e=this.tooltip.offset(),this.offsetLeft=e.left,this.offsetTop=e.top,this.hidden=!0,a(window).unbind("resize.jqs scroll.jqs"),a(window).bind("resize.jqs scroll.jqs",a.proxy(this.updateWindowDims,this)),this.updateWindowDims()},updateWindowDims:function(){this.scrollTop=a(window).scrollTop(),this.scrollLeft=a(window).scrollLeft(),this.scrollRight=this.scrollLeft+a(window).width(),this.updatePosition()},getSize:function(a){this.sizetip.html(a).appendTo(this.container),this.width=this.sizetip.width()+1,this.height=this.sizetip.height(),this.sizetip.remove()},setContent:function(a){if(!a){this.tooltip.css("visibility","hidden"),this.hidden=!0;return}this.getSize(a),this.tooltip.html(a).css({width:this.width,height:this.height,visibility:"visible"}),this.hidden&&(this.hidden=!1,this.updatePosition())},updatePosition:function(a,b){if(a===undefined){if(this.mousex===undefined)return;a=this.mousex-this.offsetLeft,b=this.mousey-this.offsetTop}else this.mousex=a-=this.offsetLeft,this.mousey=b-=this.offsetTop;if(!this.height||!this.width||this.hidden)return;b-=this.height+this.tooltipOffsetY,a+=this.tooltipOffsetX,bthis.scrollRight&&(a=this.scrollRight-this.width),this.tooltip.css({left:a,top:b})},remove:function(){this.tooltip.remove(),this.sizetip.remove(),this.sizetip=this.tooltip=undefined,a(window).unbind("resize.jqs scroll.jqs")}}),C=function(){n(B)},a(C),H=[],a.fn.sparkline=function(b,c){return this.each(function(){var d=new a.fn.sparkline.options(this,c),e=a(this),f,g;f=function(){var c,f,g,h,i,j,k;if(b==="html"||b===undefined){k=this.getAttribute(d.get("tagValuesAttribute"));if(k===undefined||k===null)k=e.html();c=k.replace(/(^\s*\s*$)|\s+/g,"").split(",")}else c=b;f=d.get("width")==="auto"?c.length*d.get("defaultPixelsPerValue"):d.get("width");if(d.get("height")==="auto"){if(!d.get("composite")||!a.data(this,"_jqs_vcanvas"))h=document.createElement("span"),h.innerHTML="a",e.html(h),g=a(h).innerHeight()||a(h).height(),a(h).remove(),h=null}else g=d.get("height");d.get("disableInteraction")?i=!1:(i=a.data(this,"_jqs_mhandler"),i?d.get("composite")||i.reset():(i=new r(this,d),a.data(this,"_jqs_mhandler",i)));if(d.get("composite")&&!a.data(this,"_jqs_vcanvas")){a.data(this,"_jqs_errnotify")||(alert("Attempted to attach a composite sparkline to an element with no existing sparkline"),a.data(this,"_jqs_errnotify",!0));return}j=new(a.fn.sparkline[d.get("type")])(this,c,d,f,g),j.render(),i&&i.registerSparkline(j)};if(a(this).html()&&!d.get("disableHiddenCheck")&&a(this).is(":hidden")||a.fn.jquery<"1.3.0"&&a(this).parents().is(":hidden")||!a(this).parents("body").length){if(!d.get("composite")&&a.data(this,"_jqs_pending"))for(g=H.length;g;g--)H[g-1][0]==this&&H.splice(g-1,1);H.push([this,f]),a.data(this,"_jqs_pending",!0)}else f.call(this)})},a.fn.sparkline.defaults=c(),a.sparkline_display_visible=function(){var b,c,d,e=[];for(c=0,d=H.length;cthis.canvasWidth||c>this.canvasHeight||b<0||c<0?null:(f=this.getRegion(a,b,c),d!==f?(d!==undefined&&e&&this.removeHighlight(),this.currentRegion=f,f!==undefined&&e&&this.renderHighlight(),!0):!1)},clearRegionHighlight:function(){return this.currentRegion!==undefined?(this.removeHighlight(),this.currentRegion=undefined,!0):!1},renderHighlight:function(){this.changeHighlight(!0)},removeHighlight:function(){this.changeHighlight(!1)},changeHighlight:function(a){},getCurrentRegionTooltip:function(){var b=this.options,c="",d=[],f,g,h,i,j,k,l,m,n,o,p,q,r,s;if(this.currentRegion===undefined)return"";f=this.getCurrentRegionFields(),p=b.get("tooltipFormatter");if(p)return p(this,b,f);b.get("tooltipChartTitle")&&(c+=''+b.get("tooltipChartTitle")+"
\n"),g=this.options.get("tooltipFormat");if(!g)return"";a.isArray(g)||(g=[g]),a.isArray(f)||(f=[f]),l=this.options.get("tooltipFormatFieldlist"),m=this.options.get("tooltipFormatFieldlistKey");if(l&&m){n=[];for(k=f.length;k--;)o=f[k][m],(s=a.inArray(o,l))!=-1&&(n[s]=f[k]);f=n}h=g.length,r=f.length;for(k=0;k'+j+" ")}return d.length?c+d.join("\n"):""},getCurrentRegionFields:function(){},calcHighlightColor:function(a,b){var c=b.get("highlightColor"),d=b.get("highlightLighten"),e,g,h,i;if(c)return c;if(d){e=/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(a)||/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(a);if(e){h=[],g=a.length===4?16:1;for(i=0;i<3;i++)h[i]=f(Math.round(parseInt(e[i+1],16)*g*d),0,255);return"rgb("+h.join(",")+")"}}return a}}),t={changeHighlight:function(b){var c=this.currentRegion,d=this.target,e=this.regionShapes[c],f;e&&(f=this.renderRegion(c,b),a.isArray(f)||a.isArray(e)?(d.replaceWithShapes(e,f),this.regionShapes[c]=a.map(f,function(a){return a.id})):(d.replaceWithShape(e,f),this.regionShapes[c]=f.id))},render:function(){var b=this.values,c=this.target,d=this.regionShapes,e,f,g,h;if(!this.cls._super.render.call(this))return;for(g=b.length;g--;){e=this.renderRegion(g);if(e)if(a.isArray(e)){f=[];for(h=e.length;h--;)e[h].append(),f.push(e[h].id);d[g]=f}else e.append(),d[g]=e.id;else d[g]=null}c.render()}},a.fn.sparkline.line=u=d(a.fn.sparkline._base,{type:"line",init:function(a,b,c,d,e){u._super.init.call(this,a,b,c,d,e),this.vertices=[],this.regionMap=[],this.xvalues=[],this.yvalues=[],this.yminmax=[],this.hightlightSpotId=null,this.lastShapeId=null,this.initTarget()},getRegion:function(a,b,c){var d,e=this.regionMap;for(d=e.length;d--;)if(e[d]!==null&&b>=e[d][0]&&b<=e[d][1])return e[d][2];return undefined},getCurrentRegionFields:function(){var a=this.currentRegion;return{isNull:this.yvalues[a]===null,x:this.xvalues[a],y:this.yvalues[a],color:this.options.get("lineColor"),fillColor:this.options.get("fillColor"),offset:a}},renderHighlight:function(){var a=this.currentRegion,b=this.target,c=this.vertices[a],d=this.options,e=d.get("spotRadius"),f=d.get("highlightSpotColor"),g=d.get("highlightLineColor"),h,i;if(!c)return;e&&f&&(h=b.drawCircle(c[0],c[1],e,undefined,f),this.highlightSpotId=h.id,b.insertAfterShape(this.lastShapeId,h)),g&&(i=b.drawLine(c[0],this.canvasTop,c[0],this.canvasTop+this.canvasHeight,g),this.highlightLineId=i.id,b.insertAfterShape(this.lastShapeId,i))},removeHighlight:function(){var a=this.target;this.highlightSpotId&&(a.removeShapeId(this.highlightSpotId),this.highlightSpotId=null),this.highlightLineId&&(a.removeShapeId(this.highlightLineId),this.highlightLineId=null)},scanValues:function(){var a=this.values,b=a.length,c=this.xvalues,d=this.yvalues,e=this.yminmax,f,g,h,i,j;for(f=0;f
this.maxy&&(this.maxy=c)),a.get("chartRangeMin")!==undefined&&(a.get("chartRangeClip")||a.get("chartRangeMin")this.maxy)&&(this.maxy=a.get("chartRangeMax")),a.get("chartRangeMinX")!==undefined&&(a.get("chartRangeClipX")||a.get("chartRangeMinX")this.maxx)&&(this.maxx=a.get("chartRangeMaxX"))},drawNormalRange:function(a,b,c,d,e){var f=this.options.get("normalRangeMin"),g=this.options.get("normalRangeMax"),h=b+Math.round(c-c*((g-this.miny)/e)),i=Math.round(c*(g-f)/e);this.target.drawRect(a,h,d,i,undefined,this.options.get("normalRangeColor")).append()},render:function(){var b=this.options,c=this.target,d=this.canvasWidth,e=this.canvasHeight,f=this.vertices,g=b.get("spotRadius"),h=this.regionMap,i,j,k,l,m,n,o,p,r,s,t,v,w,x,y,z,A,B,C,D,E,F,G,H,I;if(!u._super.render.call(this))return;this.scanValues(),this.processRangeOptions(),G=this.xvalues,H=this.yvalues;if(!this.yminmax.length||this.yvalues.length<2)return;l=m=0,i=this.maxx-this.minx===0?1:this.maxx-this.minx,j=this.maxy-this.miny===0?1:this.maxy-this.miny,k=this.yvalues.length-1,g&&(dthis.maxy&&(s=this.maxy),o.length||o.push([v,l+e]),n=[v,l+Math.round(e-e*((s-this.miny)/j))],o.push(n),f.push(n));A=[],B=[],C=p.length;for(I=0;I2&&(o[0]=[o[0][0],o[1][1]]),A.push(o));C=B.length;for(I=0;I-1;if(s||a.isArray(M))H=!0,s&&(M=c[y]=i(M.split(":"))),M=j(M,null),t=Math.min.apply(Math,M),u=Math.max.apply(Math,M),tr&&(r=u)}this.stacked=H,this.regionShapes={},this.barWidth=k,this.barSpacing=l,this.totalBarWidth=k+l,this.width=e=c.length*k+(c.length-1)*l,this.initTarget(),o&&(F=m===undefined?-Infinity:m,G=n===undefined?Infinity:n),x=[],w=H?[]:x;var Q=[],R=[];for(y=0,z=c.length;y0&&(Q[y]+=M),p<0&&r>0?M<0?R[y]+=Math.abs(M):w[y]+=M:w[y]+=Math.abs(M-(M<0?r:p)),x.push(M))}else M=o?f(c[y],F,G):c[y],M=c[y]=h(M),M!==null&&x.push(M);this.max=E=Math.max.apply(Math,x),this.min=D=Math.min.apply(Math,x),this.stackMax=r=H?Math.max.apply(Math,Q):E,this.stackMin=p=H?Math.min.apply(Math,x):D,d.get("chartRangeMin")!==undefined&&(d.get("chartRangeClip")||d.get("chartRangeMin")E)&&(E=d.get("chartRangeMax")),this.zeroAxis=B=d.get("zeroAxis",!0),D<=0&&E>=0&&B?C=0:B==0?C=D:D>0?C=D:C=E,this.xaxisOffset=C,A=H?Math.max.apply(Math,w)+Math.max.apply(Math,R):E-D,this.canvasHeightEf=B&&D<0?this.canvasHeight-2:this.canvasHeight-1,D=0?r:E,N=(O-C)/A*this.canvasHeight,N!==Math.ceil(N)&&(this.canvasHeightEf-=2,N=Math.ceil(N))):N=this.canvasHeight,this.yoffset=N,a.isArray(d.get("colorMap"))?(this.colorMapByIndex=d.get("colorMap"),this.colorMapByValue=null):(this.colorMapByIndex=null,this.colorMapByValue=d.get("colorMap"),this.colorMapByValue&&this.colorMapByValue.get===undefined&&(this.colorMapByValue=new q(this.colorMapByValue))),this.range=A},getRegion:function(a,b,c){var d=Math.floor(b/this.totalBarWidth);return d<0||d>=this.values.length?undefined:d},getCurrentRegionFields:function(){var a=this.currentRegion,b=o(this.values[a]),c=[],d,e;for(e=b.length;e--;)d=b[e],c.push({isNull:d===null,value:d,color:this.calcColor(e,d,a),offset:a});return c},calcColor:function(b,c,d){var e=this.colorMapByIndex,f=this.colorMapByValue,g=this.options,h,i;return this.stacked?h=g.get("stackedBarColor"):h=c<0?g.get("negBarColor"):g.get("barColor"),c===0&&g.get("zeroColor")!==undefined&&(h=g.get("zeroColor")),f&&(i=f.get(c))?h=i:e&&e.length>d&&(h=e[d]),a.isArray(h)?h[b%h.length]:h},renderRegion:function(b,c){var d=this.values[b],e=this.options,f=this.xaxisOffset,g=[],h=this.range,i=this.stacked,j=this.target,k=b*this.totalBarWidth,m=this.canvasHeightEf,n=this.yoffset,o,p,q,r,s,t,u,v,w,x;d=a.isArray(d)?d:[d],u=d.length,v=d[0],r=l(null,d),x=l(f,d,!0);if(r)return e.get("nullColor")?(q=c?e.get("nullColor"):this.calcHighlightColor(e.get("nullColor"),e),o=n>0?n-1:n,j.drawRect(k,o,this.barWidth-1,0,q,q)):undefined;s=n;for(t=0;t0?p=Math.floor(m*(Math.abs(v-f)/h))+1:p=1,vb?g=e[b]:c[b]<0?g=d.get("negBarColor"):c[b]>0?g=d.get("posBarColor"):g=d.get("zeroBarColor"),g},renderRegion:function(a,b){var c=this.values,d=this.options,e=this.target,f,g,h,i,j,k;f=e.pixelHeight,h=Math.round(f/2),i=a*this.totalBarWidth,c[a]<0?(j=h,g=h-1):c[a]>0?(j=0,g=h-1):(j=h-1,g=2),k=this.calcColor(c[a],a);if(k===null)return;return b&&(k=this.calcHighlightColor(k,d)),e.drawRect(i,j,this.barWidth-1,g-1,k,k)}}),a.fn.sparkline.discrete=x=d(a.fn.sparkline._base,t,{type:"discrete",init:function(b,c,d,e,f){x._super.init.call(this,b,c,d,e,f),this.regionShapes={},this.values=c=a.map(c,Number),this.min=Math.min.apply(Math,c),this.max=Math.max.apply(Math,c),this.range=this.max-this.min,this.width=e=d.get("width")==="auto"?c.length*2:this.width,this.interval=Math.floor(e/c.length),this.itemWidth=e/c.length,d.get("chartRangeMin")!==undefined&&(d.get("chartRangeClip")||d.get("chartRangeMin")this.max)&&(this.max=d.get("chartRangeMax")),this.initTarget(),this.target&&(this.lineHeight=d.get("lineHeight")==="auto"?Math.round(this.canvasHeight*.3):d.get("lineHeight"))},getRegion:function(a,b,c){return Math.floor(b/this.itemWidth)},getCurrentRegionFields:function(){var a=this.currentRegion;return{isNull:this.values[a]===undefined,value:this.values[a],offset:a}},renderRegion:function(a,b){var c=this.values,d=this.options,e=this.min,g=this.max,h=this.range,i=this.interval,j=this.target,k=this.canvasHeight,l=this.lineHeight,m=k-l,n,o,p,q;return o=f(c[a],e,g),q=a*i,n=Math.round(m-m*((o-e)/h)),p=d.get("thresholdColor")&&o0)for(h=c.length;h--;)g+=c[h];this.total=g,this.initTarget(),this.radius=Math.floor(Math.min(this.canvasWidth,this.canvasHeight)/2)},getRegion:function(a,b,c){var d=this.target.getShapeAt(a,b,c);return d!==undefined&&this.shapes[d]!==undefined?this.shapes[d]:undefined},getCurrentRegionFields:function(){var a=this.currentRegion;return{isNull:this.values[a]===undefined,value:this.values[a],percent:this.values[a]/this.total*100,color:this.options.get("sliceColors")[a%this.options.get("sliceColors").length],offset:a}},changeHighlight:function(a){var b=this.currentRegion,c=this.renderSlice(b,a),d=this.valueShapes[b];delete this.shapes[d],this.target.replaceWithShape(d,c),this.valueShapes[b]=c.id,this.shapes[c.id]=b},renderSlice:function(a,b){var c=this.target,d=this.options,e=this.radius,f=d.get("borderWidth"),g=d.get("offset"),h=2*Math.PI,i=this.values,j=this.total,k=g?2*Math.PI*(g/360):0,l,m,n,o,p;o=i.length;for(n=0;n0&&(m=k+h*(i[n]/j));if(a===n)return p=d.get("sliceColors")[n%d.get("sliceColors").length],b&&(p=this.calcHighlightColor(p,d)),c.drawPieSlice(e,e,e-f,l,m,undefined,p);k=m}},render:function(){var a=this.target,b=this.values,c=this.options,d=this.radius,e=c.get("borderWidth"),f,g;if(!z._super.
+render.call(this))return;e&&a.drawCircle(d,d,Math.floor(d-e/2),c.get("borderColor"),undefined,e).append();for(g=b.length;g--;)b[g]&&(f=this.renderSlice(g).append(),this.valueShapes[g]=f.id,this.shapes[f.id]=g);a.render()}}),a.fn.sparkline.box=A=d(a.fn.sparkline._base,{type:"box",init:function(b,c,d,e,f){A._super.init.call(this,b,c,d,e,f),this.values=a.map(c,Number),this.width=d.get("width")==="auto"?"4.0em":e,this.initTarget(),this.values.length||(this.disabled=1)},getRegion:function(){return 1},getCurrentRegionFields:function(){var a=[{field:"lq",value:this.quartiles[0]},{field:"med",value:this.quartiles[1]},{field:"uq",value:this.quartiles[2]}];return this.loutlier!==undefined&&a.push({field:"lo",value:this.loutlier}),this.routlier!==undefined&&a.push({field:"ro",value:this.routlier}),this.lwhisker!==undefined&&a.push({field:"lw",value:this.lwhisker}),this.rwhisker!==undefined&&a.push({field:"rw",value:this.rwhisker}),a},render:function(){var a=this.target,b=this.values,c=b.length,d=this.options,e=this.canvasWidth,f=this.canvasHeight,h=d.get("chartRangeMin")===undefined?Math.min.apply(Math,b):d.get("chartRangeMin"),i=d.get("chartRangeMax")===undefined?Math.max.apply(Math,b):d.get("chartRangeMax"),j=0,k,l,m,n,o,p,q,r,s,t,u;if(!A._super.render.call(this))return;if(d.get("raw"))d.get("showOutliers")&&b.length>5?(l=b[0],k=b[1],n=b[2],o=b[3],p=b[4],q=b[5],r=b[6]):(k=b[0],n=b[1],o=b[2],p=b[3],q=b[4]);else{b.sort(function(a,b){return a-b}),n=g(b,1),o=g(b,2),p=g(b,3),m=p-n;if(d.get("showOutliers")){k=q=undefined;for(s=0;sn-m*d.get("outlierIQR")&&(k=b[s]),b[s]q&&a.drawCircle((r-h)*u+j,f/2,d.get("spotRadius"),d.get("outlierLineColor"),d.get("outlierFillColor")).append()),a.drawRect(Math.round((n-h)*u+j),Math.round(f*.1),Math.round((p-n)*u),Math.round(f*.8),d.get("boxLineColor"),d.get("boxFillColor")).append(),a.drawLine(Math.round((k-h)*u+j),Math.round(f/2),Math.round((n-h)*u+j),Math.round(f/2),d.get("lineColor")).append(),a.drawLine(Math.round((k-h)*u+j),Math.round(f/4),Math.round((k-h)*u+j),Math.round(f-f/4),d.get("whiskerColor")).append(),a.drawLine(Math.round((q-h)*u+j),Math.round(f/2),Math.round((p-h)*u+j),Math.round(f/2),d.get("lineColor")).append(),a.drawLine(Math.round((q-h)*u+j),Math.round(f/4),Math.round((q-h)*u+j),Math.round(f-f/4),d.get("whiskerColor")).append(),a.drawLine(Math.round((o-h)*u+j),Math.round(f*.1),Math.round((o-h)*u+j),Math.round(f*.9),d.get("medianColor")).append(),d.get("target")&&(t=Math.ceil(d.get("spotRadius")),a.drawLine(Math.round((d.get("target")-h)*u+j),Math.round(f/2-t),Math.round((d.get("target")-h)*u+j),Math.round(f/2+t),d.get("targetColor")).append(),a.drawLine(Math.round((d.get("target")-h)*u+j-t),Math.round(f/2),Math.round((d.get("target")-h)*u+j+t),Math.round(f/2),d.get("targetColor")).append()),a.render()}}),function(){document.namespaces&&!document.namespaces.v?(a.fn.sparkline.hasVML=!0,document.namespaces.add("v","urn:schemas-microsoft-com:vml","#default#VML")):a.fn.sparkline.hasVML=!1;var b=document.createElement("canvas");a.fn.sparkline.hasCanvas=!!b.getContext&&!!b.getContext("2d")}(),D=d({init:function(a,b,c,d){this.target=a,this.id=b,this.type=c,this.args=d},append:function(){return this.target.appendShape(this),this}}),E=d({_pxregex:/(\d+)(px)?\s*$/i,init:function(b,c,d){if(!b)return;this.width=b,this.height=c,this.target=d,this.lastShapeId=null,d[0]&&(d=d[0]),a.data(d,"_jqs_vcanvas",this)},drawLine:function(a,b,c,d,e,f){return this.drawShape([[a,b],[c,d]],e,f)},drawShape:function(a,b,c,d){return this._genShape("Shape",[a,b,c,d])},drawCircle:function(a,b,c,d,e,f){return this._genShape("Circle",[a,b,c,d,e,f])},drawPieSlice:function(a,b,c,d,e,f,g){return this._genShape("PieSlice",[a,b,c,d,e,f,g])},drawRect:function(a,b,c,d,e,f){return this._genShape("Rect",[a,b,c,d,e,f])},getElement:function(){return this.canvas},getLastShapeId:function(){return this.lastShapeId},reset:function(){alert("reset not implemented")},_insert:function(b,c){a(c).html(b)},_calculatePixelDims:function(b,c,d){var e;e=this._pxregex.exec(c),e?this.pixelHeight=e[1]:this.pixelHeight=a(d).height(),e=this._pxregex.exec(b),e?this.pixelWidth=e[1]:this.pixelWidth=a(d).width()},_genShape:function(a,b){var c=I++;return b.unshift(c),new D(this,c,a,b)},appendShape:function(a){alert("appendShape not implemented")},replaceWithShape:function(a,b){alert("replaceWithShape not implemented")},insertAfterShape:function(a,b){alert("insertAfterShape not implemented")},removeShapeId:function(a){alert("removeShapeId not implemented")},getShapeAt:function(a,b,c){alert("getShapeAt not implemented")},render:function(){alert("render not implemented")}}),F=d(E,{init:function(b,c,d,e){F._super.init.call(this,b,c,d),this.canvas=document.createElement("canvas"),d[0]&&(d=d[0]),a.data(d,"_jqs_vcanvas",this),a(this.canvas).css({display:"inline-block",width:b,height:c,verticalAlign:"top"}),this._insert(this.canvas,d),this._calculatePixelDims(b,c,this.canvas),this.canvas.width=this.pixelWidth,this.canvas.height=this.pixelHeight,this.interact=e,this.shapes={},this.shapeseq=[],this.currentTargetShapeId=undefined,a(this.canvas).css({width:this.pixelWidth,height:this.pixelHeight})},_getContext:function(a,b,c){var d=this.canvas.getContext("2d");return a!==undefined&&(d.strokeStyle=a),d.lineWidth=c===undefined?1:c,b!==undefined&&(d.fillStyle=b),d},reset:function(){var a=this._getContext();a.clearRect(0,0,this.pixelWidth,this.pixelHeight),this.shapes={},this.shapeseq=[],this.currentTargetShapeId=undefined},_drawShape:function(a,b,c,d,e){var f=this._getContext(c,d,e),g,h;f.beginPath(),f.moveTo(b[0][0]+.5,b[0][1]+.5);for(g=1,h=b.length;g',this.canvas.insertAdjacentHTML("beforeEnd",e),this.group=a(this.canvas).children()[0],this.rendered=!1,this.prerender=""},_drawShape:function(a,b,c,d,e){var f=[],g,h,i,j,k,l,m;for(m=0,l=b.length;m'+" ",k},_drawCircle:function(a,b,c,d,e,f,g){var h,i,j;return b-=d,c-=d,h=e===undefined?' stroked="false" ':' strokeWeight="'+g+'px" strokeColor="'+e+'" ',i=f===undefined?' filled="false"':' fillColor="'+f+'" filled="true" ',j=' ',j},_drawPieSlice:function(a,b,c,d,e,f,g,h){var i,j,k,l,m,n,o,p;if(e===f)return"";f-e===2*Math.PI&&(e=0,f=2*Math.PI),j=b+Math.round(Math.cos(e)*d),k=c+Math.round(Math.sin(e)*d),l=b+Math.round(Math.cos(f)*d),m=c+Math.round(Math.sin(f)*d);if(j===l&&k===m){if(f-e'+" ",p)},_drawRect:function(a,b,c,d,e,f,g){return this._drawShape(a,[[b,c],[b,c+e],[b+d,c+e],[b+d,c],[b,c]],f,g)},reset:function(){this.group.innerHTML=""},appendShape:function(a){var b=this["_draw"+a.type].apply(this,a.args);return this.rendered?this.group.insertAdjacentHTML("beforeEnd",b):this.prerender+=b,this.lastShapeId=a.id,a.id},replaceWithShape:function(b,c){var d=a("#jqsshape"+b),e=this["_draw"+c.type].apply(this,c.args);d[0].outerHTML=e},replaceWithShapes:function(b,c){var d=a("#jqsshape"+b[0]),e="",f=c.length,g;for(g=0;g ');
+ $input = $(' ').val($el.data('value'));
+ $div.append($input);
+ $el.prepend($div)
+ $input.focus();
+ $input.keydown(function (event) {
+ event.stopPropagation();
+ if (event.keyCode == 13 || event.keyCode == 9) {
+ if ($input.val() != $el.data('value')) {
+ self.view.dataset.call($el.data('action'), [self.id, $input.val()]).then(function () {
+ self.do_reload();
+ });
+ } else {
+ $div.remove();
+ }
+ }
+ });
+ }
+ });
+ }
+ });
+ });
+ }
+ },
+ on_card_clicked: function() {
+ if (this.view.dataset.model === 'crm.case.section') {
+ this.$('.oe_kanban_crm_salesteams_list a').first().click();
+ } else {
+ this._super.apply(this, arguments);
+ }
+ },
+ });
+
+};
diff --git a/addons/project_timesheet/__openerp__.py b/addons/project_timesheet/__openerp__.py
index 0651d7b9ce4..4dff3695c5c 100644
--- a/addons/project_timesheet/__openerp__.py
+++ b/addons/project_timesheet/__openerp__.py
@@ -34,7 +34,7 @@ with the effect of creating, editing and deleting either ways.
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'images': ['images/invoice_task_work.jpeg', 'images/my_timesheet.jpeg', 'images/working_hour.jpeg'],
- 'depends': ['project', 'hr_timesheet_sheet', 'hr_timesheet_invoice', 'account_analytic_analysis'],
+ 'depends': ['resource', 'project', 'hr_timesheet_sheet', 'hr_timesheet_invoice', 'account_analytic_analysis'],
'data': [
'security/ir.model.access.csv',
'security/project_timesheet_security.xml',
diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py
index 3177aec4c5f..edaf5de3829 100644
--- a/addons/sale_crm/sale_crm.py
+++ b/addons/sale_crm/sale_crm.py
@@ -58,8 +58,11 @@ class crm_case_section(osv.osv):
'sum_month_invoice': fields.function(_get_sum_month_invoice,
string='Total invoiced this month',
type='integer', readonly=True),
+ 'forcasted': fields.integer(string='Total forcasted for this month'),
}
+ def action_forcasted(self, cr, uid, id, value, context=None):
+ return self.write(cr, uid, [id], {'forcasted': value}, context=context)
class res_users(osv.Model):
_inherit = 'res.users'
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index f8b4228334e..5f3a42003be 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -217,6 +217,8 @@
+
+
@@ -237,8 +239,8 @@
-
-
Invoiced this month
+
Invoiced
+
Forcasted
From f53ed5bb561816241d4b99c901a2b0ebb75a1584 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Mon, 15 Apr 2013 15:08:29 +0200
Subject: [PATCH 08/72] [IMP] crm salesteams: add justgage lib
bzr revid: chm@openerp.com-20130415130829-szds3xx93yhev0cw
---
addons/crm/static/lib/justgage.js | 883 ++++++++++++++++++++++++++++++
1 file changed, 883 insertions(+)
create mode 100644 addons/crm/static/lib/justgage.js
diff --git a/addons/crm/static/lib/justgage.js b/addons/crm/static/lib/justgage.js
new file mode 100644
index 00000000000..52b5c6ea0c3
--- /dev/null
+++ b/addons/crm/static/lib/justgage.js
@@ -0,0 +1,883 @@
+/**
+ * JustGage - this is work-in-progress, unreleased, unofficial code, so it might not work top-notch :)
+ * Check http://www.justgage.com for official releases
+ * Licensed under MIT.
+ * @author Bojan Djuricic (@Toorshia)
+ *
+ * LATEST UPDATES
+
+ * -----------------------------
+ * April 01, 2013.
+ * -----------------------------
+ * fix - https://github.com/toorshia/justgage/issues/46
+
+ * -----------------------------
+ * March 26, 2013.
+ * -----------------------------
+ * customSectors - define specific color for value range (0-10 : red, 10-30 : blue etc.)
+
+ * -----------------------------
+ * March 23, 2013.
+ * -----------------------------
+ * counter - option to animate value in counting fashion
+ * fix - https://github.com/toorshia/justgage/issues/45
+
+ * -----------------------------
+ * March 13, 2013.
+ * -----------------------------
+ * refresh method - added optional 'max' parameter to use when you need to update max value
+
+ * -----------------------------
+ * February 26, 2013.
+ * -----------------------------
+ * decimals - option to define/limit number of decimals when not using humanFriendly or customRenderer to display value
+ * fixed a missing parameters bug when calling generateShadow() for IE < 9
+
+ * -----------------------------
+ * December 31, 2012.
+ * -----------------------------
+ * fixed text y-position for hidden divs - workaround for Raphael 'dy' bug - https://github.com/DmitryBaranovskiy/raphael/issues/491
+ * 'show' parameters, like showMinMax are now 'hide' because I am lame developer - please update these in your setups
+ * Min and Max labels are now auto-off when in donut mode
+ * Start angle in donut mode is now 90
+ * donutStartAngle - option to define start angle for donut
+
+ * -----------------------------
+ * November 25, 2012.
+ * -----------------------------
+ * Option to define custom rendering function for displayed value
+
+ * -----------------------------
+ * November 19, 2012.
+ * -----------------------------
+ * Config.value is now updated after gauge refresh
+
+ * -----------------------------
+ * November 13, 2012.
+ * -----------------------------
+ * Donut display mode added
+ * Option to hide value label
+ * Option to enable responsive gauge size
+ * Removed default title attribute
+ * Option to accept min and max defined as string values
+ * Option to configure value symbol
+ * Fixed bad aspect ratio calculations
+ * Option to configure minimum font size for all texts
+ * Option to show shorthand big numbers (human friendly)
+ */
+
+ JustGage = function(config) {
+
+ if (!config.id) {alert("Missing id parameter for gauge!"); return false;}
+ if (!config.node) {
+ if (!document.getElementById(config.id)) {alert("No element with id: \""+config.id+"\" found!"); return false;}
+ config.node = document.getElementById(config.id);
+ }
+
+ var obj = this;
+
+ // configurable parameters
+ obj.config =
+ {
+ // id : string
+ // this is container element id
+ id : config.id,
+
+ // node : string
+ // the node to use instead of DOM
+ node : config.node,
+
+ // title : string
+ // gauge title
+ title : (config.title) ? config.title : "",
+
+ // titleFontColor : string
+ // color of gauge title
+ titleFontColor : (config.titleFontColor) ? config.titleFontColor : "#999999",
+
+ // value : int
+ // value gauge is showing
+ value : (config.value) ? config.value : 0,
+
+
+ // valueFontColor : string
+ // color of label showing current value
+ valueFontColor : (config.valueFontColor) ? config.valueFontColor : "#010101",
+
+ // symbol : string
+ // special symbol to show next to value
+ symbol : (config.symbol) ? config.symbol : "",
+
+ // min : int
+ // min value
+ min : (config.min) ? parseFloat(config.min) : 0,
+
+ // max : int
+ // max value
+ max : (config.max) ? parseFloat(config.max) : 100,
+
+ // humanFriendlyDecimal : int
+ // number of decimal places for our human friendly number to contain
+ humanFriendlyDecimal : (config.humanFriendlyDecimal) ? config.humanFriendlyDecimal : 0,
+
+ // textRenderer: func
+ // function applied before rendering text
+ textRenderer : (config.textRenderer) ? config.textRenderer : null,
+
+ // gaugeWidthScale : float
+ // width of the gauge element
+ gaugeWidthScale : (config.gaugeWidthScale) ? config.gaugeWidthScale : 1.0,
+
+ // gaugeColor : string
+ // background color of gauge element
+ gaugeColor : (config.gaugeColor) ? config.gaugeColor : "#edebeb",
+
+ // label : string
+ // text to show below value
+ label : (config.label) ? config.label : "",
+
+ // labelFontColor : string
+ // color of label showing label under value
+ labelFontColor : (config.labelFontColor) ? config.labelFontColor : "#b3b3b3",
+
+ // shadowOpacity : int
+ // 0 ~ 1
+ shadowOpacity : (config.shadowOpacity) ? config.shadowOpacity : 0.2,
+
+ // shadowSize: int
+ // inner shadow size
+ shadowSize : (config.shadowSize) ? config.shadowSize : 5,
+
+ // shadowVerticalOffset : int
+ // how much shadow is offset from top
+ shadowVerticalOffset : (config.shadowVerticalOffset) ? config.shadowVerticalOffset : 3,
+
+ // levelColors : string[]
+ // colors of indicator, from lower to upper, in RGB format
+ levelColors : (config.levelColors) ? config.levelColors : [
+ "#a9d70b",
+ "#f9c802",
+ "#ff0000"
+ ],
+
+ // startAnimationTime : int
+ // length of initial animation
+ startAnimationTime : (config.startAnimationTime) ? config.startAnimationTime : 700,
+
+ // startAnimationType : string
+ // type of initial animation (linear, >, <, <>, bounce)
+ startAnimationType : (config.startAnimationType) ? config.startAnimationType : ">",
+
+ // refreshAnimationTime : int
+ // length of refresh animation
+ refreshAnimationTime : (config.refreshAnimationTime) ? config.refreshAnimationTime : 700,
+
+ // refreshAnimationType : string
+ // type of refresh animation (linear, >, <, <>, bounce)
+ refreshAnimationType : (config.refreshAnimationType) ? config.refreshAnimationType : ">",
+
+ // donutStartAngle : int
+ // angle to start from when in donut mode
+ donutStartAngle : (config.donutStartAngle) ? config.donutStartAngle : 90,
+
+ // valueMinFontSize : int
+ // absolute minimum font size for the value
+ valueMinFontSize : config.valueMinFontSize || 16,
+
+ // titleMinFontSize
+ // absolute minimum font size for the title
+ titleMinFontSize : config.titleMinFontSize || 10,
+
+ // labelMinFontSize
+ // absolute minimum font size for the label
+ labelMinFontSize : config.labelMinFontSize || 10,
+
+ // minLabelMinFontSize
+ // absolute minimum font size for the minimum label
+ minLabelMinFontSize : config.minLabelMinFontSize || 10,
+
+ // maxLabelMinFontSize
+ // absolute minimum font size for the maximum label
+ maxLabelMinFontSize : config.maxLabelMinFontSize || 10,
+
+ // hideValue : bool
+ // hide value text
+ hideValue : (config.hideValue) ? config.hideValue : false,
+
+ // hideMinMax : bool
+ // hide min and max values
+ hideMinMax : (config.hideMinMax) ? config.hideMinMax : false,
+
+ // hideInnerShadow : bool
+ // hide inner shadow
+ hideInnerShadow : (config.hideInnerShadow) ? config.hideInnerShadow : false,
+
+ // humanFriendly : bool
+ // convert large numbers for min, max, value to human friendly (e.g. 1234567 -> 1.23M)
+ humanFriendly : (config.humanFriendly) ? config.humanFriendly : false,
+
+ // noGradient : bool
+ // whether to use gradual color change for value, or sector-based
+ noGradient : (config.noGradient) ? config.noGradient : false,
+
+ // donut : bool
+ // show full donut gauge
+ donut : (config.donut) ? config.donut : false,
+
+ // relativeGaugeSize : bool
+ // whether gauge size should follow changes in container element size
+ relativeGaugeSize : (config.relativeGaugeSize) ? config.relativeGaugeSize : false,
+
+ // counter : bool
+ // animate level number change
+ counter : (config.counter) ? config.counter : false,
+
+ // decimals : int
+ // number of digits after floating point
+ decimals : (config.decimals) ? config.decimals : 0,
+
+ // customSectors : [] of objects
+ // number of digits after floating point
+ customSectors : (config.customSectors) ? config.customSectors : []
+ };
+
+ // variables
+ var
+ canvasW,
+ canvasH,
+ widgetW,
+ widgetH,
+ aspect,
+ dx,
+ dy,
+ titleFontSize,
+ titleX,
+ titleY,
+ valueFontSize,
+ valueX,
+ valueY,
+ labelFontSize,
+ labelX,
+ labelY,
+ minFontSize,
+ minX,
+ minY,
+ maxFontSize,
+ maxX,
+ maxY;
+
+ // overflow values
+ if (this.config.value > this.config.max) this.config.value = this.config.max;
+ if (this.config.value < this.config.min) this.config.value = this.config.min;
+ this.originalValue = config.value;
+
+ // canvas
+ this.canvas = Raphael(this.config.node, "100%", "100%");
+ if (this.config.relativeGaugeSize === true) {
+ this.canvas.setViewBox(0, 0, 200, 150, true);
+ }
+
+ // canvas dimensions
+ if (this.config.relativeGaugeSize === true) {
+ canvasW = 200;
+ canvasH = 150;
+ } else {
+ canvasW = getStyle(this.config.node, "width").slice(0, -2) * 1;
+ canvasH = getStyle(this.config.node, "height").slice(0, -2) * 1;
+ }
+
+ // widget dimensions
+ if (this.config.donut === true) {
+
+ // DONUT *******************************
+
+ // width more than height
+ if(canvasW > canvasH) {
+ widgetH = canvasH;
+ widgetW = widgetH;
+ // width less than height
+ } else if (canvasW < canvasH) {
+ widgetW = canvasW;
+ widgetH = widgetW;
+ // if height don't fit, rescale both
+ if(widgetH > canvasH) {
+ aspect = widgetH / canvasH;
+ widgetH = widgetH / aspect;
+ widgetW = widgetH / aspect;
+ }
+ // equal
+ } else {
+ widgetW = canvasW;
+ widgetH = widgetW;
+ }
+
+ // delta
+ dx = (canvasW - widgetW)/2;
+ dy = (canvasH - widgetH)/2;
+
+ // title
+ titleFontSize = ((widgetH / 8) > 10) ? (widgetH / 10) : 10;
+ titleX = dx + widgetW / 2;
+ titleY = dy + widgetH / 11;
+
+ // value
+ valueFontSize = ((widgetH / 6.4) > 16) ? (widgetH / 5.4) : 18;
+ valueX = dx + widgetW / 2;
+ if(this.config.label !== '') {
+ valueY = dy + widgetH / 1.85;
+ } else {
+ valueY = dy + widgetH / 1.7;
+ }
+
+ // label
+ labelFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10;
+ labelX = dx + widgetW / 2;
+ labelY = valueY + labelFontSize;
+
+ // min
+ minFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10;
+ minX = dx + (widgetW / 10) + (widgetW / 6.666666666666667 * this.config.gaugeWidthScale) / 2 ;
+ minY = labelY;
+
+ // max
+ maxFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10;
+ maxX = dx + widgetW - (widgetW / 10) - (widgetW / 6.666666666666667 * this.config.gaugeWidthScale) / 2 ;
+ maxY = labelY;
+
+ } else {
+ // HALF *******************************
+
+ // width more than height
+ if(canvasW > canvasH) {
+ widgetH = canvasH;
+ widgetW = widgetH * 1.25;
+ //if width doesn't fit, rescale both
+ if(widgetW > canvasW) {
+ aspect = widgetW / canvasW;
+ widgetW = widgetW / aspect;
+ widgetH = widgetH / aspect;
+ }
+ // width less than height
+ } else if (canvasW < canvasH) {
+ widgetW = canvasW;
+ widgetH = widgetW / 1.25;
+ // if height don't fit, rescale both
+ if(widgetH > canvasH) {
+ aspect = widgetH / canvasH;
+ widgetH = widgetH / aspect;
+ widgetW = widgetH / aspect;
+ }
+ // equal
+ } else {
+ widgetW = canvasW;
+ widgetH = widgetW * 0.75;
+ }
+
+ // delta
+ dx = (canvasW - widgetW)/2;
+ dy = (canvasH - widgetH)/2;
+
+ // title
+ titleFontSize = ((widgetH / 8) > this.config.titleMinFontSize) ? (widgetH / 10) : this.config.titleMinFontSize;
+ titleX = dx + widgetW / 2;
+ titleY = dy + widgetH / 6.4;
+
+ // value
+ valueFontSize = ((widgetH / 6.5) > this.config.valueMinFontSize) ? (widgetH / 6.5) : this.config.valueMinFontSize;
+ valueX = dx + widgetW / 2;
+ valueY = dy + widgetH / 1.275;
+
+ // label
+ labelFontSize = ((widgetH / 16) > this.config.labelMinFontSize) ? (widgetH / 16) : this.config.labelMinFontSize;
+ labelX = dx + widgetW / 2;
+ labelY = valueY + valueFontSize / 2 + 5;
+
+ // min
+ minFontSize = ((widgetH / 16) > this.config.minLabelMinFontSize) ? (widgetH / 16) : this.config.minLabelMinFontSize;
+ minX = dx + (widgetW / 10) + (widgetW / 6.666666666666667 * this.config.gaugeWidthScale) / 2 ;
+ minY = labelY;
+
+ // max
+ maxFontSize = ((widgetH / 16) > this.config.maxLabelMinFontSize) ? (widgetH / 16) : this.config.maxLabelMinFontSize;
+ maxX = dx + widgetW - (widgetW / 10) - (widgetW / 6.666666666666667 * this.config.gaugeWidthScale) / 2 ;
+ maxY = labelY;
+ }
+
+ // parameters
+ this.params = {
+ canvasW : canvasW,
+ canvasH : canvasH,
+ widgetW : widgetW,
+ widgetH : widgetH,
+ dx : dx,
+ dy : dy,
+ titleFontSize : titleFontSize,
+ titleX : titleX,
+ titleY : titleY,
+ valueFontSize : valueFontSize,
+ valueX : valueX,
+ valueY : valueY,
+ labelFontSize : labelFontSize,
+ labelX : labelX,
+ labelY : labelY,
+ minFontSize : minFontSize,
+ minX : minX,
+ minY : minY,
+ maxFontSize : maxFontSize,
+ maxX : maxX,
+ maxY : maxY
+ };
+
+ // var clear
+ canvasW, canvasH, widgetW, widgetH, aspect, dx, dy, titleFontSize, titleX, titleY, valueFontSize, valueX, valueY, labelFontSize, labelX, labelY, minFontSize, minX, minY, maxFontSize, maxX, maxY = null
+
+ // pki - custom attribute for generating gauge paths
+ this.canvas.customAttributes.pki = function (value, min, max, w, h, dx, dy, gws, donut) {
+
+ var alpha, Ro, Ri, Cx, Cy, Xo, Yo, Xi, Yi, path;
+
+ if (donut) {
+ alpha = (1 - 2 * (value - min) / (max - min)) * Math.PI;
+ Ro = w / 2 - w / 7;
+ Ri = Ro - w / 6.666666666666667 * gws;
+
+ Cx = w / 2 + dx;
+ Cy = h / 1.95 + dy;
+
+ Xo = w / 2 + dx + Ro * Math.cos(alpha);
+ Yo = h - (h - Cy) + 0 - Ro * Math.sin(alpha);
+ Xi = w / 2 + dx + Ri * Math.cos(alpha);
+ Yi = h - (h - Cy) + 0 - Ri * Math.sin(alpha);
+
+ path += "M" + (Cx - Ri) + "," + Cy + " ";
+ path += "L" + (Cx - Ro) + "," + Cy + " ";
+ if (value > ((max - min) / 2)) {
+ path += "A" + Ro + "," + Ro + " 0 0 1 " + (Cx + Ro) + "," + Cy + " ";
+ }
+ path += "A" + Ro + "," + Ro + " 0 0 1 " + Xo + "," + Yo + " ";
+ path += "L" + Xi + "," + Yi + " ";
+ if (value > ((max - min) / 2)) {
+ path += "A" + Ri + "," + Ri + " 0 0 0 " + (Cx + Ri) + "," + Cy + " ";
+ }
+ path += "A" + Ri + "," + Ri + " 0 0 0 " + (Cx - Ri) + "," + Cy + " ";
+ path += "Z ";
+
+ return { path: path };
+
+ } else {
+ alpha = (1 - (value - min) / (max - min)) * Math.PI;
+ Ro = w / 2 - w / 10;
+ Ri = Ro - w / 6.666666666666667 * gws;
+
+ Cx = w / 2 + dx;
+ Cy = h / 1.25 + dy;
+
+ Xo = w / 2 + dx + Ro * Math.cos(alpha);
+ Yo = h - (h - Cy) + 0 - Ro * Math.sin(alpha);
+ Xi = w / 2 + dx + Ri * Math.cos(alpha);
+ Yi = h - (h - Cy) + 0 - Ri * Math.sin(alpha);
+
+ path += "M" + (Cx - Ri) + "," + Cy + " ";
+ path += "L" + (Cx - Ro) + "," + Cy + " ";
+ path += "A" + Ro + "," + Ro + " 0 0 1 " + Xo + "," + Yo + " ";
+ path += "L" + Xi + "," + Yi + " ";
+ path += "A" + Ri + "," + Ri + " 0 0 0 " + (Cx - Ri) + "," + Cy + " ";
+ path += "Z ";
+
+ return { path: path };
+ }
+
+ // var clear
+ alpha, Ro, Ri, Cx, Cy, Xo, Yo, Xi, Yi, path = null;
+ };
+
+ // gauge
+ this.gauge = this.canvas.path().attr({
+ "stroke": "none",
+ "fill": this.config.gaugeColor,
+ pki: [this.config.max, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale, this.config.donut]
+ });
+ this.gauge.id = this.config.id+"-gauge";
+
+ // level
+ this.level = this.canvas.path().attr({
+ "stroke": "none",
+ "fill": getColor(this.config.value, (this.config.value - this.config.min) / (this.config.max - this.config.min), this.config.levelColors, this.config.noGradient, this.config.customSectors),
+ pki: [this.config.min, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale, this.config.donut]
+ });
+ if(this.config.donut) {
+ this.level.transform("r" + this.config.donutStartAngle + ", " + (this.params.widgetW/2 + this.params.dx) + ", " + (this.params.widgetH/1.95 + this.params.dy));
+ }
+ this.level.id = this.config.id+"-level";
+
+ // title
+ this.txtTitle = this.canvas.text(this.params.titleX, this.params.titleY, this.config.title);
+ this.txtTitle.attr({
+ "font-size":this.params.titleFontSize,
+ "font-weight":"bold",
+ "font-family":"Arial",
+ "fill":this.config.titleFontColor,
+ "fill-opacity":"1"
+ });
+ setDy(this.txtTitle, this.params.titleFontSize, this.params.titleY);
+ this.txtTitle.id = this.config.id+"-txttitle";
+
+ // value
+ this.txtValue = this.canvas.text(this.params.valueX, this.params.valueY, 0);
+ this.txtValue.attr({
+ "font-size":this.params.valueFontSize,
+ "font-weight":"bold",
+ "font-family":"Arial",
+ "fill":this.config.valueFontColor,
+ "fill-opacity":"0"
+ });
+ setDy(this.txtValue, this.params.valueFontSize, this.params.valueY);
+ this.txtValue.id = this.config.id+"-txtvalue";
+
+ // label
+ this.txtLabel = this.canvas.text(this.params.labelX, this.params.labelY, this.config.label);
+ this.txtLabel.attr({
+ "font-size":this.params.labelFontSize,
+ "font-weight":"normal",
+ "font-family":"Arial",
+ "fill":this.config.labelFontColor,
+ "fill-opacity":"0"
+ });
+ setDy(this.txtLabel, this.params.labelFontSize, this.params.labelY);
+ this.txtLabel.id = this.config.id+"-txtlabel";
+
+ // min
+ this.txtMinimum = this.config.min;
+ if( this.config.humanFriendly ) this.txtMinimum = humanFriendlyNumber( this.config.min, this.config.humanFriendlyDecimal );
+ this.txtMin = this.canvas.text(this.params.minX, this.params.minY, this.txtMinimum);
+ this.txtMin.attr({
+ "font-size":this.params.minFontSize,
+ "font-weight":"normal",
+ "font-family":"Arial",
+ "fill":this.config.labelFontColor,
+ "fill-opacity": (this.config.hideMinMax || this.config.donut)? "0" : "1"
+ });
+ setDy(this.txtMin, this.params.minFontSize, this.params.minY);
+ this.txtMin.id = this.config.id+"-txtmin";
+
+ // max
+ this.txtMaximum = this.config.max;
+ if( this.config.humanFriendly ) this.txtMaximum = humanFriendlyNumber( this.config.max, this.config.humanFriendlyDecimal );
+ this.txtMax = this.canvas.text(this.params.maxX, this.params.maxY, this.txtMaximum);
+ this.txtMax.attr({
+ "font-size":this.params.maxFontSize,
+ "font-weight":"normal",
+ "font-family":"Arial",
+ "fill":this.config.labelFontColor,
+ "fill-opacity": (this.config.hideMinMax || this.config.donut)? "0" : "1"
+ });
+ setDy(this.txtMax, this.params.maxFontSize, this.params.maxY);
+ this.txtMax.id = this.config.id+"-txtmax";
+
+ var defs = this.canvas.canvas.childNodes[1];
+ var svg = "http://www.w3.org/2000/svg";
+
+ if (ie < 9) {
+ onCreateElementNsReady(function() {
+ this.generateShadow(svg, defs);
+ });
+ } else {
+ this.generateShadow(svg, defs);
+ }
+
+ // var clear
+ defs, svg = null;
+
+ // execute on each animation frame
+ function onAnimate() {
+ if (obj.config.counter) {
+ var currentValue = obj.level.attr("pki");
+
+ if(obj.config.textRenderer) {
+ // this.originalValue = this.config.textRenderer(this.originalValue);
+ obj.txtValue.attr("text", obj.config.textRenderer(Math.floor(currentValue[0])));
+ } else if(obj.config.humanFriendly) {
+ // this.originalValue = humanFriendlyNumber( this.originalValue, this.config.humanFriendlyDecimal ) + this.config.symbol;
+ obj.txtValue.attr("text", humanFriendlyNumber( Math.floor(currentValue[0]), obj.config.humanFriendlyDecimal ) + obj.config.symbol);
+ } else {
+ // this.originalValue += this.config.symbol;
+ obj.txtValue.attr("text", (currentValue[0] * 1).toFixed(obj.config.decimals) + obj.config.symbol);
+ }
+
+ setDy(obj.txtValue, obj.params.valueFontSize, obj.params.valueY);
+ currentValue = null;
+ }
+ }
+
+ if(!obj.config.counter) {
+ if(obj.config.textRenderer) {
+ obj.originalValue = obj.config.textRenderer(obj.originalValue);
+ } else if(obj.config.humanFriendly) {
+ obj.originalValue = humanFriendlyNumber( obj.originalValue, obj.config.humanFriendlyDecimal ) + obj.config.symbol;
+ } else {
+ obj.originalValue = (obj.originalValue * 1).toFixed(obj.config.decimals) + obj.config.symbol;
+ }
+
+ obj.txtValue.attr("text", obj.originalValue);
+ setDy(obj.txtValue, obj.params.valueFontSize, obj.params.valueY);
+ }
+
+ //event fired on each animation frame
+ eve.on("raphael.anim.frame.*", onAnimate);
+
+ // animate gauge level
+ this.level.animate({pki: [this.config.value, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale, this.config.donut]}, this.config.startAnimationTime, this.config.startAnimationType);
+
+ // animate value
+ this.txtValue.animate({"fill-opacity":"1"}, this.config.startAnimationTime, this.config.startAnimationType);
+
+ // animate label
+ this.txtLabel.animate({"fill-opacity":"1"}, this.config.startAnimationTime, this.config.startAnimationType);
+};
+
+/** Refresh gauge level */
+JustGage.prototype.refresh = function(val, max) {
+
+ var originalVal, displayVal, color, max = max || null;
+
+ // set new max
+ if(max !== null) {
+ this.config.max = max;
+
+ this.txtMaximum = this.config.max;
+ if( this.config.humanFriendly ) this.txtMaximum = humanFriendlyNumber( this.config.max, this.config.humanFriendlyDecimal );
+ this.txtMax.attr({"text" : this.config.max});
+ setDy(this.txtMax, this.params.maxFontSize, this.params.maxY);
+ }
+
+ // overflow values
+ originalVal = val;
+ displayVal = val;
+ if ((val * 1) > (this.config.max * 1)) {val = (this.config.max * 1);}
+ if ((val * 1) < (this.config.min * 1)) {val = (this.config.min * 1);}
+
+ color = getColor(val, (val - this.config.min) / (this.config.max - this.config.min), this.config.levelColors, this.config.noGradient, this.config.customSectors);
+
+ if(this.config.textRenderer) {
+ displayVal = this.config.textRenderer(displayVal);
+ } else if( this.config.humanFriendly ) {
+ displayVal = humanFriendlyNumber( displayVal, this.config.humanFriendlyDecimal ) + this.config.symbol;
+ } else {
+ displayVal = (displayVal * 1).toFixed(this.config.decimals) + this.config.symbol;
+ }
+
+ if(!this.config.counter) {
+ this.canvas.getById(this.config.id+"-txtvalue").attr({"text":displayVal});
+ setDy(this.canvas.getById(this.config.id+"-txtvalue"), this.params.valueFontSize, this.params.valueY);
+ }
+
+ this.canvas.getById(this.config.id+"-level").animate({pki: [val, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale, this.config.donut], "fill":color}, this.config.refreshAnimationTime, this.config.refreshAnimationType);
+ this.config.value = val * 1;
+
+ // var clear
+ originalVal, displayVal, color, max = null;
+};
+
+/** Generate shadow */
+JustGage.prototype.generateShadow = function(svg, defs) {
+
+ var gaussFilter, feOffset, feGaussianBlur, feComposite1, feFlood, feComposite2, feComposite3;
+
+ // FILTER
+ gaussFilter=document.createElementNS(svg,"filter");
+ gaussFilter.setAttribute("id", this.config.id + "-inner-shadow");
+ defs.appendChild(gaussFilter);
+
+ // offset
+ feOffset = document.createElementNS(svg,"feOffset");
+ feOffset.setAttribute("dx", 0);
+ feOffset.setAttribute("dy", this.config.shadowVerticalOffset);
+ gaussFilter.appendChild(feOffset);
+
+ // blur
+ feGaussianBlur = document.createElementNS(svg,"feGaussianBlur");
+ feGaussianBlur.setAttribute("result","offset-blur");
+ feGaussianBlur.setAttribute("stdDeviation", this.config.shadowSize);
+ gaussFilter.appendChild(feGaussianBlur);
+
+ // composite 1
+ feComposite1 = document.createElementNS(svg,"feComposite");
+ feComposite1.setAttribute("operator","out");
+ feComposite1.setAttribute("in", "SourceGraphic");
+ feComposite1.setAttribute("in2","offset-blur");
+ feComposite1.setAttribute("result","inverse");
+ gaussFilter.appendChild(feComposite1);
+
+ // flood
+ feFlood = document.createElementNS(svg,"feFlood");
+ feFlood.setAttribute("flood-color","black");
+ feFlood.setAttribute("flood-opacity", this.config.shadowOpacity);
+ feFlood.setAttribute("result","color");
+ gaussFilter.appendChild(feFlood);
+
+ // composite 2
+ feComposite2 = document.createElementNS(svg,"feComposite");
+ feComposite2.setAttribute("operator","in");
+ feComposite2.setAttribute("in", "color");
+ feComposite2.setAttribute("in2","inverse");
+ feComposite2.setAttribute("result","shadow");
+ gaussFilter.appendChild(feComposite2);
+
+ // composite 3
+ feComposite3 = document.createElementNS(svg,"feComposite");
+ feComposite3.setAttribute("operator","over");
+ feComposite3.setAttribute("in", "shadow");
+ feComposite3.setAttribute("in2","SourceGraphic");
+ gaussFilter.appendChild(feComposite3);
+
+ // set shadow
+ if (!this.config.hideInnerShadow) {
+ this.canvas.canvas.childNodes[2].setAttribute("filter", "url(#" + this.config.id + "-inner-shadow)");
+ this.canvas.canvas.childNodes[3].setAttribute("filter", "url(#" + this.config.id + "-inner-shadow)");
+ }
+
+ // var clear
+ gaussFilter, feOffset, feGaussianBlur, feComposite1, feFlood, feComposite2, feComposite3 = null;
+
+};
+
+/** Get color for value */
+function getColor(val, pct, col, noGradient, custSec) {
+
+ var no, inc, colors, percentage, rval, gval, bval, lower, upper, range, rangePct, pctLower, pctUpper, color;
+ var noGradient = noGradient || custSec.length > 0;
+
+ if(custSec.length > 0) {
+ for(var i = 0; i < custSec.length; i++) {
+ if(val > custSec[i].lo && val <= custSec[i].hi) {
+ return custSec[i].color;
+ }
+ }
+ }
+
+ no = col.length;
+ if (no === 1) return col[0];
+ inc = (noGradient) ? (1 / no) : (1 / (no - 1));
+ colors = [];
+ for (var i = 0; i < col.length; i++) {
+ percentage = (noGradient) ? (inc * (i + 1)) : (inc * i);
+ rval = parseInt((cutHex(col[i])).substring(0,2),16);
+ gval = parseInt((cutHex(col[i])).substring(2,4),16);
+ bval = parseInt((cutHex(col[i])).substring(4,6),16);
+ colors[i] = { pct: percentage, color: { r: rval, g: gval, b: bval } };
+ }
+
+ if(pct === 0) {
+ return 'rgb(' + [colors[0].color.r, colors[0].color.g, colors[0].color.b].join(',') + ')';
+ }
+
+ for (var j = 0; j < colors.length; j++) {
+ if (pct <= colors[j].pct) {
+ if (noGradient) {
+ return 'rgb(' + [colors[j].color.r, colors[j].color.g, colors[j].color.b].join(',') + ')';
+ } else {
+ lower = colors[j - 1];
+ upper = colors[j];
+ range = upper.pct - lower.pct;
+ rangePct = (pct - lower.pct) / range;
+ pctLower = 1 - rangePct;
+ pctUpper = rangePct;
+ color = {
+ r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper),
+ g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper),
+ b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper)
+ };
+ return 'rgb(' + [color.r, color.g, color.b].join(',') + ')';
+ }
+ }
+ }
+
+}
+
+/** Fix Raphael display:none tspan dy attribute bug */
+function setDy(elem, fontSize, txtYpos) {
+ if ((!ie || ie > 9) && (elem.node.firstChild.attributes.dy)) {
+ elem.node.firstChild.attributes.dy.value = 0;
+ }
+}
+
+/** Random integer */
+function getRandomInt (min, max) {
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+}
+
+/** Cut hex */
+function cutHex(str) {
+ return (str.charAt(0)=="#") ? str.substring(1,7):str;
+}
+
+/** Human friendly number suffix - From: http://stackoverflow.com/questions/2692323/code-golf-friendly-number-abbreviator */
+function humanFriendlyNumber( n, d ) {
+ var p, d2, i, s;
+
+ p = Math.pow;
+ d2 = p(10, d);
+ i = 7;
+ while( i ) {
+ s = p(10,i--*3);
+ if( s <= n ) {
+ n = Math.round(n*d2/s)/d2+"KMGTPE"[i];
+ }
+ }
+ return n;
+}
+
+/** Get style */
+function getStyle(oElm, strCssRule){
+ var strValue = "";
+ if(document.defaultView && document.defaultView.getComputedStyle){
+ strValue = document.defaultView.getComputedStyle(oElm).getPropertyValue(strCssRule);
+ }
+ else if(oElm.currentStyle){
+ strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
+ return p1.toUpperCase();
+ });
+ strValue = oElm.currentStyle[strCssRule];
+ }
+ return strValue;
+}
+
+/** Create Element NS Ready */
+function onCreateElementNsReady(func) {
+ if (document.createElementNS !== undefined) {
+ func();
+ } else {
+ setTimeout(function() { onCreateElementNsReady(func); }, 100);
+ }
+}
+
+/** Get IE version */
+// ----------------------------------------------------------
+// A short snippet for detecting versions of IE in JavaScript
+// without resorting to user-agent sniffing
+// ----------------------------------------------------------
+// If you're not in IE (or IE version is less than 5) then:
+// ie === undefined
+// If you're in IE (>=5) then you can determine which version:
+// ie === 7; // IE7
+// Thus, to detect IE:
+// if (ie) {}
+// And to detect the version:
+// ie === 6 // IE6
+// ie > 7 // IE8, IE9 ...
+// ie < 9 // Anything less than IE9
+// ----------------------------------------------------------
+// UPDATE: Now using Live NodeList idea from @jdalton
+var ie = (function(){
+
+ var undef,
+ v = 3,
+ div = document.createElement('div'),
+ all = div.getElementsByTagName('i');
+
+ while (
+ div.innerHTML = '',
+ all[0]
+ );
+ return v > 4 ? v : undef;
+}());
\ No newline at end of file
From 7394f41f83e05c66d57bff6b78f448500c545438 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Tue, 16 Apr 2013 14:40:11 +0200
Subject: [PATCH 09/72] [IMP] crm: salesteams, add target_invoice field
bzr revid: chm@openerp.com-20130416124011-qbyf0wa5pvs36jp3
---
addons/crm/static/src/js/crm_case_section.js | 2 +-
addons/sale_crm/sale_crm.py | 3 ++-
addons/sale_crm/sale_crm_view.xml | 18 ++++++++++++++++--
3 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js
index b4341989358..58e42b71542 100644
--- a/addons/crm/static/src/js/crm_case_section.js
+++ b/addons/crm/static/src/js/crm_case_section.js
@@ -32,7 +32,7 @@ openerp.crm = function(openerp) {
if ($el.data('action')) {
$el.click(function (event) {
event.stopPropagation();
- if (!$el.find(".oe_justgage_edit").size()) {
+ if (self.view.is_action_enabled('edit') && !$el.find(".oe_justgage_edit").size()) {
var $svg = $el.find('svg');
$div = $('
');
$input = $(' ').val($el.data('value'));
diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py
index edaf5de3829..b0e4cf974e2 100644
--- a/addons/sale_crm/sale_crm.py
+++ b/addons/sale_crm/sale_crm.py
@@ -58,7 +58,8 @@ class crm_case_section(osv.osv):
'sum_month_invoice': fields.function(_get_sum_month_invoice,
string='Total invoiced this month',
type='integer', readonly=True),
- 'forcasted': fields.integer(string='Total forcasted for this month'),
+ 'forcasted': fields.integer(string='Total forcasted'),
+ 'target_invoice': fields.integer(string='Target Invoice'),
}
def action_forcasted(self, cr, uid, id, value, context=None):
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index 5f3a42003be..437a894e638 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -207,6 +207,19 @@
+
+ crm.case.section.form
+ crm.case.section
+
+
+
+
+
+
+
+
+
+
crm.case.section.kanban
crm.case.section
@@ -219,6 +232,7 @@
+
@@ -239,8 +253,8 @@
-
Invoiced
-
Forcasted
+
Invoiced
+
Forcasted
From f6b6035922ac4637888a53fccb37731adad2932b Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Tue, 16 Apr 2013 15:45:20 +0200
Subject: [PATCH 10/72] [IMP] crm: salesteams, add target_invoice_duration
attribute
bzr revid: chm@openerp.com-20130416134520-znocwh73q6x3nk1r
---
addons/sale_crm/sale_crm.py | 47 +++++++++++++++++++++++++------
addons/sale_crm/sale_crm_view.xml | 23 +++++++++++----
2 files changed, 56 insertions(+), 14 deletions(-)
diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py
index b0e4cf974e2..dc3be897c35 100644
--- a/addons/sale_crm/sale_crm.py
+++ b/addons/sale_crm/sale_crm.py
@@ -19,8 +19,10 @@
#
##############################################################################
-from datetime import datetime
+from datetime import date
+from dateutil.relativedelta import relativedelta
from openerp.osv import osv, fields
+from openerp.tools.translate import _
class sale_order(osv.osv):
@@ -35,14 +37,35 @@ class sale_order(osv.osv):
class crm_case_section(osv.osv):
_inherit = 'crm.case.section'
- def _get_sum_month_invoice(self, cr, uid, ids, field_name, arg, context=None):
+ def _get_sum_duration_invoice(self, cr, uid, ids, field_name, arg, context=None):
res = dict.fromkeys(ids, 0)
obj = self.pool.get('account.invoice.report')
- when = datetime.today()
- for section_id in ids:
- invoice_ids = obj.search(cr, uid, [("section_id", "=", section_id), ('state', 'not in', ['draft', 'cancel']), ('year', '=', when.year), ('month', '=', when.month > 9 and when.month or "0%s" % when.month)], context=context)
+
+ previous_month = {
+ "monthly": 0,
+ "semesterly": 2,
+ "semiannually": 5,
+ "annually": 11
+ }
+ for section in self.browse(cr, uid, ids, context=context):
+ when = date.today().replace(day=1) + relativedelta(months=-previous_month[section.target_invoice_duration])
+
+ invoice_ids = obj.search(cr, uid, [("section_id", "=", section.id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', when)], context=context)
for invoice in obj.browse(cr, uid, invoice_ids, context=context):
- res[section_id] += invoice.price_total
+ res[section.id] += invoice.price_total
+ return res
+
+ def _get_target_invoice_duration_txt(self, cr, uid, ids, field_name, arg, context=None):
+ res = dict.fromkeys(ids, "")
+
+ duration_txt = {
+ "monthly": _("this month"),
+ "semesterly": _("this semester"),
+ "semiannually": _("this semi"),
+ "annually": _("this year")
+ }
+ for section in self.browse(cr, uid, ids, context=context):
+ res[section.id] = duration_txt[section.target_invoice_duration]
return res
_columns = {
@@ -55,11 +78,19 @@ class crm_case_section(osv.osv):
'invoice_ids': fields.one2many('account.invoice', 'section_id',
string='Invoices', readonly=True,
domain=[('state', 'not in', ['draft', 'cancel'])]),
- 'sum_month_invoice': fields.function(_get_sum_month_invoice,
- string='Total invoiced this month',
+ 'sum_duration_invoice': fields.function(_get_sum_duration_invoice,
+ string='Total invoiced',
type='integer', readonly=True),
'forcasted': fields.integer(string='Total forcasted'),
'target_invoice': fields.integer(string='Target Invoice'),
+ 'target_invoice_duration': fields.selection([("monthly", "Monthly"), ("semesterly", "Semesterly"), ("semiannually", "Semiannually"), ("annually", "Annually")],
+ string='Report duration view', required=True),
+ 'target_invoice_duration_txt': fields.function(_get_target_invoice_duration_txt,
+ string='Duration',
+ type="string", readonly=True),
+ }
+ _defaults = {
+ 'target_invoice_duration': "monthly",
}
def action_forcasted(self, cr, uid, id, value, context=None):
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index 437a894e638..6ba3886cb1b 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -213,9 +213,12 @@
-
-
-
+
+
+
+
+
+
@@ -230,9 +233,10 @@
-
+
+
@@ -253,8 +257,15 @@
-
Invoiced
-
Forcasted
+
Invoiced
+
Forcasted
From f515c23a13e4db6107c02d79feedb3b74a8979ff Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Wed, 17 Apr 2013 12:43:02 +0200
Subject: [PATCH 11/72] [IMP] crm: salesteams
bzr revid: chm@openerp.com-20130417104302-g4zjti19suenxnky
---
addons/crm/crm.py | 19 +++++++++
addons/crm/crm_case_section_view.xml | 26 ++++++------
addons/crm/static/src/css/crm.css | 26 ++++++++++--
addons/crm/static/src/js/crm_case_section.js | 33 +++++++++------
addons/sale_crm/sale_crm.py | 23 +----------
addons/sale_crm/sale_crm_view.xml | 42 +++++++++-----------
6 files changed, 93 insertions(+), 76 deletions(-)
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index 7b6fe02afa8..8c573740076 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -44,6 +44,13 @@ AVAILABLE_PRIORITIES = [
('5', 'Lowest'),
]
+duration_txt = {
+ "monthly": _("this month"),
+ "semesterly": _("this semester"),
+ "semiannually": _("this semi"),
+ "annually": _("this year")
+}
+
class crm_case_channel(osv.osv):
_name = "crm.case.channel"
_description = "Channels"
@@ -110,6 +117,12 @@ class crm_case_section(osv.osv):
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_target_duration_txt(self, cr, uid, ids, field_name, arg, context=None):
+ res = dict.fromkeys(ids, "")
+ for section in self.browse(cr, uid, ids, context=context):
+ res[section.id] = duration_txt[section.target_duration]
+ 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),
@@ -138,6 +151,11 @@ class crm_case_section(osv.osv):
'color': fields.integer('Color Index'),
'use_leads': fields.boolean('Leads',
help="This enables the management of leads in the sales team. Otherwise the sales team manages only opportunities."),
+ 'target_duration': fields.selection([("monthly", "Monthly"), ("semesterly", "Semesterly"), ("semiannually", "Semiannually"), ("annually", "Annually")],
+ string='Report duration view', required=True),
+ 'target_duration_txt': fields.function(_get_target_duration_txt,
+ string='Duration',
+ type="string", readonly=True),
}
def _get_stage_common(self, cr, uid, context):
@@ -148,6 +166,7 @@ class crm_case_section(osv.osv):
'active': 1,
'stage_ids': _get_stage_common,
'use_leads': True,
+ 'target_duration': "monthly",
}
_sql_constraints = [
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index 1c812b23685..44f45f2f94a 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -81,6 +81,7 @@
+
@@ -98,21 +99,15 @@
%%
-
@@ -178,6 +173,9 @@
+
+
+
diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css
index d5705e01f27..a64dd76b5fb 100644
--- a/addons/crm/static/src/css/crm.css
+++ b/addons/crm/static/src/css/crm.css
@@ -20,11 +20,27 @@
}
.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list {
+ position: relative;
margin: 10px;
}
-.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list a {
- width: 160px;
- display: inline-block;
+.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div {
+ width: 120px;
+ height: 20px;
+ margin: 0;
+}
+.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div:nth-child(2n) {
+ position: absolute;
+ right: 0px;
+}
+.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div:nth-child(2n) a {
+ position: relative;
+ top: -20px;
+}
+.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_kanban_content > div {
+ clear: both;
+}
+.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div a:first-child {
+ width: 80px;
}
.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list a:hover {
text-decoration: underline !important;
@@ -44,4 +60,8 @@
.openerp .oe_kanban_view .oe_justgage {
color: black;
display: inline-block;
+}
+
+.openerp .oe_kanban_view .oe_sparkline_bar {
+ float: right;
}
\ No newline at end of file
diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js
index 58e42b71542..4e69bf8fc6e 100644
--- a/addons/crm/static/src/js/crm_case_section.js
+++ b/addons/crm/static/src/js/crm_case_section.js
@@ -32,29 +32,36 @@ openerp.crm = function(openerp) {
if ($el.data('action')) {
$el.click(function (event) {
event.stopPropagation();
- if (self.view.is_action_enabled('edit') && !$el.find(".oe_justgage_edit").size()) {
+ if (!self.view.is_action_enabled('edit')) {
+ return;
+ }
+ if ($el.find(".oe_justgage_edit").size()) {
+ $el.find(".oe_justgage_edit").remove();
+ } else {
var $svg = $el.find('svg');
$div = $('
');
$input = $(' ').val($el.data('value'));
$div.append($input);
$el.prepend($div)
- $input.focus();
- $input.keydown(function (event) {
- event.stopPropagation();
- if (event.keyCode == 13 || event.keyCode == 9) {
- if ($input.val() != $el.data('value')) {
- self.view.dataset.call($el.data('action'), [self.id, $input.val()]).then(function () {
- self.do_reload();
- });
- } else {
- $div.remove();
+ $input.focus()
+ .keydown(function (event) {
+ event.stopPropagation();
+ if (event.keyCode == 13 || event.keyCode == 9) {
+ if ($input.val() != $el.data('value')) {
+ self.view.dataset.call($el.data('action'), [self.id, $input.val()]).then(function () {
+ self.do_reload();
+ });
+ } else {
+ $div.remove();
+ }
}
- }
- });
+ })
+ .click(function (event) {event.stopPropagation();});
}
});
}
});
+ setTimeout(function () {self.$(".oe_sparkline_bar").sparkline('html', {type: 'bar', barWidth: 5, zeroColor: '#ff0000'} );}, 0);
});
}
},
diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py
index dc3be897c35..433df31a669 100644
--- a/addons/sale_crm/sale_crm.py
+++ b/addons/sale_crm/sale_crm.py
@@ -48,26 +48,13 @@ class crm_case_section(osv.osv):
"annually": 11
}
for section in self.browse(cr, uid, ids, context=context):
- when = date.today().replace(day=1) + relativedelta(months=-previous_month[section.target_invoice_duration])
+ when = date.today().replace(day=1) + relativedelta(months=-previous_month[section.target_duration])
invoice_ids = obj.search(cr, uid, [("section_id", "=", section.id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', when)], context=context)
for invoice in obj.browse(cr, uid, invoice_ids, context=context):
res[section.id] += invoice.price_total
return res
- def _get_target_invoice_duration_txt(self, cr, uid, ids, field_name, arg, context=None):
- res = dict.fromkeys(ids, "")
-
- duration_txt = {
- "monthly": _("this month"),
- "semesterly": _("this semester"),
- "semiannually": _("this semi"),
- "annually": _("this year")
- }
- for section in self.browse(cr, uid, ids, context=context):
- res[section.id] = duration_txt[section.target_invoice_duration]
- return res
-
_columns = {
'quotation_ids': fields.one2many('sale.order', 'section_id',
string='Quotations', readonly=True,
@@ -83,14 +70,6 @@ class crm_case_section(osv.osv):
type='integer', readonly=True),
'forcasted': fields.integer(string='Total forcasted'),
'target_invoice': fields.integer(string='Target Invoice'),
- 'target_invoice_duration': fields.selection([("monthly", "Monthly"), ("semesterly", "Semesterly"), ("semiannually", "Semiannually"), ("annually", "Annually")],
- string='Report duration view', required=True),
- 'target_invoice_duration_txt': fields.function(_get_target_invoice_duration_txt,
- string='Duration',
- type="string", readonly=True),
- }
- _defaults = {
- 'target_invoice_duration': "monthly",
}
def action_forcasted(self, cr, uid, id, value, context=None):
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index 6ba3886cb1b..7cabe1c13ec 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -213,12 +213,9 @@
-
-
-
-
-
-
+
+
+
@@ -236,35 +233,32 @@
-
-
-
- Quotations
- Quotation
-
-
-
- Sales Orders
- Sales Order
-
-
-
- Invoices
- Invoice
-
+
+
+
Invoiced
+ t-att-data-label="record.target_duration_txt.raw_value">Invoiced
Forcasted
From d0920dea504b0a9872586e72032caebbad0e991f Mon Sep 17 00:00:00 2001
From: "Ishwar Malvi (OpenERP Trainee)"
Date: Wed, 17 Apr 2013 17:01:19 +0530
Subject: [PATCH 12/72] [IMP] when create contact of a supplier it should be
supplier instead of customer.
bzr revid: nco@tinyerp.com-20130417113119-ilgy4k945h31azod
---
openerp/addons/base/res/res_partner.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/openerp/addons/base/res/res_partner.py b/openerp/addons/base/res/res_partner.py
index 31e2d44b306..3085a5bfde5 100644
--- a/openerp/addons/base/res/res_partner.py
+++ b/openerp/addons/base/res/res_partner.py
@@ -378,6 +378,7 @@ class res_partner(osv.osv, format_address):
context = {}
# Update parent and siblings records
if vals.get('parent_id'):
+ is_supplier = self.browse(cr, uid, vals.get('parent_id'), context=context).supplier
if 'use_parent_address' in vals:
use_parent_address = vals['use_parent_address']
else:
@@ -395,6 +396,9 @@ class res_partner(osv.osv, format_address):
for key, value in onchange_values.iteritems()
if key in ADDRESS_FIELDS and key not in vals))
+ if is_supplier:
+ vals.update({'supplier': 1,'customer': 0})
+
return super(res_partner, self).create(cr, uid, vals, context=context)
def update_address(self, cr, uid, ids, vals, context=None):
From 797cecf8dff61f20011a1fa4e4ea1b5321190f8e Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Wed, 17 Apr 2013 15:17:59 +0200
Subject: [PATCH 13/72] [IMP] crm: salesteams sparkline for lead and
opportunity
bzr revid: chm@openerp.com-20130417131759-ysnbosoikd16kwlo
---
addons/crm/crm.py | 68 ++++++++++++++++----
addons/crm/crm_case_section_view.xml | 12 ++--
addons/crm/static/src/js/crm_case_section.js | 2 +-
addons/sale_crm/sale_crm_view.xml | 8 +--
4 files changed, 67 insertions(+), 23 deletions(-)
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index 8c573740076..d6cea46f0f7 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -19,13 +19,11 @@
#
##############################################################################
-import base64
-import time
-from lxml import etree
from openerp.osv import fields
from openerp.osv import osv
-from openerp import tools
from openerp.tools.translate import _
+from datetime import date
+from dateutil.relativedelta import relativedelta
MAX_LEVEL = 15
AVAILABLE_STATES = [
@@ -44,13 +42,22 @@ AVAILABLE_PRIORITIES = [
('5', 'Lowest'),
]
-duration_txt = {
+DURATION_TXT = {
"monthly": _("this month"),
"semesterly": _("this semester"),
"semiannually": _("this semi"),
"annually": _("this year")
}
+MONTHS = {
+ "monthly": 1,
+ "semesterly": 3,
+ "semiannually": 6,
+ "annually": 12
+}
+
+_strftime = '%Y-%m-%d %H:%M:%S'
+
class crm_case_channel(osv.osv):
_name = "crm.case.channel"
_description = "Channels"
@@ -120,7 +127,45 @@ class crm_case_section(osv.osv):
def _get_target_duration_txt(self, cr, uid, ids, field_name, arg, context=None):
res = dict.fromkeys(ids, "")
for section in self.browse(cr, uid, ids, context=context):
- res[section.id] = duration_txt[section.target_duration]
+ res[section.id] = DURATION_TXT[section.target_duration]
+ return res
+
+ def _get_open_lead_per_duration(self, cr, uid, ids, field_name, arg, context=None):
+ res = dict.fromkeys(ids, [])
+ lead_obj = self.pool.get('crm.lead')
+ first_day = date.today().replace(day=1)
+
+ for section in self.browse(cr, uid, ids, context=context):
+ dates = [first_day + relativedelta(months=-(MONTHS[section.target_duration]*(key+1)-1)) for key in range(0, 5)]
+ nb_leads = []
+ for when in range(0, 5):
+ domain = [('type', '!=', 'opportunity'), ("section_id", "=", section.id), ('create_date', '>=', dates[when].strftime(_strftime))]
+ if when:
+ domain += [('create_date', '<', dates[when-1].strftime(_strftime))]
+ nb_leads.append(lead_obj.search(cr, uid, domain, context=context, count=True))
+ nb_leads.reverse()
+ res[section.id] = nb_leads
+ return res
+
+ def _get_won_opportunity_per_duration(self, cr, uid, ids, field_name, arg, context=None):
+ res = dict.fromkeys(ids, [])
+ lead_obj = self.pool.get('crm.lead')
+ first_day = date.today().replace(day=1)
+
+ for section in self.browse(cr, uid, ids, context=context):
+ dates = [first_day + relativedelta(months=-(MONTHS[section.target_duration]*(key+1)-1)) for key in range(0, 5)]
+ nb_leads = []
+ for when in range(0, 5):
+ domain = [('type', '=', 'opportunity'), ("section_id", "=", section.id), ('state', '!=', 'cancel'), ('date_closed', '>=', dates[when].strftime(_strftime))]
+ if when:
+ domain += [('date_closed', '<', dates[when-1].strftime(_strftime))]
+ rate = 0
+ opportunity_ids = lead_obj.search(cr, uid, domain, context=context)
+ for opportunity in lead_obj.browse(cr, uid, opportunity_ids, context=context):
+ rate += opportunity.planned_revenue
+ nb_leads.append(rate)
+ nb_leads.reverse()
+ res[section.id] = nb_leads
return res
_columns = {
@@ -142,20 +187,19 @@ class crm_case_section(osv.osv):
'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True,
help="The email address associated with this team. New emails received will automatically "
"create new leads assigned to the team."),
- 'open_lead_ids': fields.one2many('crm.lead', 'section_id',
- string='Open Leads', readonly=True,
- domain=['&', ('type', '!=', 'opportunity'), ('state', 'not in', ['done', 'cancel'])]),
- 'open_opportunity_ids': fields.one2many('crm.lead', 'section_id',
- string='Open Opportunities', readonly=True,
- domain=['&', '|', ('type', '=', 'opportunity'), ('type', '=', 'both'), ('state', 'not in', ['done', 'cancel'])]),
'color': fields.integer('Color Index'),
'use_leads': fields.boolean('Leads',
help="This enables the management of leads in the sales team. Otherwise the sales team manages only opportunities."),
+
'target_duration': fields.selection([("monthly", "Monthly"), ("semesterly", "Semesterly"), ("semiannually", "Semiannually"), ("annually", "Annually")],
string='Report duration view', required=True),
'target_duration_txt': fields.function(_get_target_duration_txt,
string='Duration',
type="string", readonly=True),
+
+ 'open_lead_per_duration': fields.function(_get_open_lead_per_duration, string='Open Leads per duration', type="string", readonly=True),
+ 'won_opportunity_per_duration': fields.function(_get_won_opportunity_per_duration, string='Rate of opporunities whon per duration', type="string", readonly=True)
+
}
def _get_stage_common(self, cr, uid, context):
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index 44f45f2f94a..c32ce4e6451 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -79,9 +79,9 @@
-
-
+
+
@@ -100,12 +100,12 @@
diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js
index 4e69bf8fc6e..a228d09f4ed 100644
--- a/addons/crm/static/src/js/crm_case_section.js
+++ b/addons/crm/static/src/js/crm_case_section.js
@@ -61,7 +61,7 @@ openerp.crm = function(openerp) {
});
}
});
- setTimeout(function () {self.$(".oe_sparkline_bar").sparkline('html', {type: 'bar', barWidth: 5, zeroColor: '#ff0000'} );}, 0);
+ setTimeout(function () {self.$(".oe_sparkline_bar").sparkline('html', {type: 'bar', barWidth: 5} );}, 0);
});
}
},
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index 7cabe1c13ec..f3453dda7a0 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -226,7 +226,7 @@
-
+
@@ -237,16 +237,16 @@
From 2471265eaef15688113b46a0db4753e88bf95cce Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Thu, 18 Apr 2013 11:17:59 +0200
Subject: [PATCH 14/72] [IMP] salesteams: crm_case_section kanban sparkline
with html tooltip
bzr revid: chm@openerp.com-20130418091759-7q02xo1p4pqauowa
---
addons/crm/crm.py | 1 -
addons/crm/crm_case_section_view.xml | 4 ++--
addons/crm/static/src/css/crm.css | 1 +
addons/crm/static/src/js/crm_case_section.js | 9 ++++++++-
addons/sale_crm/sale_crm_view.xml | 17 ++++++++++-------
5 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index d6cea46f0f7..9a1c2856e25 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -199,7 +199,6 @@ class crm_case_section(osv.osv):
'open_lead_per_duration': fields.function(_get_open_lead_per_duration, string='Open Leads per duration', type="string", readonly=True),
'won_opportunity_per_duration': fields.function(_get_won_opportunity_per_duration, string='Rate of opporunities whon per duration', type="string", readonly=True)
-
}
def _get_stage_common(self, cr, uid, context):
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index c32ce4e6451..183eeceedbd 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -101,11 +101,11 @@
diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css
index a64dd76b5fb..128f3daf31c 100644
--- a/addons/crm/static/src/css/crm.css
+++ b/addons/crm/static/src/css/crm.css
@@ -64,4 +64,5 @@
.openerp .oe_kanban_view .oe_sparkline_bar {
float: right;
+ height: 20px;
}
\ No newline at end of file
diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js
index a228d09f4ed..fd307880afd 100644
--- a/addons/crm/static/src/js/crm_case_section.js
+++ b/addons/crm/static/src/js/crm_case_section.js
@@ -61,7 +61,14 @@ openerp.crm = function(openerp) {
});
}
});
- setTimeout(function () {self.$(".oe_sparkline_bar").sparkline('html', {type: 'bar', barWidth: 5} );}, 0);
+ setTimeout(function () {
+ self.$(".oe_sparkline_bar").each(function () {
+ var $el = $(this);
+ $el.data("title", $el.html());
+ $el.sparkline($el.data("value").split(','), {type: 'bar', barWidth: 5} );
+ $el.tipsy({'delayIn': 0, 'html': true, 'title': function(){return $(this).data("title")}, 'gravity': 'n'});
+ });
+ }, 0);
});
}
},
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index f3453dda7a0..961ac059fa2 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -227,26 +227,29 @@
+
+
+
+
From 66c07802837757a983ba3e349548ba4415a8442b Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Thu, 18 Apr 2013 12:05:25 +0200
Subject: [PATCH 15/72] [IMP] salesteams: sale_crm: sparkline for quotation,
sales orders and invoices
bzr revid: chm@openerp.com-20130418100525-zmxq06ups6o89stz
---
addons/crm/crm_case_section_view.xml | 4 +-
addons/sale_crm/sale_crm.py | 94 ++++++++++++++++++++++------
addons/sale_crm/sale_crm_view.xml | 24 +++----
3 files changed, 86 insertions(+), 36 deletions(-)
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index 183eeceedbd..348d1aef237 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -101,11 +101,11 @@
diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py
index 433df31a669..13e814ce432 100644
--- a/addons/sale_crm/sale_crm.py
+++ b/addons/sale_crm/sale_crm.py
@@ -22,7 +22,15 @@
from datetime import date
from dateutil.relativedelta import relativedelta
from openerp.osv import osv, fields
-from openerp.tools.translate import _
+
+MONTHS = {
+ "monthly": 1,
+ "semesterly": 3,
+ "semiannually": 6,
+ "annually": 12
+}
+
+_strftime = '%Y-%m-%d %H:%M:%S'
class sale_order(osv.osv):
@@ -37,22 +45,67 @@ class sale_order(osv.osv):
class crm_case_section(osv.osv):
_inherit = 'crm.case.section'
- def _get_sum_duration_invoice(self, cr, uid, ids, field_name, arg, context=None):
- res = dict.fromkeys(ids, 0)
- obj = self.pool.get('account.invoice.report')
+ def _get_created_quotation_per_duration(self, cr, uid, ids, field_name, arg, context=None):
+ res = dict.fromkeys(ids, [])
+ obj = self.pool.get('sale.order')
+ first_day = date.today().replace(day=1)
- previous_month = {
- "monthly": 0,
- "semesterly": 2,
- "semiannually": 5,
- "annually": 11
- }
for section in self.browse(cr, uid, ids, context=context):
- when = date.today().replace(day=1) + relativedelta(months=-previous_month[section.target_duration])
+ dates = [first_day + relativedelta(months=-(MONTHS[section.target_duration]*(key+1)-1)) for key in range(0, 5)]
+ rate_invoice = []
+ for when in range(0, 5):
+ domain = [("section_id", "=", section.id), ('state', 'in', ['draft', 'sent']), ('date_order', '>=', dates[when].strftime(_strftime))]
+ if when:
+ domain += [('date_order', '<', dates[when-1].strftime(_strftime))]
+ rate = 0
+ opportunity_ids = obj.search(cr, uid, domain, context=context)
+ for invoice in obj.browse(cr, uid, opportunity_ids, context=context):
+ rate += invoice.amount_total
+ rate_invoice.append(rate)
+ rate_invoice.reverse()
+ res[section.id] = rate_invoice
+ return res
- invoice_ids = obj.search(cr, uid, [("section_id", "=", section.id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', when)], context=context)
- for invoice in obj.browse(cr, uid, invoice_ids, context=context):
- res[section.id] += invoice.price_total
+ def _get_validate_saleorder_per_duration(self, cr, uid, ids, field_name, arg, context=None):
+ res = dict.fromkeys(ids, [])
+ obj = self.pool.get('sale.order')
+ first_day = date.today().replace(day=1)
+
+ for section in self.browse(cr, uid, ids, context=context):
+ dates = [first_day + relativedelta(months=-(MONTHS[section.target_duration]*(key+1)-1)) for key in range(0, 5)]
+ rate_invoice = []
+ for when in range(0, 5):
+ domain = [("section_id", "=", section.id), ('state', 'not in', ['draft', 'sent']), ('date_confirm', '>=', dates[when].strftime(_strftime))]
+ if when:
+ domain += [('date_confirm', '<', dates[when-1].strftime(_strftime))]
+ rate = 0
+ opportunity_ids = obj.search(cr, uid, domain, context=context)
+ for invoice in obj.browse(cr, uid, opportunity_ids, context=context):
+ rate += invoice.amount_total
+ rate_invoice.append(rate)
+ rate_invoice.reverse()
+ res[section.id] = rate_invoice
+ return res
+
+ def _get_sent_invoice_per_duration(self, cr, uid, ids, field_name, arg, context=None):
+ res = dict.fromkeys(ids, [])
+ obj = self.pool.get('account.invoice.report')
+ first_day = date.today().replace(day=1)
+
+ for section in self.browse(cr, uid, ids, context=context):
+ dates = [first_day + relativedelta(months=-(MONTHS[section.target_duration]*(key+1)-1)) for key in range(0, 5)]
+ rate_invoice = []
+ for when in range(0, 5):
+ domain = [("section_id", "=", section.id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', dates[when].strftime(_strftime))]
+ if when:
+ domain += [('date', '<', dates[when-1].strftime(_strftime))]
+ rate = 0
+ opportunity_ids = obj.search(cr, uid, domain, context=context)
+ for invoice in obj.browse(cr, uid, opportunity_ids, context=context):
+ rate += invoice.price_total
+ rate_invoice.append(rate)
+ rate_invoice.reverse()
+ res[section.id] = rate_invoice
return res
_columns = {
@@ -65,15 +118,16 @@ class crm_case_section(osv.osv):
'invoice_ids': fields.one2many('account.invoice', 'section_id',
string='Invoices', readonly=True,
domain=[('state', 'not in', ['draft', 'cancel'])]),
- 'sum_duration_invoice': fields.function(_get_sum_duration_invoice,
- string='Total invoiced',
- type='integer', readonly=True),
- 'forcasted': fields.integer(string='Total forcasted'),
+
+ 'forecast': fields.integer(string='Total forecast'),
'target_invoice': fields.integer(string='Target Invoice'),
+ 'created_quotation_per_duration': fields.function(_get_created_quotation_per_duration, string='Rate of created quotation per duration', type="string", readonly=True),
+ 'validate_saleorder_per_duration': fields.function(_get_validate_saleorder_per_duration, string='Rate of validate sales orders per duration', type="string", readonly=True),
+ 'sent_invoice_per_duration': fields.function(_get_sent_invoice_per_duration, string='Rate of sent invoices per duration', type="string", readonly=True),
}
- def action_forcasted(self, cr, uid, id, value, context=None):
- return self.write(cr, uid, [id], {'forcasted': value}, context=context)
+ def action_forecast(self, cr, uid, id, value, context=None):
+ return self.write(cr, uid, [id], {'forecast': value}, context=context)
class res_users(osv.Model):
_inherit = 'res.users'
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index 961ac059fa2..be66724ad24 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -227,42 +227,38 @@
+
+
+
-
-
-
-
-
-
-
-
+
Invoiced
Forcasted
+ data-action="action_forecast">Forecast
From f55291bcf9528662fa36c90032180451fcf91f54 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Thu, 18 Apr 2013 12:13:21 +0200
Subject: [PATCH 16/72] [IMP] salesteams: fix oe_kanban_card size
bzr revid: chm@openerp.com-20130418101321-xktltdb86nnvfeys
---
addons/crm/static/src/js/crm_case_section.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js
index fd307880afd..ad29bfcb62f 100644
--- a/addons/crm/static/src/js/crm_case_section.js
+++ b/addons/crm/static/src/js/crm_case_section.js
@@ -68,6 +68,7 @@ openerp.crm = function(openerp) {
$el.sparkline($el.data("value").split(','), {type: 'bar', barWidth: 5} );
$el.tipsy({'delayIn': 0, 'html': true, 'title': function(){return $(this).data("title")}, 'gravity': 'n'});
});
+ self.$(".oe_kanban_card").css('min-height', "");
}, 0);
});
}
From d388383adb3347149602082868141f61c2f73ecd Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Thu, 18 Apr 2013 12:30:15 +0200
Subject: [PATCH 17/72] [IMP] salesteams: gauge label color, action context
bzr revid: chm@openerp.com-20130418103015-si1vjq1scivoqxfu
---
addons/crm/static/src/js/crm_case_section.js | 4 +++-
addons/sale/sale_view.xml | 2 +-
addons/sale_crm/sale_crm.py | 2 +-
addons/sale_crm/sale_crm_view.xml | 2 --
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js
index ad29bfcb62f..6c8a20dbf1a 100644
--- a/addons/crm/static/src/js/crm_case_section.js
+++ b/addons/crm/static/src/js/crm_case_section.js
@@ -21,6 +21,9 @@ openerp.crm = function(openerp) {
max: +$el.data('max'),
relativeGaugeSize: true,
humanFriendly: true,
+ titleFontColor: '#333333',
+ valueFontColor: '#333333',
+ labelFontColor: '#000',
label: $el.data('label'),
levelColors: [
"#ff0000",
@@ -68,7 +71,6 @@ openerp.crm = function(openerp) {
$el.sparkline($el.data("value").split(','), {type: 'bar', barWidth: 5} );
$el.tipsy({'delayIn': 0, 'html': true, 'title': function(){return $(this).data("title")}, 'gravity': 'n'});
});
- self.$(".oe_kanban_card").css('min-height', "");
}, 0);
});
}
diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml
index 9a4866d14f1..76e2f08aeb3 100644
--- a/addons/sale/sale_view.xml
+++ b/addons/sale/sale_view.xml
@@ -307,7 +307,7 @@
-
+
diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py
index 13e814ce432..bcc2c28f417 100644
--- a/addons/sale_crm/sale_crm.py
+++ b/addons/sale_crm/sale_crm.py
@@ -120,7 +120,7 @@ class crm_case_section(osv.osv):
domain=[('state', 'not in', ['draft', 'cancel'])]),
'forecast': fields.integer(string='Total forecast'),
- 'target_invoice': fields.integer(string='Target Invoice'),
+ 'target_invoice': fields.integer(string='Target Invoicing'),
'created_quotation_per_duration': fields.function(_get_created_quotation_per_duration, string='Rate of created quotation per duration', type="string", readonly=True),
'validate_saleorder_per_duration': fields.function(_get_validate_saleorder_per_duration, string='Rate of validate sales orders per duration', type="string", readonly=True),
'sent_invoice_per_duration': fields.function(_get_sent_invoice_per_duration, string='Rate of sent invoices per duration', type="string", readonly=True),
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index be66724ad24..19cf0c5b9d2 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -130,7 +130,6 @@
{
'search_default_section_id': [active_id],
'default_section_id': active_id,
- 'search_default_my_sale_orders_filter': 1,
}
@@ -154,7 +153,6 @@
{
'search_default_section_id': [active_id],
'default_section_id': active_id, 'show_address': 1,
- 'search_default_my_sale_orders_filter': 1
}
[('state','in',('draft','sent','cancel'))]
From 985d397c805070ce63e663f927e4c9a0a3f9ace2 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Thu, 18 Apr 2013 12:52:06 +0200
Subject: [PATCH 18/72] [IMP] salesteams: css
bzr revid: chm@openerp.com-20130418105206-8niwlqxp63zyv71s
---
addons/crm/crm_case_section_view.xml | 2 +-
addons/crm/static/src/js/crm_case_section.js | 4 ++--
addons/sale_crm/sale_crm_view.xml | 7 +++++--
3 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index 348d1aef237..dd46d0570e9 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -173,7 +173,7 @@
-
+
diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js
index 6c8a20dbf1a..05b71ade809 100644
--- a/addons/crm/static/src/js/crm_case_section.js
+++ b/addons/crm/static/src/js/crm_case_section.js
@@ -2,9 +2,9 @@ openerp.crm = function(openerp) {
openerp.web_kanban.KanbanRecord.include({
renderElement: function () {
var rendering = this._super();
- var self = this;
- if (self.view.dataset.model === 'crm.case.section') {
+ if (this.view.dataset.model === 'crm.case.section') {
+ var self = this;
$.when(rendering).done(function() {
self.$(".oe_justgage").each(function () {
var $el = $(this);
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index 19cf0c5b9d2..2e055a91864 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -211,7 +211,7 @@
-
+
@@ -247,7 +247,7 @@
-
+
+
+ Not target invoicing define
+
From 33ae814ff6f91868465cafb5d07eaddacb02fe59 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Thu, 18 Apr 2013 17:18:27 +0200
Subject: [PATCH 19/72] [IMP] tools convert: allow to use relative delta in xml
file
bzr revid: chm@openerp.com-20130418151827-coda7ldtjd7hw7to
---
openerp/tools/convert.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/openerp/tools/convert.py b/openerp/tools/convert.py
index cdcaff35b9e..409b9810fab 100644
--- a/openerp/tools/convert.py
+++ b/openerp/tools/convert.py
@@ -48,6 +48,7 @@ except:
from datetime import datetime, timedelta
+from dateutil.relativedelta import relativedelta
from lxml import etree
import misc
from config import config
@@ -86,7 +87,9 @@ def _get_idref(self, cr, uid, model_str, context, idref):
idref2 = dict(idref,
time=time,
DateTime=datetime,
+ datetime=datetime,
timedelta=timedelta,
+ relativedelta=relativedelta,
version=openerp.release.major_version,
ref=_ref(self, cr),
pytz=pytz)
From d45ee609b4dd251b8b8182cb3ee75041639e4f1b Mon Sep 17 00:00:00 2001
From: "Ishwar Malvi (OpenERP Trainee)"
Date: Fri, 19 Apr 2013 14:12:18 +0530
Subject: [PATCH 20/72] [IMP] when create company from the supplier it should
be set supplier.
bzr revid: nco@tinyerp.com-20130419084218-e3pflb3jqq6kihfa
---
openerp/addons/base/res/res_partner_view.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/openerp/addons/base/res/res_partner_view.xml b/openerp/addons/base/res/res_partner_view.xml
index 88a1266b689..8251f4f6dd6 100644
--- a/openerp/addons/base/res/res_partner_view.xml
+++ b/openerp/addons/base/res/res_partner_view.xml
@@ -138,7 +138,7 @@
From 57fdf116353e0cff9336dc55a159070ecf59cd6f Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Fri, 19 Apr 2013 12:36:00 +0200
Subject: [PATCH 21/72] [IMP] salesteams: add demo data
bzr revid: chm@openerp.com-20130419103600-71j6munkzygnffe4
---
addons/crm/__openerp__.py | 4 +-
addons/crm/crm.py | 11 +++---
addons/crm/crm_data.xml | 4 +-
addons/crm/crm_demo.xml | 18 ++-------
addons/crm/crm_lead_demo.xml | 61 ++++++++++++++++++-------------
addons/crm/crm_phonecall_demo.xml | 6 +--
addons/sale/sale_demo.xml | 2 +
addons/sale_crm/__openerp__.py | 2 +-
addons/sale_crm/sale_crm.py | 16 ++++----
9 files changed, 61 insertions(+), 63 deletions(-)
diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py
index 577a51f4784..4b88fc13d6d 100644
--- a/addons/crm/__openerp__.py
+++ b/addons/crm/__openerp__.py
@@ -81,8 +81,6 @@ Dashboard for CRM will include:
'crm_lead_view.xml',
'crm_lead_menu.xml',
- 'crm_case_section_view.xml',
-
'crm_meeting_menu.xml',
'crm_phonecall_view.xml',
@@ -97,6 +95,8 @@ Dashboard for CRM will include:
'board_crm_view.xml',
'res_config_view.xml',
+
+ 'crm_case_section_view.xml',
],
'demo': [
'crm_demo.xml',
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index 9a1c2856e25..9787a21b9dd 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -19,6 +19,7 @@
#
##############################################################################
+from openerp import tools
from openerp.osv import fields
from openerp.osv import osv
from openerp.tools.translate import _
@@ -56,8 +57,6 @@ MONTHS = {
"annually": 12
}
-_strftime = '%Y-%m-%d %H:%M:%S'
-
class crm_case_channel(osv.osv):
_name = "crm.case.channel"
_description = "Channels"
@@ -139,9 +138,9 @@ class crm_case_section(osv.osv):
dates = [first_day + relativedelta(months=-(MONTHS[section.target_duration]*(key+1)-1)) for key in range(0, 5)]
nb_leads = []
for when in range(0, 5):
- domain = [('type', '!=', 'opportunity'), ("section_id", "=", section.id), ('create_date', '>=', dates[when].strftime(_strftime))]
+ domain = [("section_id", "=", section.id), '|', ('type', '=', 'lead'), ('date_open', '!=', None), ('create_date', '>=', dates[when].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
if when:
- domain += [('create_date', '<', dates[when-1].strftime(_strftime))]
+ domain += [('create_date', '<', dates[when-1].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
nb_leads.append(lead_obj.search(cr, uid, domain, context=context, count=True))
nb_leads.reverse()
res[section.id] = nb_leads
@@ -156,9 +155,9 @@ class crm_case_section(osv.osv):
dates = [first_day + relativedelta(months=-(MONTHS[section.target_duration]*(key+1)-1)) for key in range(0, 5)]
nb_leads = []
for when in range(0, 5):
- domain = [('type', '=', 'opportunity'), ("section_id", "=", section.id), ('state', '!=', 'cancel'), ('date_closed', '>=', dates[when].strftime(_strftime))]
+ domain = [("section_id", "=", section.id), ('state', '=', 'done'), ('type', '=', 'opportunity'), ('date_closed', '>=', dates[when].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
if when:
- domain += [('date_closed', '<', dates[when-1].strftime(_strftime))]
+ domain += [('date_closed', '<', dates[when-1].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
rate = 0
opportunity_ids = lead_obj.search(cr, uid, domain, context=context)
for opportunity in lead_obj.browse(cr, uid, opportunity_ids, context=context):
diff --git a/addons/crm/crm_data.xml b/addons/crm/crm_data.xml
index f24aee13c1d..153771532a5 100644
--- a/addons/crm/crm_data.xml
+++ b/addons/crm/crm_data.xml
@@ -27,8 +27,8 @@
- Sales
- Sales
+ Direct Marketing
+ DM
True
diff --git a/addons/crm/crm_demo.xml b/addons/crm/crm_demo.xml
index 203193dc3bc..0f7368fa907 100644
--- a/addons/crm/crm_demo.xml
+++ b/addons/crm/crm_demo.xml
@@ -7,9 +7,9 @@
- Sales Marketing Department
- SMD
-
+ Indirect Marketing
+ IM
+ semesterly
@@ -18,18 +18,6 @@
-
- Direct Marketing
- DM
-
-
-
-
- Online Support
- OS
-
-
-
OpenERP partners
True
diff --git a/addons/crm/crm_lead_demo.xml b/addons/crm/crm_lead_demo.xml
index 21d60ad2961..e7e6beae33e 100644
--- a/addons/crm/crm_lead_demo.xml
+++ b/addons/crm/crm_lead_demo.xml
@@ -20,7 +20,7 @@
1
-
+
Hello,
@@ -44,7 +44,7 @@ Can you send me the details ?
4
-
+
@@ -63,7 +63,7 @@ Can you send me the details ?
2
-
+
@@ -82,7 +82,7 @@ Can you send me the details ?
3
-
+
@@ -133,7 +133,7 @@ Contact: +1 813 494 5005
3
-
+
@@ -152,7 +152,7 @@ Contact: +1 813 494 5005
5
-
+
@@ -197,6 +197,9 @@ Contact: +1 813 494 5005
+
+
+
lead
@@ -211,7 +214,7 @@ Contact: +1 813 494 5005
2
-
+
Hi,
@@ -235,7 +238,7 @@ Andrew
3
-
+
@@ -291,7 +294,7 @@ Andrew
Meeting for pricing information.
-
+
@@ -317,7 +320,7 @@ Andrew
Send Catalogue by Email
-
+
@@ -326,7 +329,7 @@ Andrew
opportunity
Plan to buy RedHat servers
-
+
69 rue de Chimay
@@ -339,10 +342,11 @@ Andrew
Call to ask system requirement
-
+
-
+
+
@@ -367,6 +371,7 @@ Andrew
+
@@ -388,10 +393,11 @@ Andrew
Send price list regarding our interventions
-
+
-
+
+
@@ -412,7 +418,7 @@ Andrew
Call to define real needs about training
-
+
@@ -438,8 +444,9 @@ Andrew
Ask for the good receprion of the proposition
-
+
+
@@ -468,10 +475,11 @@ Andrew
3
-
+
-
+
+
@@ -486,7 +494,7 @@ Andrew
3
-
+
@@ -504,14 +512,15 @@ Andrew
5
-
+
+
opportunity
Need 20 Days of Consultancy
-
+
info@mycompany.net
@@ -525,6 +534,7 @@ Andrew
+
@@ -544,7 +554,7 @@ Andrew
2
Conf call with technical service
-
+
@@ -569,10 +579,11 @@ Andrew
Send Catalogue by Email
-
+
-
+
+
diff --git a/addons/crm/crm_phonecall_demo.xml b/addons/crm/crm_phonecall_demo.xml
index f441c29a7e6..4002e18815e 100644
--- a/addons/crm/crm_phonecall_demo.xml
+++ b/addons/crm/crm_phonecall_demo.xml
@@ -37,7 +37,7 @@
Ask for convenient time of meeting
open
+1 786 525 0724
-
+
@@ -50,7 +50,7 @@
done
(077) 582-4035
(077) 341-3591
-
+
@@ -74,7 +74,7 @@
Proposal for discount offer
open
+34 230 953 485
-
+
diff --git a/addons/sale/sale_demo.xml b/addons/sale/sale_demo.xml
index 428b586ffa8..eb937199aba 100644
--- a/addons/sale/sale_demo.xml
+++ b/addons/sale/sale_demo.xml
@@ -288,6 +288,8 @@
+
+
diff --git a/addons/sale_crm/__openerp__.py b/addons/sale_crm/__openerp__.py
index 6591589342e..7bc1dc24bfc 100644
--- a/addons/sale_crm/__openerp__.py
+++ b/addons/sale_crm/__openerp__.py
@@ -47,7 +47,7 @@ modules.
'security/ir.model.access.csv',
'report/sale_crm_account_invoice_report_view.xml',
],
- 'demo': [],
+ 'demo': ['sale_crm_demo.xml'],
'test': ['test/sale_crm.yml'],
'installable': True,
'auto_install': True,
diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py
index bcc2c28f417..46c0bcb4aa6 100644
--- a/addons/sale_crm/sale_crm.py
+++ b/addons/sale_crm/sale_crm.py
@@ -20,6 +20,7 @@
##############################################################################
from datetime import date
+from openerp import tools
from dateutil.relativedelta import relativedelta
from openerp.osv import osv, fields
@@ -30,9 +31,6 @@ MONTHS = {
"annually": 12
}
-_strftime = '%Y-%m-%d %H:%M:%S'
-
-
class sale_order(osv.osv):
_inherit = 'sale.order'
_columns = {
@@ -54,9 +52,9 @@ class crm_case_section(osv.osv):
dates = [first_day + relativedelta(months=-(MONTHS[section.target_duration]*(key+1)-1)) for key in range(0, 5)]
rate_invoice = []
for when in range(0, 5):
- domain = [("section_id", "=", section.id), ('state', 'in', ['draft', 'sent']), ('date_order', '>=', dates[when].strftime(_strftime))]
+ domain = [("section_id", "=", section.id), ('state', 'in', ['draft', 'sent']), ('date_order', '>=', dates[when].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
if when:
- domain += [('date_order', '<', dates[when-1].strftime(_strftime))]
+ domain += [('date_order', '<', dates[when-1].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
rate = 0
opportunity_ids = obj.search(cr, uid, domain, context=context)
for invoice in obj.browse(cr, uid, opportunity_ids, context=context):
@@ -75,9 +73,9 @@ class crm_case_section(osv.osv):
dates = [first_day + relativedelta(months=-(MONTHS[section.target_duration]*(key+1)-1)) for key in range(0, 5)]
rate_invoice = []
for when in range(0, 5):
- domain = [("section_id", "=", section.id), ('state', 'not in', ['draft', 'sent']), ('date_confirm', '>=', dates[when].strftime(_strftime))]
+ domain = [("section_id", "=", section.id), ('state', 'not in', ['draft', 'sent']), ('date_confirm', '>=', dates[when].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
if when:
- domain += [('date_confirm', '<', dates[when-1].strftime(_strftime))]
+ domain += [('date_confirm', '<', dates[when-1].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
rate = 0
opportunity_ids = obj.search(cr, uid, domain, context=context)
for invoice in obj.browse(cr, uid, opportunity_ids, context=context):
@@ -96,9 +94,9 @@ class crm_case_section(osv.osv):
dates = [first_day + relativedelta(months=-(MONTHS[section.target_duration]*(key+1)-1)) for key in range(0, 5)]
rate_invoice = []
for when in range(0, 5):
- domain = [("section_id", "=", section.id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', dates[when].strftime(_strftime))]
+ domain = [("section_id", "=", section.id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', dates[when].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
if when:
- domain += [('date', '<', dates[when-1].strftime(_strftime))]
+ domain += [('date', '<', dates[when-1].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
rate = 0
opportunity_ids = obj.search(cr, uid, domain, context=context)
for invoice in obj.browse(cr, uid, opportunity_ids, context=context):
From c2a93abb9348223c66c60613cc59a5671b2f0a40 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Fri, 19 Apr 2013 15:47:55 +0200
Subject: [PATCH 22/72] [IMP] salesteams: demo data
bzr revid: chm@openerp.com-20130419134755-a5332igy2mszgik1
---
addons/crm/crm_data.xml | 2 +-
addons/crm/crm_demo.xml | 5 +-
addons/sale_crm/sale_crm_demo.xml | 128 ++++++++++++++++++++++++++++++
3 files changed, 131 insertions(+), 4 deletions(-)
create mode 100644 addons/sale_crm/sale_crm_demo.xml
diff --git a/addons/crm/crm_data.xml b/addons/crm/crm_data.xml
index 153771532a5..ba1a8307a3f 100644
--- a/addons/crm/crm_data.xml
+++ b/addons/crm/crm_data.xml
@@ -27,7 +27,7 @@
- Direct Marketing
+ Direct Sales
DM
True
diff --git a/addons/crm/crm_demo.xml b/addons/crm/crm_demo.xml
index 0f7368fa907..931470b394e 100644
--- a/addons/crm/crm_demo.xml
+++ b/addons/crm/crm_demo.xml
@@ -7,13 +7,12 @@
- Indirect Marketing
+ Indirect Sales
IM
- semesterly
- Support Department
+ Marketing
SPD
diff --git a/addons/sale_crm/sale_crm_demo.xml b/addons/sale_crm/sale_crm_demo.xml
new file mode 100644
index 00000000000..6d024537b82
--- /dev/null
+++ b/addons/sale_crm/sale_crm_demo.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+ 52700
+ 60000
+
+
+
+ 3600
+ 40000
+
+
+
+
+
+
+
+
+
+
+ draft
+ out_invoice
+
+ Test invoice 1
+
+
+ Basic computer with Dvorak keyboard and left-handed mouse
+
+ 250
+ 1
+
+
+
+ Little server with raid 1 and 512ECC ram
+
+ 800
+ 2
+
+
+
+ Server with raid 10 and 2048ECC ram
+
+ 4800
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ draft
+ out_invoice
+
+ Test invoice 1
+
+
+ Basic formation with Dvorak
+
+ 500
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 485af4a3d47be6ebeba40a20c0aea78afdbbaab2 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Fri, 19 Apr 2013 15:49:27 +0200
Subject: [PATCH 23/72] [IMP] salesteams: demo data
bzr revid: chm@openerp.com-20130419134927-b5ykxmdyokv10jq1
---
addons/sale_crm/sale_crm_demo.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/sale_crm/sale_crm_demo.xml b/addons/sale_crm/sale_crm_demo.xml
index 6d024537b82..4e865b0be7e 100644
--- a/addons/sale_crm/sale_crm_demo.xml
+++ b/addons/sale_crm/sale_crm_demo.xml
@@ -8,7 +8,7 @@
- 3600
+ 36000
40000
From e9fb05e90874c41f76e431b0cca1f5c755d3e42b Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Fri, 19 Apr 2013 17:25:55 +0200
Subject: [PATCH 24/72] [FIX] crm_lead test
bzr revid: chm@openerp.com-20130419152555-9dipa1b3llf8wgyl
---
addons/crm/crm_demo.xml | 1 -
addons/crm/test/crm_lead_cancel.yml | 6 ++++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/addons/crm/crm_demo.xml b/addons/crm/crm_demo.xml
index 931470b394e..2858250e86a 100644
--- a/addons/crm/crm_demo.xml
+++ b/addons/crm/crm_demo.xml
@@ -14,7 +14,6 @@
Marketing
SPD
-
diff --git a/addons/crm/test/crm_lead_cancel.yml b/addons/crm/test/crm_lead_cancel.yml
index edaf844952a..8749675bf23 100644
--- a/addons/crm/test/crm_lead_cancel.yml
+++ b/addons/crm/test/crm_lead_cancel.yml
@@ -1,7 +1,9 @@
-
- I cancel unqualified lead.
+ I set a new sale team (with Marketing at parent) and I cancel unqualified lead .
-
!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")})
+ self.write(cr, uid, [ref("crm_case_1")], {'section_id': section_id})
self.case_cancel(cr, uid, [ref("crm_case_1")])
-
I check cancelled lead.
@@ -42,4 +44,4 @@
I check the lead is correctly escalated to the parent team.
-
!assert {model: crm.lead, id: crm.crm_case_1, string: Escalate lead to parent team}:
- - section_id.name == "Support Department"
+ - section_id.name == "Marketing"
From 4634dd6f8ef17098086b7769665566eb566abb5b Mon Sep 17 00:00:00 2001
From: Ronald Portier
Date: Wed, 24 Apr 2013 16:29:58 +0200
Subject: [PATCH 25/72] [FIX] Add __iter__ method to browse_null to prevent
server hang.
lp bug: https://launchpad.net/bugs/1172292 fixed
bzr revid: ronald@therp.nl-20130424142958-u5r2mxtxqj9v431h
---
openerp/osv/orm.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py
index 870efd3dd8b..b9be7d79453 100644
--- a/openerp/osv/orm.py
+++ b/openerp/osv/orm.py
@@ -285,6 +285,9 @@ class browse_null(object):
def __unicode__(self):
return u''
+ def __iter__(self):
+ raise NotImplementedError("Iteration is not allowed on %s" % self)
+
#
# TODO: execute an object method on browse_record_list
From e1c042f2d30f79bd3b008dbbbadeb389cf11cf6e Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Thu, 25 Apr 2013 13:16:25 +0200
Subject: [PATCH 26/72] [IMP] salesteams: display input is function of
click/blur event on gage
bzr revid: chm@openerp.com-20130425111625-537q0ktfcsoewbv2
---
addons/crm/static/src/js/crm_case_section.js | 25 +++++++++++++++-----
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js
index 05b71ade809..22d042c4f3e 100644
--- a/addons/crm/static/src/js/crm_case_section.js
+++ b/addons/crm/static/src/js/crm_case_section.js
@@ -32,18 +32,18 @@ openerp.crm = function(openerp) {
],
});
+ var flag_open = false;
if ($el.data('action')) {
$el.click(function (event) {
event.stopPropagation();
+ flag_open = false;
if (!self.view.is_action_enabled('edit')) {
return;
}
- if ($el.find(".oe_justgage_edit").size()) {
- $el.find(".oe_justgage_edit").remove();
- } else {
+ if (!$el.find(".oe_justgage_edit").size()) {
var $svg = $el.find('svg');
- $div = $('
');
- $input = $(' ').val($el.data('value'));
+ $div = $('
');
+ $input = $(' ').val($el.data('value'));
$div.append($input);
$el.prepend($div)
$input.focus()
@@ -59,8 +59,21 @@ openerp.crm = function(openerp) {
}
}
})
- .click(function (event) {event.stopPropagation();});
+ .click(function (event) {
+ event.stopPropagation();
+ flag_open = false;
+ })
+ .blur(function (event) {
+ if(!flag_open) {
+ $el.find(".oe_justgage_edit").remove();
+ } else {
+ flag_open = false;
+ setTimeout(function () {$input.focus();}, 0);
+ }
+ });
}
+ }).mousedown(function () {
+ flag_open = true;
});
}
});
From 3cf1b01d1cb6a01e8b61a65c1c6ab0853dac3869 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Thu, 25 Apr 2013 15:01:11 +0200
Subject: [PATCH 27/72] [IMP] salesteams: comment and tooltip
bzr revid: chm@openerp.com-20130425130111-pq94g5v9o5rwyzts
---
addons/crm/crm.py | 12 ++++++------
addons/crm/crm_case_section_view.xml | 22 ++++++++++++++++++++--
addons/crm/static/src/css/crm.css | 2 +-
addons/sale_crm/sale_crm.py | 2 +-
addons/sale_crm/sale_crm_demo.xml | 2 ++
addons/sale_crm/sale_crm_view.xml | 12 ++++++------
6 files changed, 36 insertions(+), 16 deletions(-)
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index 9787a21b9dd..352a1493c1d 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -44,10 +44,10 @@ AVAILABLE_PRIORITIES = [
]
DURATION_TXT = {
- "monthly": _("this month"),
- "semesterly": _("this semester"),
- "semiannually": _("this semi"),
- "annually": _("this year")
+ "monthly": _("month"),
+ "semesterly": _("semester"),
+ "semiannually": _("semi"),
+ "annually": _("year")
}
MONTHS = {
@@ -191,13 +191,13 @@ class crm_case_section(osv.osv):
help="This enables the management of leads in the sales team. Otherwise the sales team manages only opportunities."),
'target_duration': fields.selection([("monthly", "Monthly"), ("semesterly", "Semesterly"), ("semiannually", "Semiannually"), ("annually", "Annually")],
- string='Report duration view', required=True),
+ string='Report Duration View', required=True),
'target_duration_txt': fields.function(_get_target_duration_txt,
string='Duration',
type="string", readonly=True),
'open_lead_per_duration': fields.function(_get_open_lead_per_duration, string='Open Leads per duration', type="string", readonly=True),
- 'won_opportunity_per_duration': fields.function(_get_won_opportunity_per_duration, string='Rate of opporunities whon per duration', type="string", readonly=True)
+ 'won_opportunity_per_duration': fields.function(_get_won_opportunity_per_duration, string='Revenue of opporunities whon per duration', type="string", readonly=True)
}
def _get_stage_common(self, cr, uid, context):
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index dd46d0570e9..79a0abebe6e 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -101,11 +101,11 @@
@@ -116,6 +116,24 @@
+
+
+
+ Case Sections - Search
+ crm.case.section
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css
index 128f3daf31c..ae5062bb9f6 100644
--- a/addons/crm/static/src/css/crm.css
+++ b/addons/crm/static/src/css/crm.css
@@ -25,7 +25,7 @@
}
.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div {
width: 120px;
- height: 20px;
+ height: 22px;
margin: 0;
}
.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div:nth-child(2n) {
diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py
index 46c0bcb4aa6..b865edf6d0f 100644
--- a/addons/sale_crm/sale_crm.py
+++ b/addons/sale_crm/sale_crm.py
@@ -118,7 +118,7 @@ class crm_case_section(osv.osv):
domain=[('state', 'not in', ['draft', 'cancel'])]),
'forecast': fields.integer(string='Total forecast'),
- 'target_invoice': fields.integer(string='Target Invoicing'),
+ 'target_invoice': fields.integer(string='Invoicing Target'),
'created_quotation_per_duration': fields.function(_get_created_quotation_per_duration, string='Rate of created quotation per duration', type="string", readonly=True),
'validate_saleorder_per_duration': fields.function(_get_validate_saleorder_per_duration, string='Rate of validate sales orders per duration', type="string", readonly=True),
'sent_invoice_per_duration': fields.function(_get_sent_invoice_per_duration, string='Rate of sent invoices per duration', type="string", readonly=True),
diff --git a/addons/sale_crm/sale_crm_demo.xml b/addons/sale_crm/sale_crm_demo.xml
index 4e865b0be7e..ef106046ddc 100644
--- a/addons/sale_crm/sale_crm_demo.xml
+++ b/addons/sale_crm/sale_crm_demo.xml
@@ -8,6 +8,8 @@
+ Indirect Sales
+ IM
36000
40000
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index 2e055a91864..fe29c72041d 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -235,15 +235,15 @@
@@ -251,15 +251,15 @@
Invoiced
+ t-att-data-label="_.str.sprintf(_t('this %%s'), record.target_duration_txt.value)">Invoiced
Forecast
- Not target invoicing define
+ Not target invoicing defined
From dd7614e71623848d84c7f89c5416dd6dc6c0023c Mon Sep 17 00:00:00 2001
From: ggh-openerp
Date: Mon, 29 Apr 2013 12:59:38 +0530
Subject: [PATCH 28/72] [IMP] Remove space before Exclamation marks [!] in
Warning
bzr revid: ggh@tinyerp.com-20130429072938-knas9ymmny5o2z0v
---
openerp/addons/base/ir/ir_actions.py | 2 +-
openerp/addons/base/ir/ir_values.py | 4 ++--
openerp/addons/base/module/wizard/base_module_import.py | 6 +++---
openerp/addons/base/module/wizard/base_module_upgrade.py | 2 +-
openerp/addons/base/res/res_lang.py | 6 +++---
openerp/addons/base/res/res_partner.py | 6 +++---
6 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py
index a130ab13a32..aaaa8513925 100644
--- a/openerp/addons/base/ir/ir_actions.py
+++ b/openerp/addons/base/ir/ir_actions.py
@@ -657,7 +657,7 @@ class actions_server(osv.osv):
if action.state=='client_action':
if not action.action_id:
- raise osv.except_osv(_('Error'), _("Please specify an action to launch !"))
+ raise osv.except_osv(_('Error'), _("Please specify an action to launch!"))
return self.pool[action.action_id.type].read(cr, uid, action.action_id.id, context=context)
if action.state=='code':
diff --git a/openerp/addons/base/ir/ir_values.py b/openerp/addons/base/ir/ir_values.py
index dd8501230ec..a21b034d1b0 100644
--- a/openerp/addons/base/ir/ir_values.py
+++ b/openerp/addons/base/ir/ir_values.py
@@ -411,8 +411,8 @@ class ir_values(osv.osv):
(tuple(groups), uid))
if not cr.fetchone():
if action['name'] == 'Menuitem':
- raise osv.except_osv('Error !',
- 'You do not have the permission to perform this operation !!!')
+ raise osv.except_osv('Error!',
+ 'You do not have the permission to perform this operation!!!')
continue
# keep only the first action registered for each action name
results[action['name']] = (action['id'], action['name'], action_def)
diff --git a/openerp/addons/base/module/wizard/base_module_import.py b/openerp/addons/base/module/wizard/base_module_import.py
index 67408b60669..61694d5f8c3 100644
--- a/openerp/addons/base/module/wizard/base_module_import.py
+++ b/openerp/addons/base/module/wizard/base_module_import.py
@@ -55,7 +55,7 @@ class base_module_import(osv.osv_memory):
try:
file_data = zipfile.ZipFile(fp, 'r')
except zipfile.BadZipfile:
- raise osv.except_osv(_('Error !'), _('File is not a zip file!'))
+ raise osv.except_osv(_('Error!'), _('File is not a zip file!'))
init_file_name = sorted(file_data.namelist())[0]
module_name = os.path.split(init_file_name)[0]
@@ -63,8 +63,8 @@ class base_module_import(osv.osv_memory):
try:
zip_file = open(file_path, 'wb')
except IOError:
- raise osv.except_osv(_('Error !'),
- _('Can not create the module file: %s !') % \
+ raise osv.except_osv(_('Error!'),
+ _('Can not create the module file: %s!') % \
(file_path,) )
zip_file.write(zip_data)
zip_file.close()
diff --git a/openerp/addons/base/module/wizard/base_module_upgrade.py b/openerp/addons/base/module/wizard/base_module_upgrade.py
index c081663d8ed..c75058ab835 100644
--- a/openerp/addons/base/module/wizard/base_module_upgrade.py
+++ b/openerp/addons/base/module/wizard/base_module_upgrade.py
@@ -81,7 +81,7 @@ class base_module_upgrade(osv.osv_memory):
(tuple(ids), ('uninstalled',)))
unmet_packages = [x[0] for x in cr.fetchall()]
if unmet_packages:
- raise osv.except_osv(_('Unmet dependency !'),
+ raise osv.except_osv(_('Unmet Dependency!'),
_('Following modules are not installed or unknown: %s') % ('\n\n' + '\n'.join(unmet_packages)))
ir_module.download(cr, uid, ids, context=context)
diff --git a/openerp/addons/base/res/res_lang.py b/openerp/addons/base/res/res_lang.py
index 9abab79e46c..160e86fd8d2 100644
--- a/openerp/addons/base/res/res_lang.py
+++ b/openerp/addons/base/res/res_lang.py
@@ -182,11 +182,11 @@ class lang(osv.osv):
for language in languages:
ctx_lang = context.get('lang')
if language['code']=='en_US':
- raise osv.except_osv(_('User Error'), _("Base Language 'en_US' can not be deleted !"))
+ raise osv.except_osv(_('User Error'), _("Base Language 'en_US' can not be deleted!"))
if ctx_lang and (language['code']==ctx_lang):
- raise osv.except_osv(_('User Error'), _("You cannot delete the language which is User's Preferred Language !"))
+ raise osv.except_osv(_('User Error'), _("You cannot delete the language which is User's Preferred Language!"))
if language['active']:
- raise osv.except_osv(_('User Error'), _("You cannot delete the language which is Active !\nPlease de-activate the language first."))
+ raise osv.except_osv(_('User Error'), _("You cannot delete the language which is Active!\nPlease de-activate the language first."))
trans_obj = self.pool.get('ir.translation')
trans_ids = trans_obj.search(cr, uid, [('lang','=',language['code'])], context=context)
trans_obj.unlink(cr, uid, trans_ids, context=context)
diff --git a/openerp/addons/base/res/res_partner.py b/openerp/addons/base/res/res_partner.py
index 404fe5696c7..5ac5f1212f7 100644
--- a/openerp/addons/base/res/res_partner.py
+++ b/openerp/addons/base/res/res_partner.py
@@ -446,7 +446,7 @@ class res_partner(osv.osv, format_address):
commercial_fields = self._commercial_fields(cr, uid, context=context)
sync_vals = self._update_fields_values(cr, uid, partner.commercial_partner_id,
commercial_fields, context=context)
- return self.write(cr, uid, partner.id, sync_vals, context=context)
+ partner.write(sync_vals)
def _commercial_sync_to_children(self, cr, uid, partner, context=None):
""" Handle sync of commercial fields to descendants """
@@ -472,7 +472,7 @@ class res_partner(osv.osv, format_address):
use_parent_address=partner.use_parent_address,
parent_id=partner.parent_id.id,
context=context).get('value', {})
- self.update_address(cr, uid, partner.id, onchange_vals, context=context)
+ partner.update_address(onchange_vals)
# 2. To DOWNSTREAM: sync children
if partner.child_ids:
@@ -575,7 +575,7 @@ class res_partner(osv.osv, format_address):
context = {}
name, email = self._parse_partner_name(name, context=context)
if context.get('force_email') and not email:
- raise osv.except_osv(_('Warning'), _("Couldn't create contact without email address !"))
+ raise osv.except_osv(_('Warning'), _("Couldn't create contact without email address!"))
if not name and email:
name = email
rec_id = self.create(cr, uid, {self._rec_name: name or email, 'email': email or False}, context=context)
From 058c816a8e79dcef85528f0ab418887e03007215 Mon Sep 17 00:00:00 2001
From: ggh-openerp
Date: Mon, 29 Apr 2013 16:28:49 +0530
Subject: [PATCH 29/72] [IMP] Improved code
bzr revid: ggh@tinyerp.com-20130429105849-2peih2y8kknt65tu
---
openerp/addons/base/ir/ir_mail_server.py | 4 ++--
openerp/addons/base/ir/ir_model.py | 2 +-
openerp/addons/base/ir/workflow/workflow.py | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/openerp/addons/base/ir/ir_mail_server.py b/openerp/addons/base/ir/ir_mail_server.py
index 10fc5f82187..93b213461a7 100644
--- a/openerp/addons/base/ir/ir_mail_server.py
+++ b/openerp/addons/base/ir/ir_mail_server.py
@@ -213,14 +213,14 @@ class ir_mail_server(osv.osv):
password=smtp_server.smtp_pass, encryption=smtp_server.smtp_encryption,
smtp_debug=smtp_server.smtp_debug)
except Exception, e:
- raise osv.except_osv(_("Connection test failed!"), _("Here is what we got instead:\n %s") % tools.ustr(e))
+ raise osv.except_osv(_("Connection Test Failed!"), _("Here is what we got instead:\n %s") % tools.ustr(e))
finally:
try:
if smtp: smtp.quit()
except Exception:
# ignored, just a consequence of the previous exception
pass
- raise osv.except_osv(_("Connection test succeeded!"), _("Everything seems properly set up!"))
+ raise osv.except_osv(_("Connection Test Succeeded!"), _("Everything seems properly set up!"))
def connect(self, host, port, user=None, password=None, encryption=False, smtp_debug=False):
"""Returns a new SMTP connection to the give SMTP server, authenticated
diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py
index 222d8f34c9c..165fd8a4be9 100644
--- a/openerp/addons/base/ir/ir_model.py
+++ b/openerp/addons/base/ir/ir_model.py
@@ -82,7 +82,7 @@ class ir_model(osv.osv):
return []
__, operator, value = domain[0]
if operator not in ['=', '!=']:
- raise osv.except_osv(_('Invalid search criterions'), _('The osv_memory field can only be compared with = and != operator.'))
+ raise osv.except_osv(_("Invalid Search Criteria"), _('The osv_memory field can only be compared with = and != operator.'))
value = bool(value) if operator == '=' else not bool(value)
all_model_ids = self.search(cr, uid, [], context=context)
is_osv_mem = self._is_osv_memory(cr, uid, all_model_ids, 'osv_memory', arg=None, context=context)
diff --git a/openerp/addons/base/ir/workflow/workflow.py b/openerp/addons/base/ir/workflow/workflow.py
index 0ce951940a9..a7e792072e3 100644
--- a/openerp/addons/base/ir/workflow/workflow.py
+++ b/openerp/addons/base/ir/workflow/workflow.py
@@ -94,7 +94,7 @@ class wkf_activity(osv.osv):
def unlink(self, cr, uid, ids, context=None):
if context is None: context = {}
if not context.get('_force_unlink') and self.pool.get('workflow.workitem').search(cr, uid, [('act_id', 'in', ids)]):
- raise osv.except_osv(_('Operation forbidden'),
+ raise osv.except_osv(_('Operation Forbidden'),
_('Please make sure no workitems refer to an activity before deleting it!'))
super(wkf_activity, self).unlink(cr, uid, ids, context=context)
From de13720f0e09c36c2a65d8bf16c88deee7db8c84 Mon Sep 17 00:00:00 2001
From: Xavier Morel
Date: Mon, 29 Apr 2013 14:24:23 +0200
Subject: [PATCH 30/72] [IMP] rng of field tag in data xml
also fix nonsensical XML files specifying @ref and @model
bzr revid: xmo@openerp.com-20130429122423-qubsy2ccwo5tb8bq
---
openerp/addons/base/ir/ir_actions.xml | 2 +-
openerp/addons/base/ir/ir_filters.xml | 2 +-
openerp/addons/base/res/res_security.xml | 4 +-
openerp/import_xml.rng | 94 ++++++++++++++++++------
4 files changed, 75 insertions(+), 27 deletions(-)
diff --git a/openerp/addons/base/ir/ir_actions.xml b/openerp/addons/base/ir/ir_actions.xml
index 82514692c06..145627058ef 100644
--- a/openerp/addons/base/ir/ir_actions.xml
+++ b/openerp/addons/base/ir/ir_actions.xml
@@ -297,7 +297,7 @@
Property multi-company
-
+
['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)]
diff --git a/openerp/addons/base/ir/ir_filters.xml b/openerp/addons/base/ir/ir_filters.xml
index 72c5dad8885..88cc4b9838a 100644
--- a/openerp/addons/base/ir/ir_filters.xml
+++ b/openerp/addons/base/ir/ir_filters.xml
@@ -4,7 +4,7 @@
ir.filters.owner
-
+
[('user_id','in',[False,user.id])]
diff --git a/openerp/addons/base/res/res_security.xml b/openerp/addons/base/res/res_security.xml
index 783243411ef..0c8c0202d60 100644
--- a/openerp/addons/base/res/res_security.xml
+++ b/openerp/addons/base/res/res_security.xml
@@ -16,7 +16,7 @@
company rule
-
+
[('id','child_of',[user.company_id.id])]
@@ -25,7 +25,7 @@
user rule
-
+
[('company_ids','child_of',[user.company_id.id])]
diff --git a/openerp/import_xml.rng b/openerp/import_xml.rng
index 5f4c40b65d3..c055009a35d 100644
--- a/openerp/import_xml.rng
+++ b/openerp/import_xml.rng
@@ -1,17 +1,18 @@
-
+
-
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -125,19 +126,66 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ char
+ file
+
+
-
-
+
+
+ int
+
+
+ None
+
+
+
+ float
+
+
+
+
+
+ list
+ tuple
+
+
+
+
+
+
+
+ html
+ xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From de4c781392fbf6a818e0f98605bb6277762c0fa2 Mon Sep 17 00:00:00 2001
From: Xavier Morel
Date: Mon, 29 Apr 2013 14:38:31 +0200
Subject: [PATCH 31/72] [FIX] turns out @model is optional on field when using
@search
it's required on @value though.
or on recursive @field calls, but let's just ignore that eldritch horror
bzr revid: xmo@openerp.com-20130429123831-waj3snv8pqcfda1n
---
openerp/import_xml.rng | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/openerp/import_xml.rng b/openerp/import_xml.rng
index c055009a35d..716a525b496 100644
--- a/openerp/import_xml.rng
+++ b/openerp/import_xml.rng
@@ -178,7 +178,7 @@
-
+
From 7a2d912964ee6d6f4076c31bb4e4e8213846df75 Mon Sep 17 00:00:00 2001
From: Xavier Morel
Date: Mon, 29 Apr 2013 14:43:33 +0200
Subject: [PATCH 32/72] [REM] bunch of nonsensical @model + @ref
bzr revid: xmo@openerp.com-20130429124333-p1h11fpy04y3sljy
---
.../security/account_analytic_default_security.xml | 2 +-
.../account_followup/security/account_followup_security.xml | 2 +-
.../account_payment/security/account_payment_security.xml | 6 +++---
.../account_voucher/security/account_voucher_security.xml | 4 ++--
addons/analytic/security/analytic_security.xml | 4 ++--
addons/hr/security/hr_security.xml | 4 ++--
addons/hr_attendance/security/ir_rule.xml | 4 ++--
addons/hr_evaluation/security/hr_evaluation_security.xml | 4 ++--
addons/hr_expense/security/ir_rule.xml | 6 +++---
addons/hr_holidays/security/ir_rule.xml | 4 ++--
addons/hr_payroll/security/hr_security.xml | 4 ++--
addons/hr_recruitment/security/hr_recruitment_security.xml | 2 +-
addons/hr_timesheet/security/hr_timesheet_security.xml | 4 ++--
addons/mrp_repair/security/mrp_repair_security.xml | 2 +-
addons/note/security/ir.rule.xml | 4 ++--
15 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/addons/account_analytic_default/security/account_analytic_default_security.xml b/addons/account_analytic_default/security/account_analytic_default_security.xml
index 63c6e30eb22..cdce2a9955e 100644
--- a/addons/account_analytic_default/security/account_analytic_default_security.xml
+++ b/addons/account_analytic_default/security/account_analytic_default_security.xml
@@ -4,7 +4,7 @@
Analytic Default multi company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
diff --git a/addons/account_followup/security/account_followup_security.xml b/addons/account_followup/security/account_followup_security.xml
index 1586403d701..69c9a44a1b9 100644
--- a/addons/account_followup/security/account_followup_security.xml
+++ b/addons/account_followup/security/account_followup_security.xml
@@ -4,7 +4,7 @@
Account Follow-up multi company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
diff --git a/addons/account_payment/security/account_payment_security.xml b/addons/account_payment/security/account_payment_security.xml
index 4bca00eaa6d..fbe29699e86 100644
--- a/addons/account_payment/security/account_payment_security.xml
+++ b/addons/account_payment/security/account_payment_security.xml
@@ -15,21 +15,21 @@
Payment Mode company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
Payment order multi company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
Payment line multi company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
diff --git a/addons/account_voucher/security/account_voucher_security.xml b/addons/account_voucher/security/account_voucher_security.xml
index 04540fd8aa0..daa5934b256 100644
--- a/addons/account_voucher/security/account_voucher_security.xml
+++ b/addons/account_voucher/security/account_voucher_security.xml
@@ -3,13 +3,13 @@
Voucher multi-company
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
Voucher Line multi-company
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
diff --git a/addons/analytic/security/analytic_security.xml b/addons/analytic/security/analytic_security.xml
index 70688275de2..bf103ca8988 100644
--- a/addons/analytic/security/analytic_security.xml
+++ b/addons/analytic/security/analytic_security.xml
@@ -4,14 +4,14 @@
Analytic multi company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
Analytic line multi company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
diff --git a/addons/hr/security/hr_security.xml b/addons/hr/security/hr_security.xml
index 8f3697f234a..4383f2d6c74 100644
--- a/addons/hr/security/hr_security.xml
+++ b/addons/hr/security/hr_security.xml
@@ -25,13 +25,13 @@
Department multi company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
Job multi company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
diff --git a/addons/hr_attendance/security/ir_rule.xml b/addons/hr_attendance/security/ir_rule.xml
index 857fc2f54ae..0bf3b73d76e 100644
--- a/addons/hr_attendance/security/ir_rule.xml
+++ b/addons/hr_attendance/security/ir_rule.xml
@@ -9,13 +9,13 @@
Manager Attendance
-
+
[(1,'=',1)]
Employee Attendance
-
+
[('employee_id.user_id','=',user.id)]
diff --git a/addons/hr_evaluation/security/hr_evaluation_security.xml b/addons/hr_evaluation/security/hr_evaluation_security.xml
index 7c7d6f43ee3..209ca281396 100644
--- a/addons/hr_evaluation/security/hr_evaluation_security.xml
+++ b/addons/hr_evaluation/security/hr_evaluation_security.xml
@@ -31,14 +31,14 @@
Evaluation Plan multi company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
Evaluation Plan Phase multi company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
diff --git a/addons/hr_expense/security/ir_rule.xml b/addons/hr_expense/security/ir_rule.xml
index b9ad5778d1e..3c3cb018489 100644
--- a/addons/hr_expense/security/ir_rule.xml
+++ b/addons/hr_expense/security/ir_rule.xml
@@ -4,19 +4,19 @@
Manager Expense
-
+
[(1,'=',1)]
Employee Expense
-
+
[('employee_id.user_id','=',user.id)]
Expense multi company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
diff --git a/addons/hr_holidays/security/ir_rule.xml b/addons/hr_holidays/security/ir_rule.xml
index ff0270026a4..e2c0ffb1a6f 100644
--- a/addons/hr_holidays/security/ir_rule.xml
+++ b/addons/hr_holidays/security/ir_rule.xml
@@ -3,14 +3,14 @@
Employee Holidays
-
+
[('employee_id.user_id','=',user.id)]
Holidays Officer
-
+
[(1,'=',1)]
diff --git a/addons/hr_payroll/security/hr_security.xml b/addons/hr_payroll/security/hr_security.xml
index d55f1c81cce..e410eae7250 100644
--- a/addons/hr_payroll/security/hr_security.xml
+++ b/addons/hr_payroll/security/hr_security.xml
@@ -8,10 +8,10 @@
Employee Payslip
-
+
['|', ('employee_id.user_id', '=', user.id), ('employee_id.department_id.manager_id.user_id', '=', user.id)]
-
\ No newline at end of file
+
diff --git a/addons/hr_recruitment/security/hr_recruitment_security.xml b/addons/hr_recruitment/security/hr_recruitment_security.xml
index 570b0f036cd..4fccca1a791 100644
--- a/addons/hr_recruitment/security/hr_recruitment_security.xml
+++ b/addons/hr_recruitment/security/hr_recruitment_security.xml
@@ -4,7 +4,7 @@
Applicant multi company rule
-
+
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
diff --git a/addons/hr_timesheet/security/hr_timesheet_security.xml b/addons/hr_timesheet/security/hr_timesheet_security.xml
index d7cf34fa1fd..c5549d1ed27 100644
--- a/addons/hr_timesheet/security/hr_timesheet_security.xml
+++ b/addons/hr_timesheet/security/hr_timesheet_security.xml
@@ -4,14 +4,14 @@
Manager HR Analytic Timesheet
-
+
[(1,'=',1)]
Employee HR Analytic Timesheet
-
+
[('user_id', '=', user.id)]
diff --git a/addons/mrp_repair/security/mrp_repair_security.xml b/addons/mrp_repair/security/mrp_repair_security.xml
index 0b34310fc4e..e94b675b57f 100644
--- a/addons/mrp_repair/security/mrp_repair_security.xml
+++ b/addons/mrp_repair/security/mrp_repair_security.xml
@@ -11,4 +11,4 @@
-
\ No newline at end of file
+
diff --git a/addons/note/security/ir.rule.xml b/addons/note/security/ir.rule.xml
index b0c79561355..2c6e9f3bc7f 100644
--- a/addons/note/security/ir.rule.xml
+++ b/addons/note/security/ir.rule.xml
@@ -3,7 +3,7 @@
Only followers can access a sticky notes
-
+
[('message_follower_ids','=',user.partner_id.id)]
@@ -14,7 +14,7 @@
Each user have his stage name
-
+
['|',('user_id','=',False),('user_id','=',user.id)]
From 07607f054bc8f3b427a98f479f0b3d3cb92224c1 Mon Sep 17 00:00:00 2001
From: Xavier Morel
Date: Mon, 29 Apr 2013 14:52:17 +0200
Subject: [PATCH 33/72] [REM] more nonsensical @model + @ref
bzr revid: xmo@openerp.com-20130429125217-slmp1v9t7uri6ev6
---
.../process/customer_invoice_process.xml | 32 ++++++-------
addons/account/process/statement_process.xml | 14 +++---
.../process/supplier_invoice_process.xml | 28 +++++------
.../crm/process/crm_configuration_process.xml | 12 ++---
addons/hr/process/hr_process.xml | 10 ++--
.../hr_expense/process/hr_expense_process.xml | 26 +++++-----
.../process/hr_holidays_process.xml | 26 +++++-----
.../process/hr_timesheet_sheet_process.xml | 24 +++++-----
.../membership/process/membership_process.xml | 22 ++++-----
addons/mrp/process/procurement_process.xml | 48 +++++++++----------
.../mrp/process/service_product_process.xml | 6 +--
.../mrp/process/stockable_product_process.xml | 8 ++--
.../process/mrp_operation_process.xml | 16 +++----
addons/product/process/product_process.xml | 4 +-
addons/project/process/task_process.xml | 12 ++---
.../process/project_mrp_process.xml | 12 ++---
.../process/project_timesheet_process.xml | 12 ++---
addons/purchase/process/purchase_process.xml | 36 +++++++-------
addons/sale/process/sale_process.xml | 12 ++---
addons/sale_crm/process/sale_crm_process.xml | 16 +++----
.../sale_stock/process/sale_stock_process.xml | 20 ++++----
21 files changed, 198 insertions(+), 198 deletions(-)
diff --git a/addons/account/process/customer_invoice_process.xml b/addons/account/process/customer_invoice_process.xml
index 8971235c1f7..e45f8080518 100644
--- a/addons/account/process/customer_invoice_process.xml
+++ b/addons/account/process/customer_invoice_process.xml
@@ -123,24 +123,24 @@
-
-
+
+
-
-
+
+
-
-
+
+
@@ -148,40 +148,40 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/addons/sale_stock/process/sale_stock_process.xml b/addons/sale_stock/process/sale_stock_process.xml
index c10b34c8866..1ebebec7809 100644
--- a/addons/sale_stock/process/sale_stock_process.xml
+++ b/addons/sale_stock/process/sale_stock_process.xml
@@ -72,40 +72,40 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
@@ -403,8 +403,8 @@
TOEJ
situation
-
-
+
+
@@ -413,8 +413,8 @@
USD Bank Journal - (test)
TUBK
bank
-
-
+
+
SJ
- San José
-
+ San José
+
A
Alajuela
-
+
H
Heredia
-
+
C
Cartago
-
+
P
Puntarenas
-
+
G
Guanacaste
-
+
L
- Limón
-
+ Limón
+
@@ -66,13 +65,13 @@
-
-
+
+
-
-
+
+
@@ -85,9 +84,9 @@
OpenERP Partner
create_date
-
+
-
+
\ No newline at end of file
diff --git a/addons/marketing_campaign_crm_demo/marketing_campaign_demo.xml b/addons/marketing_campaign_crm_demo/marketing_campaign_demo.xml
index 141981a21d6..f6eb8a1d905 100644
--- a/addons/marketing_campaign_crm_demo/marketing_campaign_demo.xml
+++ b/addons/marketing_campaign_crm_demo/marketing_campaign_demo.xml
@@ -1,11 +1,10 @@
-
Dummy Action
dummy
-
+
@@ -40,10 +39,10 @@
- Hello,
- Thanks for the genuine interest you have shown in OpenERP.
- If any further information is required, do not hesitate to reply to this message.
- Regards,OpenERP Team,
]]>
+ <p>Hello,</p>
+ <p>Thanks for the genuine interest you have shown in OpenERP.</p>
+ <p>If any further information is required, do not hesitate to reply to this message.</p>
+ <p>Regards,OpenERP Team,</p>
For OpenERP OnDemand Free Trial 2010
@@ -53,11 +52,11 @@
- Hello,
- We have very good offer that might suit you.
- We suggest you subscribe to the OpenERP Discovery Day on May 2010.
- If any further information is required, do not hesitate to reply to this message.
- Regards,OpenERP Team,
]]>
+ <p>Hello,</p>
+ <p>We have very good offer that might suit you.
+ We suggest you subscribe to the OpenERP Discovery Day on May 2010.</p>
+ <p>If any further information is required, do not hesitate to reply to this message.</p>
+ <p>Regards,OpenERP Team,</p>
For OpenERP Discovery Day on May 2010
@@ -68,10 +67,10 @@
- Hello,
- Thanks for showing interest and for subscribing to the OpenERP Discovery Day.
- If any further information is required, do not hesitate to reply to this message.
- Regards,OpenERP Team,
]]>
+ <p>Hello,</p>
+ <p>Thanks for showing interest and for subscribing to the OpenERP Discovery Day.</p>
+ <p>If any further information is required, do not hesitate to reply to this message.</p>
+ <p>Regards,OpenERP Team,</p>
For OpenERP Discovery Day
@@ -82,10 +81,10 @@
- Hello,
- Thanks for showing interest and buying the OpenERP book.
+ <p>Hello,</p>
+ <p>Thanks for showing interest and buying the OpenERP book.</p>
If any further information required kindly revert back.
- Regards,OpenERP Team,
]]>
+ <p>Regards,OpenERP Team,</p>
For OpenERP book
@@ -95,11 +94,11 @@
- Hello,
- We have very good offer that might suit you.
- For our gold partners,We are arranging free technical training on june,2010.
- If any further information is required, do not hesitate to reply to this message.
- Regards,OpenERP Team,
]]>
+ <p>Hello,</p>
+ <p>We have very good offer that might suit you.
+ For our gold partners,We are arranging free technical training on june,2010.</p>
+ <p>If any further information is required, do not hesitate to reply to this message.</p>
+ <p>Regards,OpenERP Team,</p>
technical training to gold partners
@@ -110,11 +109,11 @@
- Hello,
- We have very good offer that might suit you.
- For our silver partners,We are paid technical training on june,2010.
- If any further information is required, do not hesitate to reply to this message.
- Regards,OpenERP Team,
]]>
+ <p>Hello,</p>
+ <p>We have very good offer that might suit you.
+ For our silver partners,We are paid technical training on june,2010.</p>
+ <p>If any further information is required, do not hesitate to reply to this message.</p>
+ <p>Regards,OpenERP Team,</p>
training to silver partners
@@ -125,11 +124,11 @@
- Hello,
- We have very good offer that might suit you.
- For our silver partners, we are offering Gold partnership.
- If any further information is required, do not hesitate to reply to this message.
- Regards,OpenERP Team,
]]>
+ <p>Hello,</p>
+ <p>We have very good offer that might suit you.
+ For our silver partners, we are offering Gold partnership.</p>
+ <p>If any further information is required, do not hesitate to reply to this message.</p>
+ <p>Regards,OpenERP Team,</p>
gold partnership to silver partners
@@ -140,11 +139,11 @@
- Hello,
- Thanks for showing interest and for subscribing to technical training.
- If any further information required kindly revert back.I really appreciate your co-operation on this.
- If any further information is required, do not hesitate to reply to this message.
- Regards,OpenERP Team,
]]>
+ <p>Hello,</p>
+ <p>Thanks for showing interest and for subscribing to technical training.</p>
+ If any further information required kindly revert back.I really appreciate your co-operation on this.</p>
+ <p>If any further information is required, do not hesitate to reply to this message.</p>
+ <p>Regards,OpenERP Team,</p>
subscribing to technical training
@@ -171,7 +170,7 @@
Propose a 1 month free trial for an OnDemand offer
-
+
report
@@ -180,27 +179,27 @@
Thanks for showing interest
-
+
Propose to subscribe to the OpenERP Discovery Day on May 2010
-
+
Thanks for subscribing to the OpenERP Discovery Day
-
+
Propose to buy the OpenERP Book
-
+
report
@@ -209,81 +208,81 @@
Thanks for buying the OpenERP book
-
+
Propose a free technical training to Gold partners
-
+
Propose paid training to Silver partners
-
+
Propose gold partnership to silver partners
-
+
Thanks for subscribing to technical training
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
@@ -296,4 +295,4 @@
-
+
\ No newline at end of file
diff --git a/addons/procurement/stock_orderpoint.xml b/addons/procurement/stock_orderpoint.xml
index 31c48046d64..67d610c65c9 100644
--- a/addons/procurement/stock_orderpoint.xml
+++ b/addons/procurement/stock_orderpoint.xml
@@ -1,4 +1,3 @@
-
@@ -7,50 +6,50 @@
25.0
5.0
-
+
-
+
10.0
5.0
-
+
-
+
12.0
5.0
-
+
-
+
50.0
10.0
-
+
-
+
15.0
5.0
-
+
-
+
5.0
3.0
-
+
-
+
-
+
\ No newline at end of file
diff --git a/addons/purchase/purchase_workflow.xml b/addons/purchase/purchase_workflow.xml
index 11936db0bed..68a6cd12733 100644
--- a/addons/purchase/purchase_workflow.xml
+++ b/addons/purchase/purchase_workflow.xml
@@ -57,7 +57,7 @@
invoice
subflow
-
+
action_invoice_create()
diff --git a/addons/stock_location/stock_location_demo_cpu1.xml b/addons/stock_location/stock_location_demo_cpu1.xml
index 6578a961ff3..d9a220daef6 100644
--- a/addons/stock_location/stock_location_demo_cpu1.xml
+++ b/addons/stock_location/stock_location_demo_cpu1.xml
@@ -1,4 +1,3 @@
-
@@ -73,7 +72,7 @@
-
+
@@ -89,4 +88,4 @@
-
+
\ No newline at end of file
From f8dad517525da1913d6266e45b93d33a455aa426 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Fri, 3 May 2013 15:43:56 +0200
Subject: [PATCH 36/72] [IMP] crm: change access right for crm.case.section.
Only manager can write, create and unlink
bzr revid: chm@openerp.com-20130503134356-9wxf9i9cifj3b7q6
---
addons/crm/security/ir.model.access.csv | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/crm/security/ir.model.access.csv b/addons/crm/security/ir.model.access.csv
index 52648c8e347..f3c6bdf5c1f 100644
--- a/addons/crm/security/ir.model.access.csv
+++ b/addons/crm/security/ir.model.access.csv
@@ -12,7 +12,7 @@ access_crm_phonecall_manager,crm.phonecall.manager,model_crm_phonecall,base.grou
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,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
From 0c9110c7f719a7701c2d9f45a6a09c1840a0b253 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Fri, 3 May 2013 17:46:52 +0200
Subject: [PATCH 37/72] [IMP] crm,sale_crm: kanban widget
bzr revid: chm@openerp.com-20130503154652-r9mv3zovs8lnpj3e
---
addons/crm/__openerp__.py | 1 -
addons/crm/crm_case_section_view.xml | 4 +-
addons/crm/static/src/css/crm.css | 26 ++---
addons/crm/static/src/js/crm_case_section.js | 101 +++---------------
addons/sale_crm/__openerp__.py | 4 +
addons/sale_crm/sale_crm_view.xml | 19 ++--
.../{crm => sale_crm}/static/lib/justgage.js | 0
addons/sale_crm/static/src/js/sale_crm.js | 44 ++++++++
8 files changed, 80 insertions(+), 119 deletions(-)
rename addons/{crm => sale_crm}/static/lib/justgage.js (100%)
create mode 100644 addons/sale_crm/static/src/js/sale_crm.js
diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py
index 8bd87db35b1..937e0790aed 100644
--- a/addons/crm/__openerp__.py
+++ b/addons/crm/__openerp__.py
@@ -122,7 +122,6 @@ Dashboard for CRM will include:
'js': [
'static/src/js/crm_case_section.js',
'static/lib/sparkline/jquery.sparkline.min.js',
- 'static/lib/justgage.js',
],
'installable': True,
'application': True,
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index 79a0abebe6e..f9591ff873c 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -101,11 +101,11 @@
diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css
index ae5062bb9f6..b1fadda6c40 100644
--- a/addons/crm/static/src/css/crm.css
+++ b/addons/crm/static/src/css/crm.css
@@ -24,27 +24,23 @@
margin: 10px;
}
.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div {
- width: 120px;
+ width: 160px;
height: 22px;
margin: 0;
-}
-.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div:nth-child(2n) {
- position: absolute;
- right: 0px;
-}
-.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div:nth-child(2n) a {
position: relative;
- top: -20px;
-}
-.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_kanban_content > div {
- clear: both;
-}
-.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div a:first-child {
- width: 80px;
+ display: inline-block;
}
.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list a:hover {
text-decoration: underline !important;
}
+.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div a:nth-child(2n) {
+ position: absolute;
+ left: 90px;
+ top: 0;
+}
+.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div:nth-child(2n) a:nth-child(2n) {
+ left: 110px;
+}
.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_center {
text-align: center;
margin: 3px 0;
@@ -63,6 +59,6 @@
}
.openerp .oe_kanban_view .oe_sparkline_bar {
- float: right;
height: 20px;
+ width: 36px;
}
\ No newline at end of file
diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js
index 22d042c4f3e..656e3680dec 100644
--- a/addons/crm/static/src/js/crm_case_section.js
+++ b/addons/crm/static/src/js/crm_case_section.js
@@ -1,93 +1,5 @@
openerp.crm = function(openerp) {
openerp.web_kanban.KanbanRecord.include({
- renderElement: function () {
- var rendering = this._super();
-
- if (this.view.dataset.model === 'crm.case.section') {
- var self = this;
- $.when(rendering).done(function() {
- self.$(".oe_justgage").each(function () {
- var $el = $(this);
- var title = $el.html();
- var unique_id = _.uniqueId("JustGage");
- $el.empty().css("position", "relative").attr('id', unique_id);
-
- new JustGage({
- id: unique_id,
- node: this,
- title: title,
- value: +$el.data('value'),
- min: 0,
- max: +$el.data('max'),
- relativeGaugeSize: true,
- humanFriendly: true,
- titleFontColor: '#333333',
- valueFontColor: '#333333',
- labelFontColor: '#000',
- label: $el.data('label'),
- levelColors: [
- "#ff0000",
- "#f9c802",
- "#a9d70b"
- ],
- });
-
- var flag_open = false;
- if ($el.data('action')) {
- $el.click(function (event) {
- event.stopPropagation();
- flag_open = false;
- if (!self.view.is_action_enabled('edit')) {
- return;
- }
- if (!$el.find(".oe_justgage_edit").size()) {
- var $svg = $el.find('svg');
- $div = $('
');
- $input = $(' ').val($el.data('value'));
- $div.append($input);
- $el.prepend($div)
- $input.focus()
- .keydown(function (event) {
- event.stopPropagation();
- if (event.keyCode == 13 || event.keyCode == 9) {
- if ($input.val() != $el.data('value')) {
- self.view.dataset.call($el.data('action'), [self.id, $input.val()]).then(function () {
- self.do_reload();
- });
- } else {
- $div.remove();
- }
- }
- })
- .click(function (event) {
- event.stopPropagation();
- flag_open = false;
- })
- .blur(function (event) {
- if(!flag_open) {
- $el.find(".oe_justgage_edit").remove();
- } else {
- flag_open = false;
- setTimeout(function () {$input.focus();}, 0);
- }
- });
- }
- }).mousedown(function () {
- flag_open = true;
- });
- }
- });
- setTimeout(function () {
- self.$(".oe_sparkline_bar").each(function () {
- var $el = $(this);
- $el.data("title", $el.html());
- $el.sparkline($el.data("value").split(','), {type: 'bar', barWidth: 5} );
- $el.tipsy({'delayIn': 0, 'html': true, 'title': function(){return $(this).data("title")}, 'gravity': 'n'});
- });
- }, 0);
- });
- }
- },
on_card_clicked: function() {
if (this.view.dataset.model === 'crm.case.section') {
this.$('.oe_kanban_crm_salesteams_list a').first().click();
@@ -97,4 +9,17 @@ openerp.crm = function(openerp) {
},
});
+ openerp.crm.SparklineBarWidget = openerp.web_kanban.AbstractField.extend({
+ className: "oe_sparkline_bar",
+ start: function() {
+ var self = this;
+ var title = this.$node.html();
+ setTimeout(function () {
+ self.$el.sparkline(self.field.value, {type: 'bar', barWidth: 5} );
+ self.$el.tipsy({'delayIn': 0, 'html': true, 'title': function(){return title}, 'gravity': 'n'});
+ }, 0);
+ },
+ });
+ openerp.web_kanban.fields_registry.add("sparkline_bar", "openerp.crm.SparklineBarWidget");
+
};
diff --git a/addons/sale_crm/__openerp__.py b/addons/sale_crm/__openerp__.py
index 7bc1dc24bfc..8cadff5b5ad 100644
--- a/addons/sale_crm/__openerp__.py
+++ b/addons/sale_crm/__openerp__.py
@@ -47,6 +47,10 @@ modules.
'security/ir.model.access.csv',
'report/sale_crm_account_invoice_report_view.xml',
],
+ 'js': [
+ 'static/src/js/sale_crm.js',
+ 'static/lib/justgage.js',
+ ],
'demo': ['sale_crm_demo.xml'],
'test': ['test/sale_crm.yml'],
'installable': True,
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index fe29c72041d..d169bf40b69 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -234,29 +234,22 @@
-
Invoiced
-
Forecast
+
Invoiced
+
Forecast
Not target invoicing defined
diff --git a/addons/crm/static/lib/justgage.js b/addons/sale_crm/static/lib/justgage.js
similarity index 100%
rename from addons/crm/static/lib/justgage.js
rename to addons/sale_crm/static/lib/justgage.js
diff --git a/addons/sale_crm/static/src/js/sale_crm.js b/addons/sale_crm/static/src/js/sale_crm.js
new file mode 100644
index 00000000000..066ec062f70
--- /dev/null
+++ b/addons/sale_crm/static/src/js/sale_crm.js
@@ -0,0 +1,44 @@
+openerp.sale_crm = function(openerp) {
+
+openerp.sale_crm.GaugeWidget = openerp.web_kanban.AbstractField.extend({
+ className: "oe_gage",
+ start: function() {
+ var max = 100;
+ if (this.options.max_field) {
+ max = this.getParent().record[this.options.max_field].raw_value;
+ }
+ var label = "";
+ if (this.options.label_field) {
+ label = this.getParent().record[this.options.label_field].raw_value;
+ }
+ var title = this.$node.html();
+ var value = _.isArray(this.field.value) ? this.field.value.pop() : this.field.value;
+ var unique_id = _.uniqueId("JustGage");
+
+ this.$el.empty()
+ .attr('style', this.$node.attr('style') + ';position:relative; display:inline-block;')
+ .attr('id', unique_id);
+ this.gage = new JustGage({
+ id: unique_id,
+ node: this.$el[0],
+ title: title,
+ value: value,
+ min: 0,
+ max: max,
+ relativeGaugeSize: true,
+ humanFriendly: true,
+ titleFontColor: '#333333',
+ valueFontColor: '#333333',
+ labelFontColor: '#000',
+ label: label,
+ levelColors: [
+ "#ff0000",
+ "#f9c802",
+ "#a9d70b"
+ ],
+ });
+ },
+});
+openerp.web_kanban.fields_registry.add("gage", "openerp.sale_crm.GaugeWidget");
+
+};
From 64eceedcb9a9cbbe9e56af23eea46f74b72b4956 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Mon, 6 May 2013 10:00:16 +0200
Subject: [PATCH 38/72] [IMP] crm_case_section: quarter in selection field
bzr revid: chm@openerp.com-20130506080016-1336mjc2212wyauj
---
addons/crm/crm.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index 352a1493c1d..93831e50d91 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -45,15 +45,15 @@ AVAILABLE_PRIORITIES = [
DURATION_TXT = {
"monthly": _("month"),
+ "quarterly": _("quarter"),
"semesterly": _("semester"),
- "semiannually": _("semi"),
"annually": _("year")
}
MONTHS = {
"monthly": 1,
- "semesterly": 3,
- "semiannually": 6,
+ "quarterly": 3,
+ "semesterly": 6,
"annually": 12
}
@@ -190,7 +190,7 @@ class crm_case_section(osv.osv):
'use_leads': fields.boolean('Leads',
help="This enables the management of leads in the sales team. Otherwise the sales team manages only opportunities."),
- 'target_duration': fields.selection([("monthly", "Monthly"), ("semesterly", "Semesterly"), ("semiannually", "Semiannually"), ("annually", "Annually")],
+ 'target_duration': fields.selection([("monthly", "Monthly"), ("quarterly", "Quarterly"), ("semesterly", "Semesterly"), ("annually", "Annually")],
string='Report Duration View', required=True),
'target_duration_txt': fields.function(_get_target_duration_txt,
string='Duration',
From 137859f01e2551767396b3a4df0f3e814df47062 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Mon, 6 May 2013 10:13:54 +0200
Subject: [PATCH 39/72] [FIX] sale_crm: fix JustGage widget
bzr revid: chm@openerp.com-20130506081354-ulx7c02wo4uxq2h7
---
addons/sale_crm/sale_crm.py | 16 ++++++++--------
addons/sale_crm/static/src/js/sale_crm.js | 3 ++-
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py
index 798c6a0d86b..0576bf6d731 100644
--- a/addons/sale_crm/sale_crm.py
+++ b/addons/sale_crm/sale_crm.py
@@ -56,9 +56,9 @@ class crm_case_section(osv.osv):
if when:
domain += [('date_order', '<', dates[when-1].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
rate = 0
- opportunity_ids = obj.search(cr, uid, domain, context=context)
- for invoice in obj.browse(cr, uid, opportunity_ids, context=context):
- rate += invoice.amount_total
+ order_ids = obj.search(cr, uid, domain, context=context)
+ for order in obj.browse(cr, uid, order_ids, context=context):
+ rate += order.amount_total
rate_invoice.append(rate)
rate_invoice.reverse()
res[section.id] = rate_invoice
@@ -77,9 +77,9 @@ class crm_case_section(osv.osv):
if when:
domain += [('date_confirm', '<', dates[when-1].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
rate = 0
- opportunity_ids = obj.search(cr, uid, domain, context=context)
- for invoice in obj.browse(cr, uid, opportunity_ids, context=context):
- rate += invoice.amount_total
+ order_ids = obj.search(cr, uid, domain, context=context)
+ for order in obj.browse(cr, uid, order_ids, context=context):
+ rate += order.amount_total
rate_invoice.append(rate)
rate_invoice.reverse()
res[section.id] = rate_invoice
@@ -98,8 +98,8 @@ class crm_case_section(osv.osv):
if when:
domain += [('date', '<', dates[when-1].strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
rate = 0
- opportunity_ids = obj.search(cr, uid, domain, context=context)
- for invoice in obj.browse(cr, uid, opportunity_ids, context=context):
+ invoice_ids = obj.search(cr, uid, domain, context=context)
+ for invoice in obj.browse(cr, uid, invoice_ids, context=context):
rate += invoice.price_total
rate_invoice.append(rate)
rate_invoice.reverse()
diff --git a/addons/sale_crm/static/src/js/sale_crm.js b/addons/sale_crm/static/src/js/sale_crm.js
index 066ec062f70..7284dea57e9 100644
--- a/addons/sale_crm/static/src/js/sale_crm.js
+++ b/addons/sale_crm/static/src/js/sale_crm.js
@@ -12,7 +12,8 @@ openerp.sale_crm.GaugeWidget = openerp.web_kanban.AbstractField.extend({
label = this.getParent().record[this.options.label_field].raw_value;
}
var title = this.$node.html();
- var value = _.isArray(this.field.value) ? this.field.value.pop() : this.field.value;
+ var val = this.field.value;
+ var value = _.isArray(val) && val.length ? val[val.length-1] : val;
var unique_id = _.uniqueId("JustGage");
this.$el.empty()
From 2e2b0ec3b1729875c5ccaa0f853bf13708e6598d Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Mon, 6 May 2013 11:00:19 +0200
Subject: [PATCH 40/72] [IMP] sale_crm: widget gage add an input to change the
value
bzr revid: chm@openerp.com-20130506090019-wvq5o0qm5zn6umba
---
addons/sale_crm/sale_crm_view.xml | 2 +-
addons/sale_crm/static/src/js/sale_crm.js | 59 ++++++++++++++++++++---
2 files changed, 52 insertions(+), 9 deletions(-)
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index d169bf40b69..0493a603305 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -249,7 +249,7 @@
Invoiced
- Forecast
+ Forecast
Not target invoicing defined
diff --git a/addons/sale_crm/static/src/js/sale_crm.js b/addons/sale_crm/static/src/js/sale_crm.js
index 7284dea57e9..431bd9a4a9d 100644
--- a/addons/sale_crm/static/src/js/sale_crm.js
+++ b/addons/sale_crm/static/src/js/sale_crm.js
@@ -3,14 +3,12 @@ openerp.sale_crm = function(openerp) {
openerp.sale_crm.GaugeWidget = openerp.web_kanban.AbstractField.extend({
className: "oe_gage",
start: function() {
- var max = 100;
- if (this.options.max_field) {
- max = this.getParent().record[this.options.max_field].raw_value;
- }
- var label = "";
- if (this.options.label_field) {
- label = this.getParent().record[this.options.label_field].raw_value;
- }
+ var self = this;
+ console.log("start");
+
+ var parent = this.getParent();
+ var max = this.options.max_field ? parent.record[this.options.max_field].raw_value : 100;
+ var label = this.options.label_field ? parent.record[this.options.label_field].raw_value : "";
var title = this.$node.html();
var val = this.field.value;
var value = _.isArray(val) && val.length ? val[val.length-1] : val;
@@ -38,6 +36,51 @@ openerp.sale_crm.GaugeWidget = openerp.web_kanban.AbstractField.extend({
"#a9d70b"
],
});
+
+ var flag_open = false;
+ if (self.options.action_change) {
+ self.$el.click(function (event) {
+ event.stopPropagation();
+ flag_open = false;
+ if (!parent.view.is_action_enabled('edit')) {
+ return;
+ }
+ if (!self.$el.find(".oe_justgage_edit").size()) {
+ var $svg = self.$el.find('svg');
+ $div = $('
');
+ $input = $('
').val(value);
+ $div.append($input);
+ self.$el.prepend($div)
+ $input.focus()
+ .keydown(function (event) {
+ event.stopPropagation();
+ if (event.keyCode == 13 || event.keyCode == 9) {
+ if ($input.val() != value) {
+ parent.view.dataset.call(self.options.action_change, [parent.id, $input.val()]).then(function () {
+ parent.do_reload();
+ });
+ } else {
+ $div.remove();
+ }
+ }
+ })
+ .click(function (event) {
+ event.stopPropagation();
+ flag_open = false;
+ })
+ .blur(function (event) {
+ if(!flag_open) {
+ self.$el.find(".oe_justgage_edit").remove();
+ } else {
+ flag_open = false;
+ setTimeout(function () {$input.focus();}, 0);
+ }
+ });
+ }
+ }).mousedown(function () {
+ flag_open = true;
+ });
+ }
},
});
openerp.web_kanban.fields_registry.add("gage", "openerp.sale_crm.GaugeWidget");
From e4d24fe58a22911d98fce0d10f802521cf3080bb Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Mon, 6 May 2013 15:52:43 +0200
Subject: [PATCH 41/72] [IMP] crm.case.section: change use_leads tootip
bzr revid: chm@openerp.com-20130506135243-qbdgveg2y6pzfd3z
---
addons/crm/crm.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index 93831e50d91..153ccb39712 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -188,7 +188,7 @@ class crm_case_section(osv.osv):
"create new leads assigned to the team."),
'color': fields.integer('Color Index'),
'use_leads': fields.boolean('Leads',
- help="This enables the management of leads in the sales team. Otherwise the sales team manages only opportunities."),
+ 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."),
'target_duration': fields.selection([("monthly", "Monthly"), ("quarterly", "Quarterly"), ("semesterly", "Semesterly"), ("annually", "Annually")],
string='Report Duration View', required=True),
From f04357cc28e04bc0c02fd20fb6006832a5059177 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Mon, 6 May 2013 17:01:36 +0200
Subject: [PATCH 42/72] [IMP] crm.case.section: change link order
bzr revid: chm@openerp.com-20130506150136-7v8adhcumz9whg0e
---
addons/crm/crm_case_section_view.xml | 5 ++---
addons/sale_crm/sale_crm_view.xml | 16 +++++++++-------
2 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index f9591ff873c..9fc3f648a1c 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -99,11 +99,10 @@
%%
-
+
-
+
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index 0493a603305..12169c318f3 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -232,19 +232,21 @@
-
-
-
+
+
-
+
+
+
+
From 92784aad43e932f52c714af88163c59b11825c88 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Tue, 7 May 2013 10:15:51 +0200
Subject: [PATCH 43/72] [IMP] sale_crm: gage widget: click to change value
bzr revid: chm@openerp.com-20130507081551-y7guw9utkl228dcx
---
addons/sale_crm/static/src/js/sale_crm.js | 28 +++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/addons/sale_crm/static/src/js/sale_crm.js b/addons/sale_crm/static/src/js/sale_crm.js
index 431bd9a4a9d..c0dc4dc56d3 100644
--- a/addons/sale_crm/static/src/js/sale_crm.js
+++ b/addons/sale_crm/static/src/js/sale_crm.js
@@ -1,10 +1,10 @@
openerp.sale_crm = function(openerp) {
+var _t = openerp.web._t;
openerp.sale_crm.GaugeWidget = openerp.web_kanban.AbstractField.extend({
className: "oe_gage",
start: function() {
var self = this;
- console.log("start");
var parent = this.getParent();
var max = this.options.max_field ? parent.record[this.options.max_field].raw_value : 100;
@@ -39,6 +39,14 @@ openerp.sale_crm.GaugeWidget = openerp.web_kanban.AbstractField.extend({
var flag_open = false;
if (self.options.action_change) {
+ var $svg = self.$el.find('svg');
+ var css = {
+ 'text-align': 'center',
+ 'position': 'absolute',
+ 'width': self.$el.outerWidth() + 'px',
+ 'top': (self.$el.outerHeight()/2-5) + 'px'
+ };
+
self.$el.click(function (event) {
event.stopPropagation();
flag_open = false;
@@ -46,9 +54,14 @@ openerp.sale_crm.GaugeWidget = openerp.web_kanban.AbstractField.extend({
return;
}
if (!self.$el.find(".oe_justgage_edit").size()) {
- var $svg = self.$el.find('svg');
- $div = $('
');
- $input = $(' ').val(value);
+ $div = $('
');
+ $div.css(css);
+ $input = $(' ').val(value);
+ $input.css({
+ 'text-align': 'center',
+ 'margin': 'auto',
+ 'width': ($svg.outerWidth()-40) + 'px'
+ });
$div.append($input);
self.$el.prepend($div)
$input.focus()
@@ -80,6 +93,13 @@ openerp.sale_crm.GaugeWidget = openerp.web_kanban.AbstractField.extend({
}).mousedown(function () {
flag_open = true;
});
+
+ if (!+value) {
+ $svg.fadeTo(0, 0.3);
+ $div = $('
').text(_t("Click to change value"));
+ $div.css(css);
+ self.$el.append($div);
+ }
}
},
});
From 7542d8885d5ab01d7447448c707618e1f97d0928 Mon Sep 17 00:00:00 2001
From: "Dharmraj Zala (OpenERP Trainee)"
Date: Thu, 9 May 2013 17:06:15 +0530
Subject: [PATCH 44/72] [FIX] set ondelete restrict on state and country.
bzr revid: nco@tinyerp.com-20130509113615-u2xedxu5ihn9lqh9
---
openerp/addons/base/res/res_partner.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/openerp/addons/base/res/res_partner.py b/openerp/addons/base/res/res_partner.py
index 404fe5696c7..d50f38a77ee 100644
--- a/openerp/addons/base/res/res_partner.py
+++ b/openerp/addons/base/res/res_partner.py
@@ -259,8 +259,8 @@ class res_partner(osv.osv, format_address):
'street2': fields.char('Street2', size=128),
'zip': fields.char('Zip', change_default=True, size=24),
'city': fields.char('City', size=128),
- 'state_id': fields.many2one("res.country.state", 'State'),
- 'country_id': fields.many2one('res.country', 'Country'),
+ 'state_id': fields.many2one("res.country.state", 'State', ondelete='restrict'),
+ 'country_id': fields.many2one('res.country', 'Country', ondelete='restrict'),
'country': fields.related('country_id', type='many2one', relation='res.country', string='Country',
deprecated="This field will be removed as of OpenERP 7.1, use country_id instead"),
'email': fields.char('Email', size=240),
@@ -446,7 +446,7 @@ class res_partner(osv.osv, format_address):
commercial_fields = self._commercial_fields(cr, uid, context=context)
sync_vals = self._update_fields_values(cr, uid, partner.commercial_partner_id,
commercial_fields, context=context)
- return self.write(cr, uid, partner.id, sync_vals, context=context)
+ partner.write(sync_vals)
def _commercial_sync_to_children(self, cr, uid, partner, context=None):
""" Handle sync of commercial fields to descendants """
@@ -472,7 +472,7 @@ class res_partner(osv.osv, format_address):
use_parent_address=partner.use_parent_address,
parent_id=partner.parent_id.id,
context=context).get('value', {})
- self.update_address(cr, uid, partner.id, onchange_vals, context=context)
+ partner.update_address(onchange_vals)
# 2. To DOWNSTREAM: sync children
if partner.child_ids:
From d0236bd31e0c7773d68dc3f365c2c82c0e1dbc7a Mon Sep 17 00:00:00 2001
From: "Nimesh Contractor (OpenERP)"
Date: Wed, 15 May 2013 13:21:40 +0530
Subject: [PATCH 45/72] [FIX] set defalut customer and add field customer to
store value of customer.
bzr revid: nco@tinyerp.com-20130515075140-dql9dhnygtgi7319
---
openerp/addons/base/res/res_partner_view.xml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/openerp/addons/base/res/res_partner_view.xml b/openerp/addons/base/res/res_partner_view.xml
index 5b9d21045aa..1d196cda0ed 100644
--- a/openerp/addons/base/res/res_partner_view.xml
+++ b/openerp/addons/base/res/res_partner_view.xml
@@ -186,7 +186,7 @@
-
+
@@ -286,6 +286,7 @@
+
From 7e080f91367246c9fa2687a771edc4efdfcbb8f9 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Tue, 21 May 2013 12:28:03 +0200
Subject: [PATCH 46/72] [IMP] salesteams: change graph tooltips
bzr revid: chm@openerp.com-20130521102803-w5irg289skloi3f8
---
addons/crm/crm_case_section_view.xml | 4 ++--
addons/sale_crm/sale_crm_view.xml | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index 9fc3f648a1c..3ff6bae6abb 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -101,10 +101,10 @@
diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml
index 12169c318f3..c65f26b4da3 100644
--- a/addons/sale_crm/sale_crm_view.xml
+++ b/addons/sale_crm/sale_crm_view.xml
@@ -241,11 +241,11 @@
From 7e901f89ba6b511dac8d74b8e448d3c6b83804ae Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Tue, 21 May 2013 13:17:17 +0200
Subject: [PATCH 47/72] [IMP] salesteams: view
bzr revid: chm@openerp.com-20130521111717-m38hgfi2sevhzy5h
---
addons/crm/crm.py | 2 +-
addons/crm/crm_case_section_view.xml | 17 ++++++-----------
addons/sale_crm/sale_crm_view.xml | 15 +++++----------
3 files changed, 12 insertions(+), 22 deletions(-)
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index 153ccb39712..3841c252a9a 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -191,7 +191,7 @@ class crm_case_section(osv.osv):
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."),
'target_duration': fields.selection([("monthly", "Monthly"), ("quarterly", "Quarterly"), ("semesterly", "Semesterly"), ("annually", "Annually")],
- string='Report Duration View', required=True),
+ string='Report Duration', required=True),
'target_duration_txt': fields.function(_get_target_duration_txt,
string='Duration',
type="string", readonly=True),
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index 3ff6bae6abb..11c68bc51b1 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -99,8 +99,8 @@
%%
-
-
Leads
+
diff --git a/addons/hr/html/index.html b/addons/hr/html/index.html
index acfa5041a9d..0bc3db084a4 100644
--- a/addons/hr/html/index.html
+++ b/addons/hr/html/index.html
@@ -7,7 +7,6 @@
-
diff --git a/addons/mail/html/index.html b/addons/mail/html/index.html
index ad7f9daf1a9..b564cf3ab1e 100644
--- a/addons/mail/html/index.html
+++ b/addons/mail/html/index.html
@@ -9,7 +9,6 @@
-
diff --git a/addons/mrp/html/index.html b/addons/mrp/html/index.html
index d3e7a048490..5777eb5f147 100644
--- a/addons/mrp/html/index.html
+++ b/addons/mrp/html/index.html
@@ -9,7 +9,6 @@
-
diff --git a/addons/note/html/index.html b/addons/note/html/index.html
index 4e7db34df58..7ef37c646e9 100644
--- a/addons/note/html/index.html
+++ b/addons/note/html/index.html
@@ -7,7 +7,6 @@
-
diff --git a/addons/point_of_sale/html/index.html b/addons/point_of_sale/html/index.html
index 41f935d8ad9..130806ddb11 100644
--- a/addons/point_of_sale/html/index.html
+++ b/addons/point_of_sale/html/index.html
@@ -9,7 +9,6 @@
-
diff --git a/addons/project/html/index.html b/addons/project/html/index.html
index c1b6c3b966f..93f1be8a41d 100644
--- a/addons/project/html/index.html
+++ b/addons/project/html/index.html
@@ -7,7 +7,6 @@
-
diff --git a/addons/purchase/html/index.html b/addons/purchase/html/index.html
index 9134c1ab9d2..67b122ac557 100644
--- a/addons/purchase/html/index.html
+++ b/addons/purchase/html/index.html
@@ -9,7 +9,6 @@
-
diff --git a/addons/sale/html/index.html b/addons/sale/html/index.html
index be0d9ac0f94..1eb6215db2f 100644
--- a/addons/sale/html/index.html
+++ b/addons/sale/html/index.html
@@ -7,7 +7,6 @@
-
diff --git a/addons/stock/html/index.html b/addons/stock/html/index.html
index 21d2800e0dc..aac71e529ae 100644
--- a/addons/stock/html/index.html
+++ b/addons/stock/html/index.html
@@ -9,7 +9,6 @@
-
From 95c9d77c9f6330c4f87da460124cc59d4bd852b2 Mon Sep 17 00:00:00 2001
From: Launchpad Translations on behalf of openerp <>
Date: Tue, 4 Jun 2013 05:20:44 +0000
Subject: [PATCH 66/72] Launchpad automatic translations update.
bzr revid: launchpad_translations_on_behalf_of_openerp-20130604052044-tc482yzk760flm6m
---
addons/account_check_writing/i18n/ru.po | 32 +++----
addons/fleet/i18n/de.po | 28 +++---
addons/mrp/i18n/mn.po | 113 ++++++++++++------------
addons/sale/i18n/ru.po | 13 +--
addons/sale_analytic_plans/i18n/ru.po | 8 +-
addons/sale_crm/i18n/ru.po | 32 +++----
addons/stock/i18n/ru.po | 10 ++-
7 files changed, 121 insertions(+), 115 deletions(-)
diff --git a/addons/account_check_writing/i18n/ru.po b/addons/account_check_writing/i18n/ru.po
index ba474c72a22..8e68dd7152a 100644
--- a/addons/account_check_writing/i18n/ru.po
+++ b/addons/account_check_writing/i18n/ru.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME \n"
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
-"PO-Revision-Date: 2012-12-11 13:00+0000\n"
-"Last-Translator: FULL NAME \n"
+"PO-Revision-Date: 2013-06-03 07:47+0000\n"
+"Last-Translator: Chertykov Denis \n"
"Language-Team: Russian \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2013-03-16 05:51+0000\n"
-"X-Generator: Launchpad (build 16532)\n"
+"X-Launchpad-Export-Date: 2013-06-04 05:20+0000\n"
+"X-Generator: Launchpad (build 16660)\n"
#. module: account_check_writing
#: selection:res.company,check_layout:0
@@ -25,13 +25,13 @@ msgstr ""
#. module: account_check_writing
#: report:account.print.check.top:0
msgid "Open Balance"
-msgstr ""
+msgstr "Открытый баланс"
#. module: account_check_writing
#: view:account.check.write:0
#: view:account.voucher:0
msgid "Print Check"
-msgstr ""
+msgstr "Напечатать чек"
#. module: account_check_writing
#: selection:res.company,check_layout:0
@@ -82,7 +82,7 @@ msgstr "Описание"
#. module: account_check_writing
#: model:ir.model,name:account_check_writing.model_account_journal
msgid "Journal"
-msgstr ""
+msgstr "Журнал"
#. module: account_check_writing
#: model:ir.actions.act_window,name:account_check_writing.action_write_check
@@ -95,14 +95,14 @@ msgstr ""
#: report:account.print.check.middle:0
#: report:account.print.check.top:0
msgid "Discount"
-msgstr ""
+msgstr "Скидка"
#. module: account_check_writing
#: report:account.print.check.bottom:0
#: report:account.print.check.middle:0
#: report:account.print.check.top:0
msgid "Original Amount"
-msgstr ""
+msgstr "Первоначальная сумма"
#. module: account_check_writing
#: field:res.company,check_layout:0
@@ -119,7 +119,7 @@ msgstr ""
#: report:account.print.check.middle:0
#: report:account.print.check.top:0
msgid "Payment"
-msgstr ""
+msgstr "Платеж"
#. module: account_check_writing
#: field:account.journal,use_preprint_check:0
@@ -153,7 +153,7 @@ msgstr ""
#: report:account.print.check.middle:0
#: report:account.print.check.top:0
msgid "Due Date"
-msgstr ""
+msgstr "Дата исполнения"
#. module: account_check_writing
#: model:ir.actions.report.xml,name:account_check_writing.account_print_check_middle
@@ -163,13 +163,13 @@ msgstr ""
#. module: account_check_writing
#: model:ir.model,name:account_check_writing.model_res_company
msgid "Companies"
-msgstr ""
+msgstr "Компании"
#. module: account_check_writing
#: code:addons/account_check_writing/wizard/account_check_batch_printing.py:59
#, python-format
msgid "Error!"
-msgstr ""
+msgstr "Ошибка!"
#. module: account_check_writing
#: help:account.check.write,check_number:0
@@ -202,12 +202,12 @@ msgstr ""
#. module: account_check_writing
#: view:account.check.write:0
msgid "or"
-msgstr ""
+msgstr "или"
#. module: account_check_writing
#: field:account.voucher,amount_in_word:0
msgid "Amount in Word"
-msgstr ""
+msgstr "Сумма прописью"
#. module: account_check_writing
#: model:ir.model,name:account_check_writing.model_account_check_write
@@ -217,7 +217,7 @@ msgstr ""
#. module: account_check_writing
#: view:account.check.write:0
msgid "Cancel"
-msgstr ""
+msgstr "Отмена"
#. module: account_check_writing
#: field:account.check.write,check_number:0
diff --git a/addons/fleet/i18n/de.po b/addons/fleet/i18n/de.po
index 422f5b2fe7a..b47b8ba2074 100644
--- a/addons/fleet/i18n/de.po
+++ b/addons/fleet/i18n/de.po
@@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2013-06-03 06:02+0000\n"
+"X-Launchpad-Export-Date: 2013-06-04 05:20+0000\n"
"X-Generator: Launchpad (build 16660)\n"
#. module: fleet
@@ -35,7 +35,7 @@ msgstr "Ersatz Kälteklimakompressor"
#. module: fleet
#: help:fleet.vehicle,vin_sn:0
msgid "Unique number written on the vehicle motor (VIN/SN number)"
-msgstr "Eindeutige Fahrzeug-Identifizierungsnummer (FIN)"
+msgstr "Fahrgestellnummer"
#. module: fleet
#: selection:fleet.service.type,category:0
@@ -157,7 +157,7 @@ msgstr "Batterie Inspektion"
#. module: fleet
#: field:fleet.vehicle,company_id:0
msgid "Company"
-msgstr "Unternehmen"
+msgstr "Firma"
#. module: fleet
#: view:fleet.vehicle.log.contract:0
@@ -919,7 +919,7 @@ msgstr "Kaufdatum des Fahrzeugs"
#. module: fleet
#: view:fleet.vehicle.model:0
msgid "Models"
-msgstr "Modell"
+msgstr "Modelle"
#. module: fleet
#: view:fleet.vehicle.log.contract:0
@@ -1057,7 +1057,7 @@ msgstr "Kraftstoffart"
#. module: fleet
#: model:fleet.service.type,name:fleet.type_service_22
msgid "Fuel Injector Replacement"
-msgstr "Ersatz Einfüllstutzen"
+msgstr "Ersatz Einspritzdüse"
#. module: fleet
#: model:ir.actions.act_window,name:fleet.fleet_vehicle_state_act
@@ -1103,7 +1103,7 @@ msgstr "Datum Vertragsbeginn"
#. module: fleet
#: selection:fleet.vehicle,fuel_type:0
msgid "Electric"
-msgstr "Elektrisch"
+msgstr "Elektrik"
#. module: fleet
#: field:fleet.vehicle,tag_ids:0
@@ -1176,7 +1176,7 @@ msgstr "Ersatz Lichtmaschine"
#. module: fleet
#: model:fleet.service.type,name:fleet.type_service_3
msgid "A/C Diagnosis"
-msgstr "Diagnose Batterie"
+msgstr "Diagnose Klimaanlage"
#. module: fleet
#: model:fleet.service.type,name:fleet.type_service_23
@@ -1201,7 +1201,7 @@ msgstr "Ersatz Wärmepumpe"
#. module: fleet
#: view:fleet.vehicle:0
msgid "show all the costs for this vehicle"
-msgstr "gesamte Kostenübersicht der Fahrzeuge"
+msgstr "gesamte Kostenübersicht des Fahrzeugs"
#. module: fleet
#: view:fleet.vehicle.odometer:0
@@ -1536,7 +1536,7 @@ msgstr "Achsvermessung"
#. module: fleet
#: model:fleet.vehicle.tag,name:fleet.vehicle_tag_purchased
msgid "Purchased"
-msgstr "Ersatzteil"
+msgstr "Eingekauft"
#. module: fleet
#: model:ir.actions.act_window,help:fleet.fleet_vehicle_odometer_act
@@ -1749,7 +1749,7 @@ msgstr "Preis"
#: field:fleet.vehicle.cost,odometer:0
#: field:fleet.vehicle.odometer,value:0
msgid "Odometer Value"
-msgstr "Tachostand"
+msgstr "Kilometerstand"
#. module: fleet
#: view:fleet.vehicle:0
@@ -1807,7 +1807,7 @@ msgstr "Schreiben Sie hier alle erforderlichen Zusatzinformationen auf."
#. module: fleet
#: model:fleet.service.type,name:fleet.type_service_29
msgid "Ignition Coil Replacement"
-msgstr "Ersatz Zündkerzen"
+msgstr "Ersatz Zündspule"
#. module: fleet
#: model:fleet.service.type,name:fleet.type_service_service_16
@@ -1817,7 +1817,7 @@ msgstr "Optionen"
#. module: fleet
#: model:fleet.service.type,name:fleet.type_contract_repairing
msgid "Repairing"
-msgstr "Reparieren"
+msgstr "Wird repariert"
#. module: fleet
#: model:ir.actions.act_window,name:fleet.action_fleet_reporting_costs
@@ -1855,7 +1855,7 @@ msgstr "Getriebe"
#. module: fleet
#: field:fleet.vehicle,vin_sn:0
msgid "Chassis Number"
-msgstr "Fahrzeuqinformationsnummer"
+msgstr "Fahrgestellnummer"
#. module: fleet
#: help:fleet.vehicle,color:0
@@ -2011,7 +2011,7 @@ msgstr "Reifenwechsel"
#. module: fleet
#: model:fleet.service.type,name:fleet.type_service_42
msgid "Starter Replacement"
-msgstr "Erstinspektion"
+msgstr "Ersatz Anlasser"
#. module: fleet
#: view:fleet.vehicle.cost:0
diff --git a/addons/mrp/i18n/mn.po b/addons/mrp/i18n/mn.po
index 8d3c0aee785..39cc3d602cf 100644
--- a/addons/mrp/i18n/mn.po
+++ b/addons/mrp/i18n/mn.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME \n"
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
-"PO-Revision-Date: 2012-07-21 12:33+0000\n"
-"Last-Translator: FULL NAME \n"
+"PO-Revision-Date: 2013-06-04 04:00+0000\n"
+"Last-Translator: erdenebold \n"
"Language-Team: Mongolian \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2013-03-16 05:01+0000\n"
-"X-Generator: Launchpad (build 16532)\n"
+"X-Launchpad-Export-Date: 2013-06-04 05:20+0000\n"
+"X-Generator: Launchpad (build 16660)\n"
#. module: mrp
#: help:mrp.config.settings,module_mrp_repair:0
@@ -48,7 +48,7 @@ msgstr "Дамжлагуудын Ашиглалт"
#. module: mrp
#: view:mrp.routing.workcenter:0
msgid "Routing Work Centers"
-msgstr "Дамжлагуудын маршрут"
+msgstr "Шугамын дамжлагууд"
#. module: mrp
#: field:mrp.production.workcenter.line,cycle:0
@@ -86,12 +86,12 @@ msgstr ""
#: model:ir.actions.act_window,name:mrp.mrp_routing_action
#: model:ir.ui.menu,name:mrp.menu_mrp_routing_action
msgid "Routings"
-msgstr "Маршрут"
+msgstr "Шугам"
#. module: mrp
#: view:mrp.bom:0
msgid "Search Bill Of Material"
-msgstr "Жор Хайх"
+msgstr "Орц Хайх"
#. module: mrp
#: model:process.node,note:mrp.process_node_stockproduct1
@@ -112,8 +112,8 @@ msgid ""
"Number of iterations this work center has to do in the specified operation "
"of the routing."
msgstr ""
-"Үйлдвэрлэлийн нэг маршрутын хувьд уг дамжлага өөрийн үйлдлийг давтан "
-"гүйцэтгэх давтамжын тоо."
+"Үйлдвэрлэлийн нэг шугамын хувьд уг дамжлага өөрийн үйлдлийг давтан гүйцэтгэх "
+"давтамжын тоо."
#. module: mrp
#: view:product.product:0
@@ -214,7 +214,7 @@ msgstr ""
#: code:addons/mrp/mrp.py:633
#, python-format
msgid "Cannot cancel manufacturing order!"
-msgstr ""
+msgstr "Үйлдвэрийн захиалгыг цуцлах боломжгүй!"
#. module: mrp
#: field:mrp.workcenter,costs_cycle_account_id:0
@@ -337,7 +337,7 @@ msgstr "Бараа Үйлдвэрлэх"
#. module: mrp
#: constraint:mrp.bom:0
msgid "Error ! You cannot create recursive BoM."
-msgstr "Алдаа ! Тойрог хамааралтай жор үүсгэж болохгүй."
+msgstr "Алдаа ! Тойрог хамааралтай орц үүсгэж болохгүй."
#. module: mrp
#: model:ir.model,name:mrp.model_mrp_routing_workcenter
@@ -423,7 +423,7 @@ msgstr ""
#: field:mrp.production,product_qty:0
#: field:mrp.production.product.line,product_qty:0
msgid "Product Quantity"
-msgstr ""
+msgstr "Барааны тоо"
#. module: mrp
#: help:mrp.production,picking_id:0
@@ -491,13 +491,13 @@ msgstr "Товлогдсон огноо"
#: code:addons/mrp/procurement.py:124
#, python-format
msgid "Manufacturing Order %s created."
-msgstr ""
+msgstr "Үйлдвэрлэлийн захиалга %s үүсгэгдлээ."
#. module: mrp
#: view:mrp.bom:0
#: report:mrp.production.order:0
msgid "Bill Of Material"
-msgstr "Жор"
+msgstr "Орц"
#. module: mrp
#: help:mrp.routing,location_id:0
@@ -518,7 +518,7 @@ msgstr "Нөөцийн Үнийн Хазайлт"
#. module: mrp
#: model:ir.actions.act_window,name:mrp.action2
msgid "Bill of Materials Structure"
-msgstr "Жорын Бүтэц"
+msgstr "Орцын Бүтэц"
#. module: mrp
#: model:process.node,note:mrp.process_node_serviceproduct0
@@ -560,7 +560,7 @@ msgstr ""
msgid ""
"The Bill of Material is linked to a routing, i.e. the succession of work "
"centers."
-msgstr "Жор нь маршруттай холбогдсон, ө.х. дамжлагуудын цуваанууд"
+msgstr "Орц нь шугамтай холбогдсон, ө.х. дамжлагуудын цуваанууд"
#. module: mrp
#: view:mrp.production:0
@@ -592,7 +592,7 @@ msgstr "Нөөцийн үнэ"
#. module: mrp
#: model:ir.actions.act_window,name:mrp.action_product_bom_structure
msgid "Product BoM Structure"
-msgstr "Барааны Жорын Бүтэц"
+msgstr "Барааны Орцын Бүтэц"
#. module: mrp
#: view:mrp.production:0
@@ -603,12 +603,12 @@ msgstr "Үйлдвэрлэл Хайх"
#: help:mrp.routing.workcenter,sequence:0
msgid ""
"Gives the sequence order when displaying a list of routing Work Centers."
-msgstr "Маршруутын дамжлагуудыг жагсаалтаар харуулах дэс дарааллыг өгнө."
+msgstr "Шугамын дамжлагуудыг жагсаалтаар харуулах дэс дарааллыг өгнө."
#. module: mrp
#: field:mrp.bom,child_complete_ids:0
msgid "BoM Hierarchy"
-msgstr "Жорын удамшил"
+msgstr "Орцын удамшил"
#. module: mrp
#: model:process.transition,name:mrp.process_transition_stockproduction0
@@ -637,7 +637,7 @@ msgstr "Бэлтгэлтийн Сондгойрол"
#. module: mrp
#: field:mrp.bom,bom_lines:0
msgid "BoM Lines"
-msgstr "Жорын мөрүүд"
+msgstr "Орцын мөрүүд"
#. module: mrp
#: field:mrp.workcenter,time_start:0
@@ -650,13 +650,13 @@ msgid ""
"If the active field is set to False, it will allow you to hide the routing "
"without removing it."
msgstr ""
-"Идэвхтэй талбар нь худал буюу тэмдэглэгдээгүй байвал машрутыг устгалгүйгээр "
+"Идэвхтэй талбар нь худал буюу тэмдэглэгдээгүй байвал шугамыг устгалгүйгээр "
"нуудаг."
#. module: mrp
#: model:process.transition,name:mrp.process_transition_billofmaterialrouting0
msgid "Material Routing"
-msgstr "Материалын Машрут"
+msgstr "Материалын Шугам"
#. module: mrp
#: view:mrp.production:0
@@ -676,13 +676,13 @@ msgstr "Дамжлагын Ачаалал"
#: code:addons/mrp/procurement.py:50
#, python-format
msgid "No BoM defined for this product !"
-msgstr "Энэ бараанд жор тодорхойлогдоогүй байна !"
+msgstr "Энэ бараанд орц тодорхойлогдоогүй байна !"
#. module: mrp
#: model:ir.actions.act_window,name:mrp.mrp_bom_form_action2
#: model:ir.ui.menu,name:mrp.menu_mrp_bom_form_action2
msgid "Bill of Material Components"
-msgstr "Жорын бүрдэлүүд"
+msgstr "Орцын бүрдэлүүд"
#. module: mrp
#: model:ir.model,name:mrp.model_stock_move
@@ -708,7 +708,7 @@ msgid ""
"operations and to plan future loads on work centers based on production "
"plannification."
msgstr ""
-"Эцсийн барааг үйлдвэрлэх үйлдлүүд (дамжлагууд)-н жагсаалт. Машрут гэдэг нь "
+"Эцсийн барааг үйлдвэрлэх үйлдлүүд (дамжлагууд)-н жагсаалт. Шугам гэдэг нь "
"үндсэндээ дамжлагуудын өртөгийг тооцоолох болон ажлын ачааллыг тооцоолоход "
"үйлдвэрлэлийн төлөвлөгөөн дээр суурилан бодоход хэрэглэгддэг."
@@ -734,7 +734,7 @@ msgstr ""
#. module: mrp
#: constraint:mrp.bom:0
msgid "BoM line product should not be same as BoM product."
-msgstr "Жорын мөр нь жорын бараатайгаа ижил байж болохгүй."
+msgstr "Орцын мөр нь орцын бараатайгаа ижил байж болохгүй."
#. module: mrp
#: view:mrp.production:0
@@ -744,7 +744,7 @@ msgstr "Үйлдвэрлэгдэж буй"
#. module: mrp
#: model:ir.ui.menu,name:mrp.menu_mrp_property
msgid "Master Bill of Materials"
-msgstr "Мастер жор"
+msgstr "Мастер орц"
#. module: mrp
#: help:mrp.config.settings,module_product_manufacturer:0
@@ -907,7 +907,7 @@ msgid ""
"All product quantities must be greater than 0.\n"
"You should install the mrp_byproduct module if you want to manage extra "
"products on BoMs !"
-msgstr ""
+msgstr "Бүх бүтээгдэхүүнийг тоог 0-оос эхэлж үүсгэнэ."
#. module: mrp
#: view:mrp.production:0
@@ -954,7 +954,7 @@ msgstr ""
#. module: mrp
#: field:mrp.bom,type:0
msgid "BoM Type"
-msgstr "Жорын төрөл"
+msgstr "Орцын төрөл"
#. module: mrp
#: code:addons/mrp/procurement.py:52
@@ -962,7 +962,7 @@ msgstr "Жорын төрөл"
msgid ""
"Procurement '%s' has an exception: 'No BoM defined for this product !'"
msgstr ""
-"'%s' татан авалтад сондгойрол байна: 'Бараанд жор тодорхойлогдоогүй !'"
+"'%s' татан авалтад сондгойрол байна: 'Бараанд орц тодорхойлогдоогүй !'"
#. module: mrp
#: view:mrp.property:0
@@ -1077,7 +1077,7 @@ msgstr "Нөөцлүүлэх"
#. module: mrp
#: report:bom.structure:0
msgid "BOM Name"
-msgstr "Жорын нэр"
+msgstr "Орцын нэр"
#. module: mrp
#: model:ir.actions.act_window,name:mrp.act_product_manufacturing_open
@@ -1167,7 +1167,7 @@ msgstr "Хийгдэж байгаа үйлдвэрлэлийн захиалгу
#. module: mrp
#: model:ir.actions.client,name:mrp.action_client_mrp_menu
msgid "Open MRP Menu"
-msgstr ""
+msgstr "Нээлттэй MRP цэс"
#. module: mrp
#: model:ir.actions.act_window,name:mrp.mrp_production_action4
@@ -1194,7 +1194,7 @@ msgstr "Циклийн Өртөг"
#: code:addons/mrp/wizard/change_production_qty.py:88
#, python-format
msgid "Cannot find bill of material for this product."
-msgstr ""
+msgstr "Энэ бараа материалаар хайх боломжгүй."
#. module: mrp
#: selection:mrp.workcenter.load,measure_unit:0
@@ -1216,8 +1216,7 @@ msgstr "Нөөцүүд"
msgid ""
"Time in hours for this Work Center to achieve the operation of the specified "
"routing."
-msgstr ""
-"Заагдсан маршрутын үйлдлийг гүйцэтгэхэд энэ дамжлагын хугацаа цагаар."
+msgstr "Заагдсан шугамын үйлдлийг гүйцэтгэхэд энэ дамжлагын хугацаа цагаар."
#. module: mrp
#: field:mrp.workcenter,costs_journal_id:0
@@ -1272,7 +1271,7 @@ msgstr "Үйлдвэрлэлийг эхлүүлэхэд бэлэн Үйлдвэ
#: field:mrp.production,bom_id:0
#: model:process.node,name:mrp.process_node_billofmaterial0
msgid "Bill of Material"
-msgstr "Жор"
+msgstr "Орц"
#. module: mrp
#: view:mrp.workcenter.load:0
@@ -1304,9 +1303,9 @@ msgid ""
"Routing is indicated then,the third tab of a production order (Work Centers) "
"will be automatically pre-completed."
msgstr ""
-"Маршрут нь дамжлагууд ямар хугацаагаар хэдэн циклээр ашиглагдахыг "
-"илэрхийлдэг. Хэрэв маршрут байгаа бол гурав дахь үйлдвэрлэлийн дарааллын "
-"хавтас автоматаар бөглөгддөг."
+"Шугам нь дамжлагууд ямар хугацаагаар хэдэн циклээр ашиглагдахыг илэрхийлдэг. "
+"Хэрэв шугам байгаа бол гурав дахь үйлдвэрлэлийн дарааллын хавтас автоматаар "
+"бөглөгддөг."
#. module: mrp
#: code:addons/mrp/mrp.py:505
@@ -1431,7 +1430,7 @@ msgstr "Үйлдвэрлэлийн дамжлагыг хайх"
#. module: mrp
#: view:mrp.bom:0
msgid "BoM Structure"
-msgstr "Жорын бүтэц"
+msgstr "Орцын бүтэц"
#. module: mrp
#: field:mrp.production,date_start:0
@@ -1541,7 +1540,7 @@ msgstr "БЗ Дугаар"
#: code:addons/mrp/mrp.py:505
#, python-format
msgid "Cannot delete a manufacturing order in state '%s'."
-msgstr ""
+msgstr "Үйлдвэрлэлийн захиалгын '%s' төлөвт устгах боломжгүй."
#. module: mrp
#: selection:mrp.production,state:0
@@ -1689,7 +1688,7 @@ msgstr "Нөөц"
#: help:mrp.bom,date_stop:0
msgid "Validity of this BoM or component. Keep empty if it's always valid."
msgstr ""
-"Энэ жор эсвэл бүрэлдхүүний зөв эсэхийн шалгалт. Хэрэв байнга зөв бол хоосон "
+"Орц эсвэл бүрэлдэхүүний зөв эсэхийн шалгалт. Хэрэв байнга зөв бол хоосон "
"үлдээнэ."
#. module: mrp
@@ -1710,7 +1709,7 @@ msgid ""
"operations and to plan future loads on work centers based on production "
"planning."
msgstr ""
-"Барааг үйлдвэрлэх үйлдлийн жагсаалт (дамжлагын жагсаалт). Машрут нь "
+"Барааг үйлдвэрлэх үйлдлийн жагсаалт (дамжлагын жагсаалт). Шугам нь "
"үйлдвэрлэлийн захиалга дээр үндэслэн дамжлагуудын өртөгийг тооцох, ажлын "
"ирээдүйн ачааллыг тооцоолох зэрэгт голчлон хэрэглэгддэг."
@@ -1816,8 +1815,8 @@ msgid ""
"are products themselves) can also have their own Bill of Material (multi-"
"level)."
msgstr ""
-"Жор гэдэг нь барааны задаргаа юм. Бүрэлдхүүнүүд (мөн бараанууд) нь мөн "
-"өөрийн жортой буюу олон түвшний жор байж болно."
+"Орц гэдэг нь барааны задаргаа юм. Бүрэлдэхүүнүүд (мөн бараанууд) нь мөн "
+"өөрийн орцтой буюу олон түвшний орц байж болно."
#. module: mrp
#: field:mrp.bom,company_id:0
@@ -1882,7 +1881,7 @@ msgstr "Бүрэлдэхүүнүүд"
#: report:bom.structure:0
#: model:ir.actions.report.xml,name:mrp.report_bom_structure
msgid "BOM Structure"
-msgstr "Жорын бүтэц"
+msgstr "Орцын бүтэц"
#. module: mrp
#: field:mrp.config.settings,module_mrp_jit:0
@@ -1902,7 +1901,7 @@ msgstr "Хүчинтэй байх эхлэл"
#. module: mrp
#: selection:mrp.bom,type:0
msgid "Normal BoM"
-msgstr "Хэвийн жор"
+msgstr "Хэвийн орц"
#. module: mrp
#: field:res.company,manufacturing_lead:0
@@ -1958,7 +1957,7 @@ msgid ""
"If the active field is set to False, it will allow you to hide the bills of "
"material without removing it."
msgstr ""
-"Идэвхтэй талбар худал буюу тэмдэглэгдээгүй бол жорыг устгалгүйгээр нууна."
+"Идэвхтэй талбар худал буюу тэмдэглэгдээгүй бол орцыг устгалгүйгээр нууна."
#. module: mrp
#: field:mrp.bom,product_rounding:0
@@ -2083,7 +2082,7 @@ msgstr ""
#. module: mrp
#: field:procurement.order,bom_id:0
msgid "BoM"
-msgstr "Жор"
+msgstr "Орц"
#. module: mrp
#: model:ir.model,name:mrp.model_report_mrp_inout
@@ -2145,12 +2144,12 @@ msgstr "Хангах болон Үйлдвэрлэх"
#. module: mrp
#: field:mrp.bom,bom_id:0
msgid "Parent BoM"
-msgstr "Эцэг жор"
+msgstr "Эцэг орц"
#. module: mrp
#: report:bom.structure:0
msgid "BOM Ref"
-msgstr "Жорын код"
+msgstr "Орцын код"
#. module: mrp
#: code:addons/mrp/mrp.py:765
@@ -2190,7 +2189,7 @@ msgstr "Тоо хэмжээг сонго"
#: view:product.product:0
#: field:product.product,bom_ids:0
msgid "Bill of Materials"
-msgstr "Жорууд"
+msgstr "Орцууд"
#. module: mrp
#: code:addons/mrp/mrp.py:610
@@ -2322,7 +2321,7 @@ msgstr ""
msgid ""
"a manufacturing\n"
" order"
-msgstr ""
+msgstr "үйлдвэрлэл"
#. module: mrp
#: field:mrp.config.settings,group_mrp_properties:0
@@ -2344,7 +2343,7 @@ msgstr "Үзүүлэлтийн бүлэгүүд"
#: view:mrp.routing:0
#: model:process.node,name:mrp.process_node_routing0
msgid "Routing"
-msgstr "Маршрут"
+msgstr "Шугам"
#. module: mrp
#: model:process.transition,note:mrp.process_transition_procurestockableproduct0
@@ -2390,8 +2389,8 @@ msgid ""
"the BOM, through a run of the schedulers (MRP)."
msgstr ""
"Түүхий эдийг хангахын тулд (худалдан авах эсвэл үйлдвэрлэх) үйлдвэрлэлийн "
-"захиалга нь жорд тодорхойлогдсон бүрдлүүдээр татан авах захиалга үүсгэдэг ба "
-"энэ нь товлогчийг ажиллахад үүсдэг."
+"захиалга нь орцонд тодорхойлогдсон бүрдлүүдээр татан авах захиалга үүсгэдэг "
+"ба энэ нь товлогчийг ажиллахад үүсдэг."
#. module: mrp
#: help:mrp.product_price,number:0
@@ -2423,7 +2422,7 @@ msgstr "Нөөцийн Амралт, Чөлөө"
#. module: mrp
#: help:mrp.bom,sequence:0
msgid "Gives the sequence order when displaying a list of bills of material."
-msgstr "Жорын жагсаалтыг харуулах дэс дарааллыг өгдөг."
+msgstr "Орцын жагсаалтыг харуулах дэс дарааллыг өгдөг."
#. module: mrp
#: model:ir.model,name:mrp.model_mrp_config_settings
diff --git a/addons/sale/i18n/ru.po b/addons/sale/i18n/ru.po
index 405ac210af5..8449bd83224 100644
--- a/addons/sale/i18n/ru.po
+++ b/addons/sale/i18n/ru.po
@@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
-"PO-Revision-Date: 2012-12-28 14:12+0000\n"
+"PO-Revision-Date: 2013-06-03 07:54+0000\n"
"Last-Translator: Chertykov Denis \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2013-03-16 05:22+0000\n"
-"X-Generator: Launchpad (build 16532)\n"
+"X-Launchpad-Export-Date: 2013-06-04 05:20+0000\n"
+"X-Generator: Launchpad (build 16660)\n"
#. module: sale
#: model:res.groups,name:sale.group_analytic_accounting
@@ -89,6 +89,8 @@ msgid ""
"${(object.name or '').replace('/','_')}_${object.state == 'draft' and "
"'draft' or ''}"
msgstr ""
+"${(object.name or '').replace('/','_')}_${object.state == 'draft' and "
+"'draft' or ''}"
#. module: sale
#: view:sale.order.line:0
@@ -558,6 +560,7 @@ msgstr "Подготовить счета основанные на распис
#, python-format
msgid "You cannot cancel a sales order line that has already been invoiced."
msgstr ""
+"Нельзя отменить позиции заказа продажи на которые был выставлен счет."
#. module: sale
#: view:account.invoice.report:0
@@ -1550,7 +1553,7 @@ msgstr "Искать позиции без счетов"
#. module: sale
#: model:ir.model,name:sale.model_account_config_settings
msgid "account.config.settings"
-msgstr ""
+msgstr "account.config.settings"
#. module: sale
#: sql_constraint:sale.order:0
@@ -1876,7 +1879,7 @@ msgstr ""
#. module: sale
#: view:res.partner:0
msgid "sale.group_delivery_invoice_address"
-msgstr ""
+msgstr "sale.group_delivery_invoice_address"
#. module: sale
#: code:addons/sale/sale.py:781
diff --git a/addons/sale_analytic_plans/i18n/ru.po b/addons/sale_analytic_plans/i18n/ru.po
index 90d9eb5c3b9..8995b7b45c0 100644
--- a/addons/sale_analytic_plans/i18n/ru.po
+++ b/addons/sale_analytic_plans/i18n/ru.po
@@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
-"PO-Revision-Date: 2010-12-25 22:14+0000\n"
+"PO-Revision-Date: 2013-06-03 08:05+0000\n"
"Last-Translator: Chertykov Denis \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2013-03-16 05:32+0000\n"
-"X-Generator: Launchpad (build 16532)\n"
+"X-Launchpad-Export-Date: 2013-06-04 05:20+0000\n"
+"X-Generator: Launchpad (build 16660)\n"
#. module: sale_analytic_plans
#: field:sale.order.line,analytics_id:0
@@ -24,7 +24,7 @@ msgstr "Разнесение аналитики"
#. module: sale_analytic_plans
#: model:ir.model,name:sale_analytic_plans.model_sale_order
msgid "Sales Order"
-msgstr ""
+msgstr "Заказ продаж"
#. module: sale_analytic_plans
#: model:ir.model,name:sale_analytic_plans.model_sale_order_line
diff --git a/addons/sale_crm/i18n/ru.po b/addons/sale_crm/i18n/ru.po
index 8ae90e42303..f6266edf8ca 100644
--- a/addons/sale_crm/i18n/ru.po
+++ b/addons/sale_crm/i18n/ru.po
@@ -7,20 +7,20 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
-"PO-Revision-Date: 2011-03-23 13:50+0000\n"
+"PO-Revision-Date: 2013-06-03 08:05+0000\n"
"Last-Translator: Chertykov Denis \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2013-03-16 05:10+0000\n"
-"X-Generator: Launchpad (build 16532)\n"
+"X-Launchpad-Export-Date: 2013-06-04 05:20+0000\n"
+"X-Generator: Launchpad (build 16660)\n"
#. module: sale_crm
#: code:addons/sale_crm/wizard/crm_make_sale.py:90
#, python-format
msgid "Insufficient Data!"
-msgstr ""
+msgstr "Недостаточно данных!"
#. module: sale_crm
#: view:crm.lead:0
@@ -31,12 +31,12 @@ msgstr "Преобразовать в предложение"
#. module: sale_crm
#: model:ir.model,name:sale_crm.model_account_invoice_report
msgid "Invoices Statistics"
-msgstr ""
+msgstr "Статистика по счетам"
#. module: sale_crm
#: field:crm.make.sale,close:0
msgid "Mark Won"
-msgstr ""
+msgstr "Отметить \"Выиграна\""
#. module: sale_crm
#: field:res.users,default_section_id:0
@@ -51,23 +51,23 @@ msgstr "Мои отделы продаж"
#. module: sale_crm
#: model:ir.model,name:sale_crm.model_res_users
msgid "Users"
-msgstr ""
+msgstr "Пользователи"
#. module: sale_crm
#: help:crm.make.sale,close:0
msgid ""
"Check this to close the opportunity after having created the sales order."
-msgstr ""
+msgstr "Отметьте, для закрытия предложения после создания заказа продажи."
#. module: sale_crm
#: model:mail.message.subtype,name:sale_crm.mt_salesteam_order_sent
msgid "Quotation Send"
-msgstr ""
+msgstr "Отправить предложение"
#. module: sale_crm
#: field:sale.order,categ_ids:0
msgid "Categories"
-msgstr ""
+msgstr "Категории"
#. module: sale_crm
#: code:addons/sale_crm/wizard/crm_make_sale.py:124
@@ -94,7 +94,7 @@ msgstr "Продать"
#. module: sale_crm
#: model:ir.model,name:sale_crm.model_account_invoice
msgid "Invoice"
-msgstr ""
+msgstr "Счет"
#. module: sale_crm
#: code:addons/sale_crm/wizard/crm_make_sale.py:93
@@ -106,7 +106,7 @@ msgstr "Предложение: %s"
#: code:addons/sale_crm/wizard/crm_make_sale.py:110
#, python-format
msgid "Opportunity has been converted to the quotation %s ."
-msgstr ""
+msgstr "Предложение преобразовано в заявку %s ."
#. module: sale_crm
#: field:crm.make.sale,shop_id:0
@@ -117,12 +117,12 @@ msgstr "Магазин"
#: code:addons/sale_crm/wizard/crm_make_sale.py:90
#, python-format
msgid "No addresse(s) defined for this customer."
-msgstr ""
+msgstr "Не задан адрес для этого заказчика"
#. module: sale_crm
#: model:mail.message.subtype,name:sale_crm.mt_salesteam_order_confirmed
msgid "Sales Order Confirmed"
-msgstr ""
+msgstr "Заказ продаж подтвержден"
#. module: sale_crm
#: view:account.invoice:0
@@ -136,7 +136,7 @@ msgstr "Отдел продаж"
#. module: sale_crm
#: view:crm.lead:0
msgid "Create Quotation"
-msgstr ""
+msgstr "Создать заявку"
#. module: sale_crm
#: model:ir.actions.act_window,name:sale_crm.action_crm_make_sale
@@ -156,7 +156,7 @@ msgstr "Заказ продаж"
#. module: sale_crm
#: view:crm.make.sale:0
msgid "or"
-msgstr ""
+msgstr "или"
#~ msgid "Create"
#~ msgstr "Создать"
diff --git a/addons/stock/i18n/ru.po b/addons/stock/i18n/ru.po
index 4a79cec4b89..d0d34b32191 100644
--- a/addons/stock/i18n/ru.po
+++ b/addons/stock/i18n/ru.po
@@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
-"PO-Revision-Date: 2013-01-22 08:16+0000\n"
+"PO-Revision-Date: 2013-06-03 12:48+0000\n"
"Last-Translator: Chertykov Denis \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2013-03-16 04:58+0000\n"
-"X-Generator: Launchpad (build 16532)\n"
+"X-Launchpad-Export-Date: 2013-06-04 05:20+0000\n"
+"X-Generator: Launchpad (build 16660)\n"
#. module: stock
#: field:stock.inventory.line.split,line_exist_ids:0
@@ -660,6 +660,8 @@ msgid ""
"Forces to specify a Serial Number for all moves containing this product and "
"going to a Customer Location"
msgstr ""
+"Принудительно установить порядковый номер для всех перемещений этой "
+"продукции в место хранения заказчика"
#. module: stock
#: model:ir.ui.menu,name:stock.menu_stock_products_moves
@@ -845,6 +847,8 @@ msgid ""
"Forces to specify a Serial Number for all moves containing this product and "
"coming from a Supplier Location"
msgstr ""
+"Принудительно установить порядковый номер для всех перемещений этой "
+"продукции из мест хранения поставщика"
#. module: stock
#: help:stock.config.settings,group_uos:0
From 6049940fea116945415310a388f7f2fa9073fd1b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?=
Date: Tue, 4 Jun 2013 11:10:14 +0200
Subject: [PATCH 67/72] [IMP] crm: lead form view: escalate as a link
bzr revid: tde@openerp.com-20130604091014-25vsm5upv9z1oq97
---
addons/crm/crm_lead_view.xml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml
index a2ecc45652f..eb5536e3574 100644
--- a/addons/crm/crm_lead_view.xml
+++ b/addons/crm/crm_lead_view.xml
@@ -157,7 +157,8 @@
-
From 8480636380f4a398d1d2ba03f5b209c15675ba5a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?=
Date: Tue, 4 Jun 2013 12:56:48 +0200
Subject: [PATCH 68/72] [CLEAN] crm: removed minified file for sparkline as we
use plain file; small changes in views.
bzr revid: tde@openerp.com-20130604105648-ytfhdb5wmdrzkyay
---
addons/crm/__openerp__.py | 5 +++--
addons/crm/crm_case_section_view.xml | 7 ++++---
addons/crm/crm_lead_view.xml | 2 +-
addons/crm/static/lib/sparkline/jquery.sparkline.min.js | 5 -----
4 files changed, 8 insertions(+), 11 deletions(-)
delete mode 100644 addons/crm/static/lib/sparkline/jquery.sparkline.min.js
diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py
index 937e0790aed..3a37f33e090 100644
--- a/addons/crm/__openerp__.py
+++ b/addons/crm/__openerp__.py
@@ -81,6 +81,8 @@ Dashboard for CRM will include:
'crm_lead_view.xml',
'crm_lead_menu.xml',
+ 'crm_case_section_view.xml',
+
'crm_meeting_menu.xml',
'crm_phonecall_view.xml',
@@ -95,7 +97,6 @@ Dashboard for CRM will include:
'board_crm_view.xml',
'res_config_view.xml',
- 'crm_case_section_view.xml',
'base_partner_merge_view.xml',
],
'demo': [
@@ -120,8 +121,8 @@ Dashboard for CRM will include:
'static/src/css/crm.css'
],
'js': [
+ 'static/lib/sparkline/jquery.sparkline.js',
'static/src/js/crm_case_section.js',
- 'static/lib/sparkline/jquery.sparkline.min.js',
],
'installable': True,
'application': True,
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index c73390cdb71..c55aec749d6 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -125,8 +125,9 @@
-
+
+
@@ -140,7 +141,7 @@
crm.case.section
form
kanban,tree,form
- {'search_default_my': True}
+ {'search_default_personal': True}
@@ -164,7 +165,7 @@
-
+
diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml
index eb5536e3574..6ddd06c1c90 100644
--- a/addons/crm/crm_lead_view.xml
+++ b/addons/crm/crm_lead_view.xml
@@ -557,7 +557,7 @@
-
● {{prefix}}{{y}}{{suffix}}')},bar:{barColor:"#3366cc",negBarColor:"#f44",stackedBarColor:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],zeroColor:undefined,nullColor:undefined,zeroAxis:!0,barWidth:4,barSpacing:1,chartRangeMax:undefined,chartRangeMin:undefined,chartRangeClip:!1,colorMap:undefined,tooltipFormat:new e('● {{prefix}}{{value}}{{suffix}}')},tristate:{barWidth:4,barSpacing:1,posBarColor:"#6f6",negBarColor:"#f44",zeroBarColor:"#999",colorMap:{},tooltipFormat:new e('● {{value:map}}'),tooltipValueLookups:{map:{"-1":"Loss",0:"Draw",1:"Win"}}},discrete:{lineHeight:"auto",thresholdColor:undefined,thresholdValue:0,chartRangeMax:undefined,chartRangeMin:undefined,chartRangeClip:!1,tooltipFormat:new e("{{prefix}}{{value}}{{suffix}}")},bullet:{targetColor:"#f33",targetWidth:3,performanceColor:"#33f",rangeColors:["#d3dafe","#a8b6ff","#7f94ff"],base:undefined,tooltipFormat:new e("{{fieldkey:fields}} - {{value}}"),tooltipValueLookups:{fields:{r:"Range",p:"Performance",t:"Target"}}},pie:{offset:0,sliceColors:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],borderWidth:0,borderColor:"#000",tooltipFormat:new e('● {{value}} ({{percent.1}}%)')},box:{raw:!1,boxLineColor:"#000",boxFillColor:"#cdf",whiskerColor:"#000",outlierLineColor:"#333",outlierFillColor:"#fff",medianColor:"#f00",showOutliers:!0,outlierIQR:1.5,spotRadius:1.5,target:undefined,targetColor:"#4a2",chartRangeMax:undefined,chartRangeMin:undefined,tooltipFormat:new e("{{field:fields}}: {{value}}"),tooltipFormatFieldlistKey:"field",tooltipValueLookups:{fields:{lq:"Lower Quartile",med:"Median",uq:"Upper Quartile",lo:"Left Outlier",ro:"Right Outlier",lw:"Left Whisker",rw:"Right Whisker"}}}}},B='.jqstooltip { position: absolute;left: 0px;top: 0px;visibility: hidden;background: rgb(0, 0, 0) transparent;background-color: rgba(0,0,0,0.6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";color: white;font: 10px arial, san serif;text-align: left;white-space: nowrap;padding: 5px;border: 1px solid white;z-index: 10000;}.jqsfield { color: white;font: 10px arial, san serif;text-align: left;}',d=function(){var b,c;return b=function(){this.init.apply(this,arguments)},arguments.length>1?(arguments[0]?(b.prototype=a.extend(new arguments[0],arguments[arguments.length-1]),b._super=arguments[0].prototype):b.prototype=arguments[arguments.length-1],arguments.length>2&&(c=Array.prototype.slice.call(arguments,1,-1),c.unshift(b.prototype),a.extend.apply(a,c))):b.prototype=arguments[0],b.prototype.cls=b,b},a.SPFormatClass=e=d({fre:/\{\{([\w.]+?)(:(.+?))?\}\}/g,precre:/(\w+)\.(\d+)/,init:function(a,b){this.format=a,this.fclass=b},render:function(a,b,c){var d=this,e=a,f,g,h,i,j;return this.format.replace(this.fre,function(){var a;return g=arguments[1],h=arguments[3],f=d.precre.exec(g),f?(j=f[2],g=f[1]):j=!1,i=e[g],i===undefined?"":h&&b&&b[h]?(a=b[h],a.get?b[h].get(i)||i:b[h][i]||i):(k(i)&&(c.get("numberFormatter")?i=c.get("numberFormatter")(i):i=p(i,j,c.get("numberDigitGroupCount"),c.get("numberDigitGroupSep"),c.get("numberDecimalMark"))),i)})}}),a.spformat=function(a,b){return new e(a,b)},f=function(a,b,c){return ac?c:a},g=function(a,b){var c;return b===2?(c=Math.floor(a.length/2),a.length%2?a[c]:(a[c-1]+a[c])/2):a.length%2?(c=(a.length*b+b)/4,c%1?(a[Math.floor(c)]+a[Math.floor(c)-1])/2:a[c-1]):(c=(a.length*b+2)/4,c%1?(a[Math.floor(c)]+a[Math.floor(c)-1])/2:a[c-1])},h=function(a){var b;switch(a){case"undefined":a=undefined;break;case"null":a=null;break;case"true":a=!0;break;case"false":a=!1;break;default:b=parseFloat(a),a==b&&(a=b)}return a},i=function(a){var b,c=[];for(b=a.length;b--;)c[b]=h(a[b]);return c},j=function(a,b){var c,d,e=[];for(c=0,d=a.length;c0;h-=d)b.splice(h,0,e);return b.join("")},l=function(a,b,c){var d;for(d=b.length;d--;){if(c&&b[d]===null)continue;if(b[d]!==a)return!1}return!0},m=function(a){var b=0,c;for(c=a.length;c--;)b+=typeof a[c]=="number"?a[c]:0;return b},o=function(b){return a.isArray(b)?b:[b]},n=function(a){var b;document.createStyleSheet?document.createStyleSheet().cssText=a:(b=document.createElement("style"),b.type="text/css",document.getElementsByTagName("head")[0].appendChild(b),b[typeof document.body.style.WebkitAppearance=="string"?"innerText":"innerHTML"]=a)},a.fn.simpledraw=function(b,c,d,e){var f,g;if(d&&(f=this.data("_jqs_vcanvas")))return f;b===undefined&&(b=a(this).innerWidth()),c===undefined&&(c=a(this).innerHeight());if(a.fn.sparkline.hasCanvas)f=new F(b,c,this,e);else{if(!a.fn.sparkline.hasVML)return!1;f=new G(b,c,this)}return g=a(this).data("_jqs_mhandler"),g&&g.registerCanvas(f),f},a.fn.cleardraw=function(){var a=this.data("_jqs_vcanvas");a&&a.reset()},a.RangeMapClass=q=d({init:function(a){var b,c,d=[];for(b in a)a.hasOwnProperty(b)&&typeof b=="string"&&b.indexOf(":")>-1&&(c=b.split(":"),c[0]=c[0].length===0?-Infinity:parseFloat(c[0]),c[1]=c[1].length===0?Infinity:parseFloat(c[1]),c[2]=a[b],d.push(c));this.map=a,this.rangelist=d||!1},get:function(a){var b=this.rangelist,c,d,e;if((e=this.map[a])!==undefined)return e;if(b)for(c=b.length;c--;){d=b[c];if(d[0]<=a&&d[1]>=a)return d[2]}return undefined}}),a.range_map=function(a){return new q(a)},r=d({init:function(b,c){var d=a(b);this.$el=d,this.options=c,this.currentPageX=0,this.currentPageY=0,this.el=b,this.splist=[],this.tooltip=null,this.over=!1,this.displayTooltips=!c.get("disableTooltips"),this.highlightEnabled=!c.get("disableHighlight")},registerSparkline:function(a){this.splist.push(a),this.over&&this.updateDisplay()},registerCanvas:function(b){var c=a(b.canvas);this.canvas=b,this.$canvas=c,c.mouseenter(a.proxy(this.mouseenter,this)),c.mouseleave(a.proxy(this.mouseleave,this)),c.click(a.proxy(this.mouseclick,this))},reset:function(a){this.splist=[],this.tooltip&&a&&(this.tooltip.remove(),this.tooltip=undefined)},mouseclick:function(b){var c=a.Event("sparklineClick");c.originalEvent=b,c.sparklines=this.splist,this.$el.trigger(c)},mouseenter:function(b){a(document.body).unbind("mousemove.jqs"),a(document.body).bind("mousemove.jqs",a.proxy(this.mousemove,this)),this.over=!0,this.currentPageX=b.pageX,this.currentPageY=b.pageY,this.currentEl=b.target,!this.tooltip&&this.displayTooltips&&(this.tooltip=new s(this.options),this.tooltip.updatePosition(b.pageX,b.pageY)),this.updateDisplay()},mouseleave:function(){a(document.body).unbind("mousemove.jqs");var b=this.splist,c=b.length,d=!1,e,f;this.over=!1,this.currentEl=null,this.tooltip&&(this.tooltip.remove(),this.tooltip=null);for(f=0;f",{id:"jqssizetip",style:d,"class":c}),this.tooltip=a("
",{id:"jqstooltip","class":c}).appendTo(this.container),e=this.tooltip.offset(),this.offsetLeft=e.left,this.offsetTop=e.top,this.hidden=!0,a(window).unbind("resize.jqs scroll.jqs"),a(window).bind("resize.jqs scroll.jqs",a.proxy(this.updateWindowDims,this)),this.updateWindowDims()},updateWindowDims:function(){this.scrollTop=a(window).scrollTop(),this.scrollLeft=a(window).scrollLeft(),this.scrollRight=this.scrollLeft+a(window).width(),this.updatePosition()},getSize:function(a){this.sizetip.html(a).appendTo(this.container),this.width=this.sizetip.width()+1,this.height=this.sizetip.height(),this.sizetip.remove()},setContent:function(a){if(!a){this.tooltip.css("visibility","hidden"),this.hidden=!0;return}this.getSize(a),this.tooltip.html(a).css({width:this.width,height:this.height,visibility:"visible"}),this.hidden&&(this.hidden=!1,this.updatePosition())},updatePosition:function(a,b){if(a===undefined){if(this.mousex===undefined)return;a=this.mousex-this.offsetLeft,b=this.mousey-this.offsetTop}else this.mousex=a-=this.offsetLeft,this.mousey=b-=this.offsetTop;if(!this.height||!this.width||this.hidden)return;b-=this.height+this.tooltipOffsetY,a+=this.tooltipOffsetX,bthis.scrollRight&&(a=this.scrollRight-this.width),this.tooltip.css({left:a,top:b})},remove:function(){this.tooltip.remove(),this.sizetip.remove(),this.sizetip=this.tooltip=undefined,a(window).unbind("resize.jqs scroll.jqs")}}),C=function(){n(B)},a(C),H=[],a.fn.sparkline=function(b,c){return this.each(function(){var d=new a.fn.sparkline.options(this,c),e=a(this),f,g;f=function(){var c,f,g,h,i,j,k;if(b==="html"||b===undefined){k=this.getAttribute(d.get("tagValuesAttribute"));if(k===undefined||k===null)k=e.html();c=k.replace(/(^\s*\s*$)|\s+/g,"").split(",")}else c=b;f=d.get("width")==="auto"?c.length*d.get("defaultPixelsPerValue"):d.get("width");if(d.get("height")==="auto"){if(!d.get("composite")||!a.data(this,"_jqs_vcanvas"))h=document.createElement("span"),h.innerHTML="a",e.html(h),g=a(h).innerHeight()||a(h).height(),a(h).remove(),h=null}else g=d.get("height");d.get("disableInteraction")?i=!1:(i=a.data(this,"_jqs_mhandler"),i?d.get("composite")||i.reset():(i=new r(this,d),a.data(this,"_jqs_mhandler",i)));if(d.get("composite")&&!a.data(this,"_jqs_vcanvas")){a.data(this,"_jqs_errnotify")||(alert("Attempted to attach a composite sparkline to an element with no existing sparkline"),a.data(this,"_jqs_errnotify",!0));return}j=new(a.fn.sparkline[d.get("type")])(this,c,d,f,g),j.render(),i&&i.registerSparkline(j)};if(a(this).html()&&!d.get("disableHiddenCheck")&&a(this).is(":hidden")||a.fn.jquery<"1.3.0"&&a(this).parents().is(":hidden")||!a(this).parents("body").length){if(!d.get("composite")&&a.data(this,"_jqs_pending"))for(g=H.length;g;g--)H[g-1][0]==this&&H.splice(g-1,1);H.push([this,f]),a.data(this,"_jqs_pending",!0)}else f.call(this)})},a.fn.sparkline.defaults=c(),a.sparkline_display_visible=function(){var b,c,d,e=[];for(c=0,d=H.length;cthis.canvasWidth||c>this.canvasHeight||b<0||c<0?null:(f=this.getRegion(a,b,c),d!==f?(d!==undefined&&e&&this.removeHighlight(),this.currentRegion=f,f!==undefined&&e&&this.renderHighlight(),!0):!1)},clearRegionHighlight:function(){return this.currentRegion!==undefined?(this.removeHighlight(),this.currentRegion=undefined,!0):!1},renderHighlight:function(){this.changeHighlight(!0)},removeHighlight:function(){this.changeHighlight(!1)},changeHighlight:function(a){},getCurrentRegionTooltip:function(){var b=this.options,c="",d=[],f,g,h,i,j,k,l,m,n,o,p,q,r,s;if(this.currentRegion===undefined)return"";f=this.getCurrentRegionFields(),p=b.get("tooltipFormatter");if(p)return p(this,b,f);b.get("tooltipChartTitle")&&(c+=''+b.get("tooltipChartTitle")+"
\n"),g=this.options.get("tooltipFormat");if(!g)return"";a.isArray(g)||(g=[g]),a.isArray(f)||(f=[f]),l=this.options.get("tooltipFormatFieldlist"),m=this.options.get("tooltipFormatFieldlistKey");if(l&&m){n=[];for(k=f.length;k--;)o=f[k][m],(s=a.inArray(o,l))!=-1&&(n[s]=f[k]);f=n}h=g.length,r=f.length;for(k=0;k'+j+" ")}return d.length?c+d.join("\n"):""},getCurrentRegionFields:function(){},calcHighlightColor:function(a,b){var c=b.get("highlightColor"),d=b.get("highlightLighten"),e,g,h,i;if(c)return c;if(d){e=/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(a)||/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(a);if(e){h=[],g=a.length===4?16:1;for(i=0;i<3;i++)h[i]=f(Math.round(parseInt(e[i+1],16)*g*d),0,255);return"rgb("+h.join(",")+")"}}return a}}),t={changeHighlight:function(b){var c=this.currentRegion,d=this.target,e=this.regionShapes[c],f;e&&(f=this.renderRegion(c,b),a.isArray(f)||a.isArray(e)?(d.replaceWithShapes(e,f),this.regionShapes[c]=a.map(f,function(a){return a.id})):(d.replaceWithShape(e,f),this.regionShapes[c]=f.id))},render:function(){var b=this.values,c=this.target,d=this.regionShapes,e,f,g,h;if(!this.cls._super.render.call(this))return;for(g=b.length;g--;){e=this.renderRegion(g);if(e)if(a.isArray(e)){f=[];for(h=e.length;h--;)e[h].append(),f.push(e[h].id);d[g]=f}else e.append(),d[g]=e.id;else d[g]=null}c.render()}},a.fn.sparkline.line=u=d(a.fn.sparkline._base,{type:"line",init:function(a,b,c,d,e){u._super.init.call(this,a,b,c,d,e),this.vertices=[],this.regionMap=[],this.xvalues=[],this.yvalues=[],this.yminmax=[],this.hightlightSpotId=null,this.lastShapeId=null,this.initTarget()},getRegion:function(a,b,c){var d,e=this.regionMap;for(d=e.length;d--;)if(e[d]!==null&&b>=e[d][0]&&b<=e[d][1])return e[d][2];return undefined},getCurrentRegionFields:function(){var a=this.currentRegion;return{isNull:this.yvalues[a]===null,x:this.xvalues[a],y:this.yvalues[a],color:this.options.get("lineColor"),fillColor:this.options.get("fillColor"),offset:a}},renderHighlight:function(){var a=this.currentRegion,b=this.target,c=this.vertices[a],d=this.options,e=d.get("spotRadius"),f=d.get("highlightSpotColor"),g=d.get("highlightLineColor"),h,i;if(!c)return;e&&f&&(h=b.drawCircle(c[0],c[1],e,undefined,f),this.highlightSpotId=h.id,b.insertAfterShape(this.lastShapeId,h)),g&&(i=b.drawLine(c[0],this.canvasTop,c[0],this.canvasTop+this.canvasHeight,g),this.highlightLineId=i.id,b.insertAfterShape(this.lastShapeId,i))},removeHighlight:function(){var a=this.target;this.highlightSpotId&&(a.removeShapeId(this.highlightSpotId),this.highlightSpotId=null),this.highlightLineId&&(a.removeShapeId(this.highlightLineId),this.highlightLineId=null)},scanValues:function(){var a=this.values,b=a.length,c=this.xvalues,d=this.yvalues,e=this.yminmax,f,g,h,i,j;for(f=0;f
this.maxy&&(this.maxy=c)),a.get("chartRangeMin")!==undefined&&(a.get("chartRangeClip")||a.get("chartRangeMin")this.maxy)&&(this.maxy=a.get("chartRangeMax")),a.get("chartRangeMinX")!==undefined&&(a.get("chartRangeClipX")||a.get("chartRangeMinX")this.maxx)&&(this.maxx=a.get("chartRangeMaxX"))},drawNormalRange:function(a,b,c,d,e){var f=this.options.get("normalRangeMin"),g=this.options.get("normalRangeMax"),h=b+Math.round(c-c*((g-this.miny)/e)),i=Math.round(c*(g-f)/e);this.target.drawRect(a,h,d,i,undefined,this.options.get("normalRangeColor")).append()},render:function(){var b=this.options,c=this.target,d=this.canvasWidth,e=this.canvasHeight,f=this.vertices,g=b.get("spotRadius"),h=this.regionMap,i,j,k,l,m,n,o,p,r,s,t,v,w,x,y,z,A,B,C,D,E,F,G,H,I;if(!u._super.render.call(this))return;this.scanValues(),this.processRangeOptions(),G=this.xvalues,H=this.yvalues;if(!this.yminmax.length||this.yvalues.length<2)return;l=m=0,i=this.maxx-this.minx===0?1:this.maxx-this.minx,j=this.maxy-this.miny===0?1:this.maxy-this.miny,k=this.yvalues.length-1,g&&(dthis.maxy&&(s=this.maxy),o.length||o.push([v,l+e]),n=[v,l+Math.round(e-e*((s-this.miny)/j))],o.push(n),f.push(n));A=[],B=[],C=p.length;for(I=0;I2&&(o[0]=[o[0][0],o[1][1]]),A.push(o));C=B.length;for(I=0;I-1;if(s||a.isArray(M))H=!0,s&&(M=c[y]=i(M.split(":"))),M=j(M,null),t=Math.min.apply(Math,M),u=Math.max.apply(Math,M),tr&&(r=u)}this.stacked=H,this.regionShapes={},this.barWidth=k,this.barSpacing=l,this.totalBarWidth=k+l,this.width=e=c.length*k+(c.length-1)*l,this.initTarget(),o&&(F=m===undefined?-Infinity:m,G=n===undefined?Infinity:n),x=[],w=H?[]:x;var Q=[],R=[];for(y=0,z=c.length;y0&&(Q[y]+=M),p<0&&r>0?M<0?R[y]+=Math.abs(M):w[y]+=M:w[y]+=Math.abs(M-(M<0?r:p)),x.push(M))}else M=o?f(c[y],F,G):c[y],M=c[y]=h(M),M!==null&&x.push(M);this.max=E=Math.max.apply(Math,x),this.min=D=Math.min.apply(Math,x),this.stackMax=r=H?Math.max.apply(Math,Q):E,this.stackMin=p=H?Math.min.apply(Math,x):D,d.get("chartRangeMin")!==undefined&&(d.get("chartRangeClip")||d.get("chartRangeMin")E)&&(E=d.get("chartRangeMax")),this.zeroAxis=B=d.get("zeroAxis",!0),D<=0&&E>=0&&B?C=0:B==0?C=D:D>0?C=D:C=E,this.xaxisOffset=C,A=H?Math.max.apply(Math,w)+Math.max.apply(Math,R):E-D,this.canvasHeightEf=B&&D<0?this.canvasHeight-2:this.canvasHeight-1,D=0?r:E,N=(O-C)/A*this.canvasHeight,N!==Math.ceil(N)&&(this.canvasHeightEf-=2,N=Math.ceil(N))):N=this.canvasHeight,this.yoffset=N,a.isArray(d.get("colorMap"))?(this.colorMapByIndex=d.get("colorMap"),this.colorMapByValue=null):(this.colorMapByIndex=null,this.colorMapByValue=d.get("colorMap"),this.colorMapByValue&&this.colorMapByValue.get===undefined&&(this.colorMapByValue=new q(this.colorMapByValue))),this.range=A},getRegion:function(a,b,c){var d=Math.floor(b/this.totalBarWidth);return d<0||d>=this.values.length?undefined:d},getCurrentRegionFields:function(){var a=this.currentRegion,b=o(this.values[a]),c=[],d,e;for(e=b.length;e--;)d=b[e],c.push({isNull:d===null,value:d,color:this.calcColor(e,d,a),offset:a});return c},calcColor:function(b,c,d){var e=this.colorMapByIndex,f=this.colorMapByValue,g=this.options,h,i;return this.stacked?h=g.get("stackedBarColor"):h=c<0?g.get("negBarColor"):g.get("barColor"),c===0&&g.get("zeroColor")!==undefined&&(h=g.get("zeroColor")),f&&(i=f.get(c))?h=i:e&&e.length>d&&(h=e[d]),a.isArray(h)?h[b%h.length]:h},renderRegion:function(b,c){var d=this.values[b],e=this.options,f=this.xaxisOffset,g=[],h=this.range,i=this.stacked,j=this.target,k=b*this.totalBarWidth,m=this.canvasHeightEf,n=this.yoffset,o,p,q,r,s,t,u,v,w,x;d=a.isArray(d)?d:[d],u=d.length,v=d[0],r=l(null,d),x=l(f,d,!0);if(r)return e.get("nullColor")?(q=c?e.get("nullColor"):this.calcHighlightColor(e.get("nullColor"),e),o=n>0?n-1:n,j.drawRect(k,o,this.barWidth-1,0,q,q)):undefined;s=n;for(t=0;t0?p=Math.floor(m*(Math.abs(v-f)/h))+1:p=1,vb?g=e[b]:c[b]<0?g=d.get("negBarColor"):c[b]>0?g=d.get("posBarColor"):g=d.get("zeroBarColor"),g},renderRegion:function(a,b){var c=this.values,d=this.options,e=this.target,f,g,h,i,j,k;f=e.pixelHeight,h=Math.round(f/2),i=a*this.totalBarWidth,c[a]<0?(j=h,g=h-1):c[a]>0?(j=0,g=h-1):(j=h-1,g=2),k=this.calcColor(c[a],a);if(k===null)return;return b&&(k=this.calcHighlightColor(k,d)),e.drawRect(i,j,this.barWidth-1,g-1,k,k)}}),a.fn.sparkline.discrete=x=d(a.fn.sparkline._base,t,{type:"discrete",init:function(b,c,d,e,f){x._super.init.call(this,b,c,d,e,f),this.regionShapes={},this.values=c=a.map(c,Number),this.min=Math.min.apply(Math,c),this.max=Math.max.apply(Math,c),this.range=this.max-this.min,this.width=e=d.get("width")==="auto"?c.length*2:this.width,this.interval=Math.floor(e/c.length),this.itemWidth=e/c.length,d.get("chartRangeMin")!==undefined&&(d.get("chartRangeClip")||d.get("chartRangeMin")this.max)&&(this.max=d.get("chartRangeMax")),this.initTarget(),this.target&&(this.lineHeight=d.get("lineHeight")==="auto"?Math.round(this.canvasHeight*.3):d.get("lineHeight"))},getRegion:function(a,b,c){return Math.floor(b/this.itemWidth)},getCurrentRegionFields:function(){var a=this.currentRegion;return{isNull:this.values[a]===undefined,value:this.values[a],offset:a}},renderRegion:function(a,b){var c=this.values,d=this.options,e=this.min,g=this.max,h=this.range,i=this.interval,j=this.target,k=this.canvasHeight,l=this.lineHeight,m=k-l,n,o,p,q;return o=f(c[a],e,g),q=a*i,n=Math.round(m-m*((o-e)/h)),p=d.get("thresholdColor")&&o0)for(h=c.length;h--;)g+=c[h];this.total=g,this.initTarget(),this.radius=Math.floor(Math.min(this.canvasWidth,this.canvasHeight)/2)},getRegion:function(a,b,c){var d=this.target.getShapeAt(a,b,c);return d!==undefined&&this.shapes[d]!==undefined?this.shapes[d]:undefined},getCurrentRegionFields:function(){var a=this.currentRegion;return{isNull:this.values[a]===undefined,value:this.values[a],percent:this.values[a]/this.total*100,color:this.options.get("sliceColors")[a%this.options.get("sliceColors").length],offset:a}},changeHighlight:function(a){var b=this.currentRegion,c=this.renderSlice(b,a),d=this.valueShapes[b];delete this.shapes[d],this.target.replaceWithShape(d,c),this.valueShapes[b]=c.id,this.shapes[c.id]=b},renderSlice:function(a,b){var c=this.target,d=this.options,e=this.radius,f=d.get("borderWidth"),g=d.get("offset"),h=2*Math.PI,i=this.values,j=this.total,k=g?2*Math.PI*(g/360):0,l,m,n,o,p;o=i.length;for(n=0;n0&&(m=k+h*(i[n]/j));if(a===n)return p=d.get("sliceColors")[n%d.get("sliceColors").length],b&&(p=this.calcHighlightColor(p,d)),c.drawPieSlice(e,e,e-f,l,m,undefined,p);k=m}},render:function(){var a=this.target,b=this.values,c=this.options,d=this.radius,e=c.get("borderWidth"),f,g;if(!z._super.
-render.call(this))return;e&&a.drawCircle(d,d,Math.floor(d-e/2),c.get("borderColor"),undefined,e).append();for(g=b.length;g--;)b[g]&&(f=this.renderSlice(g).append(),this.valueShapes[g]=f.id,this.shapes[f.id]=g);a.render()}}),a.fn.sparkline.box=A=d(a.fn.sparkline._base,{type:"box",init:function(b,c,d,e,f){A._super.init.call(this,b,c,d,e,f),this.values=a.map(c,Number),this.width=d.get("width")==="auto"?"4.0em":e,this.initTarget(),this.values.length||(this.disabled=1)},getRegion:function(){return 1},getCurrentRegionFields:function(){var a=[{field:"lq",value:this.quartiles[0]},{field:"med",value:this.quartiles[1]},{field:"uq",value:this.quartiles[2]}];return this.loutlier!==undefined&&a.push({field:"lo",value:this.loutlier}),this.routlier!==undefined&&a.push({field:"ro",value:this.routlier}),this.lwhisker!==undefined&&a.push({field:"lw",value:this.lwhisker}),this.rwhisker!==undefined&&a.push({field:"rw",value:this.rwhisker}),a},render:function(){var a=this.target,b=this.values,c=b.length,d=this.options,e=this.canvasWidth,f=this.canvasHeight,h=d.get("chartRangeMin")===undefined?Math.min.apply(Math,b):d.get("chartRangeMin"),i=d.get("chartRangeMax")===undefined?Math.max.apply(Math,b):d.get("chartRangeMax"),j=0,k,l,m,n,o,p,q,r,s,t,u;if(!A._super.render.call(this))return;if(d.get("raw"))d.get("showOutliers")&&b.length>5?(l=b[0],k=b[1],n=b[2],o=b[3],p=b[4],q=b[5],r=b[6]):(k=b[0],n=b[1],o=b[2],p=b[3],q=b[4]);else{b.sort(function(a,b){return a-b}),n=g(b,1),o=g(b,2),p=g(b,3),m=p-n;if(d.get("showOutliers")){k=q=undefined;for(s=0;sn-m*d.get("outlierIQR")&&(k=b[s]),b[s]q&&a.drawCircle((r-h)*u+j,f/2,d.get("spotRadius"),d.get("outlierLineColor"),d.get("outlierFillColor")).append()),a.drawRect(Math.round((n-h)*u+j),Math.round(f*.1),Math.round((p-n)*u),Math.round(f*.8),d.get("boxLineColor"),d.get("boxFillColor")).append(),a.drawLine(Math.round((k-h)*u+j),Math.round(f/2),Math.round((n-h)*u+j),Math.round(f/2),d.get("lineColor")).append(),a.drawLine(Math.round((k-h)*u+j),Math.round(f/4),Math.round((k-h)*u+j),Math.round(f-f/4),d.get("whiskerColor")).append(),a.drawLine(Math.round((q-h)*u+j),Math.round(f/2),Math.round((p-h)*u+j),Math.round(f/2),d.get("lineColor")).append(),a.drawLine(Math.round((q-h)*u+j),Math.round(f/4),Math.round((q-h)*u+j),Math.round(f-f/4),d.get("whiskerColor")).append(),a.drawLine(Math.round((o-h)*u+j),Math.round(f*.1),Math.round((o-h)*u+j),Math.round(f*.9),d.get("medianColor")).append(),d.get("target")&&(t=Math.ceil(d.get("spotRadius")),a.drawLine(Math.round((d.get("target")-h)*u+j),Math.round(f/2-t),Math.round((d.get("target")-h)*u+j),Math.round(f/2+t),d.get("targetColor")).append(),a.drawLine(Math.round((d.get("target")-h)*u+j-t),Math.round(f/2),Math.round((d.get("target")-h)*u+j+t),Math.round(f/2),d.get("targetColor")).append()),a.render()}}),function(){document.namespaces&&!document.namespaces.v?(a.fn.sparkline.hasVML=!0,document.namespaces.add("v","urn:schemas-microsoft-com:vml","#default#VML")):a.fn.sparkline.hasVML=!1;var b=document.createElement("canvas");a.fn.sparkline.hasCanvas=!!b.getContext&&!!b.getContext("2d")}(),D=d({init:function(a,b,c,d){this.target=a,this.id=b,this.type=c,this.args=d},append:function(){return this.target.appendShape(this),this}}),E=d({_pxregex:/(\d+)(px)?\s*$/i,init:function(b,c,d){if(!b)return;this.width=b,this.height=c,this.target=d,this.lastShapeId=null,d[0]&&(d=d[0]),a.data(d,"_jqs_vcanvas",this)},drawLine:function(a,b,c,d,e,f){return this.drawShape([[a,b],[c,d]],e,f)},drawShape:function(a,b,c,d){return this._genShape("Shape",[a,b,c,d])},drawCircle:function(a,b,c,d,e,f){return this._genShape("Circle",[a,b,c,d,e,f])},drawPieSlice:function(a,b,c,d,e,f,g){return this._genShape("PieSlice",[a,b,c,d,e,f,g])},drawRect:function(a,b,c,d,e,f){return this._genShape("Rect",[a,b,c,d,e,f])},getElement:function(){return this.canvas},getLastShapeId:function(){return this.lastShapeId},reset:function(){alert("reset not implemented")},_insert:function(b,c){a(c).html(b)},_calculatePixelDims:function(b,c,d){var e;e=this._pxregex.exec(c),e?this.pixelHeight=e[1]:this.pixelHeight=a(d).height(),e=this._pxregex.exec(b),e?this.pixelWidth=e[1]:this.pixelWidth=a(d).width()},_genShape:function(a,b){var c=I++;return b.unshift(c),new D(this,c,a,b)},appendShape:function(a){alert("appendShape not implemented")},replaceWithShape:function(a,b){alert("replaceWithShape not implemented")},insertAfterShape:function(a,b){alert("insertAfterShape not implemented")},removeShapeId:function(a){alert("removeShapeId not implemented")},getShapeAt:function(a,b,c){alert("getShapeAt not implemented")},render:function(){alert("render not implemented")}}),F=d(E,{init:function(b,c,d,e){F._super.init.call(this,b,c,d),this.canvas=document.createElement("canvas"),d[0]&&(d=d[0]),a.data(d,"_jqs_vcanvas",this),a(this.canvas).css({display:"inline-block",width:b,height:c,verticalAlign:"top"}),this._insert(this.canvas,d),this._calculatePixelDims(b,c,this.canvas),this.canvas.width=this.pixelWidth,this.canvas.height=this.pixelHeight,this.interact=e,this.shapes={},this.shapeseq=[],this.currentTargetShapeId=undefined,a(this.canvas).css({width:this.pixelWidth,height:this.pixelHeight})},_getContext:function(a,b,c){var d=this.canvas.getContext("2d");return a!==undefined&&(d.strokeStyle=a),d.lineWidth=c===undefined?1:c,b!==undefined&&(d.fillStyle=b),d},reset:function(){var a=this._getContext();a.clearRect(0,0,this.pixelWidth,this.pixelHeight),this.shapes={},this.shapeseq=[],this.currentTargetShapeId=undefined},_drawShape:function(a,b,c,d,e){var f=this._getContext(c,d,e),g,h;f.beginPath(),f.moveTo(b[0][0]+.5,b[0][1]+.5);for(g=1,h=b.length;g',this.canvas.insertAdjacentHTML("beforeEnd",e),this.group=a(this.canvas).children()[0],this.rendered=!1,this.prerender=""},_drawShape:function(a,b,c,d,e){var f=[],g,h,i,j,k,l,m;for(m=0,l=b.length;m'+" ",k},_drawCircle:function(a,b,c,d,e,f,g){var h,i,j;return b-=d,c-=d,h=e===undefined?' stroked="false" ':' strokeWeight="'+g+'px" strokeColor="'+e+'" ',i=f===undefined?' filled="false"':' fillColor="'+f+'" filled="true" ',j=' ',j},_drawPieSlice:function(a,b,c,d,e,f,g,h){var i,j,k,l,m,n,o,p;if(e===f)return"";f-e===2*Math.PI&&(e=0,f=2*Math.PI),j=b+Math.round(Math.cos(e)*d),k=c+Math.round(Math.sin(e)*d),l=b+Math.round(Math.cos(f)*d),m=c+Math.round(Math.sin(f)*d);if(j===l&&k===m){if(f-e'+" ",p)},_drawRect:function(a,b,c,d,e,f,g){return this._drawShape(a,[[b,c],[b,c+e],[b+d,c+e],[b+d,c],[b,c]],f,g)},reset:function(){this.group.innerHTML=""},appendShape:function(a){var b=this["_draw"+a.type].apply(this,a.args);return this.rendered?this.group.insertAdjacentHTML("beforeEnd",b):this.prerender+=b,this.lastShapeId=a.id,a.id},replaceWithShape:function(b,c){var d=a("#jqsshape"+b),e=this["_draw"+c.type].apply(this,c.args);d[0].outerHTML=e},replaceWithShapes:function(b,c){var d=a("#jqsshape"+b[0]),e="",f=c.length,g;for(g=0;g
Date: Tue, 4 Jun 2013 13:35:16 +0200
Subject: [PATCH 69/72] [FIX] crm: order of files in tertimonial
bzr revid: tde@openerp.com-20130604113516-v79kumeixy26538b
---
addons/crm/__openerp__.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py
index 3a37f33e090..e7ab7df27ea 100644
--- a/addons/crm/__openerp__.py
+++ b/addons/crm/__openerp__.py
@@ -81,8 +81,6 @@ Dashboard for CRM will include:
'crm_lead_view.xml',
'crm_lead_menu.xml',
- 'crm_case_section_view.xml',
-
'crm_meeting_menu.xml',
'crm_phonecall_view.xml',
@@ -98,6 +96,8 @@ Dashboard for CRM will include:
'res_config_view.xml',
'base_partner_merge_view.xml',
+
+ 'crm_case_section_view.xml',
],
'demo': [
'crm_demo.xml',
From 6dd31d5841be262f4073d26e58a886935570afae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?=
Date: Tue, 4 Jun 2013 13:51:59 +0200
Subject: [PATCH 70/72] [IMP] crm: added code on sales team search view
bzr revid: tde@openerp.com-20130604115159-56jsm8dr23gxl30c
---
addons/crm/crm_case_section_view.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml
index c55aec749d6..be5611c03c4 100644
--- a/addons/crm/crm_case_section_view.xml
+++ b/addons/crm/crm_case_section_view.xml
@@ -125,6 +125,7 @@
+
From a3c677b33710d8bb213f8bec86ba59fe2c1b3698 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?=
Date: Tue, 4 Jun 2013 14:30:26 +0200
Subject: [PATCH 71/72] [CLEAN] sale, sale_crm: reverted some changes, cleaning
before merging.
bzr revid: tde@openerp.com-20130604123026-f0xks6ky1rkw3ser
---
addons/sale/sale.py | 4 +++-
addons/sale_crm/__openerp__.py | 2 +-
addons/sale_crm/sale_crm.py | 5 -----
3 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/addons/sale/sale.py b/addons/sale/sale.py
index 577199ddaee..210e4dd3be6 100644
--- a/addons/sale/sale.py
+++ b/addons/sale/sale.py
@@ -400,6 +400,8 @@ class sale_order(osv.osv):
def _make_invoice(self, cr, uid, order, lines, context=None):
inv_obj = self.pool.get('account.invoice')
obj_invoice_line = self.pool.get('account.invoice.line')
+ if context is None:
+ context = {}
invoiced_sale_line_ids = self.pool.get('sale.order.line').search(cr, uid, [('order_id', '=', order.id), ('invoiced', '=', True)], context=context)
from_line_invoice_ids = []
for invoiced_sale_line_id in self.pool.get('sale.order.line').browse(cr, uid, invoiced_sale_line_ids, context=context):
@@ -437,7 +439,7 @@ class sale_order(osv.osv):
view of one of the newly created invoices
"""
mod_obj = self.pool.get('ir.model.data')
-
+
# create invoices through the sales orders' workflow
inv_ids0 = set(inv.id for sale in self.browse(cr, uid, ids, context) for inv in sale.invoice_ids)
self.signal_manual_invoice(cr, uid, ids)
diff --git a/addons/sale_crm/__openerp__.py b/addons/sale_crm/__openerp__.py
index 8cadff5b5ad..7db1aa1eff0 100644
--- a/addons/sale_crm/__openerp__.py
+++ b/addons/sale_crm/__openerp__.py
@@ -48,8 +48,8 @@ modules.
'report/sale_crm_account_invoice_report_view.xml',
],
'js': [
- 'static/src/js/sale_crm.js',
'static/lib/justgage.js',
+ 'static/src/js/sale_crm.js',
],
'demo': ['sale_crm_demo.xml'],
'test': ['test/sale_crm.yml'],
diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py
index 6a3e13f7016..97c5f4579e0 100644
--- a/addons/sale_crm/sale_crm.py
+++ b/addons/sale_crm/sale_crm.py
@@ -34,11 +34,6 @@ class sale_order(osv.osv):
domain="['|',('section_id','=',section_id),('section_id','=',False), ('object_id.model', '=', 'crm.lead')]")
}
- def _make_invoice(self, cr, uid, order, lines, context=None):
- if order.section_id:
- context = dict(context or {}, default_section_id= order.section_id.id)
- return super(sale_order, self)._make_invoice(cr, uid, order, lines, context=context)
-
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:
From 048b365aff23066006c63007adc5eba53053c925 Mon Sep 17 00:00:00 2001
From: Vo Minh Thu
Date: Tue, 4 Jun 2013 14:40:03 +0200
Subject: [PATCH 72/72] [FIX] renamed Partner Catgories to Partner Tags.
bzr revid: vmt@openerp.com-20130604124003-bpga8k6lq4s7qlvp
---
openerp/addons/base/res/res_partner.py | 2 +-
openerp/addons/base/res/res_partner_view.xml | 16 ++++++++--------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/openerp/addons/base/res/res_partner.py b/openerp/addons/base/res/res_partner.py
index 0e6350f00ba..01108939195 100644
--- a/openerp/addons/base/res/res_partner.py
+++ b/openerp/addons/base/res/res_partner.py
@@ -120,7 +120,7 @@ class res_partner_category(osv.osv):
res = self.name_get(cr, uid, ids, context=context)
return dict(res)
- _description = 'Partner Categories'
+ _description = 'Partner Tags'
_name = 'res.partner.category'
_columns = {
'name': fields.char('Category Name', required=True, size=64, translate=True),
diff --git a/openerp/addons/base/res/res_partner_view.xml b/openerp/addons/base/res/res_partner_view.xml
index 32cb4e83e25..b85f80e0e57 100644
--- a/openerp/addons/base/res/res_partner_view.xml
+++ b/openerp/addons/base/res/res_partner_view.xml
@@ -511,10 +511,10 @@
- Partner Categories
+ Partner Tags
res.partner.category
-
- Partner Categories
+ Partner Tags
res.partner.category
-
+
@@ -538,7 +538,7 @@
res.partner.category
child_ids
-
+
@@ -557,7 +557,7 @@
-->
- Partner Categories
+ Partner Tags
res.partner
form
tree,form
@@ -574,11 +574,11 @@
- Partner Categories
+ Partner Tags
ir.actions.act_window
res.partner.category
form
- Manage the partner categories in order to better classify them for tracking and analysis purposes. A partner may belong to several categories and categories have a hierarchy structure: a partner belonging to a category also belong to his parent category.
+ Manage the partner tags to better classify them for tracking and analysis purposes. A partner may have several tags and tags have a hierarchical structure: a partner with a tag has also the parent tags.