From edff1d957585dc36ec8a67d95df389509fc6511f Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Fri, 26 Jul 2013 12:48:34 +0530 Subject: [PATCH 01/68] [IMP] Fixed issue of traceback when we enter non integer number,improved css and help. bzr revid: tpa@tinyerp.com-20130726071834-hd9qyyotf41ja57w --- addons/crm/static/src/css/crm.css | 1 + addons/sale/report/sale_report_view.xml | 4 +-- addons/sale_crm/sale_crm_view.xml | 37 ++++++++++++++++++++--- addons/sale_crm/static/src/js/sale_crm.js | 21 ++++++++----- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css index ba4473edc26..53939191eb5 100644 --- a/addons/crm/static/src/css/crm.css +++ b/addons/crm/static/src/css/crm.css @@ -29,6 +29,7 @@ margin: 0 !important; position: relative; display: inline-block; + float: left; } .openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list a:hover { text-decoration: underline !important; diff --git a/addons/sale/report/sale_report_view.xml b/addons/sale/report/sale_report_view.xml index 69e9cc5e119..4d0ac87bc13 100644 --- a/addons/sale/report/sale_report_view.xml +++ b/addons/sale/report/sale_report_view.xml @@ -47,8 +47,8 @@ - - + + diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index ae21934ce61..09b613a40ec 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -211,6 +211,33 @@ + + Sales Analysis + sale.report + form + tree,graph + {'search_default_year':1,'search_default_month':1,'search_default_User':1,'search_default_Quotations':1,'group_by_no_leaf':1,'group_by':[]} + This report performs analysis on your quotations and sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application. + + + + Sales Analysis + sale.report + form + tree,graph + {'search_default_year':1,'search_default_month':1,'search_default_User':1,'search_default_Sales':1,'group_by_no_leaf':1,'group_by':[]} + This report performs analysis on your quotations and sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application. + + + + Invoices Analysis + account.invoice.report + form + tree,graph + {'search_default_period':1, 'search_default_current':1, 'search_default_user':1, 'search_default_year': 1, 'search_default_category_product':1, 'search_default_customer':1, 'group_by':[], 'group_by_no_leaf':1,} + From this report, you can have an overview of the amount invoiced to your customer. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs. + + crm.case.section.form crm.case.section @@ -241,9 +268,9 @@ @@ -251,7 +278,7 @@ diff --git a/addons/sale_crm/static/src/js/sale_crm.js b/addons/sale_crm/static/src/js/sale_crm.js index 207ac959af3..77871bb3a50 100644 --- a/addons/sale_crm/static/src/js/sale_crm.js +++ b/addons/sale_crm/static/src/js/sale_crm.js @@ -67,13 +67,17 @@ openerp.sale_crm.GaugeWidget = openerp.web_kanban.AbstractField.extend({ $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(); + if(isNaN($input.val())){ + self.do_warn(_t("Wrong value entered!"), _t("Only Integer Value should be valid.")); + $div.remove(); + } else { + if (event.keyCode == 13 || event.keyCode == 9) { + var val = self.parse_client($input.val()); + if (val != value) { + parent.view.dataset.call(self.options.action_change, [parent.id, $input.val()]).then(function () { + parent.do_reload(); + }); + } else { $div.remove();} } } }) @@ -102,6 +106,9 @@ openerp.sale_crm.GaugeWidget = openerp.web_kanban.AbstractField.extend({ } } }, + parse_client: function(value) { + return openerp.web.parse_value(value, { type:"integer" }); + }, }); openerp.web_kanban.fields_registry.add("gage", "openerp.sale_crm.GaugeWidget"); From c588dc46990c67f66662a34e700b295851e086df Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Fri, 26 Jul 2013 18:52:42 +0530 Subject: [PATCH 02/68] [IMP] improved typo bzr revid: tpa@tinyerp.com-20130726132242-595e8x43s3jfiov1 --- addons/sale_crm/sale_crm_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 09b613a40ec..4a624db44a1 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -270,7 +270,7 @@ Sales Orders - Revenue of confirmed sales orders per month.
Click to acces the Sales Analysis + Revenue of confirmed sales orders per month.
Click to acces the Sales Analysis.
From 0049c8efe85a67f1a20b82d086a34af3f3b09e64 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Mon, 29 Jul 2013 15:41:17 +0530 Subject: [PATCH 03/68] [IMP] Improved code of sale report to inherit and add fields in sale_crm,sale_stock modules, added proper domins on the actions in sales teams kanban bzr revid: tpa@tinyerp.com-20130729101117-6sxwrsh2coz8ygkd --- addons/crm/report/crm_lead_report_view.xml | 12 ++-- addons/sale/report/sale_report.py | 38 ++++++++---- addons/sale/report/sale_report_view.xml | 2 - addons/sale_crm/__openerp__.py | 1 + addons/sale_crm/report/__init__.py | 1 + addons/sale_crm/report/sale_report.py | 37 ++++++++++++ addons/sale_crm/report/sale_report_view.xml | 27 +++++++++ addons/sale_crm/sale_crm_view.xml | 17 +++--- addons/sale_stock/report/sale_report.py | 59 ++----------------- addons/sale_stock/report/sale_report_view.xml | 4 ++ 10 files changed, 117 insertions(+), 81 deletions(-) create mode 100644 addons/sale_crm/report/sale_report.py create mode 100644 addons/sale_crm/report/sale_report_view.xml diff --git a/addons/crm/report/crm_lead_report_view.xml b/addons/crm/report/crm_lead_report_view.xml index 09853c6379e..23594a7f9e1 100644 --- a/addons/crm/report/crm_lead_report_view.xml +++ b/addons/crm/report/crm_lead_report_view.xml @@ -66,9 +66,6 @@ crm.lead.report - - - @@ -113,7 +110,7 @@ - @@ -165,9 +162,9 @@ Leads Analysis crm.lead.report form - {'search_default_year': 1,'search_default_lead': 1, "search_default_user":1, "search_default_this_month":1, 'group_by_no_leaf':1, 'group_by':[]} + {'search_default_year': 1, "search_default_user":1, "search_default_month":1, 'group_by_no_leaf':1, 'group_by':[]} tree,graph - [] + [('type','=', 'lead')] Leads Analysis allows you to check different CRM related information like the treatment delays or number of leads per state. You can sort out your leads analysis by different groups to get accurate grained analysis.
@@ -187,8 +184,9 @@ Opportunities Analysis crm.lead.report form - {"search_default_year":1,"search_default_opportunity":1, "search_default_user":1,"search_default_this_month":1,'group_by_no_leaf':1,'group_by':[]} + {"search_default_year":1, "search_default_user":1,"search_default_month":1,'group_by_no_leaf':1,'group_by':[]} tree,graph + [('type','=', 'opportunity')] Opportunities Analysis gives you an instant access to your opportunities with information such as the expected revenue, planned cost, missed deadlines or the number of interactions per opportunity. This report is mainly used by the sales manager in order to do the periodic review with the teams of the sales pipeline. diff --git a/addons/sale/report/sale_report.py b/addons/sale/report/sale_report.py index d5f07f863b9..d2f7849a958 100644 --- a/addons/sale/report/sale_report.py +++ b/addons/sale/report/sale_report.py @@ -60,12 +60,9 @@ class sale_report(osv.osv): } _order = 'date desc' - def init(self, cr): - tools.drop_view_if_exists(cr, 'sale_report') - cr.execute(""" - create or replace view sale_report as ( - select - min(l.id) as id, + def _select(self): + select_str = """ + SELECT min(l.id) as id, l.product_id as product_id, t.uom_id as product_uom, sum(l.product_uom_qty / u.factor * u2.factor) as product_uom_qty, @@ -84,15 +81,23 @@ class sale_report(osv.osv): t.categ_id as categ_id, s.pricelist_id as pricelist_id, s.project_id as analytic_account_id - from - sale_order s + """ + return select_str + + def _from(self): + from_str = """ + sale_order s join sale_order_line l on (s.id=l.order_id) left join product_product p on (l.product_id=p.id) left join product_template t on (p.product_tmpl_id=t.id) left join product_uom u on (u.id=l.product_uom) left join product_uom u2 on (u2.id=t.uom_id) - group by - l.product_id, + """ + return from_str + + def _group_by(self): + group_by_str = """ + GROUP BY l.product_id, l.product_uom_qty, l.order_id, t.uom_id, @@ -105,7 +110,16 @@ class sale_report(osv.osv): s.state, s.pricelist_id, s.project_id - ) - """) + """ + return group_by_str + + def init(self, cr): + # self._table = sale_report + tools.drop_view_if_exists(cr, self._table) + cr.execute("""CREATE or REPLACE VIEW %s as ( + %s + FROM ( %s ) + %s + )""" % (self._table, self._select(), self._from(), self._group_by())) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/report/sale_report_view.xml b/addons/sale/report/sale_report_view.xml index 4d0ac87bc13..28a6f83936a 100644 --- a/addons/sale/report/sale_report_view.xml +++ b/addons/sale/report/sale_report_view.xml @@ -50,8 +50,6 @@ - - diff --git a/addons/sale_crm/__openerp__.py b/addons/sale_crm/__openerp__.py index 7db1aa1eff0..25b1fb5f7f4 100644 --- a/addons/sale_crm/__openerp__.py +++ b/addons/sale_crm/__openerp__.py @@ -46,6 +46,7 @@ modules. 'security/sale_crm_security.xml', 'security/ir.model.access.csv', 'report/sale_crm_account_invoice_report_view.xml', + 'report/sale_report_view.xml', ], 'js': [ 'static/lib/justgage.js', diff --git a/addons/sale_crm/report/__init__.py b/addons/sale_crm/report/__init__.py index d7c6d373468..d86b3e66de9 100644 --- a/addons/sale_crm/report/__init__.py +++ b/addons/sale_crm/report/__init__.py @@ -20,6 +20,7 @@ ############################################################################## import sales_crm_account_invoice_report +import sale_report # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_crm/report/sale_report.py b/addons/sale_crm/report/sale_report.py new file mode 100644 index 00000000000..f8fae28f600 --- /dev/null +++ b/addons/sale_crm/report/sale_report.py @@ -0,0 +1,37 @@ + # -*- coding: utf-8 -*- + ############################################################################## + # + # OpenERP, Open Source Management Solution + # Copyright (C) 2004-2010 Tiny SPRL (). + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU Affero General Public License as + # published by the Free Software Foundation, either version 3 of the + # License, or (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU Affero General Public License for more details. + # + # You should have received a copy of the GNU Affero General Public License + # along with this program. If not, see . + # + ############################################################################## + +from openerp.osv import fields, osv +from openerp import tools + +class sale_report(osv.osv): + _inherit = "sale.report" + _columns = { + 'section_id': fields.many2one('crm.case.section', 'Sales Team'), + } + + def _select(self): + return super(sale_report, self)._select() + ", s.section_id as section_id" + + def _group_by(self): + return super(sale_report, self)._group_by() + ", s.section_id" + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_crm/report/sale_report_view.xml b/addons/sale_crm/report/sale_report_view.xml new file mode 100644 index 00000000000..f054634cf1c --- /dev/null +++ b/addons/sale_crm/report/sale_report_view.xml @@ -0,0 +1,27 @@ + + + + + sale.report.tree.sale.crm + sale.report + + + + + + + + + + sale.report.search.sale.crm + sale.report + + + + + + + + + + \ No newline at end of file diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 4a624db44a1..7e0d6b27a3a 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -212,12 +212,13 @@ - Sales Analysis + Quotations Analysis sale.report form tree,graph - {'search_default_year':1,'search_default_month':1,'search_default_User':1,'search_default_Quotations':1,'group_by_no_leaf':1,'group_by':[]} - This report performs analysis on your quotations and sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application. + [('state','=','draft'),('section_id', '=', active_id)] + {'search_default_order_month':1,'search_default_User':1} + This report performs analysis on your quotations. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application. @@ -225,8 +226,9 @@ sale.report form tree,graph - {'search_default_year':1,'search_default_month':1,'search_default_User':1,'search_default_Sales':1,'group_by_no_leaf':1,'group_by':[]} - This report performs analysis on your quotations and sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application. + [('state','not in',('draft','done','cancel')),('section_id', '=', active_id)] + {'search_default_order_month':1,'search_default_User':1} + This report performs analysis on your sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application. @@ -234,7 +236,8 @@ account.invoice.report form tree,graph - {'search_default_period':1, 'search_default_current':1, 'search_default_user':1, 'search_default_year': 1, 'search_default_category_product':1, 'search_default_customer':1, 'group_by':[], 'group_by_no_leaf':1,} + [('section_id', '=', active_id)] + {'search_default_month':1, 'search_default_user':1} From this report, you can have an overview of the amount invoiced to your customer. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs. @@ -288,7 +291,7 @@ Quotations - Revenue of created quotations per month.
Click to see a detailed analysis of sales. + Revenue of created quotations per month.
Click to see a detailed analysis.
diff --git a/addons/sale_stock/report/sale_report.py b/addons/sale_stock/report/sale_report.py index 0feb3ee2392..7a8234fa7fa 100644 --- a/addons/sale_stock/report/sale_report.py +++ b/addons/sale_stock/report/sale_report.py @@ -39,58 +39,11 @@ class sale_report(osv.osv): ('cancel', 'Cancelled') ], 'Order Status', readonly=True), } - - def init(self, cr): - tools.drop_view_if_exists(cr, 'sale_report') - cr.execute(""" - create or replace view sale_report as ( - select - min(l.id) as id, - l.product_id as product_id, - t.uom_id as product_uom, - sum(l.product_uom_qty / u.factor * u2.factor) as product_uom_qty, - sum(l.product_uom_qty * l.price_unit * (100.0-l.discount) / 100.0) as price_total, - 1 as nbr, - s.date_order as date, - s.date_confirm as date_confirm, - to_char(s.date_order, 'YYYY') as year, - to_char(s.date_order, 'MM') as month, - to_char(s.date_order, 'YYYY-MM-DD') as day, - s.partner_id as partner_id, - s.user_id as user_id, - s.company_id as company_id, - s.warehouse_id as warehouse_id, - extract(epoch from avg(date_trunc('day',s.date_confirm)-date_trunc('day',s.create_date)))/(24*60*60)::decimal(16,2) as delay, - s.state, - t.categ_id as categ_id, - s.shipped, - s.shipped::integer as shipped_qty_1, - s.pricelist_id as pricelist_id, - s.project_id as analytic_account_id - from - sale_order s - join sale_order_line l on (s.id=l.order_id) - left join product_product p on (l.product_id=p.id) - left join product_template t on (p.product_tmpl_id=t.id) - left join product_uom u on (u.id=l.product_uom) - left join product_uom u2 on (u2.id=t.uom_id) - group by - l.product_id, - l.product_uom_qty, - l.order_id, - t.uom_id, - t.categ_id, - s.date_order, - s.date_confirm, - s.partner_id, - s.user_id, - s.warehouse_id, - s.company_id, - s.state, - s.shipped, - s.pricelist_id, - s.project_id - ) - """) + + def _select(self): + return super(sale_report, self)._select() + ", s.warehouse_id as warehouse_id, s.shipped, s.shipped::integer as shipped_qty_1" + + def _group_by(self): + return super(sale_report, self)._group_by() + ", s.warehouse_id, s.shipped" # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_stock/report/sale_report_view.xml b/addons/sale_stock/report/sale_report_view.xml index 6642bffcede..486cf3a7b11 100644 --- a/addons/sale_stock/report/sale_report_view.xml +++ b/addons/sale_stock/report/sale_report_view.xml @@ -17,6 +17,10 @@ sale.report + + + + From eb700cf29beb3865e26fb4f8c53469f2cac40eb7 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Tue, 30 Jul 2013 16:38:22 +0530 Subject: [PATCH 04/68] [IMP] Added company_id and currency_symbole field and improved related code to show tooltip as per requrement and 5 sec late,improved css to show proper cursore, improved actions & domains for proper results. bzr revid: tpa@tinyerp.com-20130730110822-zzioh92in4azqay8 --- addons/crm/crm.py | 12 ++++- addons/crm/crm_case_section_view.xml | 53 ++++++++++++++++++-- addons/crm/static/src/css/crm.css | 3 +- addons/crm/static/src/js/crm_case_section.js | 5 +- addons/sale_crm/report/sale_report_view.xml | 2 +- addons/sale_crm/sale_crm_view.xml | 8 +-- 6 files changed, 69 insertions(+), 14 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index d1194508b71..876814fe5b7 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -156,6 +156,13 @@ class crm_case_section(osv.osv): res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'create_date'], 'planned_revenue', 'create_date', context=context) return res + def _get_currency_symbol(self, cr, uid, ids, name, arg, context=None): + """Set currency symbole of company currency""" + res = {} + for sales_team in self.browse(cr, uid, ids, context=context): + res[sales_team.id] = sales_team.company_id.currency_id.symbol + return res + _columns = { 'name': fields.char('Sales Team', size=64, required=True, translate=True), 'complete_name': fields.function(get_full_name, type='char', size=256, readonly=True, store=True), @@ -184,7 +191,9 @@ class crm_case_section(osv.osv): string='Open Leads per Month'), 'monthly_planned_revenue': fields.function(_get_opportunities_data, type="string", readonly=True, multi='_get_opportunities_data', - string='Planned Revenue per Month') + string='Planned Revenue per Month'), + 'company_id': fields.many2one('res.company', 'Company', select=1), + 'currency_symbol': fields.function(_get_currency_symbol, string='Currency Symbol', method=True, type='char'), } def _get_stage_common(self, cr, uid, context): @@ -195,6 +204,7 @@ class crm_case_section(osv.osv): 'active': 1, 'stage_ids': _get_stage_common, 'use_leads': True, + 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case.section', context=c), } _sql_constraints = [ diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml index c957311b47f..c8d4cb01c41 100644 --- a/addons/crm/crm_case_section_view.xml +++ b/addons/crm/crm_case_section_view.xml @@ -13,7 +13,6 @@ { 'search_default_section_id': [active_id], - 'search_default_open': 1, 'default_section_id': active_id, 'default_type': 'lead', 'stage_type': 'lead', @@ -43,7 +42,6 @@ { 'search_default_section_id': [active_id], - 'search_default_assigned_to_me': 1, 'default_section_id': active_id, 'stage_type': 'opportunity', 'default_type': 'opportunity', @@ -64,6 +62,52 @@ + + Leads Analysis + crm.lead.report + form + {"search_default_month":1} + tree,graph + [('type','=', 'lead'),('section_id', '=', active_id)] + Leads Analysis allows you to check different CRM related information like the treatment delays or number of leads per state. You can sort out your leads analysis by different groups to get accurate grained analysis. + + + + tree + + + + + + graph + + + + + + Opportunities Analysis + crm.lead.report + form + {"search_default_month":1} + tree,graph + [('type','=', 'opportunity'), ('section_id', '=', active_id)] + Opportunities Analysis gives you an instant access to your opportunities with information such as the expected revenue, planned cost, missed deadlines or the number of interactions per opportunity. This report is mainly used by the sales manager in order to do the periodic review with the teams of the sales pipeline. + + + + + tree + + + + + + + graph + + + + @@ -80,6 +124,7 @@ + diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css index 53939191eb5..680c3a9bd88 100644 --- a/addons/crm/static/src/css/crm.css +++ b/addons/crm/static/src/css/crm.css @@ -1,5 +1,6 @@ .openerp .oe_kanban_view .oe_kanban_crm_salesteams { width: 345px; + cursor: default; } .openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_avatars { text-align: right; @@ -61,5 +62,5 @@ .openerp .oe_kanban_view .oe_sparkline_bar { height: 20px; - width: 36px; + width: 36px !important; } \ 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 2b4ca741d75..f16dc31f634 100644 --- a/addons/crm/static/src/js/crm_case_section.js +++ b/addons/crm/static/src/js/crm_case_section.js @@ -8,7 +8,6 @@ openerp.crm = function(openerp) { } }, }); - openerp.crm.SparklineBarWidget = openerp.web_kanban.AbstractField.extend({ className: "oe_sparkline_bar", start: function() { @@ -20,12 +19,12 @@ openerp.crm = function(openerp) { self.$el.sparkline(value, { type: 'bar', barWidth: 5, - tooltipFormat: '{{offset:offset}} {{value}}', + tooltipFormat: _.str.sprintf("{{offset:offset}}: {{value}} %s",self.$el.closest("div[class='oe_salesteams_leads']").attr('class') ? 'Leads' : self.getParent().record.currency_symbol.raw_value), tooltipValueLookups: { 'offset': tooltips }, }); - self.$el.tipsy({'delayIn': 0, 'html': true, 'title': function(){return title}, 'gravity': 'n'}); + self.$el.tipsy({'delayIn': 3000, 'html': true, 'title': function(){return title}, 'gravity': 'n'}); }, 0); }, }); diff --git a/addons/sale_crm/report/sale_report_view.xml b/addons/sale_crm/report/sale_report_view.xml index f054634cf1c..99ab196a115 100644 --- a/addons/sale_crm/report/sale_report_view.xml +++ b/addons/sale_crm/report/sale_report_view.xml @@ -17,7 +17,7 @@ sale.report - + diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 7e0d6b27a3a..667d5f4f412 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -217,7 +217,7 @@ form tree,graph [('state','=','draft'),('section_id', '=', active_id)] - {'search_default_order_month':1,'search_default_User':1} + {'search_default_order_month':1} This report performs analysis on your quotations. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application. @@ -227,7 +227,7 @@ form tree,graph [('state','not in',('draft','done','cancel')),('section_id', '=', active_id)] - {'search_default_order_month':1,'search_default_User':1} + {'search_default_order_month':1} This report performs analysis on your sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application. @@ -237,7 +237,7 @@ form tree,graph [('section_id', '=', active_id)] - {'search_default_month':1, 'search_default_user':1} + {'search_default_month':1} From this report, you can have an overview of the amount invoiced to your customer. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs. @@ -299,7 +299,7 @@
Invoiced - Forecast + Forecast

