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 @@ -
+
í
    @@ -93,9 +93,9 @@
-

- 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 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
@@ -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 @@ -
+
Forecast
+
+
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 +
Opportunities @@ -161,9 +161,9 @@
-
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 @@