Define an invoicing target in the sales team settings to see the period's achievement and forecast at a glance. From 14522ba8a8ff8787fe0b2620f3cab91c5428c1ae Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Tue, 30 Jul 2013 16:43:37 +0530 Subject: [PATCH 05/68] [IMP] improved code to show only paid invoices on click action of graph bzr revid: tpa@tinyerp.com-20130730111337-nw7s9i2dad7s4o7k --- addons/sale_crm/sale_crm_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 667d5f4f412..fa44cd7e821 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -236,7 +236,7 @@ account.invoice.report form tree,graph - [('section_id', '=', active_id)] + [('state','=','paid'),('section_id', '=', active_id)] {'search_default_month':1} From this report, you can have an overview of the amount invoiced to your customer. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs. From 4a826985a28023c3160c14d3a80dd5e9e37df9e9 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Tue, 30 Jul 2013 17:00:59 +0530 Subject: [PATCH 06/68] [IMP] improved code to show Lead(s) insted of Leads and make it traslatable also bzr revid: tpa@tinyerp.com-20130730113059-03mvlq21b3dlzknz --- addons/crm/static/src/js/crm_case_section.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js index f16dc31f634..a281e4bb0f1 100644 --- a/addons/crm/static/src/js/crm_case_section.js +++ b/addons/crm/static/src/js/crm_case_section.js @@ -1,4 +1,5 @@ openerp.crm = function(openerp) { +var _t = openerp.web._t; openerp.web_kanban.KanbanRecord.include({ on_card_clicked: function() { if (this.view.dataset.model === 'crm.case.section') { @@ -19,7 +20,7 @@ openerp.crm = function(openerp) { self.$el.sparkline(value, { type: 'bar', barWidth: 5, - tooltipFormat: _.str.sprintf("{{offset:offset}}: {{value}} %s",self.$el.closest("div[class='oe_salesteams_leads']").attr('class') ? 'Leads' : self.getParent().record.currency_symbol.raw_value), + tooltipFormat: _.str.sprintf("{{offset:offset}}: {{value}} %s",self.$el.closest("div[class='oe_salesteams_leads']").attr('class') ? _t('Lead(s)') : self.getParent().record.currency_symbol.raw_value), tooltipValueLookups: { 'offset': tooltips }, From 9010169be290874fdba68543d5f185d7b2118653 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Tue, 30 Jul 2013 18:55:39 +0530 Subject: [PATCH 07/68] [IMP] sale_crm: improved filter in search view bzr revid: tpa@tinyerp.com-20130730132539-zgh8y85sz83ly5bt --- addons/sale_crm/report/sale_report_view.xml | 2 +- addons/sale_crm/sale_crm_view.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/sale_crm/report/sale_report_view.xml b/addons/sale_crm/report/sale_report_view.xml index 99ab196a115..8661d13c470 100644 --- a/addons/sale_crm/report/sale_report_view.xml +++ b/addons/sale_crm/report/sale_report_view.xml @@ -19,7 +19,7 @@ - + diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index fa44cd7e821..73057e63bfc 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -226,7 +226,7 @@ sale.report form tree,graph - [('state','not in',('draft','done','cancel')),('section_id', '=', active_id)] + [('state','not in',('draft','sent','cancel')),('section_id', '=', active_id)] {'search_default_order_month':1} This report performs analysis on your sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application. From d2777a494fa3af402b41a88ad21a2545852174f3 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Wed, 31 Jul 2013 18:50:56 +0530 Subject: [PATCH 08/68] [IMP] improved code to show opportunity revenue in current user currency in multy currency senario for sales team sparkline bars bzr revid: tpa@tinyerp.com-20130731132056-ns7wl04r33grznrs --- addons/crm/crm.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 876814fe5b7..137f619f2e3 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -111,7 +111,7 @@ class crm_case_section(osv.osv): def get_full_name(self, cr, uid, ids, field_name, arg, context=None): return dict(self.name_get(cr, uid, ids, context=context)) - def __get_bar_values(self, cr, uid, obj, domain, read_fields, value_field, groupby_field, context=None): + def __get_bar_values(self, cr, uid, obj, domain, read_fields, value_field, groupby_field, type=None, context=None): """ Generic method to generate data for bar chart values using SparklineBarWidget. This method performs obj.read_group(cr, uid, domain, read_fields, groupby_field). @@ -135,8 +135,19 @@ class crm_case_section(osv.osv): group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context) for group in group_obj: group_begin_date = datetime.strptime(group['__domain'][0][2], tools.DEFAULT_SERVER_DATE_FORMAT) - month_delta = relativedelta.relativedelta(month_begin, group_begin_date) - section_result[self._period_number - (month_delta.months + 1)] = {'value': group.get(value_field, 0), 'tooltip': group_begin_date.strftime('%B')} + month = self._period_number - (relativedelta.relativedelta(month_begin, group_begin_date).months + 1) + section_result[month] = {'value': 0, 'tooltip': group_begin_date.strftime('%B')} + inner_groupby = (group.get('__context', {})).get('group_by',[]) + if type == "Lead" or not inner_groupby: + section_result[month]['value'] = group.get(value_field, 0) + continue + if inner_groupby: + groupby_company = obj.read_group(cr, uid, group.get('__domain'), read_fields, inner_groupby, context=context) + for groupby in groupby_company: + company = self.pool.get('res.company').browse(cr, uid, groupby['__domain'][0][2], context=context) + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + value = self.pool.get('res.currency').compute(cr, uid, company.currency_id.id, user.company_id.currency_id.id, groupby.get(value_field, 0)) + section_result[month]['value'] = section_result[month]['value'] + value return section_result def _get_opportunities_data(self, cr, uid, ids, field_name, arg, context=None): @@ -151,16 +162,16 @@ class crm_case_section(osv.osv): for id in ids: res[id] = dict() lead_domain = [('type', '=', 'lead'), ('section_id', '=', id), ('create_date', '>=', groupby_begin)] - res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date'], 'create_date_count', 'create_date', context=context) + res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date','company_id'], 'create_date_count', ['create_date', 'company_id'], type="Lead", context=context) opp_domain = [('type', '=', 'opportunity'), ('section_id', '=', id), ('create_date', '>=', groupby_begin)] - res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'create_date'], 'planned_revenue', 'create_date', context=context) + res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'create_date', 'company_id'], 'planned_revenue', ['create_date', 'company_id'], context=context) return res def _get_currency_symbol(self, cr, uid, ids, name, arg, context=None): """Set currency symbole of company currency""" res = {} - for sales_team in self.browse(cr, uid, ids, context=context): - res[sales_team.id] = sales_team.company_id.currency_id.symbol + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + res = dict.fromkeys(ids, user.company_id.currency_id.symbol) return res _columns = { @@ -192,7 +203,6 @@ class crm_case_section(osv.osv): 'monthly_planned_revenue': fields.function(_get_opportunities_data, type="string", readonly=True, multi='_get_opportunities_data', string='Planned Revenue per Month'), - 'company_id': fields.many2one('res.company', 'Company', select=1), 'currency_symbol': fields.function(_get_currency_symbol, string='Currency Symbol', method=True, type='char'), } @@ -204,7 +214,6 @@ class crm_case_section(osv.osv): 'active': 1, 'stage_ids': _get_stage_common, 'use_leads': True, - 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case.section', context=c), } _sql_constraints = [ From 7f6daa12f92c07351f349c338d2ae1666b3b3c6e Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Mon, 5 Aug 2013 17:05:49 +0530 Subject: [PATCH 09/68] [IMP] Improved report of lead/apportunity analysis to show in user currency and improved code to show target and forcast amount in current user currency. bzr revid: tpa@tinyerp.com-20130805113549-9vmwd2v0zsjwlcms --- addons/crm/report/crm_lead_report.py | 32 +++++++++++++++++++--- addons/crm/report/crm_lead_report_view.xml | 8 +++--- addons/sale_crm/sale_crm.py | 32 ++++++++++++++++++++-- addons/sale_crm/sale_crm_demo.xml | 8 +++--- addons/sale_crm/sale_crm_view.xml | 4 +-- 5 files changed, 67 insertions(+), 17 deletions(-) diff --git a/addons/crm/report/crm_lead_report.py b/addons/crm/report/crm_lead_report.py index d12facd66a0..23347988573 100644 --- a/addons/crm/report/crm_lead_report.py +++ b/addons/crm/report/crm_lead_report.py @@ -53,6 +53,19 @@ class crm_lead_report(osv.osv): _description = "CRM Lead Analysis" _rec_name = 'deadline_day' + def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names=[], args={}, context=None): + """Compute the amounts in the currency of the user + """ + res = {} + currency_obj = self.pool.get('res.currency') + user_currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id + for item in self.browse(cr, uid, ids, context=context): + res[item.id] = { + 'user_currency_planned_revenue': currency_obj.compute(cr, uid, item.company_id.currency_id.id, user_currency_id, item.planned_revenue, context=context), + 'user_currency_probable_revenue': currency_obj.compute(cr, uid, item.company_id.currency_id.id, user_currency_id, item.probable_revenue, context=context), + } + return res + _columns = { # grouping fields based on Deadline Date 'deadline_year': fields.char('Ex. Closing Year', size=10, readonly=True, help="Expected closing year"), @@ -83,7 +96,9 @@ class crm_lead_report(osv.osv): 'company_id': fields.many2one('res.company', 'Company', readonly=True), 'probability': fields.float('Probability',digits=(16,2),readonly=True, group_operator="avg"), 'planned_revenue': fields.float('Planned Revenue',digits=(16,2),readonly=True), + 'user_currency_planned_revenue': fields.function(_compute_amounts_in_user_currency, string="Planned Revenue", type='float',digits=(16,2), multi="_compute_amounts", readonly=True), 'probable_revenue': fields.float('Probable Revenue', digits=(16,2),readonly=True), + 'user_currency_probable_revenue': fields.function(_compute_amounts_in_user_currency, string="Probable Revenue", type='float',digits=(16,2), multi="_compute_amounts",readonly=True), 'stage_id': fields.many2one ('crm.case.stage', 'Stage', readonly=True, domain="[('section_ids', '=', section_id)]"), 'partner_id': fields.many2one('res.partner', 'Partner' , readonly=True), 'nbr': fields.integer('# of Cases', readonly=True), @@ -94,12 +109,21 @@ class crm_lead_report(osv.osv): ('opportunity','Opportunity'), ],'Type', help="Type is used to separate Leads and Opportunities"), } - - - + + #FIX:To show sum of values of function fields in groupby + def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False): + res = super(crm_lead_report, self).read_group(cr, uid, domain, fields, groupby, offset, limit, context, orderby) + for group in res: + group['user_currency_probable_revenue'] = 0 + group['user_currency_planned_revenue'] = 0 + group_ids = self.search(cr, uid, group.get('__domain'),context=context) + record = self._compute_amounts_in_user_currency(cr, uid, group_ids, context=context) + for id, rec in record.iteritems(): + group['user_currency_planned_revenue'] += rec['user_currency_planned_revenue'] + group['user_currency_probable_revenue'] += rec['user_currency_probable_revenue'] + return res def init(self, cr): - """ CRM Lead Report @param cr: the current row, from the database cursor diff --git a/addons/crm/report/crm_lead_report_view.xml b/addons/crm/report/crm_lead_report_view.xml index 23594a7f9e1..a9b7bc73aab 100644 --- a/addons/crm/report/crm_lead_report_view.xml +++ b/addons/crm/report/crm_lead_report_view.xml @@ -28,7 +28,7 @@ - + @@ -53,7 +53,7 @@ - + @@ -146,12 +146,12 @@ - + - + diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index 97c5f4579e0..634df104d34 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -67,12 +67,25 @@ class crm_case_section(osv.osv): res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date'], 'price_total', 'date', context=context) return res + def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names, args, context=None): + """Compute the amounts in the currency of the user """ + res = {} + currency_obj = self.pool.get('res.currency') + user_currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id + for item in self.browse(cr, uid, ids, context=context): + base_currency_id = item.user_id.company_id.currency_id.id if item.user_id else item.create_uid.company_id.currency_id.id + res[item.id] = { + 'invoiced_forecast': currency_obj.compute(cr, uid, base_currency_id, user_currency_id, item.base_invoiced_forecast, context=context), + 'invoiced_target': currency_obj.compute(cr, uid, base_currency_id, user_currency_id, item.base_invoiced_target, context=context), + } + return res + _columns = { - 'invoiced_forecast': fields.integer(string='Invoice Forecast', + 'invoiced_forecast': fields.function(_compute_amounts_in_user_currency, string='Invoice Forecast',type='integer', multi="_compute_amounts", help="Forecast of the invoice revenue for the current month. This is the amount the sales \n" "team should invoice this month. It is used to compute the progression ratio \n" " of the current and forecast revenue on the kanban view."), - 'invoiced_target': fields.integer(string='Invoice Target', + 'invoiced_target': fields.function(_compute_amounts_in_user_currency,string='Invoice Target',type='integer', multi="_compute_amounts", help="Target of invoice revenue for the current month. This is the amount the sales \n" "team estimates to be able to invoice this month."), 'monthly_quoted': fields.function(_get_sale_orders_data, @@ -84,11 +97,24 @@ class crm_case_section(osv.osv): 'monthly_invoiced': fields.function(_get_invoices_data, type='string', readonly=True, string='Rate of sent invoices per duration'), + 'base_invoiced_forecast': fields.integer(string="Invoice Forecast"), + 'base_invoiced_target': fields.integer(string="Invoice Target"), + 'create_uid': fields.many2one('res.users', 'Create User'), } def action_forecast(self, cr, uid, id, value, context=None): - return self.write(cr, uid, [id], {'invoiced_forecast': value}, context=context) + return self.write(cr, uid, [id], {'invoiced_forecast': int(value)}, context=context) + def write(self, cr, uid, ids, vals, context=None): + currency_obj = self.pool.get('res.currency') + user_currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id + for item in self.browse(cr, uid, ids, context=context): + base_currency_id = item.user_id.company_id.currency_id.id if item.user_id else item.create_uid.company_id.currency_id.id + if vals.get('invoiced_forecast'): + vals['base_invoiced_forecast'] = currency_obj.compute(cr, uid, user_currency_id, base_currency_id, vals['invoiced_forecast'], context=context) + if vals.get('invoiced_target'): + vals['base_invoiced_target'] = currency_obj.compute(cr, uid, user_currency_id, base_currency_id, vals['invoiced_target'], context=context) + return super(crm_case_section, self).write(cr, uid, ids, vals, context=context) class res_users(osv.Model): _inherit = 'res.users' diff --git a/addons/sale_crm/sale_crm_demo.xml b/addons/sale_crm/sale_crm_demo.xml index 0c2822719dd..3eff4061aa1 100644 --- a/addons/sale_crm/sale_crm_demo.xml +++ b/addons/sale_crm/sale_crm_demo.xml @@ -3,15 +3,15 @@ - 52700 - 60000 + 52700 + 60000 Indirect Sales IM - 36000 - 40000 + 36000 + 40000 diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 73057e63bfc..22dff55531d 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -248,8 +248,8 @@ - - + + From a24a72e3d3f2bdc1b0a1968e9796c81d51d0a6ae Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Tue, 6 Aug 2013 12:04:16 +0530 Subject: [PATCH 10/68] [IMP] improved view by css bzr revid: tpa@tinyerp.com-20130806063416-8rrqtf3qb7j01sb2 --- addons/crm/static/src/css/crm.css | 11 +++++++++-- addons/sale_crm/sale_crm_view.xml | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css index 680c3a9bd88..8e8d95fe2f7 100644 --- a/addons/crm/static/src/css/crm.css +++ b/addons/crm/static/src/css/crm.css @@ -1,6 +1,7 @@ .openerp .oe_kanban_view .oe_kanban_crm_salesteams { width: 345px; cursor: default; + min-height: 240px !important; } .openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_avatars { text-align: right; @@ -55,12 +56,18 @@ font-size: 10px; } -.openerp .oe_kanban_view .oe_justgage { +.openerp .oe_kanban_view .oe_salesteams_justgage { color: black; display: inline-block; + bottom: 0; } .openerp .oe_kanban_view .oe_sparkline_bar { height: 20px; width: 36px !important; -} \ No newline at end of file +} + +.openerp .oe_kanban_view .oe_salesteams_help { + display: inline-block; +} + diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 22dff55531d..1546fbc27e6 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -297,11 +297,11 @@
-
+
Invoiced Forecast
-
+

Define an invoicing target in the sales team settings to see the period's achievement and forecast at a glance.
From 9daabe375448d13f03b9be7f154f01b9ee349108 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Tue, 6 Aug 2013 14:41:24 +0530 Subject: [PATCH 11/68] [IMP] show sales analysis based on cretion date for quatation and for sales based on confirm date. bzr revid: tpa@tinyerp.com-20130806091124-650f98bm7d1e004e --- addons/crm/crm_demo.xml | 2 ++ addons/crm/report/crm_lead_report.py | 11 ++++++----- addons/sale/report/sale_report.py | 6 +++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/addons/crm/crm_demo.xml b/addons/crm/crm_demo.xml index 2858250e86a..2cf7b104a3f 100644 --- a/addons/crm/crm_demo.xml +++ b/addons/crm/crm_demo.xml @@ -9,11 +9,13 @@ Indirect Sales IM + Marketing SPD + diff --git a/addons/crm/report/crm_lead_report.py b/addons/crm/report/crm_lead_report.py index 23347988573..ed4b6c4310a 100644 --- a/addons/crm/report/crm_lead_report.py +++ b/addons/crm/report/crm_lead_report.py @@ -116,11 +116,12 @@ class crm_lead_report(osv.osv): for group in res: group['user_currency_probable_revenue'] = 0 group['user_currency_planned_revenue'] = 0 - group_ids = self.search(cr, uid, group.get('__domain'),context=context) - record = self._compute_amounts_in_user_currency(cr, uid, group_ids, context=context) - for id, rec in record.iteritems(): - group['user_currency_planned_revenue'] += rec['user_currency_planned_revenue'] - group['user_currency_probable_revenue'] += rec['user_currency_probable_revenue'] + if group.get('__domain'): + group_ids = self.search(cr, uid, group.get('__domain'),context=context) + record = self._compute_amounts_in_user_currency(cr, uid, group_ids, context=context) + for id, rec in record.iteritems(): + group['user_currency_planned_revenue'] += rec['user_currency_planned_revenue'] + group['user_currency_probable_revenue'] += rec['user_currency_probable_revenue'] return res def init(self, cr): diff --git a/addons/sale/report/sale_report.py b/addons/sale/report/sale_report.py index d2f7849a958..9b2728c1668 100644 --- a/addons/sale/report/sale_report.py +++ b/addons/sale/report/sale_report.py @@ -70,9 +70,9 @@ class sale_report(osv.osv): 1 as nbr, s.date_order as date, s.date_confirm as date_confirm, - to_char(s.date_order, 'YYYY') as year, - to_char(s.date_order, 'MM') as month, - to_char(s.date_order, 'YYYY-MM-DD') as day, + case when (s.state='draft') then to_char(s.date_order, 'YYYY') else to_char(s.date_confirm, 'YYYY') end as year, + case when (s.state='draft') then to_char(s.date_order, 'MM') else to_char(s.date_confirm, 'MM') end as month, + case when (s.state='draft') then to_char(s.date_order, 'YYYY-MM-DD') else to_char(s.date_confirm, 'YYYY-MM-DD') end as day, s.partner_id as partner_id, s.user_id as user_id, s.company_id as company_id, From 5323abfafce039fe14162cc1e1c4e3cd3df2d6ad Mon Sep 17 00:00:00 2001 From: "Chirag Dodiya (OpenERP Trainee)" Date: Tue, 6 Aug 2013 14:44:13 +0530 Subject: [PATCH 12/68] [IMP] Improved css for kanbanview of Sales Teams bzr revid: chiragdd7@gmail.com-20130806091413-yf8xyc5xvcpcc3m2 --- addons/crm/static/src/css/crm.css | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css index 8e8d95fe2f7..ddb60fb2e98 100644 --- a/addons/crm/static/src/css/crm.css +++ b/addons/crm/static/src/css/crm.css @@ -57,6 +57,7 @@ } .openerp .oe_kanban_view .oe_salesteams_justgage { + position: absolute; color: black; display: inline-block; bottom: 0; From 90983ace491a1d73c500b31f74d4762927bbcdfc Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Tue, 6 Aug 2013 16:57:13 +0530 Subject: [PATCH 13/68] [IMP] add demo user in direct sales team also. bzr revid: tpa@tinyerp.com-20130806112713-l11uio6f6sgzzfc5 --- addons/crm/crm_demo.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/crm/crm_demo.xml b/addons/crm/crm_demo.xml index 2cf7b104a3f..82bdcd8fcd9 100644 --- a/addons/crm/crm_demo.xml +++ b/addons/crm/crm_demo.xml @@ -6,6 +6,10 @@ + + + + Indirect Sales IM From ac12aa981a4fc7c2dbbabaa65831f5415751a128 Mon Sep 17 00:00:00 2001 From: "Chirag Dodiya (OpenERP Trainee)" Date: Thu, 8 Aug 2013 11:19:40 +0530 Subject: [PATCH 14/68] [IMP] Improved Sale Analysis report bzr revid: chiragdd7@gmail.com-20130808054940-b1a8ikj4hd49rpru --- addons/sale/report/sale_report.py | 22 ++++++++++++++++++++++ addons/sale/report/sale_report_view.xml | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/addons/sale/report/sale_report.py b/addons/sale/report/sale_report.py index d2f7849a958..42a3c3d7cbf 100644 --- a/addons/sale/report/sale_report.py +++ b/addons/sale/report/sale_report.py @@ -27,6 +27,17 @@ class sale_report(osv.osv): _description = "Sales Orders Statistics" _auto = False _rec_name = 'date' + def _compute_total_in_user_currency(self, cr, uid, ids, field_names=[], args={}, context=None): + """Compute the Price total in the currency of the user + """ + res = {} + currency_obj = self.pool.get('res.currency') + user_currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id + for item in self.browse(cr, uid, ids, context=context): + res[item.id] = { + 'user_price_total': currency_obj.compute(cr, uid, item.pricelist_id.currency_id.id, user_currency_id, item.price_total, context=context), + } + return res _columns = { 'date': fields.date('Date Order', readonly=True), 'date_confirm': fields.date('Date Confirm', readonly=True), @@ -57,6 +68,7 @@ class sale_report(osv.osv): ], 'Order Status', readonly=True), 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', readonly=True), 'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True), + 'user_price_total': fields.function(_compute_total_in_user_currency, string="Total Price", type='float',digits=(16,2), multi="_compute_amounts", readonly=True), } _order = 'date desc' @@ -113,6 +125,16 @@ class sale_report(osv.osv): """ return group_by_str + def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False): + res = super(sale_report, self).read_group(cr, uid, domain, fields, groupby, offset, limit, context, orderby) + for group in res: + group['user_price_total'] = 0 + group_ids = self.search(cr, uid, group.get('__domain'),context=context) + record = self._compute_total_in_user_currency(cr, uid, group_ids, context=context) + for id, rec in record.iteritems(): + group['user_price_total'] += rec['user_price_total'] + return res + def init(self, cr): # self._table = sale_report tools.drop_view_if_exists(cr, self._table) diff --git a/addons/sale/report/sale_report_view.xml b/addons/sale/report/sale_report_view.xml index 28a6f83936a..1b22f9b75d6 100644 --- a/addons/sale/report/sale_report_view.xml +++ b/addons/sale/report/sale_report_view.xml @@ -20,7 +20,7 @@ - + From c80f7589895fd2b53c4d32e73bc432648e46490c Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP Trainee)" Date: Thu, 8 Aug 2013 11:23:54 +0530 Subject: [PATCH 15/68] [IMP]:Account:account_invoice_report.py bzr revid: sunilsharma.sharma07@gmail.com-20130808055354-0379xbn60gbcmvsc --- addons/account/report/account_invoice_report.py | 15 ++++++++++++++- .../report/account_invoice_report_view.xml | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py index d8dc45f9a3b..8674e71e109 100644 --- a/addons/account/report/account_invoice_report.py +++ b/addons/account/report/account_invoice_report.py @@ -29,7 +29,7 @@ class account_invoice_report(osv.osv): _auto = False _rec_name = 'date' - def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names, args, context=None): + def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names=[], args={}, context=None): """Compute the amounts in the currency of the user """ if context is None: @@ -103,6 +103,19 @@ class account_invoice_report(osv.osv): } _order = 'date desc' + #FIX:To show sum of values of function fields in groupby + def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False): + res = super(account_invoice_report, self).read_group(cr, uid, domain, fields, groupby, offset, limit, context, orderby) + for group in res: + group['user_currency_price_total'] = 0 + group_ids = self.search(cr, uid, group.get('__domain'),context=context) + record = self._compute_amounts_in_user_currency(cr, uid, group_ids, context=context) + for id, rec in record.iteritems(): + group['user_currency_price_total'] += rec['user_currency_price_total'] + + return res + + def _select(self): select_str = """ SELECT sub.id, sub.date, sub.year, sub.month, sub.day, sub.product_id, sub.partner_id, sub.country_id, diff --git a/addons/account/report/account_invoice_report_view.xml b/addons/account/report/account_invoice_report_view.xml index 5f38db5e71f..4c297fd52e8 100644 --- a/addons/account/report/account_invoice_report_view.xml +++ b/addons/account/report/account_invoice_report_view.xml @@ -30,7 +30,7 @@ - + @@ -41,7 +41,7 @@ - + From 3fa52479281b91684075c3e307a80616b57d8d49 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Thu, 8 Aug 2013 14:46:45 +0530 Subject: [PATCH 16/68] [IMP] improved code to show invoice analysis report amounts in current user currency. bzr revid: tpa@tinyerp.com-20130808091645-8loajee0ss41d1u4 --- addons/account/report/account_invoice_report.py | 12 ++++++++++++ .../account/report/account_invoice_report_view.xml | 4 ++-- addons/crm/static/src/css/crm.css | 1 - 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py index d8dc45f9a3b..6fac59607c2 100644 --- a/addons/account/report/account_invoice_report.py +++ b/addons/account/report/account_invoice_report.py @@ -103,6 +103,18 @@ class account_invoice_report(osv.osv): } _order = 'date desc' + #FIX:To show sum of values of function fields in groupby + def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False): + res = super(account_invoice_report, self).read_group(cr, uid, domain, fields, groupby, offset, limit, context, orderby) + for group in res: + group['user_currency_price_total'] = 0 + if group.get('__domain'): + group_ids = self.search(cr, uid, group.get('__domain'),context=context) + record = self._compute_amounts_in_user_currency(cr, uid, group_ids, context=context) + for id, rec in record.iteritems(): + group['user_currency_price_total'] += rec['user_currency_price_total'] + return res + def _select(self): select_str = """ SELECT sub.id, sub.date, sub.year, sub.month, sub.day, sub.product_id, sub.partner_id, sub.country_id, diff --git a/addons/account/report/account_invoice_report_view.xml b/addons/account/report/account_invoice_report_view.xml index 5f38db5e71f..4c297fd52e8 100644 --- a/addons/account/report/account_invoice_report_view.xml +++ b/addons/account/report/account_invoice_report_view.xml @@ -30,7 +30,7 @@ - + @@ -41,7 +41,7 @@ - + diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css index 8e8d95fe2f7..09d8bb30229 100644 --- a/addons/crm/static/src/css/crm.css +++ b/addons/crm/static/src/css/crm.css @@ -1,7 +1,6 @@ .openerp .oe_kanban_view .oe_kanban_crm_salesteams { width: 345px; cursor: default; - min-height: 240px !important; } .openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_avatars { text-align: right; From 38f4192a91774a9eb957134d5eb8c43ba26b5e91 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Thu, 8 Aug 2013 14:54:29 +0530 Subject: [PATCH 17/68] [IMP] pass default value for arguments bzr revid: tpa@tinyerp.com-20130808092429-h1nvz0gtn5mqzm5w --- addons/account/report/account_invoice_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py index 6fac59607c2..ef9478faf0a 100644 --- a/addons/account/report/account_invoice_report.py +++ b/addons/account/report/account_invoice_report.py @@ -29,7 +29,7 @@ class account_invoice_report(osv.osv): _auto = False _rec_name = 'date' - def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names, args, context=None): + def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names=[], args={}, context=None): """Compute the amounts in the currency of the user """ if context is None: From 418c77da3026df95004ce98eca8a305fc04c0f42 Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP Trainee)" Date: Tue, 13 Aug 2013 12:03:15 +0530 Subject: [PATCH 18/68] [IMP]:User_Currency: bzr revid: sunilsharma.sharma07@gmail.com-20130813063315-i45q7dykm6sp3a94 --- addons/crm/crm.py | 10 ++++++++-- addons/sale_crm/sale_crm.py | 6 +++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 137f619f2e3..0ac2e887e72 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -144,9 +144,15 @@ class crm_case_section(osv.osv): if inner_groupby: groupby_company = obj.read_group(cr, uid, group.get('__domain'), read_fields, inner_groupby, context=context) for groupby in groupby_company: - company = self.pool.get('res.company').browse(cr, uid, groupby['__domain'][0][2], context=context) + if inner_groupby[0] == 'company_id': + company = self.pool.get('res.company').browse(cr, uid, groupby['__domain'][0][2], context=context) + base_currency_id = company.currency_id.id + elif inner_groupby[0] == 'pricelist_id': + base_currency_id = self.pool.get('product.pricelist').browse(cr, uid, groupby['__domain'][0][2], context=context).currency_id.id + else : + base_currency_id = groupby['__domain'][0][2] user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - value = self.pool.get('res.currency').compute(cr, uid, company.currency_id.id, user.company_id.currency_id.id, groupby.get(value_field, 0)) + value = self.pool.get('res.currency').compute(cr, uid, base_currency_id, user.company_id.currency_id.id, groupby.get(value_field, 0)) section_result[month]['value'] = section_result[month]['value'] + value return section_result diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index 634df104d34..5333016b620 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -52,9 +52,9 @@ class crm_case_section(osv.osv): for id in ids: res[id] = dict() created_domain = [('section_id', '=', id), ('state', 'in', ['draft', 'sent']), ('date_order', '>=', groupby_begin)] - res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context) + res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order','pricelist_id'], 'amount_total', ['date_order','pricelist_id'], context=context) validated_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'sent']), ('date_confirm', '>=', groupby_begin)] - res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_confirm'], 'amount_total', 'date_confirm', context=context) + res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_confirm','pricelist_id'], 'amount_total', ['date_confirm','pricelist_id'], context=context) return res def _get_invoices_data(self, cr, uid, ids, field_name, arg, context=None): @@ -64,7 +64,7 @@ class crm_case_section(osv.osv): groupby_begin = (month_begin + relativedelta.relativedelta(months=-4)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT) for id in ids: created_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', groupby_begin)] - res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date'], 'price_total', 'date', context=context) + res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date','currency_id'], 'price_total', ['date','currency_id'], context=context) return res def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names, args, context=None): From 73ff1e657dcd2f4e4a53cf040692e9bd20c73775 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Fri, 16 Aug 2013 18:37:10 +0530 Subject: [PATCH 19/68] [FIX] show error messge when we move recored in kanban. bzr revid: tpa@tinyerp.com-20130816130710-ec5x1k7tn86dejk1 --- addons/web_kanban/static/src/js/kanban.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 6f47f9c128d..7dd7c56a0b6 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -436,7 +436,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ new_group.do_save_sequences(); }).fail(function(error, evt) { evt.preventDefault(); - alert(_t("An error has occured while moving the record to this group: ") + data.message); + alert(_t("An error has occured while moving the record to this group: ") + error.data.message); self.do_reload(); // TODO: use draggable + sortable in order to cancel the dragging when the rcp fails }); } From a3508b45d7a23053a95468a1a9560951283378bf Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Fri, 16 Aug 2013 18:57:54 +0530 Subject: [PATCH 20/68] [IMP] improved code to show invoice bar value bzr revid: tpa@tinyerp.com-20130816132754-tgnd3xoi9v3o5711 --- addons/crm/crm.py | 2 -- addons/sale_crm/sale_crm.py | 4 ++-- addons/sale_crm/sale_crm_view.xml | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 0f9759a77d5..a9479000b3e 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -136,8 +136,6 @@ class crm_case_section(osv.osv): base_currency_id = self.pool.get('res.company').browse(cr, uid, groupby['__domain'][0][2], context=context).currency_id.id elif inner_groupby[0] == 'pricelist_id': base_currency_id = self.pool.get('product.pricelist').browse(cr, uid, groupby['__domain'][0][2], context=context).currency_id.id - else : - base_currency_id = groupby['__domain'][0][2] user = self.pool.get('res.users').browse(cr, uid, uid, context=context) value = self.pool.get('res.currency').compute(cr, uid, base_currency_id, user.company_id.currency_id.id, groupby.get(value_field, 0)) section_result[month]['value'] = section_result[month]['value'] + value diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index 5333016b620..c48c874b48d 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -64,7 +64,7 @@ class crm_case_section(osv.osv): groupby_begin = (month_begin + relativedelta.relativedelta(months=-4)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT) for id in ids: created_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', groupby_begin)] - res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date','currency_id'], 'price_total', ['date','currency_id'], context=context) + res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['date'], 'user_currency_price_total', 'date', context=context) return res def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names, args, context=None): @@ -103,7 +103,7 @@ class crm_case_section(osv.osv): } def action_forecast(self, cr, uid, id, value, context=None): - return self.write(cr, uid, [id], {'invoiced_forecast': int(value)}, context=context) + return self.write(cr, uid, [id], {'invoiced_forecast': int(float(value))}, context=context) def write(self, cr, uid, ids, vals, context=None): currency_obj = self.pool.get('res.currency') diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 0526efebd7d..3656fa0719a 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -236,7 +236,7 @@ account.invoice.report form tree,graph - [('state','=','paid'),('section_id', '=', active_id)] + [('section_id', '=', active_id)] {'search_default_month':1} From this report, you can have an overview of the amount invoiced to your customer. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs. From f76b2078aa312b952a97552add00fc24d46fdaa3 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Mon, 19 Aug 2013 11:15:16 +0530 Subject: [PATCH 21/68] [IMP] imrpved code to remove error of undefined record bzr revid: tpa@tinyerp.com-20130819054516-je0uzg0jlsap909r --- addons/crm/static/src/js/crm_case_section.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js index a281e4bb0f1..5c76668849e 100644 --- a/addons/crm/static/src/js/crm_case_section.js +++ b/addons/crm/static/src/js/crm_case_section.js @@ -17,10 +17,14 @@ var _t = openerp.web._t; setTimeout(function () { var value = _.pluck(self.field.value, 'value'); var tooltips = _.pluck(self.field.value, 'tooltip'); + var currency_symbol = ""; + if (self.getParent()){ + currency_symbol = self.getParent().record.currency_symbol.raw_value; + }; self.$el.sparkline(value, { type: 'bar', barWidth: 5, - tooltipFormat: _.str.sprintf("{{offset:offset}}: {{value}} %s",self.$el.closest("div[class='oe_salesteams_leads']").attr('class') ? _t('Lead(s)') : self.getParent().record.currency_symbol.raw_value), + tooltipFormat: _.str.sprintf("{{offset:offset}}: {{value}} %s",self.$el.closest("div[class='oe_salesteams_leads']").attr('class') ? _t('Lead(s)') : currency_symbol), tooltipValueLookups: { 'offset': tooltips }, From ee9b75603f0dc777736a039dbf3ebb04c9e1062e Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Wed, 21 Aug 2013 16:41:13 +0530 Subject: [PATCH 22/68] [IMP] insted of replacing int field with function and add new int fied add new function field only, use fnct_inv insted of overwriting write method. bzr revid: tpa@tinyerp.com-20130821111113-5n022roofguvea0f --- addons/sale_crm/sale_crm.py | 33 ++++++++++++++----------------- addons/sale_crm/sale_crm_demo.xml | 8 ++++---- addons/sale_crm/sale_crm_view.xml | 16 +++++++-------- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index b463eca6bb4..af5973b8e3e 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -75,17 +75,27 @@ class crm_case_section(osv.osv): for item in self.browse(cr, uid, ids, context=context): base_currency_id = item.user_id.company_id.currency_id.id if item.user_id else item.create_uid.company_id.currency_id.id res[item.id] = { - 'invoiced_forecast': currency_obj.compute(cr, uid, base_currency_id, user_currency_id, item.base_invoiced_forecast, context=context), - 'invoiced_target': currency_obj.compute(cr, uid, base_currency_id, user_currency_id, item.base_invoiced_target, context=context), + 'user_currency_invoiced_forecast': currency_obj.compute(cr, uid, base_currency_id, user_currency_id, item.invoiced_forecast, context=context), + 'user_currency_invoiced_target': currency_obj.compute(cr, uid, base_currency_id, user_currency_id, item.invoiced_target, context=context), } return res + def _set_forecast_target(self, cr, uid, team_id, name, value, arg, context=None): + user_currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id + team = self.browse(cr, uid, team_id, context=context) + base_currency_id = team.user_id.company_id.currency_id.id if team.user_id else team.create_uid.company_id.currency_id.id + amount = self.pool.get('res.currency').compute(cr, uid, user_currency_id, base_currency_id, value, context=context) + self.write(cr, uid, [team_id], {name.strip("user_currency"): amount}, context=context) + return True + _columns = { - 'invoiced_forecast': fields.function(_compute_amounts_in_user_currency, string='Invoice Forecast',type='integer', multi="_compute_amounts", + 'invoiced_forecast': fields.integer("Invoice Forecast"), + 'user_currency_invoiced_forecast': fields.function(_compute_amounts_in_user_currency, fnct_inv=_set_forecast_target, string='Invoice Forecast',type='integer', multi="_compute_amounts", help="Forecast of the invoice revenue for the current month. This is the amount the sales \n" "team should invoice this month. It is used to compute the progression ratio \n" " of the current and forecast revenue on the kanban view."), - 'invoiced_target': fields.function(_compute_amounts_in_user_currency,string='Invoice Target',type='integer', multi="_compute_amounts", + 'invoiced_target': fields.integer("Invoice Target"), + 'user_currency_invoiced_target': fields.function(_compute_amounts_in_user_currency, fnct_inv=_set_forecast_target, string='Invoice Target',type='integer', multi="_compute_amounts", help="Target of invoice revenue for the current month. This is the amount the sales \n" "team estimates to be able to invoice this month."), 'monthly_quoted': fields.function(_get_sale_orders_data, @@ -97,24 +107,11 @@ class crm_case_section(osv.osv): 'monthly_invoiced': fields.function(_get_invoices_data, type='string', readonly=True, string='Rate of sent invoices per duration'), - 'base_invoiced_forecast': fields.integer(string="Invoice Forecast"), - 'base_invoiced_target': fields.integer(string="Invoice Target"), 'create_uid': fields.many2one('res.users', 'Create User'), } def action_forecast(self, cr, uid, id, value, context=None): - return self.write(cr, uid, [id], {'invoiced_forecast': int(float(value))}, context=context) - - def write(self, cr, uid, ids, vals, context=None): - currency_obj = self.pool.get('res.currency') - user_currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id - for item in self.browse(cr, uid, ids, context=context): - base_currency_id = item.user_id.company_id.currency_id.id if item.user_id else item.create_uid.company_id.currency_id.id - if vals.get('invoiced_forecast'): - vals['base_invoiced_forecast'] = currency_obj.compute(cr, uid, user_currency_id, base_currency_id, vals['invoiced_forecast'], context=context) - if vals.get('invoiced_target'): - vals['base_invoiced_target'] = currency_obj.compute(cr, uid, user_currency_id, base_currency_id, vals['invoiced_target'], context=context) - return super(crm_case_section, self).write(cr, uid, ids, vals, context=context) + return self.write(cr, uid, [id], {'user_currency_invoiced_forecast': int(float(value))}, context=context) class res_users(osv.Model): _inherit = 'res.users' diff --git a/addons/sale_crm/sale_crm_demo.xml b/addons/sale_crm/sale_crm_demo.xml index 3eff4061aa1..0c2822719dd 100644 --- a/addons/sale_crm/sale_crm_demo.xml +++ b/addons/sale_crm/sale_crm_demo.xml @@ -3,15 +3,15 @@ - 52700 - 60000 + 52700 + 60000 Indirect Sales IM - 36000 - 40000 + 36000 + 40000 diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 7e4bca1d817..47d850c96fe 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -248,8 +248,8 @@ - - + + @@ -265,8 +265,8 @@ - - + +
@@ -297,11 +297,11 @@
-
- Invoiced - Forecast +
+ Invoiced + Forecast
-
+

Define an invoicing target in the sales team settings to see the period's achievement and forecast at a glance.
From b63f89a74ea0c0ab8ef811069164fd6396e61ee0 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Wed, 4 Sep 2013 16:55:40 +0530 Subject: [PATCH 23/68] [RMV] remove improvemets to calculate amount based on currency from analysis reports. bzr revid: tpa@tinyerp.com-20130904112540-4ovecz1mpjkok7ge --- .../account/report/account_invoice_report.py | 14 +-------- .../report/account_invoice_report_view.xml | 4 +-- addons/crm/report/crm_lead_report.py | 29 ------------------- addons/crm/report/crm_lead_report_view.xml | 8 ++--- addons/sale/report/sale_report.py | 23 +-------------- 5 files changed, 8 insertions(+), 70 deletions(-) diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py index ef9478faf0a..c08ea91e2ea 100644 --- a/addons/account/report/account_invoice_report.py +++ b/addons/account/report/account_invoice_report.py @@ -29,7 +29,7 @@ class account_invoice_report(osv.osv): _auto = False _rec_name = 'date' - def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names=[], args={}, context=None): + def def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names, args, context=None): """Compute the amounts in the currency of the user """ if context is None: @@ -103,18 +103,6 @@ class account_invoice_report(osv.osv): } _order = 'date desc' - #FIX:To show sum of values of function fields in groupby - def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False): - res = super(account_invoice_report, self).read_group(cr, uid, domain, fields, groupby, offset, limit, context, orderby) - for group in res: - group['user_currency_price_total'] = 0 - if group.get('__domain'): - group_ids = self.search(cr, uid, group.get('__domain'),context=context) - record = self._compute_amounts_in_user_currency(cr, uid, group_ids, context=context) - for id, rec in record.iteritems(): - group['user_currency_price_total'] += rec['user_currency_price_total'] - return res - def _select(self): select_str = """ SELECT sub.id, sub.date, sub.year, sub.month, sub.day, sub.product_id, sub.partner_id, sub.country_id, diff --git a/addons/account/report/account_invoice_report_view.xml b/addons/account/report/account_invoice_report_view.xml index 4c297fd52e8..5f38db5e71f 100644 --- a/addons/account/report/account_invoice_report_view.xml +++ b/addons/account/report/account_invoice_report_view.xml @@ -30,7 +30,7 @@ - + @@ -41,7 +41,7 @@ - + diff --git a/addons/crm/report/crm_lead_report.py b/addons/crm/report/crm_lead_report.py index c2f1c0b496e..f379e068b30 100644 --- a/addons/crm/report/crm_lead_report.py +++ b/addons/crm/report/crm_lead_report.py @@ -45,19 +45,6 @@ class crm_lead_report(osv.osv): _description = "CRM Lead Analysis" _rec_name = 'deadline_day' - def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names=[], args={}, context=None): - """Compute the amounts in the currency of the user - """ - res = {} - currency_obj = self.pool.get('res.currency') - user_currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id - for item in self.browse(cr, uid, ids, context=context): - res[item.id] = { - 'user_currency_planned_revenue': currency_obj.compute(cr, uid, item.company_id.currency_id.id, user_currency_id, item.planned_revenue, context=context), - 'user_currency_probable_revenue': currency_obj.compute(cr, uid, item.company_id.currency_id.id, user_currency_id, item.probable_revenue, context=context), - } - return res - _columns = { # grouping fields based on Deadline Date 'deadline_year': fields.char('Ex. Closing Year', size=10, readonly=True, help="Expected closing year"), @@ -88,9 +75,7 @@ class crm_lead_report(osv.osv): 'company_id': fields.many2one('res.company', 'Company', readonly=True), 'probability': fields.float('Probability',digits=(16,2),readonly=True, group_operator="avg"), 'planned_revenue': fields.float('Planned Revenue',digits=(16,2),readonly=True), - 'user_currency_planned_revenue': fields.function(_compute_amounts_in_user_currency, string="Planned Revenue", type='float',digits=(16,2), multi="_compute_amounts", readonly=True), 'probable_revenue': fields.float('Probable Revenue', digits=(16,2),readonly=True), - 'user_currency_probable_revenue': fields.function(_compute_amounts_in_user_currency, string="Probable Revenue", type='float',digits=(16,2), multi="_compute_amounts",readonly=True), 'stage_id': fields.many2one ('crm.case.stage', 'Stage', readonly=True, domain="[('section_ids', '=', section_id)]"), 'partner_id': fields.many2one('res.partner', 'Partner' , readonly=True), 'nbr': fields.integer('# of Cases', readonly=True), @@ -101,20 +86,6 @@ class crm_lead_report(osv.osv): ('opportunity','Opportunity'), ],'Type', help="Type is used to separate Leads and Opportunities"), } - - #FIX:To show sum of values of function fields in groupby - def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False): - res = super(crm_lead_report, self).read_group(cr, uid, domain, fields, groupby, offset, limit, context, orderby) - for group in res: - group['user_currency_probable_revenue'] = 0 - group['user_currency_planned_revenue'] = 0 - if group.get('__domain'): - group_ids = self.search(cr, uid, group.get('__domain'),context=context) - record = self._compute_amounts_in_user_currency(cr, uid, group_ids, context=context) - for id, rec in record.iteritems(): - group['user_currency_planned_revenue'] += rec['user_currency_planned_revenue'] - group['user_currency_probable_revenue'] += rec['user_currency_probable_revenue'] - return res def init(self, cr): """ diff --git a/addons/crm/report/crm_lead_report_view.xml b/addons/crm/report/crm_lead_report_view.xml index f718d7a9bf8..c7220fe66f2 100644 --- a/addons/crm/report/crm_lead_report_view.xml +++ b/addons/crm/report/crm_lead_report_view.xml @@ -28,7 +28,7 @@ - + @@ -53,7 +53,7 @@ - + @@ -148,13 +148,13 @@ - + - + diff --git a/addons/sale/report/sale_report.py b/addons/sale/report/sale_report.py index fb7ac78c3e0..969df76398c 100644 --- a/addons/sale/report/sale_report.py +++ b/addons/sale/report/sale_report.py @@ -27,17 +27,7 @@ class sale_report(osv.osv): _description = "Sales Orders Statistics" _auto = False _rec_name = 'date' - def _compute_total_in_user_currency(self, cr, uid, ids, field_names=[], args={}, context=None): - """Compute the Price total in the currency of the user - """ - res = {} - currency_obj = self.pool.get('res.currency') - user_currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id - for item in self.browse(cr, uid, ids, context=context): - res[item.id] = { - 'user_price_total': currency_obj.compute(cr, uid, item.pricelist_id.currency_id.id, user_currency_id, item.price_total, context=context), - } - return res + _columns = { 'date': fields.date('Date Order', readonly=True), 'date_confirm': fields.date('Date Confirm', readonly=True), @@ -68,7 +58,6 @@ class sale_report(osv.osv): ], 'Order Status', readonly=True), 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', readonly=True), 'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True), - 'user_price_total': fields.function(_compute_total_in_user_currency, string="Total Price", type='float',digits=(16,2), multi="_compute_amounts", readonly=True), } _order = 'date desc' @@ -125,16 +114,6 @@ class sale_report(osv.osv): """ return group_by_str - def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False): - res = super(sale_report, self).read_group(cr, uid, domain, fields, groupby, offset, limit, context, orderby) - for group in res: - group['user_price_total'] = 0 - group_ids = self.search(cr, uid, group.get('__domain'),context=context) - record = self._compute_total_in_user_currency(cr, uid, group_ids, context=context) - for id, rec in record.iteritems(): - group['user_price_total'] += rec['user_price_total'] - return res - def init(self, cr): # self._table = sale_report tools.drop_view_if_exists(cr, self._table) From 6e1b899aa98cc250ed82d0540eb9f1c7e8a93a9d Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Wed, 4 Sep 2013 17:13:35 +0530 Subject: [PATCH 24/68] [IMP] improved code bzr revid: tpa@tinyerp.com-20130904114335-jfptxzwdgt2xjzm1 --- addons/account/report/account_invoice_report.py | 2 +- addons/crm/report/crm_lead_report.py | 3 ++- addons/sale/report/sale_report_view.xml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py index c08ea91e2ea..d8dc45f9a3b 100644 --- a/addons/account/report/account_invoice_report.py +++ b/addons/account/report/account_invoice_report.py @@ -29,7 +29,7 @@ class account_invoice_report(osv.osv): _auto = False _rec_name = 'date' - def def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names, args, context=None): + def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names, args, context=None): """Compute the amounts in the currency of the user """ if context is None: diff --git a/addons/crm/report/crm_lead_report.py b/addons/crm/report/crm_lead_report.py index f379e068b30..ac1e10a84c6 100644 --- a/addons/crm/report/crm_lead_report.py +++ b/addons/crm/report/crm_lead_report.py @@ -86,8 +86,9 @@ class crm_lead_report(osv.osv): ('opportunity','Opportunity'), ],'Type', help="Type is used to separate Leads and Opportunities"), } - + def init(self, cr): + """ CRM Lead Report @param cr: the current row, from the database cursor diff --git a/addons/sale/report/sale_report_view.xml b/addons/sale/report/sale_report_view.xml index 1b22f9b75d6..28a6f83936a 100644 --- a/addons/sale/report/sale_report_view.xml +++ b/addons/sale/report/sale_report_view.xml @@ -20,7 +20,7 @@ - + From e7764209b74cd68bc561e1ee3ab83ce06862e63f Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Thu, 5 Sep 2013 12:44:04 +0530 Subject: [PATCH 25/68] [FIX] improved code for invoice sparkline bar bzr revid: tpa@tinyerp.com-20130905071404-dlgdeovxarfa9pg6 --- addons/crm/crm.py | 8 +++++--- addons/sale_crm/sale_crm.py | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index a9479000b3e..b3c560f084d 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -129,13 +129,15 @@ class crm_case_section(osv.osv): if type == "Lead" or not inner_groupby: section_result[month]['value'] = group.get(value_field, 0) continue - if inner_groupby: - groupby_company = obj.read_group(cr, uid, group.get('__domain'), read_fields, inner_groupby, context=context) - for groupby in groupby_company: + elif inner_groupby: + inner_group_obj = obj.read_group(cr, uid, group.get('__domain'), read_fields, inner_groupby, context=context) + for groupby in inner_group_obj: if inner_groupby[0] == 'company_id': base_currency_id = self.pool.get('res.company').browse(cr, uid, groupby['__domain'][0][2], context=context).currency_id.id elif inner_groupby[0] == 'pricelist_id': base_currency_id = self.pool.get('product.pricelist').browse(cr, uid, groupby['__domain'][0][2], context=context).currency_id.id + elif inner_groupby[0] == 'currency_id': + base_currency_id = self.pool.get('res.currency.rate').search_read(cr, uid, [('rate', '=', 1)], ['currency_id'], limit=1, context=context)[0]['currency_id'][0] user = self.pool.get('res.users').browse(cr, uid, uid, context=context) value = self.pool.get('res.currency').compute(cr, uid, base_currency_id, user.company_id.currency_id.id, groupby.get(value_field, 0)) section_result[month]['value'] = section_result[month]['value'] + value diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index af5973b8e3e..63017171322 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -52,9 +52,9 @@ class crm_case_section(osv.osv): for id in ids: res[id] = dict() created_domain = [('section_id', '=', id), ('state', 'in', ['draft', 'sent']), ('date_order', '>=', groupby_begin)] - res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order','pricelist_id'], 'amount_total', ['date_order','pricelist_id'], context=context) + res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order', 'pricelist_id'], 'amount_total', ['date_order', 'pricelist_id'], context=context) validated_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'sent']), ('date_confirm', '>=', groupby_begin)] - res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_confirm','pricelist_id'], 'amount_total', ['date_confirm','pricelist_id'], context=context) + res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_confirm', 'pricelist_id'], 'amount_total', ['date_confirm', 'pricelist_id'], context=context) return res def _get_invoices_data(self, cr, uid, ids, field_name, arg, context=None): @@ -64,7 +64,7 @@ class crm_case_section(osv.osv): groupby_begin = (month_begin + relativedelta.relativedelta(months=-4)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT) for id in ids: created_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', groupby_begin)] - res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['date'], 'user_currency_price_total', 'date', context=context) + res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date', 'currency_id'], 'price_total', ['date', 'currency_id'], context=context) return res def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names, args, context=None): From 17b89198ab3d59c17ce3e57d635d27756c7c91b3 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Fri, 6 Sep 2013 16:46:55 +0530 Subject: [PATCH 26/68] [FIX] round amount insted of converting in to int. bzr revid: tpa@tinyerp.com-20130906111655-f6na9nvtot6t7wdd --- addons/sale_crm/sale_crm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index 63017171322..0369a62b7fa 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -111,7 +111,7 @@ class crm_case_section(osv.osv): } def action_forecast(self, cr, uid, id, value, context=None): - return self.write(cr, uid, [id], {'user_currency_invoiced_forecast': int(float(value))}, context=context) + return self.write(cr, uid, [id], {'user_currency_invoiced_forecast': round(float(value))}, context=context) class res_users(osv.Model): _inherit = 'res.users' From 3413d5364fe1eddafd467b69782e96877d8d3a49 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Mon, 9 Sep 2013 11:28:58 +0530 Subject: [PATCH 27/68] [IMP] improved domain on invoice analysis report action bzr revid: tpa@tinyerp.com-20130909055858-yvf28wwpixak1kdy --- addons/sale_crm/sale_crm_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 47d850c96fe..c3458f3f1a3 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -236,7 +236,7 @@ account.invoice.report form tree,graph - [('section_id', '=', active_id)] + [('section_id', '=', active_id),('state', 'not in', ['draft', 'cancel'])] {'search_default_month':1} From this report, you can have an overview of the amount invoiced to your customer. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs. From da358b54ab12b175d673a067132eaa5df16af449 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Mon, 16 Sep 2013 18:37:00 +0530 Subject: [PATCH 28/68] [IMP] improved code to show bar required help and with delay bzr revid: tpa@tinyerp.com-20130916130700-e9ipoezcjeyva3mb --- addons/crm/crm_case_section_view.xml | 4 +-- addons/crm/static/src/js/crm_case_section.js | 37 +++++++------------- addons/sale_crm/sale_crm_view.xml | 6 ++-- 3 files changed, 17 insertions(+), 30 deletions(-) diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml index b5e23fec324..78727b396d3 100644 --- a/addons/crm/crm_case_section_view.xml +++ b/addons/crm/crm_case_section_view.xml @@ -144,11 +144,11 @@
diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js index 5c76668849e..83e4205f4e5 100644 --- a/addons/crm/static/src/js/crm_case_section.js +++ b/addons/crm/static/src/js/crm_case_section.js @@ -9,30 +9,17 @@ var _t = openerp.web._t; } }, }); - openerp.crm.SparklineBarWidget = openerp.web_kanban.AbstractField.extend({ - className: "oe_sparkline_bar", - start: function() { - var self = this; - var title = this.$node.html(); - setTimeout(function () { - var value = _.pluck(self.field.value, 'value'); - var tooltips = _.pluck(self.field.value, 'tooltip'); - var currency_symbol = ""; - if (self.getParent()){ - currency_symbol = self.getParent().record.currency_symbol.raw_value; - }; - self.$el.sparkline(value, { - type: 'bar', - barWidth: 5, - tooltipFormat: _.str.sprintf("{{offset:offset}}: {{value}} %s",self.$el.closest("div[class='oe_salesteams_leads']").attr('class') ? _t('Lead(s)') : currency_symbol), - tooltipValueLookups: { - 'offset': tooltips - }, - }); - self.$el.tipsy({'delayIn': 3000, 'html': true, 'title': function(){return title}, 'gravity': 'n'}); - }, 0); - }, - }); - openerp.web_kanban.fields_registry.add("sparkline_bar", "openerp.crm.SparklineBarWidget"); + openerp.web_kanban.SparklineBarWidgetSalesTeam = openerp.web_kanban.SparklineBarWidget.extend({ + set_offset:function(){ + var self = this; + var currency_symbol = ""; + if (self.getParent()){ + currency_symbol = self.getParent().record.currency_symbol.raw_value; + }; + return _.str.sprintf("{{offset:offset}}: {{value}} %s",self.$el.closest("div[class='oe_salesteams_leads']").attr('class') ? _t('Lead(s)') : currency_symbol) + } + }); + +openerp.web_kanban.fields_registry.add("sparkline_bar_sales", "openerp.web_kanban.SparklineBarWidgetSalesTeam"); }; diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index c7d3dc42292..c31e87bdf28 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -272,7 +272,7 @@
Sales Orders - + Revenue of confirmed sales orders per month.
Click to acces the Sales Analysis.
@@ -282,7 +282,7 @@
Invoices - + Revenue of sent invoices per month.
Click to see a detailed analysis of invoices.
@@ -290,7 +290,7 @@
Quotations - + Revenue of created quotations per month.
Click to see a detailed analysis.
From 3cdc986c0c7b7908c8c97176086b599cfa97f531 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Mon, 16 Sep 2013 18:38:25 +0530 Subject: [PATCH 29/68] [IMP] improved code to pass tooltip delay in option and added function so we can set requred help in inherited modules. bzr revid: tpa@tinyerp.com-20130916130825-pciwsn0doshstrp2 --- .../static/src/js/kanban_gauge.js | 28 +++++++++++++------ .../static/src/js/kanban_sparkline.js | 7 +++-- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/addons/web_kanban_gauge/static/src/js/kanban_gauge.js b/addons/web_kanban_gauge/static/src/js/kanban_gauge.js index cb2faf6874b..60cbdf1ddd8 100644 --- a/addons/web_kanban_gauge/static/src/js/kanban_gauge.js +++ b/addons/web_kanban_gauge/static/src/js/kanban_gauge.js @@ -4,7 +4,8 @@ openerp.web_kanban_gauge = function (instance) { * Kanban widgets: GaugeWidget * */ - +var _t = instance.web._t, + _lt = instance.web._lt; instance.web_kanban.GaugeWidget = instance.web_kanban.AbstractField.extend({ className: "oe_gauge", start: function() { @@ -77,13 +78,19 @@ instance.web_kanban.GaugeWidget = instance.web_kanban.AbstractField.extend({ $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(); + if(isNaN($input.val())){ + self.do_warn(_t("Wrong value entered!"), _t("Only Integer Value should be valid.")); + $div.remove(); + } else { + if (event.keyCode == 13 || event.keyCode == 9) { + var val = self.parse_client($input.val()); + if ($input.val() != value) { + parent.view.dataset.call(self.options.action_change, [parent.id, $input.val()]).then(function () { + parent.do_reload(); + }); + } else { + $div.remove(); + } } } }) @@ -112,6 +119,11 @@ instance.web_kanban.GaugeWidget = instance.web_kanban.AbstractField.extend({ } } }, + + parse_client: function(value) { + return openerp.web.parse_value(value, { type:"integer" }); + }, + }); instance.web_kanban.fields_registry.add("gauge", "instance.web_kanban.GaugeWidget"); diff --git a/addons/web_kanban_sparkline/static/src/js/kanban_sparkline.js b/addons/web_kanban_sparkline/static/src/js/kanban_sparkline.js index de21cbba945..68703d90585 100644 --- a/addons/web_kanban_sparkline/static/src/js/kanban_sparkline.js +++ b/addons/web_kanban_sparkline/static/src/js/kanban_sparkline.js @@ -20,15 +20,18 @@ instance.web_kanban.SparklineBarWidget = instance.web_kanban.AbstractField.exten barWidth: 4, barSpacing: 1, barColor: '#96d854', - tooltipFormat: '{{offset:offset}} {{value}}', + tooltipFormat: self.set_offset(), tooltipValueLookups: { 'offset': tooltips } }, self.options); self.$el.sparkline(value, sparkline_options); - self.$el.tipsy({'delayIn': 0, 'html': true, 'title': function(){return title}, 'gravity': 'n'}); + self.$el.tipsy({'delayIn': self.options.delayIn || 0, 'html': true, 'title': function(){return title}, 'gravity': 'n'}); }, 0); }, + set_offset: function(){ + return '{{offset:offset}} {{value}}'; + } }); instance.web_kanban.fields_registry.add("sparkline_bar", "instance.web_kanban.SparklineBarWidget"); From cce3b9138bc58afbd3ed9502c7ce50d588230e2d Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Fri, 11 Oct 2013 12:57:44 +0530 Subject: [PATCH 30/68] [IMP] improved code to set suffix in tooltip. bzr revid: tpa@tinyerp.com-20131011072744-pz4mcxbr4qbdrg79 --- .../static/src/js/kanban_sparkline.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/addons/web_kanban_sparkline/static/src/js/kanban_sparkline.js b/addons/web_kanban_sparkline/static/src/js/kanban_sparkline.js index db10299ddd2..630d5812f39 100644 --- a/addons/web_kanban_sparkline/static/src/js/kanban_sparkline.js +++ b/addons/web_kanban_sparkline/static/src/js/kanban_sparkline.js @@ -13,6 +13,11 @@ instance.web_kanban.SparklineBarWidget = instance.web_kanban.AbstractField.exten setTimeout(function () { var value = _.pluck(self.field.value, 'value'); var tooltips = _.pluck(self.field.value, 'tooltip'); + var suffix = self.options.tooltip_suffix || ""; + if (self.options.tooltip_suffix_field) { + suffix = self.getParent().record[self.options.tooltip_suffix_field].raw_value; + } + var tooltipformat = _.str.sprintf("{{offset:offset}}: {{value}} %s", suffix); var sparkline_options = _.extend({ type: 'bar', barWidth: 5, @@ -20,7 +25,7 @@ instance.web_kanban.SparklineBarWidget = instance.web_kanban.AbstractField.exten barWidth: 4, barSpacing: 1, barColor: '#96d854', - tooltipFormat: self.set_offset(), + tooltipFormat: tooltipformat, chartRangeMin: 0, tooltipValueLookups: { 'offset': tooltips @@ -30,9 +35,6 @@ instance.web_kanban.SparklineBarWidget = instance.web_kanban.AbstractField.exten self.$el.tipsy({'delayIn': self.options.delayIn || 0, 'html': true, 'title': function(){return title}, 'gravity': 'n'}); }, 0); }, - set_offset: function(){ - return '{{offset:offset}} {{value}}'; - } }); instance.web_kanban.fields_registry.add("sparkline_bar", "instance.web_kanban.SparklineBarWidget"); From 6279bcf4b3b37ec61c159e77a2603e8c61b592fd Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Fri, 11 Oct 2013 13:00:04 +0530 Subject: [PATCH 31/68] [IMP] improved code to set tooltip suffix in sparkline bar, pass suffix in opetion insted of overriding widget. bzr revid: tpa@tinyerp.com-20131011073004-0swv6l1aomjt71xp --- addons/crm/crm_case_section_view.xml | 4 ++-- addons/crm/static/src/js/crm_case_section.js | 14 -------------- addons/sale_crm/sale_crm_view.xml | 6 +++--- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml index 78727b396d3..7bc2b914e2c 100644 --- a/addons/crm/crm_case_section_view.xml +++ b/addons/crm/crm_case_section_view.xml @@ -144,11 +144,11 @@
diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js index 83e4205f4e5..e027313d6dc 100644 --- a/addons/crm/static/src/js/crm_case_section.js +++ b/addons/crm/static/src/js/crm_case_section.js @@ -1,5 +1,4 @@ openerp.crm = function(openerp) { -var _t = openerp.web._t; openerp.web_kanban.KanbanRecord.include({ on_card_clicked: function() { if (this.view.dataset.model === 'crm.case.section') { @@ -9,17 +8,4 @@ var _t = openerp.web._t; } }, }); - - openerp.web_kanban.SparklineBarWidgetSalesTeam = openerp.web_kanban.SparklineBarWidget.extend({ - set_offset:function(){ - var self = this; - var currency_symbol = ""; - if (self.getParent()){ - currency_symbol = self.getParent().record.currency_symbol.raw_value; - }; - return _.str.sprintf("{{offset:offset}}: {{value}} %s",self.$el.closest("div[class='oe_salesteams_leads']").attr('class') ? _t('Lead(s)') : currency_symbol) - } - }); - -openerp.web_kanban.fields_registry.add("sparkline_bar_sales", "openerp.web_kanban.SparklineBarWidgetSalesTeam"); }; diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 9e9cdbd415c..2e55ec8fa89 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -272,7 +272,7 @@
Sales Orders - + Revenue of confirmed sales orders per month.
Click to acces the Sales Analysis.
@@ -282,7 +282,7 @@
Invoices - + Revenue of sent invoices per month.
Click to see a detailed analysis of invoices.
@@ -290,7 +290,7 @@
Quotations - + Revenue of created quotations per month.
Click to see a detailed analysis.
From 62ba3155a0bc37579bc0ae184fcac8c588455ffa Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Fri, 11 Oct 2013 18:14:04 +0530 Subject: [PATCH 32/68] [IMP] remove type parameter from __get_bar_values and make base_currency_id alwas defined bzr revid: tpa@tinyerp.com-20131011124404-xoytwxfso8rg5lmb --- addons/crm/crm.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index bb4d13cf5aa..8f0e6a82591 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -100,7 +100,7 @@ class crm_case_section(osv.osv): def get_full_name(self, cr, uid, ids, field_name, arg, context=None): return dict(self.name_get(cr, uid, ids, context=context)) - def __get_bar_values(self, cr, uid, obj, domain, read_fields, value_field, groupby_field, type=None, context=None): + def __get_bar_values(self, cr, uid, obj, domain, read_fields, value_field, groupby_field, context=None): """ Generic method to generate data for bar chart values using SparklineBarWidget. This method performs obj.read_group(cr, uid, domain, read_fields, groupby_field). @@ -127,21 +127,22 @@ class crm_case_section(osv.osv): month = self._period_number - (relativedelta.relativedelta(month_begin, group_begin_date).months + 1) section_result[month] = {'value': 0, 'tooltip': group_begin_date.strftime('%B')} inner_groupby = (group.get('__context', {})).get('group_by',[]) - if type == "Lead" or not inner_groupby: + if value_field == 'create_date_count' or not inner_groupby: section_result[month]['value'] = group.get(value_field, 0) continue - elif inner_groupby: + if inner_groupby: inner_group_obj = obj.read_group(cr, uid, group.get('__domain'), read_fields, inner_groupby, context=context) for groupby in inner_group_obj: + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + base_currency_id = user.company_id.currency_id.id if inner_groupby[0] == 'company_id': base_currency_id = self.pool.get('res.company').browse(cr, uid, groupby['__domain'][0][2], context=context).currency_id.id elif inner_groupby[0] == 'pricelist_id': base_currency_id = self.pool.get('product.pricelist').browse(cr, uid, groupby['__domain'][0][2], context=context).currency_id.id elif inner_groupby[0] == 'currency_id': base_currency_id = self.pool.get('res.currency.rate').search_read(cr, uid, [('rate', '=', 1)], ['currency_id'], limit=1, context=context)[0]['currency_id'][0] - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) value = self.pool.get('res.currency').compute(cr, uid, base_currency_id, user.company_id.currency_id.id, groupby.get(value_field, 0)) - section_result[month]['value'] = section_result[month]['value'] + value + section_result[month]['value'] += value return section_result def _get_opportunities_data(self, cr, uid, ids, field_name, arg, context=None): @@ -158,7 +159,7 @@ class crm_case_section(osv.osv): for id in ids: res[id] = dict() lead_domain = date_domain + [('type', '=', 'lead'), ('section_id', '=', id)] - res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date','company_id'], 'create_date_count', ['create_date', 'company_id'], type="Lead", context=context) + res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date','company_id'], 'create_date_count', ['create_date', 'company_id'], context=context) opp_domain = date_domain + [('type', '=', 'opportunity'), ('section_id', '=', id)] res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'create_date', 'company_id'], 'planned_revenue', ['create_date', 'company_id'], context=context) return res From adbffd1e14e0a47b847361f01e4533e6df8e491e Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Tue, 15 Oct 2013 16:08:18 +0530 Subject: [PATCH 33/68] [ADD]:sale_crrm: Added:sale_crm/tests/__init__.py sale_crm/tests/test_sale_crm_case.py bzr revid: sunilsharma.sharma07@gmail.com-20131015103818-ai34ff5rbv86wtgl --- addons/sale_crm/tests/__init__.py | 28 +++++ addons/sale_crm/tests/test_sale_crm_case.py | 120 ++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 addons/sale_crm/tests/__init__.py create mode 100644 addons/sale_crm/tests/test_sale_crm_case.py diff --git a/addons/sale_crm/tests/__init__.py b/addons/sale_crm/tests/__init__.py new file mode 100644 index 00000000000..fc04080875f --- /dev/null +++ b/addons/sale_crm/tests/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2013-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from . import test_sale_crm_case + +checks = [ + test_sale_crm_case, +] + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_crm/tests/test_sale_crm_case.py b/addons/sale_crm/tests/test_sale_crm_case.py new file mode 100644 index 00000000000..bfdbee312c7 --- /dev/null +++ b/addons/sale_crm/tests/test_sale_crm_case.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2013-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.addons.mail.tests.common import TestMail +from openerp.osv.orm import except_orm + +class TestSaleCrm(TestMail): + + def setUp(self): + super(TestSaleCrm, self).setUp() + cr, uid = self.cr, self.uid + +class TestSaleCrmCase(TestSaleCrm): + + def test_sale_crm_case(self): + """ Testing Sale Crm """ + + cr, uid, = self.cr, self.uid + + # Usefull models + ir_model_obj = self.registry('ir.model.data') + res_company_obj = self.registry('res.company') + res_users_obj = self.registry('res.users') + crm_case_section_obj = self.registry('crm.case.section') + crm_lead_obj = self.registry('crm.lead') + + # Find Direct Sales + direct_sales_ref = ir_model_obj.get_object_reference(cr, uid, 'crm' , 'section_sales_department') + + # for Opportunities + usd_ref = ir_model_obj.get_object_reference(cr, uid, 'base' , 'USD') + your_company_ref = ir_model_obj.get_object_reference(cr, uid, 'base', 'main_company') + + # Before opportunities + opportunities_before = crm_case_section_obj._get_opportunities_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) + + #I Create company with USD currency. + res_company_id = res_company_obj.create(cr, uid,{ + 'name': 'New Company', + 'currency_id': usd_ref and usd_ref[1] or False, + 'parent_id': your_company_ref and your_company_ref[1] or False + }) + + # Find Sale Manager group + group_sale_manager_id = ir_model_obj.get_object_reference(cr, uid, 'base', 'group_sale_manager')[1] + + #I Create User of new company. + res_users_id = res_users_obj.create(cr, uid,{ + 'name': 'User', + 'login': 'admin@example.com', + 'company_id': res_company_id, + 'company_ids': [(6, 0, [res_company_id])], + 'email': 'admin@gmail.com', + 'groups_id': [(6, 0, [group_sale_manager_id])] + }) + + #I create Opportunitie for new created user + opportunities_id = crm_lead_obj.create(cr, res_users_id,{ + 'name': 'Opportunities', + 'type': 'opportunity', + 'planned_revenue': 10000, + 'user_id' : res_users_id, + 'section_id': direct_sales_ref and direct_sales_ref[1] or False + }) + + user_rate = res_company_obj.browse(cr, uid ,[res_company_id])[0].currency_id.rate_silent + + # After opportunities + opportunities_after = crm_case_section_obj._get_opportunities_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) + + #Assertion Error + self.assertTrue(int(opportunities_after[1]['monthly_planned_revenue'][4]['value']) == int(opportunities_before[1]['monthly_planned_revenue'][4]['value'] + (10000 / user_rate)), "Not Proper Currency Conversion for Opportunities") + + # Before Sale or Quotations + sale_order_before = crm_case_section_obj._get_sale_orders_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) + + # Before Invoice + invoice_before = crm_case_section_obj._get_invoices_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) + + #I Write company . + company_id = res_users_obj._get_company(cr, uid, context=False, uid2=False) + in_ref = ir_model_obj.get_object_reference(cr, uid, 'base' , 'INR') + res_company_obj.write(cr, uid,[company_id], { + 'currency_id': in_ref and in_ref[1] or False + }) + + # After Sale or Quotations + sale_order_after = crm_case_section_obj._get_sale_orders_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) + + # After Invoice + invoice_after = crm_case_section_obj._get_invoices_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) + + new_rate = res_company_obj.browse(cr, uid , [company_id])[0].currency_id.rate_silent + + #Assertion Error + for i in range(0,5): + # for Quotations + self.assertTrue(sale_order_before[1]['monthly_quoted'][i]['value'] * new_rate == sale_order_after[1]['monthly_quoted'][i]['value'], "Not Proper Currency Conversion for Quotations ") + # for sale odrer + self.assertTrue(sale_order_before[1]['monthly_confirmed'][i]['value'] * new_rate == sale_order_after[1]['monthly_confirmed'][i]['value'], "Not Proper Currency Conversion for Sale Order") + # for Invoice + self.assertTrue(invoice_before[1][i]['value'] * new_rate == invoice_after[1][i]['value'], "Not Proper Currency Conversion for Invoice") From e52f18cc9ab6d3cd5229ac650404a1020a439ac2 Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Thu, 17 Oct 2013 12:07:49 +0530 Subject: [PATCH 34/68] [IMP]:sale_crm:add Round function and improve Assertion msg bzr revid: sunilsharma.sharma07@gmail.com-20131017063749-mexdixqszsnwtu4q --- addons/sale_crm/tests/test_sale_crm_case.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/addons/sale_crm/tests/test_sale_crm_case.py b/addons/sale_crm/tests/test_sale_crm_case.py index bfdbee312c7..1171c0759ed 100644 --- a/addons/sale_crm/tests/test_sale_crm_case.py +++ b/addons/sale_crm/tests/test_sale_crm_case.py @@ -87,7 +87,7 @@ class TestSaleCrmCase(TestSaleCrm): opportunities_after = crm_case_section_obj._get_opportunities_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) #Assertion Error - self.assertTrue(int(opportunities_after[1]['monthly_planned_revenue'][4]['value']) == int(opportunities_before[1]['monthly_planned_revenue'][4]['value'] + (10000 / user_rate)), "Not Proper Currency Conversion for Opportunities") + self.assertTrue(round(opportunities_after[1]['monthly_planned_revenue'][4]['value']) == round(opportunities_before[1]['monthly_planned_revenue'][4]['value'] + (10000 / user_rate)), "Not Proper Currency Conversion for Opportunities") # Before Sale or Quotations sale_order_before = crm_case_section_obj._get_sale_orders_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) @@ -113,8 +113,9 @@ class TestSaleCrmCase(TestSaleCrm): #Assertion Error for i in range(0,5): # for Quotations - self.assertTrue(sale_order_before[1]['monthly_quoted'][i]['value'] * new_rate == sale_order_after[1]['monthly_quoted'][i]['value'], "Not Proper Currency Conversion for Quotations ") - # for sale odrer - self.assertTrue(sale_order_before[1]['monthly_confirmed'][i]['value'] * new_rate == sale_order_after[1]['monthly_confirmed'][i]['value'], "Not Proper Currency Conversion for Sale Order") + self.assertTrue(round(sale_order_before[1]['monthly_quoted'][i]['value'] * new_rate) == round(sale_order_after[1]['monthly_quoted'][i]['value']), "Currency Conversion For Quotations Is Wrong") + # for sale order + self.assertTrue(round(sale_order_before[1]['monthly_confirmed'][i]['value'] * new_rate) == round(sale_order_after[1]['monthly_confirmed'][i]['value']), "Currency Conversion For Sale Order Is Wrong") # for Invoice - self.assertTrue(invoice_before[1][i]['value'] * new_rate == invoice_after[1][i]['value'], "Not Proper Currency Conversion for Invoice") + self.assertTrue(round(invoice_before[1][i]['value'] * new_rate) == round(invoice_after[1][i]['value']), "Currency Conversion For Invoice Is Wrong") + From 09789afc2bef4c2c77bd6ebcd722eaa7be4fd40b Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Thu, 17 Oct 2013 12:53:00 +0530 Subject: [PATCH 35/68] [IMP]:sale_crm:improve code standard bzr revid: sunilsharma.sharma07@gmail.com-20131017072300-vt3y65394wv5tl8q --- addons/sale_crm/tests/test_sale_crm_case.py | 23 ++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/addons/sale_crm/tests/test_sale_crm_case.py b/addons/sale_crm/tests/test_sale_crm_case.py index 1171c0759ed..3081b9c08a7 100644 --- a/addons/sale_crm/tests/test_sale_crm_case.py +++ b/addons/sale_crm/tests/test_sale_crm_case.py @@ -43,10 +43,10 @@ class TestSaleCrmCase(TestSaleCrm): crm_lead_obj = self.registry('crm.lead') # Find Direct Sales - direct_sales_ref = ir_model_obj.get_object_reference(cr, uid, 'crm' , 'section_sales_department') + direct_sales_ref = ir_model_obj.get_object_reference(cr, uid, 'crm', 'section_sales_department') # for Opportunities - usd_ref = ir_model_obj.get_object_reference(cr, uid, 'base' , 'USD') + usd_ref = ir_model_obj.get_object_reference(cr, uid, 'base', 'USD') your_company_ref = ir_model_obj.get_object_reference(cr, uid, 'base', 'main_company') # Before opportunities @@ -78,7 +78,7 @@ class TestSaleCrmCase(TestSaleCrm): 'type': 'opportunity', 'planned_revenue': 10000, 'user_id' : res_users_id, - 'section_id': direct_sales_ref and direct_sales_ref[1] or False + 'section_id': direct_sales_ref and direct_sales_ref[1] or False }) user_rate = res_company_obj.browse(cr, uid ,[res_company_id])[0].currency_id.rate_silent @@ -97,9 +97,9 @@ class TestSaleCrmCase(TestSaleCrm): #I Write company . company_id = res_users_obj._get_company(cr, uid, context=False, uid2=False) - in_ref = ir_model_obj.get_object_reference(cr, uid, 'base' , 'INR') - res_company_obj.write(cr, uid,[company_id], { - 'currency_id': in_ref and in_ref[1] or False + inr_ref = ir_model_obj.get_object_reference(cr, uid, 'base', 'INR') + res_company_obj.write(cr, uid, [company_id], { + 'currency_id': inr_ref and inr_ref[1] or False }) # After Sale or Quotations @@ -108,14 +108,13 @@ class TestSaleCrmCase(TestSaleCrm): # After Invoice invoice_after = crm_case_section_obj._get_invoices_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) - new_rate = res_company_obj.browse(cr, uid , [company_id])[0].currency_id.rate_silent + new_rate = res_company_obj.browse(cr, uid, [company_id])[0].currency_id.rate_silent #Assertion Error - for i in range(0,5): + for month in range(0,5): # for Quotations - self.assertTrue(round(sale_order_before[1]['monthly_quoted'][i]['value'] * new_rate) == round(sale_order_after[1]['monthly_quoted'][i]['value']), "Currency Conversion For Quotations Is Wrong") + self.assertTrue(round(sale_order_before[1]['monthly_quoted'][month]['value'] * new_rate) == round(sale_order_after[1]['monthly_quoted'][month]['value']), "Currency Conversion For Quotations Is Wrong") # for sale order - self.assertTrue(round(sale_order_before[1]['monthly_confirmed'][i]['value'] * new_rate) == round(sale_order_after[1]['monthly_confirmed'][i]['value']), "Currency Conversion For Sale Order Is Wrong") + self.assertTrue(round(sale_order_before[1]['monthly_confirmed'][month]['value'] * new_rate) == round(sale_order_after[1]['monthly_confirmed'][month]['value']), "Currency Conversion For Sale Order Is Wrong") # for Invoice - self.assertTrue(round(invoice_before[1][i]['value'] * new_rate) == round(invoice_after[1][i]['value']), "Currency Conversion For Invoice Is Wrong") - + self.assertTrue(round(invoice_before[1][month]['value'] * new_rate) == round(invoice_after[1][month]['value']), "Currency Conversion For Invoice Is Wrong") From b686ee51af6a709722b5a3d2280d827eb65750db Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Thu, 17 Oct 2013 16:14:42 +0530 Subject: [PATCH 36/68] [IMP]:sale_crm:remove setup method and improve msg bzr revid: sunilsharma.sharma07@gmail.com-20131017104442-6kw85h9524grftsw --- addons/sale_crm/tests/test_sale_crm_case.py | 80 +++++++++------------ 1 file changed, 32 insertions(+), 48 deletions(-) diff --git a/addons/sale_crm/tests/test_sale_crm_case.py b/addons/sale_crm/tests/test_sale_crm_case.py index 3081b9c08a7..71cad1aff93 100644 --- a/addons/sale_crm/tests/test_sale_crm_case.py +++ b/addons/sale_crm/tests/test_sale_crm_case.py @@ -20,49 +20,38 @@ ############################################################################## from openerp.addons.mail.tests.common import TestMail -from openerp.osv.orm import except_orm class TestSaleCrm(TestMail): - def setUp(self): - super(TestSaleCrm, self).setUp() - cr, uid = self.cr, self.uid - -class TestSaleCrmCase(TestSaleCrm): - def test_sale_crm_case(self): - """ Testing Sale Crm """ + """Testing __get_bar_values method""" cr, uid, = self.cr, self.uid - # Usefull models + #Usefull models ir_model_obj = self.registry('ir.model.data') res_company_obj = self.registry('res.company') res_users_obj = self.registry('res.users') crm_case_section_obj = self.registry('crm.case.section') crm_lead_obj = self.registry('crm.lead') - # Find Direct Sales - direct_sales_ref = ir_model_obj.get_object_reference(cr, uid, 'crm', 'section_sales_department') + #Get required ids + direct_sales_id = ir_model_obj.get_object_reference(cr, uid, 'crm', 'section_sales_department')[1] + usd_id = ir_model_obj.get_object_reference(cr, uid, 'base', 'USD')[1] + your_company_id = ir_model_obj.get_object_reference(cr, uid, 'base', 'main_company')[1] - # for Opportunities - usd_ref = ir_model_obj.get_object_reference(cr, uid, 'base', 'USD') - your_company_ref = ir_model_obj.get_object_reference(cr, uid, 'base', 'main_company') + #Call _get_opportunities_data method before creating new opportunity in another currency. + opportunities_before = crm_case_section_obj._get_opportunities_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - # Before opportunities - opportunities_before = crm_case_section_obj._get_opportunities_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) - - #I Create company with USD currency. + #Create new company with USD currency. res_company_id = res_company_obj.create(cr, uid,{ 'name': 'New Company', - 'currency_id': usd_ref and usd_ref[1] or False, - 'parent_id': your_company_ref and your_company_ref[1] or False + 'currency_id': usd_id, + 'parent_id': your_company_id }) - # Find Sale Manager group + #Create sales manager of new company. group_sale_manager_id = ir_model_obj.get_object_reference(cr, uid, 'base', 'group_sale_manager')[1] - - #I Create User of new company. res_users_id = res_users_obj.create(cr, uid,{ 'name': 'User', 'login': 'admin@example.com', @@ -72,49 +61,44 @@ class TestSaleCrmCase(TestSaleCrm): 'groups_id': [(6, 0, [group_sale_manager_id])] }) - #I create Opportunitie for new created user + #Create Opportunitie by new created user. opportunities_id = crm_lead_obj.create(cr, res_users_id,{ 'name': 'Opportunities', 'type': 'opportunity', 'planned_revenue': 10000, 'user_id' : res_users_id, - 'section_id': direct_sales_ref and direct_sales_ref[1] or False + 'section_id': direct_sales_id }) - user_rate = res_company_obj.browse(cr, uid ,[res_company_id])[0].currency_id.rate_silent + user_rate = res_company_obj.browse(cr, uid, [res_company_id])[0].currency_id.rate_silent - # After opportunities - opportunities_after = crm_case_section_obj._get_opportunities_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) - - #Assertion Error + #Call _get_opportunities_data method and check Currency Conversion of amount for new created opportunity which was in another currency. + opportunities_after = crm_case_section_obj._get_opportunities_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) self.assertTrue(round(opportunities_after[1]['monthly_planned_revenue'][4]['value']) == round(opportunities_before[1]['monthly_planned_revenue'][4]['value'] + (10000 / user_rate)), "Not Proper Currency Conversion for Opportunities") - # Before Sale or Quotations - sale_order_before = crm_case_section_obj._get_sale_orders_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) + #Call _get_sale_orders_data method before changing currency of user. + sale_order_before = crm_case_section_obj._get_sale_orders_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - # Before Invoice - invoice_before = crm_case_section_obj._get_invoices_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) + #Call _get_invoices_data method before changing currency of user. + invoice_before = crm_case_section_obj._get_invoices_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - #I Write company . + #Change currency of company to INR. company_id = res_users_obj._get_company(cr, uid, context=False, uid2=False) - inr_ref = ir_model_obj.get_object_reference(cr, uid, 'base', 'INR') + inr_id = ir_model_obj.get_object_reference(cr, uid, 'base', 'INR')[1] res_company_obj.write(cr, uid, [company_id], { - 'currency_id': inr_ref and inr_ref[1] or False + 'currency_id': inr_id, }) - # After Sale or Quotations - sale_order_after = crm_case_section_obj._get_sale_orders_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) + #Call _get_sale_orders_data method after changing currency of user. + sale_order_after = crm_case_section_obj._get_sale_orders_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - # After Invoice - invoice_after = crm_case_section_obj._get_invoices_data(cr, uid, [direct_sales_ref[1]], field_name=False, arg=False, context=False) + #Call _get_invoices_data method after changing currency of user. + invoice_after = crm_case_section_obj._get_invoices_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) new_rate = res_company_obj.browse(cr, uid, [company_id])[0].currency_id.rate_silent - #Assertion Error + #Check currency conversion for Quotations,Sale orders and Invoices in to the current users currency for month in range(0,5): - # for Quotations - self.assertTrue(round(sale_order_before[1]['monthly_quoted'][month]['value'] * new_rate) == round(sale_order_after[1]['monthly_quoted'][month]['value']), "Currency Conversion For Quotations Is Wrong") - # for sale order - self.assertTrue(round(sale_order_before[1]['monthly_confirmed'][month]['value'] * new_rate) == round(sale_order_after[1]['monthly_confirmed'][month]['value']), "Currency Conversion For Sale Order Is Wrong") - # for Invoice - self.assertTrue(round(invoice_before[1][month]['value'] * new_rate) == round(invoice_after[1][month]['value']), "Currency Conversion For Invoice Is Wrong") + self.assertTrue(round(sale_order_before[1]['monthly_quoted'][month]['value'] * new_rate) == round(sale_order_after[1]['monthly_quoted'][month]['value']), "Currency conversion for quotations is wrong") + self.assertTrue(round(sale_order_before[1]['monthly_confirmed'][month]['value'] * new_rate) == round(sale_order_after[1]['monthly_confirmed'][month]['value']), "Currency conversion for sale orders is wrong") + self.assertTrue(round(invoice_before[1][month]['value'] * new_rate) == round(invoice_after[1][month]['value']), "Currency conversion for invoices is wrong") From f684f68173a31455d6113ce6a8cdced749233642 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Wed, 23 Oct 2013 11:12:54 +0530 Subject: [PATCH 37/68] [RMV] removed code to convert forcast and target amount in user currency. bzr revid: tpa@tinyerp.com-20131023054254-gw4mjjighbbn1unm --- addons/sale_crm/sale_crm.py | 31 +++++-------------------------- addons/sale_crm/sale_crm_view.xml | 17 +++++++++-------- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index fa8a1f8c467..29aa81a3c35 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -70,35 +70,12 @@ class crm_case_section(osv.osv): res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date', 'currency_id'], 'price_total', ['date', 'currency_id'], context=context) return res - def _compute_amounts_in_user_currency(self, cr, uid, ids, field_names, args, context=None): - """Compute the amounts in the currency of the user """ - res = {} - currency_obj = self.pool.get('res.currency') - user_currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id - for item in self.browse(cr, uid, ids, context=context): - base_currency_id = item.user_id.company_id.currency_id.id if item.user_id else item.create_uid.company_id.currency_id.id - res[item.id] = { - 'user_currency_invoiced_forecast': currency_obj.compute(cr, uid, base_currency_id, user_currency_id, item.invoiced_forecast, context=context), - 'user_currency_invoiced_target': currency_obj.compute(cr, uid, base_currency_id, user_currency_id, item.invoiced_target, context=context), - } - return res - - def _set_forecast_target(self, cr, uid, team_id, name, value, arg, context=None): - user_currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id - team = self.browse(cr, uid, team_id, context=context) - base_currency_id = team.user_id.company_id.currency_id.id if team.user_id else team.create_uid.company_id.currency_id.id - amount = self.pool.get('res.currency').compute(cr, uid, user_currency_id, base_currency_id, value, context=context) - self.write(cr, uid, [team_id], {name.strip("user_currency"): amount}, context=context) - return True - _columns = { - 'invoiced_forecast': fields.integer("Invoice Forecast"), - 'user_currency_invoiced_forecast': fields.function(_compute_amounts_in_user_currency, fnct_inv=_set_forecast_target, string='Invoice Forecast',type='integer', multi="_compute_amounts", + 'invoiced_forecast': fields.integer("Invoice Forecast", help="Forecast of the invoice revenue for the current month. This is the amount the sales \n" "team should invoice this month. It is used to compute the progression ratio \n" " of the current and forecast revenue on the kanban view."), - 'invoiced_target': fields.integer("Invoice Target"), - 'user_currency_invoiced_target': fields.function(_compute_amounts_in_user_currency, fnct_inv=_set_forecast_target, string='Invoice Target',type='integer', multi="_compute_amounts", + 'invoiced_target': fields.integer("Invoice Target", help="Target of invoice revenue for the current month. This is the amount the sales \n" "team estimates to be able to invoice this month."), 'monthly_quoted': fields.function(_get_sale_orders_data, @@ -111,10 +88,12 @@ class crm_case_section(osv.osv): type='string', readonly=True, string='Rate of sent invoices per duration'), 'create_uid': fields.many2one('res.users', 'Create User'), + 'company_id': fields.related('create_uid', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), + 'currency_id': fields.related('company_id', 'currency_id', type='many2one', relation='res.currency', required=True, readonly=True, string='Currency'), } def action_forecast(self, cr, uid, id, value, context=None): - return self.write(cr, uid, [id], {'user_currency_invoiced_forecast': round(float(value))}, context=context) + return self.write(cr, uid, [id], {'invoiced_forecast': round(float(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 2e55ec8fa89..afaa6a3578c 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -248,8 +248,9 @@ - - + + + @@ -265,8 +266,8 @@ - - + +
@@ -297,11 +298,11 @@
-
- Invoiced - Forecast +
+ Invoiced + Forecast
-
+

Define an invoicing target in the sales team settings to see the period's achievement and forecast at a glance.
From 44eaa430cc780f72f21ab62d658ce24a07d57cd0 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Wed, 23 Oct 2013 11:40:53 +0530 Subject: [PATCH 38/68] [IMP] add direct currency_id field insted of related field. bzr revid: tpa@tinyerp.com-20131023061053-3avp9mp31v7vl9o6 --- addons/crm/crm.py | 4 +++- addons/sale_crm/sale_crm.py | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 8f0e6a82591..fe67db23aed 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -200,7 +200,8 @@ class crm_case_section(osv.osv): 'monthly_planned_revenue': fields.function(_get_opportunities_data, type="string", readonly=True, multi='_get_opportunities_data', string='Planned Revenue per Month'), - 'currency_symbol': fields.function(_get_currency_symbol, string='Currency Symbol', method=True, type='char'), + 'currency_id': fields.many2one('res.currency', 'Currency', required=True), + 'currency_symbol': fields.function(_get_currency_symbol, string="Current User's Currency Symbol", method=True, type='char'), } def _get_stage_common(self, cr, uid, context): @@ -211,6 +212,7 @@ class crm_case_section(osv.osv): 'active': 1, 'stage_ids': _get_stage_common, 'use_leads': True, + 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id, } _sql_constraints = [ diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index 29aa81a3c35..7287e7dc1a8 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -88,8 +88,6 @@ class crm_case_section(osv.osv): type='string', readonly=True, string='Rate of sent invoices per duration'), 'create_uid': fields.many2one('res.users', 'Create User'), - 'company_id': fields.related('create_uid', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), - 'currency_id': fields.related('company_id', 'currency_id', type='many2one', relation='res.currency', required=True, readonly=True, string='Currency'), } def action_forecast(self, cr, uid, id, value, context=None): From 4e0a8f917378f5fc18b56868bc8597b4c3dd7a13 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Wed, 23 Oct 2013 12:14:55 +0530 Subject: [PATCH 39/68] [IMP] removed unused create_uid field. bzr revid: tpa@tinyerp.com-20131023064455-ge7pxrmz1qskh6jf --- addons/sale_crm/sale_crm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index 7287e7dc1a8..67a7c9d39f9 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -87,7 +87,6 @@ class crm_case_section(osv.osv): 'monthly_invoiced': fields.function(_get_invoices_data, type='string', readonly=True, string='Rate of sent invoices per duration'), - 'create_uid': fields.many2one('res.users', 'Create User'), } def action_forecast(self, cr, uid, id, value, context=None): From cabc3bbece5646d11048e2a34b676fd80515091f Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Wed, 23 Oct 2013 15:20:00 +0530 Subject: [PATCH 40/68] [IMP] added new _currency_conversation method. bzr revid: tpa@tinyerp.com-20131023095000-082hgraqi6i7h3o6 --- addons/crm/crm.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index fe67db23aed..f0b4ffeb0cd 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -100,6 +100,19 @@ class crm_case_section(osv.osv): def get_full_name(self, cr, uid, ids, field_name, arg, context=None): return dict(self.name_get(cr, uid, ids, context=context)) + def _currency_conversation(self, cr, uid, amount, relation_id, relation_field, context=None): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + base_currency_id = user.company_id.currency_id.id + if relation_field == 'company_id': + base_currency_id = self.pool.get('res.company').browse(cr, uid, relation_id, context=context).currency_id.id + elif relation_field == 'pricelist_id': + base_currency_id = self.pool.get('product.pricelist').browse(cr, uid, relation_id, context=context).currency_id.id + elif relation_field == 'currency_id': + currency_rates = self.pool.get('res.currency.rate').search_read(cr, uid, [('rate', '=', 1)], ['currency_id'], limit=1, context=context) + if currency_rates: + base_currency_id = currency_rates[0]['currency_id'][0] + return self.pool.get('res.currency').compute(cr, uid, base_currency_id, user.company_id.currency_id.id, amount, context=context) + def __get_bar_values(self, cr, uid, obj, domain, read_fields, value_field, groupby_field, context=None): """ Generic method to generate data for bar chart values using SparklineBarWidget. This method performs obj.read_group(cr, uid, domain, read_fields, groupby_field). @@ -133,16 +146,7 @@ class crm_case_section(osv.osv): if inner_groupby: inner_group_obj = obj.read_group(cr, uid, group.get('__domain'), read_fields, inner_groupby, context=context) for groupby in inner_group_obj: - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - base_currency_id = user.company_id.currency_id.id - if inner_groupby[0] == 'company_id': - base_currency_id = self.pool.get('res.company').browse(cr, uid, groupby['__domain'][0][2], context=context).currency_id.id - elif inner_groupby[0] == 'pricelist_id': - base_currency_id = self.pool.get('product.pricelist').browse(cr, uid, groupby['__domain'][0][2], context=context).currency_id.id - elif inner_groupby[0] == 'currency_id': - base_currency_id = self.pool.get('res.currency.rate').search_read(cr, uid, [('rate', '=', 1)], ['currency_id'], limit=1, context=context)[0]['currency_id'][0] - value = self.pool.get('res.currency').compute(cr, uid, base_currency_id, user.company_id.currency_id.id, groupby.get(value_field, 0)) - section_result[month]['value'] += value + section_result[month]['value'] += self._currency_conversation(cr, uid, groupby.get(value_field, 0), groupby['__domain'][0][2],inner_groupby[0], context=context) return section_result def _get_opportunities_data(self, cr, uid, ids, field_name, arg, context=None): From 416c47f3244ee6c01cd9f5a7406879fe9c99ec69 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Wed, 23 Oct 2013 17:06:52 +0530 Subject: [PATCH 41/68] [FIX] replace integer with float in validation bzr revid: tpa@tinyerp.com-20131023113652-rj74krv1lecyp2xf --- addons/web_kanban_gauge/static/src/js/kanban_gauge.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web_kanban_gauge/static/src/js/kanban_gauge.js b/addons/web_kanban_gauge/static/src/js/kanban_gauge.js index 60cbdf1ddd8..0d8a21ef638 100644 --- a/addons/web_kanban_gauge/static/src/js/kanban_gauge.js +++ b/addons/web_kanban_gauge/static/src/js/kanban_gauge.js @@ -121,7 +121,7 @@ instance.web_kanban.GaugeWidget = instance.web_kanban.AbstractField.extend({ }, parse_client: function(value) { - return openerp.web.parse_value(value, { type:"integer" }); + return openerp.web.parse_value(value, { type:"float" }); }, }); From f48db35788352e1dc53646739ad75f50a0537730 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Wed, 23 Oct 2013 17:07:42 +0530 Subject: [PATCH 42/68] [FIX] sale_crmconvert target and forcat fields to float from int bzr revid: tpa@tinyerp.com-20131023113742-55szrqznr1imequi --- addons/sale_crm/sale_crm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index 67a7c9d39f9..2e7d61a6487 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -71,11 +71,11 @@ class crm_case_section(osv.osv): return res _columns = { - 'invoiced_forecast': fields.integer("Invoice Forecast", + 'invoiced_forecast': fields.float("Invoice Forecast", help="Forecast of the invoice revenue for the current month. This is the amount the sales \n" "team should invoice this month. It is used to compute the progression ratio \n" " of the current and forecast revenue on the kanban view."), - 'invoiced_target': fields.integer("Invoice Target", + 'invoiced_target': fields.float("Invoice Target", help="Target of invoice revenue for the current month. This is the amount the sales \n" "team estimates to be able to invoice this month."), 'monthly_quoted': fields.function(_get_sale_orders_data, @@ -90,7 +90,7 @@ class crm_case_section(osv.osv): } def action_forecast(self, cr, uid, id, value, context=None): - return self.write(cr, uid, [id], {'invoiced_forecast': round(float(value))}, context=context) + return self.write(cr, uid, [id], {'invoiced_forecast': value}, context=context) class res_users(osv.Model): _inherit = 'res.users' From 29214f08942c6cf44810f48f3a1d6497b147b6b1 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Fri, 25 Oct 2013 15:15:12 +0530 Subject: [PATCH 43/68] [IMP] improved code. bzr revid: tpa@tinyerp.com-20131025094512-m0mx0a0s94h1ldjg --- addons/crm/crm.py | 8 +------- addons/sale_crm/sale_crm.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index f0b4ffeb0cd..9e1a9abfe5e 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -105,12 +105,6 @@ class crm_case_section(osv.osv): base_currency_id = user.company_id.currency_id.id if relation_field == 'company_id': base_currency_id = self.pool.get('res.company').browse(cr, uid, relation_id, context=context).currency_id.id - elif relation_field == 'pricelist_id': - base_currency_id = self.pool.get('product.pricelist').browse(cr, uid, relation_id, context=context).currency_id.id - elif relation_field == 'currency_id': - currency_rates = self.pool.get('res.currency.rate').search_read(cr, uid, [('rate', '=', 1)], ['currency_id'], limit=1, context=context) - if currency_rates: - base_currency_id = currency_rates[0]['currency_id'][0] return self.pool.get('res.currency').compute(cr, uid, base_currency_id, user.company_id.currency_id.id, amount, context=context) def __get_bar_values(self, cr, uid, obj, domain, read_fields, value_field, groupby_field, context=None): @@ -146,7 +140,7 @@ class crm_case_section(osv.osv): if inner_groupby: inner_group_obj = obj.read_group(cr, uid, group.get('__domain'), read_fields, inner_groupby, context=context) for groupby in inner_group_obj: - section_result[month]['value'] += self._currency_conversation(cr, uid, groupby.get(value_field, 0), groupby['__domain'][0][2],inner_groupby[0], context=context) + section_result[month]['value'] += self._currency_conversation(cr, uid, groupby.get(value_field, 0), groupby['__domain'][0][2], inner_groupby[0], context=context) return section_result def _get_opportunities_data(self, cr, uid, ids, field_name, arg, context=None): diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index 2e7d61a6487..26b2aa05b94 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -45,6 +45,19 @@ class sale_order(osv.osv): class crm_case_section(osv.osv): _inherit = 'crm.case.section' + def _currency_conversation(self, cr, uid, amount, relation_id, relation_field, context=None): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + base_currency_id = user.company_id.currency_id.id + if relation_field not in ['pricelist_id', 'currency_id']: + return super(crm_case_section, self)._currency_conversation(cr, uid, amount, relation_id, relation_field, context=context) + if relation_field == 'pricelist_id': + base_currency_id = self.pool.get('product.pricelist').browse(cr, uid, relation_id, context=context).currency_id.id + elif relation_field == 'currency_id': + currency_rates = self.pool.get('res.currency.rate').search_read(cr, uid, [('rate', '=', 1)], ['currency_id'], limit=1, context=context) + if currency_rates: + base_currency_id = currency_rates[0]['currency_id'][0] + return self.pool.get('res.currency').compute(cr, uid, base_currency_id, user.company_id.currency_id.id, amount, context=context) + def _get_sale_orders_data(self, cr, uid, ids, field_name, arg, context=None): obj = self.pool.get('sale.order') res = dict.fromkeys(ids, False) From c05369d2b282cfad15182fe6bd16ba12b3e80366 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Tue, 29 Oct 2013 16:52:01 +0530 Subject: [PATCH 44/68] [ADD] added onchange on manger to change currency_id bzr revid: tpa@tinyerp.com-20131029112201-fs1us3vi4bhofplx --- addons/crm/crm.py | 8 ++++++++ addons/crm/crm_case_section_view.xml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 9e1a9abfe5e..f01c023329a 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -198,6 +198,7 @@ class crm_case_section(osv.osv): 'monthly_planned_revenue': fields.function(_get_opportunities_data, type="string", readonly=True, multi='_get_opportunities_data', string='Planned Revenue per Month'), + 'create_uid': fields.many2one('res.users', 'Create User'), 'currency_id': fields.many2one('res.currency', 'Currency', required=True), 'currency_symbol': fields.function(_get_currency_symbol, string="Current User's Currency Symbol", method=True, type='char'), } @@ -254,6 +255,13 @@ class crm_case_section(osv.osv): mail_alias.unlink(cr, uid, alias_ids, context=context) return res + def onchange_user(self, cr, uid, ids, user_id, context=None): + if user_id: + currency_id = self.pool.get('res.users').browse(cr, uid, user_id, context=context).company_id.currency_id.id + else: + currency_id = self.browse(cr, uid, ids[0], context=context).create_uid.company_id.currency_id.id + return {'value': {'currency_id': currency_id}} + class crm_case_categ(osv.osv): """ Category of Case """ _name = "crm.case.categ" diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml index 7bc2b914e2c..4b3c01e3bbf 100644 --- a/addons/crm/crm_case_section_view.xml +++ b/addons/crm/crm_case_section_view.xml @@ -219,7 +219,7 @@
- + From ecdb4abbd1d43922fff11b028b17dbd2afde22ed Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Tue, 29 Oct 2013 18:21:28 +0530 Subject: [PATCH 45/68] [IMP] improved code to show all amounts in sales teams currency instead on current users currency bzr revid: tpa@tinyerp.com-20131029125128-9n4myfklstueqq0v --- addons/crm/crm.py | 25 +++++++++---------------- addons/sale_crm/sale_crm.py | 16 ++++++++-------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index f01c023329a..3db95cee3ba 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -100,14 +100,14 @@ class crm_case_section(osv.osv): def get_full_name(self, cr, uid, ids, field_name, arg, context=None): return dict(self.name_get(cr, uid, ids, context=context)) - def _currency_conversation(self, cr, uid, amount, relation_id, relation_field, context=None): - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - base_currency_id = user.company_id.currency_id.id + def _currency_conversation(self, cr, uid, id, amount, relation_id, relation_field, context=None): + team = self.browse(cr, uid, id, context=context) + base_currency_id = team.currency_id.id if relation_field == 'company_id': base_currency_id = self.pool.get('res.company').browse(cr, uid, relation_id, context=context).currency_id.id - return self.pool.get('res.currency').compute(cr, uid, base_currency_id, user.company_id.currency_id.id, amount, context=context) + return self.pool.get('res.currency').compute(cr, uid, base_currency_id, team.currency_id.id, amount, context=context) - def __get_bar_values(self, cr, uid, obj, domain, read_fields, value_field, groupby_field, context=None): + def __get_bar_values(self, cr, uid, id, obj, domain, read_fields, value_field, groupby_field, context=None): """ Generic method to generate data for bar chart values using SparklineBarWidget. This method performs obj.read_group(cr, uid, domain, read_fields, groupby_field). @@ -140,7 +140,7 @@ class crm_case_section(osv.osv): if inner_groupby: inner_group_obj = obj.read_group(cr, uid, group.get('__domain'), read_fields, inner_groupby, context=context) for groupby in inner_group_obj: - section_result[month]['value'] += self._currency_conversation(cr, uid, groupby.get(value_field, 0), groupby['__domain'][0][2], inner_groupby[0], context=context) + section_result[month]['value'] += self._currency_conversation(cr, uid, id, groupby.get(value_field, 0), groupby['__domain'][0][2], inner_groupby[0], context=context) return section_result def _get_opportunities_data(self, cr, uid, ids, field_name, arg, context=None): @@ -157,16 +157,9 @@ class crm_case_section(osv.osv): for id in ids: res[id] = dict() lead_domain = date_domain + [('type', '=', 'lead'), ('section_id', '=', id)] - res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date','company_id'], 'create_date_count', ['create_date', 'company_id'], context=context) + res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, id, obj, lead_domain, ['create_date','company_id'], 'create_date_count', ['create_date', 'company_id'], context=context) opp_domain = date_domain + [('type', '=', 'opportunity'), ('section_id', '=', id)] - res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'create_date', 'company_id'], 'planned_revenue', ['create_date', 'company_id'], context=context) - return res - - def _get_currency_symbol(self, cr, uid, ids, name, arg, context=None): - """Set currency symbole of company currency""" - res = {} - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - res = dict.fromkeys(ids, user.company_id.currency_id.symbol) + res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, id, obj, opp_domain, ['planned_revenue', 'create_date', 'company_id'], 'planned_revenue', ['create_date', 'company_id'], context=context) return res _columns = { @@ -200,7 +193,7 @@ class crm_case_section(osv.osv): string='Planned Revenue per Month'), 'create_uid': fields.many2one('res.users', 'Create User'), 'currency_id': fields.many2one('res.currency', 'Currency', required=True), - 'currency_symbol': fields.function(_get_currency_symbol, string="Current User's Currency Symbol", method=True, type='char'), + 'currency_symbol': fields.related('currency_id', 'symbol', type='char', string='Currency Symbol'), } def _get_stage_common(self, cr, uid, context): diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index 26b2aa05b94..1fedc6a9945 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -45,18 +45,18 @@ class sale_order(osv.osv): class crm_case_section(osv.osv): _inherit = 'crm.case.section' - def _currency_conversation(self, cr, uid, amount, relation_id, relation_field, context=None): - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - base_currency_id = user.company_id.currency_id.id + def _currency_conversation(self, cr, uid, id, amount, relation_id, relation_field, context=None): + team = self.browse(cr, uid, id, context=context) + base_currency_id = team.currency_id.id if relation_field not in ['pricelist_id', 'currency_id']: - return super(crm_case_section, self)._currency_conversation(cr, uid, amount, relation_id, relation_field, context=context) + return super(crm_case_section, self)._currency_conversation(cr, uid, id, amount, relation_id, relation_field, context=context) if relation_field == 'pricelist_id': base_currency_id = self.pool.get('product.pricelist').browse(cr, uid, relation_id, context=context).currency_id.id elif relation_field == 'currency_id': currency_rates = self.pool.get('res.currency.rate').search_read(cr, uid, [('rate', '=', 1)], ['currency_id'], limit=1, context=context) if currency_rates: base_currency_id = currency_rates[0]['currency_id'][0] - return self.pool.get('res.currency').compute(cr, uid, base_currency_id, user.company_id.currency_id.id, amount, context=context) + return self.pool.get('res.currency').compute(cr, uid, base_currency_id, team.currency_id.id, amount, context=context) def _get_sale_orders_data(self, cr, uid, ids, field_name, arg, context=None): obj = self.pool.get('sale.order') @@ -67,9 +67,9 @@ class crm_case_section(osv.osv): for id in ids: res[id] = dict() created_domain = [('section_id', '=', id), ('state', 'in', ['draft', 'sent']), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)] - res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order', 'pricelist_id'], 'amount_total', ['date_order', 'pricelist_id'], context=context) + res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, id, obj, created_domain, ['amount_total', 'date_order', 'pricelist_id'], 'amount_total', ['date_order', 'pricelist_id'], context=context) validated_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'sent']), ('date_confirm', '>=', date_begin), ('date_order', '<=', date_end)] - res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_confirm', 'pricelist_id'], 'amount_total', ['date_confirm', 'pricelist_id'], context=context) + res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, id, obj, validated_domain, ['amount_total', 'date_confirm', 'pricelist_id'], 'amount_total', ['date_confirm', 'pricelist_id'], context=context) return res def _get_invoices_data(self, cr, uid, ids, field_name, arg, context=None): @@ -80,7 +80,7 @@ class crm_case_section(osv.osv): date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT) for id in ids: created_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', date_begin), ('date', '<=', date_end)] - res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date', 'currency_id'], 'price_total', ['date', 'currency_id'], context=context) + res[id] = self.__get_bar_values(cr, uid, id, obj, created_domain, ['price_total', 'date', 'currency_id'], 'price_total', ['date', 'currency_id'], context=context) return res _columns = { From 850593c39ad51066b30c92a0e68a2d7ddd26af73 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Wed, 30 Oct 2013 12:16:09 +0530 Subject: [PATCH 46/68] [IMP] improved test cases related to changes of currency conversation to show amounts in teams curreny instead current users currency bzr revid: tpa@tinyerp.com-20131030064609-rzkoul8d44cp93dv --- addons/sale_crm/tests/test_sale_crm_case.py | 46 ++++++--------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/addons/sale_crm/tests/test_sale_crm_case.py b/addons/sale_crm/tests/test_sale_crm_case.py index 55aea92ce41..6fb7d4c555a 100644 --- a/addons/sale_crm/tests/test_sale_crm_case.py +++ b/addons/sale_crm/tests/test_sale_crm_case.py @@ -39,8 +39,10 @@ class TestSaleCrm(TestMail): usd_id = ir_model_obj.get_object_reference(cr, uid, 'base', 'USD')[1] your_company_id = ir_model_obj.get_object_reference(cr, uid, 'base', 'main_company')[1] - # Call _get_opportunities_data method before creating new opportunity in another currency. + # Call relevant methods before changing manager of sales team. opportunities_before = crm_case_section_obj._get_opportunities_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) + sale_order_before = crm_case_section_obj._get_sale_orders_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) + invoice_before = crm_case_section_obj._get_invoices_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) # Create new company with USD currency. res_company_id = res_company_obj.create(cr, uid,{ @@ -49,7 +51,7 @@ class TestSaleCrm(TestMail): 'parent_id': your_company_id }) - # Create sales manager of new company. + # Create new user with sales manager access rights for new company. group_sale_manager_id = ir_model_obj.get_object_reference(cr, uid, 'base', 'group_sale_manager')[1] res_users_id = res_users_obj.create(cr, uid,{ 'name': 'User', @@ -60,44 +62,22 @@ class TestSaleCrm(TestMail): 'groups_id': [(6, 0, [group_sale_manager_id])] }) - # Create Opportunitie by new created user. - opportunities_id = crm_lead_obj.create(cr, res_users_id,{ - 'name': 'Opportunities', - 'type': 'opportunity', - 'planned_revenue': 10000, + # Change manager of sales team with USD currency. + crm_case_section_obj.write(cr, uid, [direct_sales_id], { 'user_id' : res_users_id, - 'section_id': direct_sales_id + 'currency_id': usd_id, }) - user_rate = res_company_obj.browse(cr, uid, [res_company_id])[0].currency_id.rate_silent - - # Call _get_opportunities_data method and check Currency Conversion of amount for new created opportunity which was in another currency. + # Call relevant methods and get converted data in another currency. opportunities_after = crm_case_section_obj._get_opportunities_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - self.assertTrue(round(opportunities_after[1]['monthly_planned_revenue'][4]['value']) == round(opportunities_before[1]['monthly_planned_revenue'][4]['value'] + (10000 / user_rate)), "Not Proper Currency Conversion for Opportunities") - - # Call _get_sale_orders_data method before changing currency of user. - sale_order_before = crm_case_section_obj._get_sale_orders_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - - # Call _get_invoices_data method before changing currency of user. - invoice_before = crm_case_section_obj._get_invoices_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - - # Change currency of company to INR. - company_id = res_users_obj._get_company(cr, uid, context=False, uid2=False) - inr_id = ir_model_obj.get_object_reference(cr, uid, 'base', 'INR')[1] - res_company_obj.write(cr, uid, [company_id], { - 'currency_id': inr_id, - }) - - # Call _get_sale_orders_data method after changing currency of user. sale_order_after = crm_case_section_obj._get_sale_orders_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - - # Call _get_invoices_data method after changing currency of user. invoice_after = crm_case_section_obj._get_invoices_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - new_rate = res_company_obj.browse(cr, uid, [company_id])[0].currency_id.rate_silent + team_currency_rate = crm_case_section_obj.browse(cr, uid, direct_sales_id).currency_id.rate_silent # Check currency conversion for Quotations,Sale orders and Invoices in to the current users currency for month in range(0,5): - self.assertTrue(round(sale_order_before[1]['monthly_quoted'][month]['value'] * new_rate) == round(sale_order_after[1]['monthly_quoted'][month]['value']), "Currency conversion for quotations is wrong") - self.assertTrue(round(sale_order_before[1]['monthly_confirmed'][month]['value'] * new_rate) == round(sale_order_after[1]['monthly_confirmed'][month]['value']), "Currency conversion for sale orders is wrong") - self.assertTrue(round(invoice_before[1][month]['value'] * new_rate) == round(invoice_after[1][month]['value']), "Currency conversion for invoices is wrong") + self.assertTrue(round(opportunities_before[1]['monthly_planned_revenue'][month]['value']) == round(opportunities_after[1]['monthly_planned_revenue'][month]['value'] / team_currency_rate), "Not Proper Currency Conversion for Opportunities") + self.assertTrue(round(sale_order_before[1]['monthly_quoted'][month]['value']) == round(sale_order_after[1]['monthly_quoted'][month]['value'] / team_currency_rate), "Currency conversion for quotations is wrong") + self.assertTrue(round(sale_order_before[1]['monthly_confirmed'][month]['value']) == round(sale_order_after[1]['monthly_confirmed'][month]['value'] / team_currency_rate), "Currency conversion for sale orders is wrong") + self.assertTrue(round(invoice_before[1][month]['value']) == round(invoice_after[1][month]['value'] / team_currency_rate), "Currency conversion for invoices is wrong") From 72ffc9a842ce97f5acff4f008fc5753706041f17 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Thu, 31 Oct 2013 12:47:28 +0530 Subject: [PATCH 47/68] [IMP] improved css. bzr revid: tpa@tinyerp.com-20131031071728-1n22alzz56ud0wl1 --- addons/crm/static/src/css/crm.css | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css index 09d8bb30229..ebd83861538 100644 --- a/addons/crm/static/src/css/crm.css +++ b/addons/crm/static/src/css/crm.css @@ -23,6 +23,7 @@ .openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list { position: relative; margin: 10px; + min-height: 10px; } .openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_items_list div { width: 160px; From 05266e0a0f5382c17ec3c0a2aa8d5d8d7cdd4fac Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Fri, 1 Nov 2013 14:32:22 +0530 Subject: [PATCH 48/68] [ADD] show avtars of members in sales team kanban if only crm is installed. bzr revid: tpa@tinyerp.com-20131101090222-f0ewbmvc4kc2vqap --- addons/crm/crm_case_section_view.xml | 5 +++ addons/crm/static/src/css/crm.css | 7 ++-- addons/crm/static/src/js/crm_case_section.js | 36 ++++++++++++++++++++ addons/sale_crm/sale_crm_view.xml | 2 +- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml index 4b3c01e3bbf..e032376cb05 100644 --- a/addons/crm/crm_case_section_view.xml +++ b/addons/crm/crm_case_section_view.xml @@ -151,6 +151,11 @@ Planned Revenue per Month
Click to see a detailed analysis of opportunities.
+
+ + + +
diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css index ebd83861538..311727fe51d 100644 --- a/addons/crm/static/src/css/crm.css +++ b/addons/crm/static/src/css/crm.css @@ -2,12 +2,11 @@ width: 345px; cursor: default; } -.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_avatars { - text-align: right; - margin: -5px 0 -10px 0; +.openerp .oe_kanban_view .oe_kanban_salesteams_avatars { + margin-top: 20px; } -.openerp .oe_kanban_view .oe_kanban_crm_salesteams .oe_avatars img { +.openerp .oe_kanban_view .oe_kanban_salesteams_avatars img { width: 30px; height: 30px; padding-left: 0px; diff --git a/addons/crm/static/src/js/crm_case_section.js b/addons/crm/static/src/js/crm_case_section.js index e027313d6dc..02f42288880 100644 --- a/addons/crm/static/src/js/crm_case_section.js +++ b/addons/crm/static/src/js/crm_case_section.js @@ -1,4 +1,40 @@ openerp.crm = function(openerp) { + openerp.web_kanban.KanbanView.include({ + crm_display_members_names: function() { + /* + * Set avatar title for members. + * In kanban views, many2many fields only return a list of ids. + * We can implement return value of m2m fields like [(1,"Adminstration"),...]. + */ + var self = this; + var members_ids = []; + + // Collect members ids + self.$el.find('img[data-member_id]').each(function() { + members_ids.push($(this).data('member_id')); + }); + + // Find their matching names + var dataset = new openerp.web.DataSetSearch(self, 'res.users', self.session.context, [['id', 'in', _.uniq(members_ids)]]); + dataset.read_slice(['id', 'name']).done(function(result) { + _.each(result, function(v, k) { + // Set the proper value in the DOM + self.$el.find('img[data-member_id=' + v.id + ']').attr('title', v.name).tipsy({ + offset: 10 + }); + }); + }); + }, + on_groups_started: function() { + var self = this; + self._super.apply(self, arguments); + + if (self.dataset.model === 'crm.case.section') { + self.crm_display_members_names(); + } + }, + }); + openerp.web_kanban.KanbanRecord.include({ on_card_clicked: function() { if (this.view.dataset.model === 'crm.case.section') { diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index afaa6a3578c..8d0d6b1d817 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -297,7 +297,7 @@
- +
Invoiced Forecast From a254e5fca9b5b436dbc5f1207ea64585bd6c96e2 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Fri, 15 Nov 2013 16:19:09 +0530 Subject: [PATCH 49/68] [ADD] added record rules for analysis report objects. bzr revid: tpa@tinyerp.com-20131115104909-izjhuoy3eyy1vfjk --- addons/crm/security/crm_security.xml | 14 ++++++++++++++ addons/sale/security/sale_security.xml | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/addons/crm/security/crm_security.xml b/addons/crm/security/crm_security.xml index ef7eb8b24db..efc335391f8 100644 --- a/addons/crm/security/crm_security.xml +++ b/addons/crm/security/crm_security.xml @@ -91,5 +91,19 @@ + + Personal Leads Analysis + + ['|',('user_id','=',user.id),('user_id','=',False)] + + + + + All Leads Analysis + + [(1,'=',1)] + + + diff --git a/addons/sale/security/sale_security.xml b/addons/sale/security/sale_security.xml index 557562275d7..c34f29fc81b 100644 --- a/addons/sale/security/sale_security.xml +++ b/addons/sale/security/sale_security.xml @@ -78,6 +78,13 @@ ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + Sales Order Analysis multi-company + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + @@ -93,6 +100,19 @@ + + Personal Orders Analysis + + ['|',('user_id','=',user.id),('user_id','=',False)] + + + + + All Orders Analysis + + [(1,'=',1)] + + Personal Order Lines From a68dbfe28693fc732eb05eacf6175e0608d8f7d9 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Tue, 26 Nov 2013 12:12:38 +0530 Subject: [PATCH 50/68] [RMV] remove all currecny related stuff as per the comments of fp sir. bzr revid: tpa@tinyerp.com-20131126064238-odztahk0kofak3vh --- addons/crm/crm.py | 38 ++-------- addons/crm/crm_case_section_view.xml | 5 +- addons/crm/static/src/css/crm.css | 11 --- addons/sale_crm/sale_crm.py | 26 ++----- addons/sale_crm/sale_crm_view.xml | 11 ++- addons/sale_crm/tests/__init__.py | 28 ------- addons/sale_crm/tests/test_sale_crm_case.py | 83 --------------------- 7 files changed, 20 insertions(+), 182 deletions(-) delete mode 100644 addons/sale_crm/tests/__init__.py delete mode 100644 addons/sale_crm/tests/test_sale_crm_case.py diff --git a/addons/crm/crm.py b/addons/crm/crm.py index cddd2a166de..a98fcadb959 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -101,14 +101,7 @@ class crm_case_section(osv.osv): def get_full_name(self, cr, uid, ids, field_name, arg, context=None): return dict(self.name_get(cr, uid, ids, context=context)) - def _currency_conversation(self, cr, uid, id, amount, relation_id, relation_field, context=None): - team = self.browse(cr, uid, id, context=context) - base_currency_id = team.currency_id.id - if relation_field == 'company_id': - base_currency_id = self.pool.get('res.company').browse(cr, uid, relation_id, context=context).currency_id.id - return self.pool.get('res.currency').compute(cr, uid, base_currency_id, team.currency_id.id, amount, context=context) - - def __get_bar_values(self, cr, uid, id, obj, domain, read_fields, value_field, groupby_field, context=None): + def __get_bar_values(self, cr, uid, obj, domain, read_fields, value_field, groupby_field, context=None): """ Generic method to generate data for bar chart values using SparklineBarWidget. This method performs obj.read_group(cr, uid, domain, read_fields, groupby_field). @@ -132,16 +125,8 @@ class crm_case_section(osv.osv): group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context) for group in group_obj: group_begin_date = datetime.strptime(group['__domain'][0][2], tools.DEFAULT_SERVER_DATE_FORMAT) - month = self._period_number - (relativedelta.relativedelta(month_begin, group_begin_date).months + 1) - section_result[month] = {'value': 0, 'tooltip': group_begin_date.strftime('%B')} - inner_groupby = (group.get('__context', {})).get('group_by',[]) - if value_field == 'create_date_count' or not inner_groupby: - section_result[month]['value'] = group.get(value_field, 0) - continue - if inner_groupby: - inner_group_obj = obj.read_group(cr, uid, group.get('__domain'), read_fields, inner_groupby, context=context) - for groupby in inner_group_obj: - section_result[month]['value'] += self._currency_conversation(cr, uid, id, groupby.get(value_field, 0), groupby['__domain'][0][2], inner_groupby[0], context=context) + month_delta = relativedelta.relativedelta(month_begin, group_begin_date) + section_result[self._period_number - (month_delta.months + 1)] = {'value': group.get(value_field, 0), 'tooltip': group_begin_date.strftime('%B')} return section_result def _get_opportunities_data(self, cr, uid, ids, field_name, arg, context=None): @@ -158,9 +143,9 @@ class crm_case_section(osv.osv): for id in ids: res[id] = dict() lead_domain = date_domain + [('type', '=', 'lead'), ('section_id', '=', id)] - res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, id, obj, lead_domain, ['create_date','company_id'], 'create_date_count', ['create_date', 'company_id'], context=context) + res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date'], 'create_date_count', 'create_date', context=context) opp_domain = date_domain + [('type', '=', 'opportunity'), ('section_id', '=', id)] - res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, id, obj, opp_domain, ['planned_revenue', 'create_date', 'company_id'], 'planned_revenue', ['create_date', 'company_id'], context=context) + res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'create_date'], 'planned_revenue', 'create_date', context=context) return res _columns = { @@ -191,10 +176,7 @@ class crm_case_section(osv.osv): string='Open Leads per Month'), 'monthly_planned_revenue': fields.function(_get_opportunities_data, type="string", readonly=True, multi='_get_opportunities_data', - string='Planned Revenue per Month'), - 'create_uid': fields.many2one('res.users', 'Create User'), - 'currency_id': fields.many2one('res.currency', 'Currency', required=True), - 'currency_symbol': fields.related('currency_id', 'symbol', type='char', string='Currency Symbol'), + string='Planned Revenue per Month') } def _get_stage_common(self, cr, uid, context): @@ -205,7 +187,6 @@ class crm_case_section(osv.osv): 'active': 1, 'stage_ids': _get_stage_common, 'use_leads': True, - 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id, } _sql_constraints = [ @@ -249,13 +230,6 @@ class crm_case_section(osv.osv): mail_alias.unlink(cr, uid, alias_ids, context=context) return res - def onchange_user(self, cr, uid, ids, user_id, context=None): - if user_id: - currency_id = self.pool.get('res.users').browse(cr, uid, user_id, context=context).company_id.currency_id.id - else: - currency_id = self.browse(cr, uid, ids[0], context=context).create_uid.company_id.currency_id.id - return {'value': {'currency_id': currency_id}} - class crm_case_categ(osv.osv): """ Category of Case """ _name = "crm.case.categ" diff --git a/addons/crm/crm_case_section_view.xml b/addons/crm/crm_case_section_view.xml index e032376cb05..b674bf95061 100644 --- a/addons/crm/crm_case_section_view.xml +++ b/addons/crm/crm_case_section_view.xml @@ -124,7 +124,6 @@ -
@@ -148,7 +147,7 @@
@@ -224,7 +223,7 @@
- + diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css index 311727fe51d..f11e932504e 100644 --- a/addons/crm/static/src/css/crm.css +++ b/addons/crm/static/src/css/crm.css @@ -55,17 +55,6 @@ font-size: 10px; } -.openerp .oe_kanban_view .oe_salesteams_justgage { - color: black; - display: inline-block; - bottom: 0; -} - -.openerp .oe_kanban_view .oe_sparkline_bar { - height: 20px; - width: 36px !important; -} - .openerp .oe_kanban_view .oe_salesteams_help { display: inline-block; } diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index 1fedc6a9945..a2c2b7e2af3 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -45,19 +45,6 @@ class sale_order(osv.osv): class crm_case_section(osv.osv): _inherit = 'crm.case.section' - def _currency_conversation(self, cr, uid, id, amount, relation_id, relation_field, context=None): - team = self.browse(cr, uid, id, context=context) - base_currency_id = team.currency_id.id - if relation_field not in ['pricelist_id', 'currency_id']: - return super(crm_case_section, self)._currency_conversation(cr, uid, id, amount, relation_id, relation_field, context=context) - if relation_field == 'pricelist_id': - base_currency_id = self.pool.get('product.pricelist').browse(cr, uid, relation_id, context=context).currency_id.id - elif relation_field == 'currency_id': - currency_rates = self.pool.get('res.currency.rate').search_read(cr, uid, [('rate', '=', 1)], ['currency_id'], limit=1, context=context) - if currency_rates: - base_currency_id = currency_rates[0]['currency_id'][0] - return self.pool.get('res.currency').compute(cr, uid, base_currency_id, team.currency_id.id, amount, context=context) - def _get_sale_orders_data(self, cr, uid, ids, field_name, arg, context=None): obj = self.pool.get('sale.order') res = dict.fromkeys(ids, False) @@ -67,9 +54,9 @@ class crm_case_section(osv.osv): for id in ids: res[id] = dict() created_domain = [('section_id', '=', id), ('state', 'in', ['draft', 'sent']), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)] - res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, id, obj, created_domain, ['amount_total', 'date_order', 'pricelist_id'], 'amount_total', ['date_order', 'pricelist_id'], context=context) + res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context) validated_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'sent']), ('date_confirm', '>=', date_begin), ('date_order', '<=', date_end)] - res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, id, obj, validated_domain, ['amount_total', 'date_confirm', 'pricelist_id'], 'amount_total', ['date_confirm', 'pricelist_id'], context=context) + res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_confirm'], 'amount_total', 'date_confirm', context=context) return res def _get_invoices_data(self, cr, uid, ids, field_name, arg, context=None): @@ -80,15 +67,15 @@ class crm_case_section(osv.osv): date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT) for id in ids: created_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', date_begin), ('date', '<=', date_end)] - res[id] = self.__get_bar_values(cr, uid, id, obj, created_domain, ['price_total', 'date', 'currency_id'], 'price_total', ['date', 'currency_id'], context=context) + res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date'], 'price_total', 'date', context=context) return res _columns = { - 'invoiced_forecast': fields.float("Invoice Forecast", + 'invoiced_forecast': fields.integer(string='Invoice Forecast', help="Forecast of the invoice revenue for the current month. This is the amount the sales \n" "team should invoice this month. It is used to compute the progression ratio \n" " of the current and forecast revenue on the kanban view."), - 'invoiced_target': fields.float("Invoice Target", + 'invoiced_target': fields.integer(string='Invoice Target', help="Target of invoice revenue for the current month. This is the amount the sales \n" "team estimates to be able to invoice this month."), 'monthly_quoted': fields.function(_get_sale_orders_data, @@ -103,7 +90,8 @@ class crm_case_section(osv.osv): } def action_forecast(self, cr, uid, id, value, context=None): - return self.write(cr, uid, [id], {'invoiced_forecast': value}, context=context) + return self.write(cr, uid, [id], {'invoiced_forecast': round(float(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 ce8c5a26efb..4669f669276 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -248,9 +248,8 @@ - - - + + @@ -273,7 +272,7 @@
Sales Orders - + Revenue of confirmed sales orders per month.
Click to acces the Sales Analysis.
@@ -283,7 +282,7 @@
Invoices - + Revenue of sent invoices per month.
Click to see a detailed analysis of invoices.
@@ -291,7 +290,7 @@
Quotations - + Revenue of created quotations per month.
Click to see a detailed analysis.
diff --git a/addons/sale_crm/tests/__init__.py b/addons/sale_crm/tests/__init__.py deleted file mode 100644 index fc04080875f..00000000000 --- a/addons/sale_crm/tests/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Business Applications -# Copyright (c) 2013-TODAY OpenERP S.A. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from . import test_sale_crm_case - -checks = [ - test_sale_crm_case, -] - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_crm/tests/test_sale_crm_case.py b/addons/sale_crm/tests/test_sale_crm_case.py deleted file mode 100644 index 6fb7d4c555a..00000000000 --- a/addons/sale_crm/tests/test_sale_crm_case.py +++ /dev/null @@ -1,83 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Business Applications -# Copyright (c) 2013-TODAY OpenERP S.A. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.addons.mail.tests.common import TestMail - -class TestSaleCrm(TestMail): - - def test_sale_crm_case(self): - """Testing __get_bar_values method""" - cr, uid, = self.cr, self.uid - - # Usefull models - ir_model_obj = self.registry('ir.model.data') - res_company_obj = self.registry('res.company') - res_users_obj = self.registry('res.users') - crm_case_section_obj = self.registry('crm.case.section') - crm_lead_obj = self.registry('crm.lead') - - # Get required ids - direct_sales_id = ir_model_obj.get_object_reference(cr, uid, 'crm', 'section_sales_department')[1] - usd_id = ir_model_obj.get_object_reference(cr, uid, 'base', 'USD')[1] - your_company_id = ir_model_obj.get_object_reference(cr, uid, 'base', 'main_company')[1] - - # Call relevant methods before changing manager of sales team. - opportunities_before = crm_case_section_obj._get_opportunities_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - sale_order_before = crm_case_section_obj._get_sale_orders_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - invoice_before = crm_case_section_obj._get_invoices_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - - # Create new company with USD currency. - res_company_id = res_company_obj.create(cr, uid,{ - 'name': 'New Company', - 'currency_id': usd_id, - 'parent_id': your_company_id - }) - - # Create new user with sales manager access rights for new company. - group_sale_manager_id = ir_model_obj.get_object_reference(cr, uid, 'base', 'group_sale_manager')[1] - res_users_id = res_users_obj.create(cr, uid,{ - 'name': 'User', - 'login': 'admin@example.com', - 'company_id': res_company_id, - 'company_ids': [(6, 0, [res_company_id])], - 'email': 'admin@gmail.com', - 'groups_id': [(6, 0, [group_sale_manager_id])] - }) - - # Change manager of sales team with USD currency. - crm_case_section_obj.write(cr, uid, [direct_sales_id], { - 'user_id' : res_users_id, - 'currency_id': usd_id, - }) - - # Call relevant methods and get converted data in another currency. - opportunities_after = crm_case_section_obj._get_opportunities_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - sale_order_after = crm_case_section_obj._get_sale_orders_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - invoice_after = crm_case_section_obj._get_invoices_data(cr, uid, [direct_sales_id], field_name=False, arg=False, context=False) - - team_currency_rate = crm_case_section_obj.browse(cr, uid, direct_sales_id).currency_id.rate_silent - - # Check currency conversion for Quotations,Sale orders and Invoices in to the current users currency - for month in range(0,5): - self.assertTrue(round(opportunities_before[1]['monthly_planned_revenue'][month]['value']) == round(opportunities_after[1]['monthly_planned_revenue'][month]['value'] / team_currency_rate), "Not Proper Currency Conversion for Opportunities") - self.assertTrue(round(sale_order_before[1]['monthly_quoted'][month]['value']) == round(sale_order_after[1]['monthly_quoted'][month]['value'] / team_currency_rate), "Currency conversion for quotations is wrong") - self.assertTrue(round(sale_order_before[1]['monthly_confirmed'][month]['value']) == round(sale_order_after[1]['monthly_confirmed'][month]['value'] / team_currency_rate), "Currency conversion for sale orders is wrong") - self.assertTrue(round(invoice_before[1][month]['value']) == round(invoice_after[1][month]['value'] / team_currency_rate), "Currency conversion for invoices is wrong") From 4f2e951cde47c1b8245a147937e90e9420c572ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 28 Nov 2013 11:39:21 +0100 Subject: [PATCH 51/68] [IMP] mail: mail_thread: routing: check for exact message_id. WIP: will be improved, notably about what to do for partial match. bzr revid: tde@openerp.com-20131128103921-q1a36otv9ptazehd --- addons/mail/mail_message.py | 4 ++++ addons/mail/mail_thread.py | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index de8e4c68fda..657c0f68e46 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -816,6 +816,10 @@ class mail_message(osv.Model): return email_reply_to + # def baseN(num, b, numerals=string.digits + string.letters): + # return ((num == 0) and "0") or (baseN(num // b, b, numerals).lstrip("0") + numerals[num % b]) + # message-id with db + def _get_message_id(self, cr, uid, values, context=None): message_id = None if not values.get('message_id') and values.get('reply_to'): diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index c169f1dc3f3..0f89159e20a 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -829,9 +829,26 @@ class mail_thread(osv.AbstractModel): email_to = decode_header(message, 'To') references = decode_header(message, 'References') in_reply_to = decode_header(message, 'In-Reply-To') + thread_references = references or in_reply_to + + # 1. message is a reply to an existing message (exact match of message_id) + msg_references = thread_references.split() + for msg_reference in msg_references: + mail_message_ids = self.pool['mail.message'].search( + cr, uid, [('message_id', '=', msg_reference)], context=context) + if mail_message_ids: + original_msg = self.pool['mail.message'].browse(cr, SUPERUSER_ID, mail_message_ids[0], context=context) + model, thread_id = original_msg.model, original_msg.res_id + _logger.info( + 'Routing mail from %s to %s with Message-Id %s: direct reply to msg: model: %s, thread_id: %s, custom_values: %s, uid: %s', + email_from, email_to, message_id, model, thread_id, custom_values, uid) + route = self.message_route_verify( + cr, uid, message, message_dict, + (model, thread_id, custom_values, uid, None), + update_author=True, assert_model=True, create_fallback=True, context=context) + return route and [route] or [] # 1. Verify if this is a reply to an existing thread - thread_references = references or in_reply_to ref_match = thread_references and tools.reference_re.search(thread_references) if ref_match: thread_id = int(ref_match.group(1)) From c553167ff87ed3b077d0cec83d82c23c432f4831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 28 Nov 2013 14:31:32 +0100 Subject: [PATCH 52/68] [FIX] mail_message: do not overwrite 'False' message_id when creating a new mail message bzr revid: tde@openerp.com-20131128133132-8oevlk1wyoqytebo --- addons/mail/mail_message.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 657c0f68e46..6b431f4ef3e 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -821,12 +821,11 @@ class mail_message(osv.Model): # message-id with db def _get_message_id(self, cr, uid, values, context=None): - message_id = None - if not values.get('message_id') and values.get('reply_to'): + if values.get('reply_to'): message_id = tools.generate_tracking_message_id('reply_to') - elif not values.get('message_id') and values.get('res_id') and values.get('model'): + elif values.get('res_id') and values.get('model'): message_id = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values) - elif not values.get('message_id'): + else: message_id = tools.generate_tracking_message_id('private') return message_id @@ -837,7 +836,7 @@ class mail_message(osv.Model): if 'email_from' not in values: # needed to compute reply_to values['email_from'] = self._get_default_from(cr, uid, context=context) - if not values.get('message_id'): + if 'message_id' not in values: values['message_id'] = self._get_message_id(cr, uid, values, context=context) if 'reply_to' not in values: values['reply_to'] = self._get_reply_to(cr, uid, values, context=context) From 8253453d6439d97f272407f02eadafc99f0df796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 28 Nov 2013 14:32:26 +0100 Subject: [PATCH 53/68] [IMP] mail: mail_thread: routing: implement compat-mode routing It now accepts <111...222..openerp-res_id-model@mydomain.com> partial matching only if the target record has messages without message_id, aka <= 6.1 record. Updated and added tests bzr revid: tde@openerp.com-20131128133226-ce1qyeswi9n83q8v --- addons/mail/mail_thread.py | 37 +++++++++++++--------- addons/mail/tests/test_mail_gateway.py | 43 ++++++++++++++++++++------ 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 0f89159e20a..4bd0ff4a94e 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -723,8 +723,8 @@ class mail_thread(osv.AbstractModel): # Private message: should not contain any thread_id if not model and thread_id: if assert_model: - assert thread_id == 0, 'Routing: posting a message without model should be with a null res_id (private message).' - _warn('posting a message without model should be with a null res_id (private message), resetting thread_id') + assert thread_id == 0, 'Routing: posting a message without model should be with a null res_id (private message), received %s.' % thread_id + _warn('posting a message without model should be with a null res_id (private message), received %s, resetting thread_id' % thread_id) thread_id = 0 # Private message: should have a parent_id (only answers) if not model and not message_dict.get('parent_id'): @@ -821,6 +821,7 @@ class mail_thread(osv.AbstractModel): :return: list of [model, thread_id, custom_values, user_id, alias] """ assert isinstance(message, Message), 'message must be an email.message.Message at this point' + mail_msg_obj = self.pool['mail.message'] fallback_model = model # Get email.message.Message variables for future processing @@ -834,10 +835,9 @@ class mail_thread(osv.AbstractModel): # 1. message is a reply to an existing message (exact match of message_id) msg_references = thread_references.split() for msg_reference in msg_references: - mail_message_ids = self.pool['mail.message'].search( - cr, uid, [('message_id', '=', msg_reference)], context=context) + mail_message_ids = mail_msg_obj.search(cr, uid, [('message_id', '=', msg_reference)], context=context) if mail_message_ids: - original_msg = self.pool['mail.message'].browse(cr, SUPERUSER_ID, mail_message_ids[0], context=context) + original_msg = mail_msg_obj.browse(cr, SUPERUSER_ID, mail_message_ids[0], context=context) model, thread_id = original_msg.model, original_msg.res_id _logger.info( 'Routing mail from %s to %s with Message-Id %s: direct reply to msg: model: %s, thread_id: %s, custom_values: %s, uid: %s', @@ -848,29 +848,38 @@ class mail_thread(osv.AbstractModel): update_author=True, assert_model=True, create_fallback=True, context=context) return route and [route] or [] - # 1. Verify if this is a reply to an existing thread + # 2. message is a reply to an existign thread (6.1 compatibility) ref_match = thread_references and tools.reference_re.search(thread_references) if ref_match: thread_id = int(ref_match.group(1)) model = ref_match.group(2) or fallback_model if thread_id and model in self.pool: model_obj = self.pool[model] - if model_obj.exists(cr, uid, thread_id) and hasattr(model_obj, 'message_update'): - _logger.info('Routing mail from %s to %s with Message-Id %s: direct reply to model: %s, thread_id: %s, custom_values: %s, uid: %s', - email_from, email_to, message_id, model, thread_id, custom_values, uid) - route = self.message_route_verify(cr, uid, message, message_dict, - (model, thread_id, custom_values, uid, None), - update_author=True, assert_model=True, create_fallback=True, context=context) + compat_mail_msg_ids = mail_msg_obj.search( + cr, uid, [ + ('message_id', '=', False), + ('model', '=', model), + ('res_id', '=', thread_id), + ], context=context) + print 'cacaprout', compat_mail_msg_ids + if compat_mail_msg_ids and model_obj.exists(cr, uid, thread_id) and hasattr(model_obj, 'message_update'): + _logger.info( + 'Routing mail from %s to %s with Message-Id %s: direct thread reply (compat-mode) to model: %s, thread_id: %s, custom_values: %s, uid: %s', + email_from, email_to, message_id, model, thread_id, custom_values, uid) + route = self.message_route_verify( + cr, uid, message, message_dict, + (model, thread_id, custom_values, uid, None), + update_author=True, assert_model=True, create_fallback=True, context=context) return route and [route] or [] # 2. Reply to a private message if in_reply_to: - mail_message_ids = self.pool.get('mail.message').search(cr, uid, [ + mail_message_ids = mail_msg_obj.search(cr, uid, [ ('message_id', '=', in_reply_to), '!', ('message_id', 'ilike', 'reply_to') ], limit=1, context=context) if mail_message_ids: - mail_message = self.pool.get('mail.message').browse(cr, uid, mail_message_ids[0], context=context) + mail_message = mail_msg_obj.browse(cr, uid, mail_message_ids[0], context=context) _logger.info('Routing mail from %s to %s with Message-Id %s: direct reply to a private message: %s, custom_values: %s, uid: %s', email_from, email_to, message_id, mail_message.id, custom_values, uid) route = self.message_route_verify(cr, uid, message, message_dict, diff --git a/addons/mail/tests/test_mail_gateway.py b/addons/mail/tests/test_mail_gateway.py index b2a1c3250aa..c88ed6555d6 100644 --- a/addons/mail/tests/test_mail_gateway.py +++ b/addons/mail/tests/test_mail_gateway.py @@ -163,7 +163,7 @@ class TestMailgateway(TestMail): self.assertIn('
Should create a multipart/mixed: from gmail, bold, with attachment.

', res.get('body', ''), 'message_parse: html version should be in body after parsing multipart/mixed') - @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm') + # @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm') def test_10_message_process(self): """ Testing incoming emails processing. """ cr, uid, user_raoul = self.cr, self.uid, self.user_raoul @@ -378,7 +378,7 @@ class TestMailgateway(TestMail): frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com', msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>', to='erroneous@example.com>', subject='Re: news', - extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id) + extra='In-Reply-To: <1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>\n') # Test: no group 'Re: news' created, still only 1 Frogs group self.assertEqual(len(frog_groups), 0, 'message_process: reply on Frogs should not have created a new group with new subject') @@ -387,16 +387,41 @@ class TestMailgateway(TestMail): 'message_process: reply on Frogs should not have created a duplicate group with old subject') frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) # Test: one new message - self.assertEqual(len(frog_group.message_ids), 3, 'message_process: group should contain 2 messages after reply') + self.assertEqual(len(frog_group.message_ids), 3, 'message_process: group should contain 3 messages after reply') # Test: author (and not recipient) added as follower frog_follower_ids = set([p.id for p in frog_group.message_follower_ids]) self.assertEqual(frog_follower_ids, set([p1id, p2id]), 'message_process: after reply, group should have 2 followers') + # Do: incoming email with ref holding model / res_id but that does not match any message in the thread: must raise since OpenERP saas-3 + self.assertRaises(AssertionError, + format_and_process, + MAIL_TEMPLATE, email_from='other5@gmail.com', + to='noone@example.com', subject='spam', + extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>' % frog_group.id, + msg_id='<1.1.JavaMail.new@agrolait.com>') + # There are 6.1 messages, activate compat mode + tmp_msg_id = self.mail_message.create(cr, uid, {'message_id': False, 'model': 'mail.group', 'res_id': frog_group.id}) + # Do: compat mode accepts partial-matching emails + frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other5@gmail.com', + msg_id='<1.2.JavaMail.new@agrolait.com>', + to='noone@example.com>', subject='spam', + extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>' % frog_group.id) + self.mail_message.unlink(cr, uid, [tmp_msg_id]) + # Test: no group 'Re: news' created, still only 1 Frogs group + self.assertEqual(len(frog_groups), 0, + 'message_process: reply on Frogs should not have created a new group with new subject') + frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')]) + self.assertEqual(len(frog_groups), 1, + 'message_process: reply on Frogs should not have created a duplicate group with old subject') + frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) + # Test: one new message + self.assertEqual(len(frog_group.message_ids), 4, 'message_process: group should contain 4 messages after reply') + # Do: due to some issue, same email goes back into the mailgateway frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com', msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>', - subject='Re: news', extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id) + subject='Re: news', extra='In-Reply-To: <1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>\n') # Test: no group 'Re: news' created, still only 1 Frogs group self.assertEqual(len(frog_groups), 0, 'message_process: reply on Frogs should not have created a new group with new subject') @@ -405,7 +430,7 @@ class TestMailgateway(TestMail): 'message_process: reply on Frogs should not have created a duplicate group with old subject') frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) # Test: no new message - self.assertEqual(len(frog_group.message_ids), 3, 'message_process: message with already existing message_id should not have been duplicated') + self.assertEqual(len(frog_group.message_ids), 4, 'message_process: message with already existing message_id should not have been duplicated') # Test: message_id is still unique msg_ids = self.mail_message.search(cr, uid, [('message_id', 'ilike', '<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>')]) self.assertEqual(len(msg_ids), 1, @@ -422,7 +447,7 @@ class TestMailgateway(TestMail): format_and_process(MAIL_TEMPLATE, email_from='Lombrik Lubrik ', to='erroneous@example.com>', subject='Re: news (2)', msg_id='<1198923581.41972151344608186760.JavaMail.new1@agrolait.com>', - extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id) + extra='In-Reply-To: <1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>') frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')]) frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) # Test: author is A-Raoul (only existing) @@ -436,7 +461,7 @@ class TestMailgateway(TestMail): format_and_process(MAIL_TEMPLATE, email_from='Lombrik Lubrik ', to='erroneous@example.com>', subject='Re: news (3)', msg_id='<1198923581.41972151344608186760.JavaMail.new2@agrolait.com>', - extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id) + extra='In-Reply-To: <1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>') frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')]) frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) # Test: author is Raoul (user), not A-Raoul @@ -451,7 +476,7 @@ class TestMailgateway(TestMail): format_and_process(MAIL_TEMPLATE, email_from='Lombrik Lubrik ', to='erroneous@example.com>', subject='Re: news (3)', msg_id='<1198923581.41972151344608186760.JavaMail.new3@agrolait.com>', - extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id) + extra='In-Reply-To: <1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>') frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')]) frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) # Test: author is Raoul (user), not A-Raoul @@ -498,7 +523,7 @@ class TestMailgateway(TestMail): self.assertIn('
\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n
', msg.body, 'message_process: plaintext incoming email incorrectly parsed') - @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm') + # @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm') def test_20_thread_parent_resolution(self): """ Testing parent/child relationships are correctly established when processing incoming mails """ cr, uid = self.cr, self.uid From 43979cfd8f4a9145504522c333a5ca09b2dde5f3 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Tue, 3 Dec 2013 20:06:39 +0100 Subject: [PATCH 54/68] [IMP] http improvement - context manager request object (removes some ugly hacks) - improve http error handling - add lazyresponses bzr revid: al@openerp.com-20131203190639-e8r1qm9wc82t8g4k --- openerp/addons/base/ir/ir_http.py | 60 ++++++++++---- openerp/http.py | 130 +++++++++++++++--------------- 2 files changed, 108 insertions(+), 82 deletions(-) diff --git a/openerp/addons/base/ir/ir_http.py b/openerp/addons/base/ir/ir_http.py index ff8a2a8ebd8..02d3fee61b7 100644 --- a/openerp/addons/base/ir/ir_http.py +++ b/openerp/addons/base/ir/ir_http.py @@ -3,6 +3,7 @@ #---------------------------------------------------------- import logging import re +import sys import werkzeug.exceptions import werkzeug.routing @@ -74,26 +75,29 @@ class ir_http(osv.AbstractModel): request.disable_db = True request.uid = None - def _authenticate(self, func, arguments): - auth_method = getattr(func, "auth", "user") + def _authenticate(self, auth_method='user'): if request.session.uid: try: request.session.check_security() # what if error in security.check() # -> res_users.check() # -> res_users.check_credentials() - except Exception: + except http.SessionExpiredException: request.session.logout() + raise http.SessionExpiredException("Session expired for request %s" % request.httprequest) getattr(self, "_auth_method_%s" % auth_method)() return auth_method - def _handle_404(self, exception): - raise exception + def _handle_exception(self, exception): + if isinstance(exception, openerp.exceptions.AccessError): + code = 403 + else: + code = getattr(exception, 'code', 500) - def _handle_403(self, exception): - raise exception + fn = getattr(self, '_handle_%d' % code, self._handle_unknown_exception) + return fn(exception) - def _handle_500(self, exception): + def _handle_unknown_exception(self, exception): raise exception def _dispatch(self): @@ -101,13 +105,16 @@ class ir_http(osv.AbstractModel): try: func, arguments = self._find_handler() except werkzeug.exceptions.NotFound, e: - return self._handle_404(e) + return self._handle_exception(e) # check authentication level try: - auth_method = self._authenticate(func, arguments) - except werkzeug.exceptions.NotFound, e: - return self._handle_403(e) + auth_method = self._authenticate(getattr(func, "auth", None)) + except Exception: + # force a Forbidden exception with the original traceback + return self._handle_exception( + convert_exception_to( + werkzeug.exceptions.Forbidden)) # post process arg to set uid on browse records for arg in arguments.itervalues(): @@ -121,9 +128,7 @@ class ir_http(osv.AbstractModel): if isinstance(result, Exception): raise result except Exception, e: - fn = getattr(self, '_handle_%s' % getattr(e, 'code', 500), - self._handle_500) - return fn(e) + return self._handle_exception(e) return result @@ -139,4 +144,29 @@ class ir_http(osv.AbstractModel): return self._routing_map +def convert_exception_to(to_type, with_message=False): + """ Should only be called from an exception handler. Fetches the current + exception data from sys.exc_info() and creates a new exception of type + ``to_type`` with the original traceback. + + If ``with_message`` is ``True``, sets the new exception's message to be + the stringification of the original exception. If ``False``, does not + set the new exception's message. Otherwise, uses ``with_message`` as the + new exception's message. + + :type with_message: str|bool + """ + etype, original, tb = sys.exc_info() + try: + if with_message is False: + message = None + elif with_message is True: + message = str(original) + else: + message = str(with_message) + + raise to_type, message, tb + except to_type, e: + return e + # vim:et: diff --git a/openerp/http.py b/openerp/http.py index 993af6d2752..6e819ba45f1 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -3,7 +3,6 @@ # OpenERP HTTP layer #---------------------------------------------------------- import ast -import cgi import collections import contextlib import errno @@ -36,13 +35,20 @@ import werkzeug.wsgi import openerp from openerp.service import security, model as service_model -from openerp.tools import config _logger = logging.getLogger(__name__) #---------------------------------------------------------- # RequestHandler #---------------------------------------------------------- +# Thread local global request object +_request_stack = werkzeug.local.LocalStack() + +request = _request_stack() +""" + A global proxy that always redirect to the current request object. +""" + class WebRequest(object): """ Parent class for all OpenERP Web request types, mostly deals with initialization and setup of the request object (the dispatching itself has @@ -138,11 +144,24 @@ class WebRequest(object): trying to access this property will raise an exception. """ # some magic to lazy create the cr - if not self._cr_cm: - self._cr_cm = self.registry.cursor() - self._cr = self._cr_cm.__enter__() + if not self._cr: + self._cr = self.registry.db.cursor() return self._cr + def __enter__(self): + _request_stack.push(self) + return self + + def __exit__(self, exc_type, exc_value, traceback): + _request_stack.pop() + if self._cr: + if exc_type is None: + self._cr.commit() + self._cr.close() + # just to be sure no one tries to re-use the request + self.disable_db = True + self.uid = None + def set_handler(self, func, arguments, auth): # is this needed ? arguments = dict((k, v) for k, v in arguments.iteritems() @@ -154,39 +173,23 @@ class WebRequest(object): self.auth_method = auth def _call_function(self, *args, **kwargs): - try: - # ugly syntax only to get the __exit__ arguments to pass to self._cr - request = self - class with_obj(object): - def __enter__(self): - pass - def __exit__(self, *args): - if request._cr_cm: - request._cr_cm.__exit__(*args) - request._cr_cm = None - request._cr = None + request = self + if self.func_request_type != self._request_type: + raise Exception("%s, %s: Function declared as capable of handling request of type '%s' but called with a request of type '%s'" \ + % (self.func, self.httprequest.path, self.func_request_type, self._request_type)) - with with_obj(): - if self.func_request_type != self._request_type: - raise Exception("%s, %s: Function declared as capable of handling request of type '%s' but called with a request of type '%s'" \ - % (self.func, self.httprequest.path, self.func_request_type, self._request_type)) + kwargs.update(self.func_arguments) - kwargs.update(self.func_arguments) - - # Backward for 7.0 - if getattr(self.func, '_first_arg_is_req', False): - args = (request,) + args - # Correct exception handling and concurency retry - @service_model.check - def checked_call(dbname, *a, **kw): - return self.func(*a, **kw) - if self.db: - return checked_call(self.db, *args, **kwargs) - return self.func(*args, **kwargs) - finally: - # just to be sure no one tries to re-use the request - self.disable_db = True - self.uid = None + # Backward for 7.0 + if getattr(self.func, '_first_arg_is_req', False): + args = (request,) + args + # Correct exception handling and concurency retry + @service_model.check + def checked_call(dbname, *a, **kw): + return self.func(*a, **kw) + if self.db: + return checked_call(self.db, *args, **kwargs) + return self.func(*args, **kwargs) @property def debug(self): @@ -197,7 +200,7 @@ class WebRequest(object): warnings.warn('please use request.registry and request.cr directly', DeprecationWarning) yield (self.registry, self.cr) -def route(route, type="http", auth="user"): +def route(route, type="http", auth="user", methods=None): """ Decorator marking the decorated method as being a handler for requests. The method must be part of a subclass of ``Controller``. @@ -214,6 +217,7 @@ def route(route, type="http", auth="user"): * ``none``: The method is always active, even if there is no database. Mainly used by the framework and authentication modules. There request code will not have any facilities to access the database nor have any configuration indicating the current database nor the current user. + :param methods: A sequence of http methods this route applies to. If not specified, all methods are allowed. """ assert type in ["http", "json"] def decorator(f): @@ -221,6 +225,7 @@ def route(route, type="http", auth="user"): f.routes = route else: f.routes = [route] + f.methods = methods f.exposed = type if getattr(f, "auth", None) is None: f.auth = auth @@ -398,17 +403,8 @@ class HttpRequest(WebRequest): def dispatch(self): try: r = self._call_function(**self.params) - except werkzeug.exceptions.HTTPException, e: + except (werkzeug.exceptions.HTTPException), e: r = e - except Exception, e: - _logger.exception("An exception occured during an http request") - se = serialize_exception(e) - error = { - 'code': 200, - 'message': "OpenERP Server Error", - 'data': se - } - r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps(error))) else: if not r: r = werkzeug.wrappers.Response(status=204) # no content @@ -451,24 +447,6 @@ def httprequest(f): base = "" return route([base, base + "/"], type="http", auth="user")(f) -#---------------------------------------------------------- -# Thread local global request object -#---------------------------------------------------------- -_request_stack = werkzeug.local.LocalStack() - -request = _request_stack() -""" - A global proxy that always redirect to the current request object. -""" - -@contextlib.contextmanager -def set_request(req): - _request_stack.push(req) - try: - yield - finally: - _request_stack.pop() - #---------------------------------------------------------- # Controller and route registration #---------------------------------------------------------- @@ -526,7 +504,7 @@ def routing_map(modules, nodb_only, converters=None): url = o._cp_path.rstrip('/') + '/' + url.lstrip('/') if url.endswith("/") and len(url) > 1: url = url[: -1] - routing_map.add(werkzeug.routing.Rule(url, endpoint=mv)) + routing_map.add(werkzeug.routing.Rule(url, endpoint=mv, methods=mv.methods)) return routing_map #---------------------------------------------------------- @@ -816,6 +794,18 @@ mimetypes.add_type('application/font-woff', '.woff') mimetypes.add_type('application/vnd.ms-fontobject', '.eot') mimetypes.add_type('application/x-font-ttf', '.ttf') +class LazyResponse(werkzeug.wrappers.Response): + """ Lazy werkzeug response. + API not yet frozen""" + + def __init__(self, callback, **kwargs): + super(LazyResponse, self).__init__(mimetype='text/html') + self.callback = callback + self.params = kwargs + def process(self): + response = self.callback(**self.params) + self.response.append(response) + class DisableCacheMiddleware(object): def __init__(self, app): self.app = app @@ -941,6 +931,12 @@ class Root(object): return HttpRequest(httprequest) def get_response(self, httprequest, result, explicit_session): + if isinstance(result, LazyResponse): + try: + result.process() + except(Exception), e: + result = request.registry['ir.http']._handle_exception(e) + if isinstance(result, basestring): response = werkzeug.wrappers.Response(result, mimetype='text/html') else: @@ -980,7 +976,7 @@ class Root(object): result = request.dispatch() return result - with set_request(request): + with request: db = request.session.db if db: openerp.modules.registry.RegistryManager.check_registry_signaling(db) From 4c45774b6d87eafa7b5c1b006894b5f3339d64b2 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Tue, 3 Dec 2013 20:16:21 +0100 Subject: [PATCH 55/68] [IMP] manual exception handling for non-json controllers bzr revid: al@openerp.com-20131203191621-xu2350cw0u3izn3s --- addons/web/controllers/main.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 41bb6ccfca7..8f7c5d4f70d 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -3,6 +3,7 @@ import ast import base64 import csv +import functools import glob import itertools import logging @@ -35,7 +36,9 @@ from openerp.tools.translate import _ from openerp.tools import config from openerp import http -from openerp.http import request +from openerp.http import request, serialize_exception as _serialize_exception + +_logger = logging.getLogger(__name__) #---------------------------------------------------------- # OpenERP Web helpers @@ -89,6 +92,22 @@ db_list = http.db_list db_monodb = http.db_monodb +def serialize_exception(f): + @functools.wraps(f) + def wrap(*args, **kwargs): + try: + return f(*args, **kwargs) + except Exception, e: + _logger.exception("An exception occured during an http request") + se = _serialize_exception(e) + error = { + 'code': 200, + 'message': "OpenERP Server Error", + 'data': se + } + return werkzeug.exceptions.InternalServerError(simplejson.dumps(error)) + return wrap + def redirect_with_hash(url, code=303): if request.httprequest.user_agent.browser in ('msie', 'safari'): # Most IE and Safari versions decided not to preserve location.hash upon @@ -542,14 +561,14 @@ class Home(http.Controller): def redirect(db): query = dict(urlparse.parse_qsl(request.httprequest.query_string, keep_blank_values=True)) - query.update({'db': db}) + query['db'] = db redirect = request.httprequest.path + '?' + urllib.urlencode(query) return redirect_with_hash(redirect) if db is None and guessed_db is not None: return redirect(guessed_db) - if db is not None and db != guessed_db: + if db is not None and db != request.session.db: request.session.logout() request.session.db = db guessed_db = db @@ -1220,6 +1239,7 @@ class Binary(http.Controller): return open(os.path.join(addons_path, 'web', 'static', 'src', 'img', image), 'rb').read() @http.route('/web/binary/saveas', type='http', auth="user") + @serialize_exception def saveas(self, model, field, id=None, filename_field=None, **kw): """ Download link for files stored as binary fields. @@ -1253,6 +1273,7 @@ class Binary(http.Controller): ('Content-Disposition', content_disposition(filename))]) @http.route('/web/binary/saveas_ajax', type='http', auth="user") + @serialize_exception def saveas_ajax(self, data, token): jdata = simplejson.loads(data) model = jdata['model'] @@ -1286,6 +1307,7 @@ class Binary(http.Controller): cookies={'fileToken': token}) @http.route('/web/binary/upload', type='http', auth="user") + @serialize_exception def upload(self, callback, ufile): # TODO: might be useful to have a configuration flag for max-length file uploads out = """