From af7eb0cc0689f270ec212450f56ace776b260573 Mon Sep 17 00:00:00 2001 From: Niko Date: Thu, 27 Dec 2012 17:18:37 +0100 Subject: [PATCH 001/305] [IE] bug 1092846 bzr revid: nwi@openerp.com-20121227161837-7qzx93roddfrtlhb --- addons/web/__openerp__.py | 1 + .../jquery.placeholder/jquery.placeholder.min.js | 10 ++++++++++ addons/web/static/src/css/base.css | 15 +++++++++------ addons/web/static/src/css/base.sass | 3 +++ 4 files changed, 23 insertions(+), 6 deletions(-) create mode 100755 addons/web/static/lib/jquery.placeholder/jquery.placeholder.min.js diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 2303b6a68e8..970d0f52e7b 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -39,6 +39,7 @@ This module provides the core of the OpenERP Web Client. "static/lib/underscore/underscore.string.js", "static/lib/backbone/backbone.js", "static/lib/cleditor/jquery.cleditor.js", + "static/lib/jquery.placeholder/jquery.placeholder.min.js", "static/lib/py.js/lib/py.js", "static/src/js/boot.js", "static/src/js/testing.js", diff --git a/addons/web/static/lib/jquery.placeholder/jquery.placeholder.min.js b/addons/web/static/lib/jquery.placeholder/jquery.placeholder.min.js new file mode 100755 index 00000000000..1ac0df1b3e1 --- /dev/null +++ b/addons/web/static/lib/jquery.placeholder/jquery.placeholder.min.js @@ -0,0 +1,10 @@ +/*! http://mths.be/placeholder v2.0.7 by @mathias */ +if ($.browser.msie && $.browser.version<=9) { +;(function(f,h,$){var a='placeholder' in h.createElement('input'),d='placeholder' in h.createElement('textarea'),i=$.fn,c=$.valHooks,k,j;if(a&&d){j=i.placeholder=function(){return this};j.input=j.textarea=true}else{j=i.placeholder=function(){var l=this;l.filter((a?'textarea':':input')+'[placeholder]').not('.placeholder').bind({'focus.placeholder':b,'blur.placeholder':e}).data('placeholder-enabled',true).trigger('blur.placeholder');return l};j.input=a;j.textarea=d;k={get:function(m){var l=$(m);return l.data('placeholder-enabled')&&l.hasClass('placeholder')?'':m.value},set:function(m,n){var l=$(m);if(!l.data('placeholder-enabled')){return m.value=n}if(n==''){m.value=n;if(m!=h.activeElement){e.call(m)}}else{if(l.hasClass('placeholder')){b.call(m,true,n)||(m.value=n)}else{m.value=n}}return l}};a||(c.input=k);d||(c.textarea=k);$(function(){$(h).delegate('form','submit.placeholder',function(){var l=$('.placeholder',this).each(b);setTimeout(function(){l.each(e)},10)})});$(f).bind('beforeunload.placeholder',function(){$('.placeholder').each(function(){this.value=''})})}function g(m){var l={},n=/^jQuery\d+$/;$.each(m.attributes,function(p,o){if(o.specified&&!n.test(o.name)){l[o.name]=o.value}});return l}function b(m,n){var l=this,o=$(l);if(l.value==o.attr('placeholder')&&o.hasClass('placeholder')){if(o.data('placeholder-password')){o=o.hide().next().show().attr('id',o.removeAttr('id').data('placeholder-id'));if(m===true){return o[0].value=n}o.focus()}else{l.value='';o.removeClass('placeholder');l==h.activeElement&&l.select()}}}function e(){var q,l=this,p=$(l),m=p,o=this.id;if(l.value==''){if(l.type=='password'){if(!p.data('placeholder-textinput')){try{q=p.clone().attr({type:'text'})}catch(n){q=$('').attr($.extend(g(this),{type:'text'}))}q.removeAttr('name').data({'placeholder-password':true,'placeholder-id':o}).bind('focus.placeholder',b);p.data({'placeholder-textinput':q,'placeholder-id':o}).before(q)}p=p.removeAttr('id').hide().prev().attr('id',o).show()}p.addClass('placeholder');p[0].value=p.attr('placeholder')}else{p.removeClass('placeholder')}}}(this,document,jQuery)); + document.addEventListener("DOMNodeInserted",function(event){ + if ( $(event.target).is("input") || $(event.target).is("textarea") ) { + $(event.target).placeholder(); + } + }); +} + diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 8a5aa5f9ad0..cf356f38185 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1,4 +1,4 @@ -@charset "utf-8"; +@charset "UTF-8"; @font-face { font-family: "mnmliconsRegular"; src: url("/web/static/src/font/mnmliconsv21-webfont.eot") format("eot"); @@ -30,7 +30,7 @@ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5); /* http://www.quirksmode.org/dom/inputfile.html * http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image - */ */ + */ } .openerp.openerp_webclient_container { height: 100%; @@ -1265,7 +1265,7 @@ color: white; padding: 2px 4px; margin: 1px 6px 0 0; - border: 1px solid lightGray; + border: 1px solid lightgrey; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -moz-border-radius: 4px; -webkit-border-radius: 4px; @@ -1290,7 +1290,7 @@ transform: scale(1.1); } .openerp .oe_secondary_submenu .oe_active { - border-top: 1px solid lightGray; + border-top: 1px solid lightgrey; border-bottom: 1px solid #dedede; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2); @@ -2240,7 +2240,7 @@ } .openerp .oe_form .oe_form_label_help[for] span, .openerp .oe_form .oe_form_label[for] span { font-size: 80%; - color: darkGreen; + color: darkgreen; vertical-align: top; position: relative; top: -4px; @@ -3131,6 +3131,10 @@ div.ui-widget-overlay { border-radius: 3px; } +.openerp_ie .placeholder { + color: #afafb6 !important; + font-style: italic !important; +} .openerp_ie .oe_form_field_boolean input { background: white; } @@ -3291,7 +3295,6 @@ div.ui-widget-overlay { overflow: hidden !important; } } - .blockUI.blockOverlay { background-color: black; opacity: 0.6; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 3aeba573041..ffe2d631337 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -2472,6 +2472,9 @@ div.ui-widget-overlay // Internet Explorer 9+ specifics {{{ .openerp_ie + .placeholder + color: $tag-border !important + font-style: italic !important .oe_form_field_boolean input background: #fff input[type='checkbox'] From 15f98fa00413e6512a7f2835822a843e399eef16 Mon Sep 17 00:00:00 2001 From: Niko Date: Fri, 28 Dec 2012 10:42:56 +0100 Subject: [PATCH 002/305] [IMP] change placeholder inclusion logic bzr revid: nwi@openerp.com-20121228094256-h7kxphh5lexb3ff5 --- addons/web/__openerp__.py | 1 - addons/web/controllers/main.py | 15 +++++++++++++-- .../jquery.placeholder/jquery.placeholder.min.js | 10 +--------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 970d0f52e7b..2303b6a68e8 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -39,7 +39,6 @@ This module provides the core of the OpenERP Web Client. "static/lib/underscore/underscore.string.js", "static/lib/backbone/backbone.js", "static/lib/cleditor/jquery.cleditor.js", - "static/lib/jquery.placeholder/jquery.placeholder.min.js", "static/lib/py.js/lib/py.js", "static/src/js/boot.js", "static/src/js/testing.js", diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 1d5cfabec2c..cfe25bb9e97 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -522,8 +522,19 @@ html_template = """ + + diff --git a/addons/web/static/lib/jquery.placeholder/jquery.placeholder.min.js b/addons/web/static/lib/jquery.placeholder/jquery.placeholder.min.js index 1ac0df1b3e1..698ddc5188d 100755 --- a/addons/web/static/lib/jquery.placeholder/jquery.placeholder.min.js +++ b/addons/web/static/lib/jquery.placeholder/jquery.placeholder.min.js @@ -1,10 +1,2 @@ /*! http://mths.be/placeholder v2.0.7 by @mathias */ -if ($.browser.msie && $.browser.version<=9) { -;(function(f,h,$){var a='placeholder' in h.createElement('input'),d='placeholder' in h.createElement('textarea'),i=$.fn,c=$.valHooks,k,j;if(a&&d){j=i.placeholder=function(){return this};j.input=j.textarea=true}else{j=i.placeholder=function(){var l=this;l.filter((a?'textarea':':input')+'[placeholder]').not('.placeholder').bind({'focus.placeholder':b,'blur.placeholder':e}).data('placeholder-enabled',true).trigger('blur.placeholder');return l};j.input=a;j.textarea=d;k={get:function(m){var l=$(m);return l.data('placeholder-enabled')&&l.hasClass('placeholder')?'':m.value},set:function(m,n){var l=$(m);if(!l.data('placeholder-enabled')){return m.value=n}if(n==''){m.value=n;if(m!=h.activeElement){e.call(m)}}else{if(l.hasClass('placeholder')){b.call(m,true,n)||(m.value=n)}else{m.value=n}}return l}};a||(c.input=k);d||(c.textarea=k);$(function(){$(h).delegate('form','submit.placeholder',function(){var l=$('.placeholder',this).each(b);setTimeout(function(){l.each(e)},10)})});$(f).bind('beforeunload.placeholder',function(){$('.placeholder').each(function(){this.value=''})})}function g(m){var l={},n=/^jQuery\d+$/;$.each(m.attributes,function(p,o){if(o.specified&&!n.test(o.name)){l[o.name]=o.value}});return l}function b(m,n){var l=this,o=$(l);if(l.value==o.attr('placeholder')&&o.hasClass('placeholder')){if(o.data('placeholder-password')){o=o.hide().next().show().attr('id',o.removeAttr('id').data('placeholder-id'));if(m===true){return o[0].value=n}o.focus()}else{l.value='';o.removeClass('placeholder');l==h.activeElement&&l.select()}}}function e(){var q,l=this,p=$(l),m=p,o=this.id;if(l.value==''){if(l.type=='password'){if(!p.data('placeholder-textinput')){try{q=p.clone().attr({type:'text'})}catch(n){q=$('').attr($.extend(g(this),{type:'text'}))}q.removeAttr('name').data({'placeholder-password':true,'placeholder-id':o}).bind('focus.placeholder',b);p.data({'placeholder-textinput':q,'placeholder-id':o}).before(q)}p=p.removeAttr('id').hide().prev().attr('id',o).show()}p.addClass('placeholder');p[0].value=p.attr('placeholder')}else{p.removeClass('placeholder')}}}(this,document,jQuery)); - document.addEventListener("DOMNodeInserted",function(event){ - if ( $(event.target).is("input") || $(event.target).is("textarea") ) { - $(event.target).placeholder(); - } - }); -} - +;(function(f,h,$){var a='placeholder' in h.createElement('input'),d='placeholder' in h.createElement('textarea'),i=$.fn,c=$.valHooks,k,j;if(a&&d){j=i.placeholder=function(){return this};j.input=j.textarea=true}else{j=i.placeholder=function(){var l=this;l.filter((a?'textarea':':input')+'[placeholder]').not('.placeholder').bind({'focus.placeholder':b,'blur.placeholder':e}).data('placeholder-enabled',true).trigger('blur.placeholder');return l};j.input=a;j.textarea=d;k={get:function(m){var l=$(m);return l.data('placeholder-enabled')&&l.hasClass('placeholder')?'':m.value},set:function(m,n){var l=$(m);if(!l.data('placeholder-enabled')){return m.value=n}if(n==''){m.value=n;if(m!=h.activeElement){e.call(m)}}else{if(l.hasClass('placeholder')){b.call(m,true,n)||(m.value=n)}else{m.value=n}}return l}};a||(c.input=k);d||(c.textarea=k);$(function(){$(h).delegate('form','submit.placeholder',function(){var l=$('.placeholder',this).each(b);setTimeout(function(){l.each(e)},10)})});$(f).bind('beforeunload.placeholder',function(){$('.placeholder').each(function(){this.value=''})})}function g(m){var l={},n=/^jQuery\d+$/;$.each(m.attributes,function(p,o){if(o.specified&&!n.test(o.name)){l[o.name]=o.value}});return l}function b(m,n){var l=this,o=$(l);if(l.value==o.attr('placeholder')&&o.hasClass('placeholder')){if(o.data('placeholder-password')){o=o.hide().next().show().attr('id',o.removeAttr('id').data('placeholder-id'));if(m===true){return o[0].value=n}o.focus()}else{l.value='';o.removeClass('placeholder');l==h.activeElement&&l.select()}}}function e(){var q,l=this,p=$(l),m=p,o=this.id;if(l.value==''){if(l.type=='password'){if(!p.data('placeholder-textinput')){try{q=p.clone().attr({type:'text'})}catch(n){q=$('').attr($.extend(g(this),{type:'text'}))}q.removeAttr('name').data({'placeholder-password':true,'placeholder-id':o}).bind('focus.placeholder',b);p.data({'placeholder-textinput':q,'placeholder-id':o}).before(q)}p=p.removeAttr('id').hide().prev().attr('id',o).show()}p.addClass('placeholder');p[0].value=p.attr('placeholder')}else{p.removeClass('placeholder')}}}(this,document,jQuery)); \ No newline at end of file From 0f093aafc0db5b69a06730e2378351a808517705 Mon Sep 17 00:00:00 2001 From: Niko Date: Fri, 28 Dec 2012 10:59:07 +0100 Subject: [PATCH 003/305] [IMP] change placeholder inclusion logic bzr revid: nwi@openerp.com-20121228095907-3mgkosm1os8w3nzz --- addons/web/controllers/main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index cfe25bb9e97..59722d12372 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -530,8 +530,9 @@ html_template = """ From 3f0bbac7e72f812b80e2a9d83b4d18c4fc06ac8b Mon Sep 17 00:00:00 2001 From: "Bhumi Thakkar (Open ERP)" Date: Fri, 18 Jan 2013 19:00:50 +0530 Subject: [PATCH 004/305] [IMP] Project User can view the extra info tab and issue/contact can search by contact name or issue name. bzr revid: bth@tinyerp.com-20130118133050-1inczspvinjwnfkz --- addons/project_issue/project_issue_view.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 77375450e9a..eee7c196998 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -95,7 +95,7 @@ - + @@ -150,7 +150,7 @@ project.issue - + From f690a9310c6b78848a7caf95e87abfdce478baba Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 13 Feb 2013 08:25:57 +0100 Subject: [PATCH 005/305] [IMP] prefix searchview tests for easy filtering bzr revid: xmo@openerp.com-20130213072557-er5xl9xcj17mhuqe --- addons/web/static/test/search.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/addons/web/static/test/search.js b/addons/web/static/test/search.js index 292954b1992..09d135ca203 100644 --- a/addons/web/static/test/search.js +++ b/addons/web/static/test/search.js @@ -1,4 +1,4 @@ -openerp.testing.section('query', { +openerp.testing.section('search.query', { dependencies: ['web.search'] }, function (test) { test('Adding a facet to the query creates a facet and a value', function (instance) { @@ -180,7 +180,7 @@ var makeSearchView = function (instance, dummy_widget_attributes, defaults) { }); return view; }; -openerp.testing.section('defaults', { +openerp.testing.section('search.defaults', { dependencies: ['web.search'], rpc: 'mock', templates: true, @@ -331,7 +331,7 @@ openerp.testing.section('defaults', { }); }); }); -openerp.testing.section('completions', { +openerp.testing.section('search.completions', { dependencies: ['web.search'], rpc: 'mock', templates: true @@ -564,7 +564,7 @@ openerp.testing.section('completions', { }); }); }); -openerp.testing.section('search-serialization', { +openerp.testing.section('search.serialization', { dependencies: ['web.search'], rpc: 'mock', templates: true @@ -871,7 +871,7 @@ openerp.testing.section('search-serialization', { return $.when(t1, t2); }); }); -openerp.testing.section('removal', { +openerp.testing.section('search.removal', { dependencies: ['web.search'], rpc: 'mock', templates: true @@ -894,7 +894,7 @@ openerp.testing.section('removal', { }); }); }); -openerp.testing.section('drawer', { +openerp.testing.section('search.drawer', { dependencies: ['web.search'], rpc: 'mock', templates: true @@ -910,7 +910,7 @@ openerp.testing.section('drawer', { }); }); }); -openerp.testing.section('filters', { +openerp.testing.section('search.filters', { dependencies: ['web.search'], rpc: 'mock', templates: true, @@ -995,7 +995,7 @@ openerp.testing.section('filters', { }); }); }); -openerp.testing.section('saved_filters', { +openerp.testing.section('search.filters.saved', { dependencies: ['web.search'], rpc: 'mock', templates: true @@ -1077,7 +1077,7 @@ openerp.testing.section('saved_filters', { }); }); }); -openerp.testing.section('advanced', { +openerp.testing.section('search.advanced', { dependencies: ['web.search'], rpc: 'mock', templates: true From dfb7493034f2b102d647967654f5c99893450e2b Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 13 Feb 2013 10:01:08 +0100 Subject: [PATCH 006/305] [FIX] add support for invisibility to fields (don't complete an invisible field) bzr revid: xmo@openerp.com-20130213090108-h38emnwscgb5v1pu --- addons/web/doc/search_view.rst | 11 +++++++ addons/web/static/src/js/search.js | 23 ++++++++------ addons/web/static/test/search.js | 49 ++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/addons/web/doc/search_view.rst b/addons/web/doc/search_view.rst index 16a91c5e9ac..12b0eaf3104 100644 --- a/addons/web/doc/search_view.rst +++ b/addons/web/doc/search_view.rst @@ -107,6 +107,12 @@ formatted differently). If an input *may* fetch multiple completion items, it *should* prefix those with a section title using its own name. This has no technical consequence but is clearer for users. +.. note:: + + If a field is :js:func:`invisible + `, its completion function will + *not* be called. + Providing drawer/supplementary UI +++++++++++++++++++++++++++++++++ @@ -145,6 +151,11 @@ started only once (per view). dynamically collects, lays out and renders filters? => exercises drawer thingies +.. note:: + + An :js:func:`invisible ` input + will not be inserted into the drawer. + Converting from facet objects +++++++++++++++++++++++++++++ diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 8391f7a8ca5..8422f4e55fe 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -499,6 +499,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea */ complete_global_search: function (req, resp) { $.when.apply(null, _(this.inputs).chain() + .filter(function (input) { return input.visible(); }) .invoke('complete', req.term) .value()).then(function () { resp(_(_(arguments).compact()).flatten(true)); @@ -903,8 +904,8 @@ instance.web.search.Input = instance.web.search.Widget.extend( /** @lends instan */ init: function (view) { this._super(view); + this.load_attrs({}); this.view.inputs.push(this); - this.style = undefined; }, /** * Fetch auto-completion values for the widget. @@ -952,15 +953,19 @@ instance.web.search.Input = instance.web.search.Widget.extend( /** @lends instan "get_domain not implemented for widget " + this.attrs.type); }, load_attrs: function (attrs) { - if (attrs.modifiers) { - attrs.modifiers = JSON.parse(attrs.modifiers); - attrs.invisible = attrs.modifiers.invisible || false; - if (attrs.invisible) { - this.style = 'display: none;' - } - } + attrs.modifiers = attrs.modifiers ? JSON.parse(attrs.modifiers) : {}; this.attrs = attrs; - } + }, + /** + * Returns whether the input is "visible". The default behavior is to + * query the ``modifiers.invisible`` flag on the input's description or + * view node. + * + * @returns {Boolean} + */ + visible: function () { + return !this.attrs.modifiers.invisible; + }, }); instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends instance.web.search.FilterGroup# */{ template: 'SearchView.filters', diff --git a/addons/web/static/test/search.js b/addons/web/static/test/search.js index 09d135ca203..340784e657d 100644 --- a/addons/web/static/test/search.js +++ b/addons/web/static/test/search.js @@ -1158,3 +1158,52 @@ openerp.testing.section('search.advanced', { }); // TODO: UI tests? }); +openerp.testing.section('search.invisible', { + dependencies: ['web.search'], + rpc: 'mock', + templates: true, +}, function (test) { + // Invisible fields should not auto-complete + test('invisible-no-autocomplete', {asserts: 1}, function (instance, $fix, mock) { + instance.web.search.fields.add('test', 'instance.test.TestWidget'); + instance.test = { + TestWidget: instance.web.search.Field.extend({ + complete: function () { + return $.when([{label: this.attrs.string}]); + }, + }), + }; + var fields = { + field0: {type: 'test', string: 'Field 0'}, + field1: {type: 'test', string: 'Field 1'}, + }; + mock('ir.filters:get_filters', function () { return []; }); + mock('test.model:fields_get', function () { return fields; }); + mock('test.model:fields_view_get', function () { + return { + type: 'search', + fields: fields, + arch: '' + + '' + + '' + + '' + }; + }); + var ds = new instance.web.DataSet(null, 'test.model'); + var view = new instance.web.SearchView(null, ds, false); + return view.appendTo($fix) + .then(function () { + var done = $.Deferred(); + view.complete_global_search({term: 'test'}, function (comps) { + done.resolve(comps); + }); + return done; + }).then(function (completions) { + deepEqual(completions, [{label: 'Field 0'}], + "should only complete the visible field"); + }); + }); + // Invisible filters should not appear in the drawer + // Invisible filter groups should not appear in the drawer + // Group invisibility should be inherited by children +}); From 2d87d908e244d6f0fc7621038e90f29621a6f8ea Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 13 Feb 2013 13:56:06 +0100 Subject: [PATCH 007/305] [IMP] do not add empty filter groups to the drawer columns bzr revid: xmo@openerp.com-20130213125606-ykbb2kg9wvfbs2ho --- addons/web/static/src/js/search.js | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 8422f4e55fe..50065b300b4 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -1715,15 +1715,22 @@ instance.web.search.Filters = instance.web.search.Input.extend({ .sum() .value(); - var col1 = [], col2 = _(this.view.controls).map(function (inputs, group) { - var filters = _(inputs).filter(is_group); - return { - name: group === 'null' ? "q " + _t("Filters") : "w " + group, - filters: filters, - length: _(filters).chain().map(function (i) { - return i.filters.length; }).sum().value() - }; - }); + var col1 = [], col2 = _(this.view.controls).chain() + .map(function (inputs, group) { + return {group: group, inputs: inputs}; + }).reject(function (item) { + return _(item.inputs).isEmpty(); + }).map(function (item) { + var filters = _(item.inputs).filter(is_group); + return { + name: item.group === 'null' + ? "q " + _t("Filters") + : "w " + item.group, + filters: filters, + length: _(filters).chain().map(function (i) { + return i.filters.length; }).sum().value() + }; + }).value(); while (col2.length) { // col1 + group should be smaller than col2 + group From 9852c6ff1e212df4e944b61cc9d1ad586b5f08ef Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 14 Feb 2013 08:43:02 +0100 Subject: [PATCH 008/305] [FIX] missing support for invisible fields and groups in new search view lp bug: https://launchpad.net/bugs/1122183 fixed bzr revid: xmo@openerp.com-20130214074302-rwm2hcmv9mpvp9dv --- addons/web/static/src/js/search.js | 141 ++++++++++++++++----------- addons/web/static/src/xml/base.xml | 11 +-- addons/web/static/test/search.js | 151 ++++++++++++++++++++++++----- 3 files changed, 215 insertions(+), 88 deletions(-) diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 50065b300b4..c57dec67229 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -359,7 +359,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea this.has_defaults = !_.isEmpty(this.defaults); this.inputs = []; - this.controls = {}; + this.controls = []; this.headless = this.options.hidden && !this.has_defaults; @@ -588,18 +588,18 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea * * @param {Array} items a list of nodes to convert to widgets * @param {Object} fields a mapping of field names to (ORM) field attributes - * @param {String} [group_name] name of the group to put the new controls in + * @param {Object} [group] group to put the new controls in */ - make_widgets: function (items, fields, group_name) { - group_name = group_name || null; - if (!(group_name in this.controls)) { - this.controls[group_name] = []; + make_widgets: function (items, fields, group) { + if (!group) { + group = new instance.web.search.Group( + this, 'q', {attrs: {string: _t("Filters")}}); } - var self = this, group = this.controls[group_name]; + var self = this; var filters = []; _.each(items, function (item) { if (filters.length && item.tag !== 'filter') { - group.push(new instance.web.search.FilterGroup(filters, this)); + group.push(new instance.web.search.FilterGroup(filters, group)); filters = []; } @@ -607,15 +607,18 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea case 'separator': case 'newline': break; case 'filter': - filters.push(new instance.web.search.Filter(item, this)); + filters.push(new instance.web.search.Filter(item, group)); break; case 'group': - self.make_widgets(item.children, fields, item.attrs.string); + self.make_widgets(item.children, fields, + new instance.web.search.Group(group, 'w', item)); break; case 'field': - group.push(this.make_field(item, fields[item['attrs'].name])); + var field = this.make_field( + item, fields[item['attrs'].name], group); + group.push(field); // filters - self.make_widgets(item.children, fields, group_name); + self.make_widgets(item.children, fields, group); break; } }, this); @@ -630,12 +633,13 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea * * @param {Object} item fields_view_get node for the field * @param {Object} field fields_get result for the field + * @param {Object} [parent] * @returns instance.web.search.Field */ - make_field: function (item, field) { + make_field: function (item, field, parent) { var obj = instance.web.search.fields.get_any( [item.attrs.widget, field.type]); if(obj) { - return new (obj) (item, field, this); + return new (obj) (item, field, parent || this); } else { console.group('Unknown field type ' + field.type); console.error('View node', item); @@ -870,13 +874,18 @@ instance.web.search.Widget = instance.web.Widget.extend( /** @lends instance.web * @constructs instance.web.search.Widget * @extends instance.web.Widget * - * @param view the ancestor view of this widget + * @param parent parent of this widget */ - init: function (view) { - this._super(view); - this.view = view; + init: function (parent) { + this._super(parent); + var ancestor = parent; + do { + this.view = ancestor; + } while (!(ancestor instanceof instance.web.SearchView) + && (ancestor = (ancestor.getParent && ancestor.getParent()))); } }); + instance.web.search.add_expand_listener = function($root) { $root.find('a.searchview_group_string').click(function (e) { $root.toggleClass('folded expanded'); @@ -885,13 +894,24 @@ instance.web.search.add_expand_listener = function($root) { }); }; instance.web.search.Group = instance.web.search.Widget.extend({ - template: 'SearchView.group', - init: function (view_section, view, fields) { - this._super(view); - this.attrs = view_section.attrs; - this.lines = view.make_widgets( - view_section.children, fields); - } + init: function (parent, icon, node) { + this._super(parent); + var attrs = node.attrs; + this.modifiers = attrs.modifiers = + attrs.modifiers ? JSON.parse(attrs.modifiers) : {}; + this.attrs = attrs; + this.icon = icon; + this.name = attrs.string; + this.children = []; + + this.view.controls.push(this); + }, + push: function (input) { + this.children.push(input); + }, + visible: function () { + return !this.modifiers.invisible; + }, }); instance.web.search.Input = instance.web.search.Widget.extend( /** @lends instance.web.search.Input# */{ @@ -900,10 +920,10 @@ instance.web.search.Input = instance.web.search.Widget.extend( /** @lends instan * @constructs instance.web.search.Input * @extends instance.web.search.Widget * - * @param view + * @param parent */ - init: function (view) { - this._super(view); + init: function (parent) { + this._super(parent); this.load_attrs({}); this.view.inputs.push(this); }, @@ -964,7 +984,18 @@ instance.web.search.Input = instance.web.search.Widget.extend( /** @lends instan * @returns {Boolean} */ visible: function () { - return !this.attrs.modifiers.invisible; + if (this.attrs.modifiers.invisible) { + return false; + } + var parent = this; + while ((parent = parent.getParent()) && + ( (parent instanceof instance.web.search.Group) + || (parent instanceof instance.web.search.Input))) { + if (!parent.visible()) { + return false; + } + } + return true; }, }); instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends instance.web.search.FilterGroup# */{ @@ -979,17 +1010,17 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in * @extends instance.web.search.Input * * @param {Array} filters elements of the group - * @param {instance.web.SearchView} view view in which the filters are contained + * @param {instance.web.SearchView} parent parent in which the filters are contained */ - init: function (filters, view) { + init: function (filters, parent) { // If all filters are group_by and we're not initializing a GroupbyGroup, // create a GroupbyGroup instead of the current FilterGroup if (!(this instanceof instance.web.search.GroupbyGroup) && _(filters).all(function (f) { return f.attrs.context && f.attrs.context.group_by; })) { - return new instance.web.search.GroupbyGroup(filters, view); + return new instance.web.search.GroupbyGroup(filters, parent); } - this._super(view); + this._super(parent); this.filters = filters; this.view.query.on('add remove change reset', this.proxy('search_change')); }, @@ -1108,6 +1139,7 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in var self = this; item = item.toLowerCase(); var facet_values = _(this.filters).chain() + .filter(function (filter) { return filter.visible(); }) .filter(function (filter) { var at = { string: filter.attrs.string || '', @@ -1134,8 +1166,8 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in instance.web.search.GroupbyGroup = instance.web.search.FilterGroup.extend({ icon: 'w', completion_label: _lt("Group by: %s"), - init: function (filters, view) { - this._super(filters, view); + init: function (filters, parent) { + this._super(filters, parent); // Not flanders: facet unicity is handled through the // (category, field) pair of facet attributes. This is all well and // good for regular filter groups where a group matches a facet, but for @@ -1143,8 +1175,8 @@ instance.web.search.GroupbyGroup = instance.web.search.FilterGroup.extend({ // view which proxies to the first GroupbyGroup, so it can be used // for every GroupbyGroup and still provides the various methods needed // by the search view. Use weirdo name to avoid risks of conflicts - if (!this.getParent()._s_groupby) { - this.getParent()._s_groupby = { + if (!this.view._s_groupby) { + this.view._s_groupby = { help: "See GroupbyGroup#init", get_context: this.proxy('get_context'), get_domain: this.proxy('get_domain'), @@ -1153,7 +1185,7 @@ instance.web.search.GroupbyGroup = instance.web.search.FilterGroup.extend({ } }, match_facet: function (facet) { - return facet.get('field') === this.getParent()._s_groupby; + return facet.get('field') === this.view._s_groupby; }, make_facet: function (values) { return { @@ -1178,10 +1210,10 @@ instance.web.search.Filter = instance.web.search.Input.extend(/** @lends instanc * @extends instance.web.search.Input * * @param node - * @param view + * @param parent */ - init: function (node, view) { - this._super(view); + init: function (node, parent) { + this._super(parent); this.load_attrs(node.attrs); }, facet_for: function () { return $.when(null); }, @@ -1197,10 +1229,10 @@ instance.web.search.Field = instance.web.search.Input.extend( /** @lends instanc * * @param view_section * @param field - * @param view + * @param parent */ - init: function (view_section, field, view) { - this._super(view); + init: function (view_section, field, parent) { + this._super(parent); this.load_attrs(_.extend({}, field, view_section.attrs)); }, facet_for: function (value) { @@ -1240,7 +1272,7 @@ instance.web.search.Field = instance.web.search.Input.extend( /** @lends instanc * * @param {String} name the field's name * @param {String} operator the field's operator (either attribute-specified or default operator for the field - * @param {Number|String} value parsed value for the field + * @param {Number|String} facet parsed value for the field * @returns {Array} domain to include in the resulting search */ make_domain: function (name, operator, facet) { @@ -1472,8 +1504,8 @@ instance.web.search.DateTimeField = instance.web.search.DateField.extend(/** @le }); instance.web.search.ManyToOneField = instance.web.search.CharField.extend({ default_operator: {}, - init: function (view_section, field, view) { - this._super(view_section, field, view); + init: function (view_section, field, parent) { + this._super(view_section, field, parent); this.model = new instance.web.Model(this.attrs.relation); }, complete: function (needle) { @@ -1716,16 +1748,13 @@ instance.web.search.Filters = instance.web.search.Input.extend({ .value(); var col1 = [], col2 = _(this.view.controls).chain() - .map(function (inputs, group) { - return {group: group, inputs: inputs}; - }).reject(function (item) { - return _(item.inputs).isEmpty(); - }).map(function (item) { - var filters = _(item.inputs).filter(is_group); + .reject(function (group) { + return _(group.children).isEmpty() || group.modifiers.invisible; + }).map(function (group) { + var filters = _(group.children).filter(is_group); return { - name: item.group === 'null' - ? "q " + _t("Filters") - : "w " + item.group, + name: _.str.sprintf("%s %s", + group.icon, group.name), filters: filters, length: _(filters).chain().map(function (i) { return i.filters.length; }).sum().value() diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index e31dcd2a4a0..655c4ddc9e2 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1496,7 +1496,7 @@
    -
  • @@ -1584,15 +1584,6 @@ - - - - - - - - -
    diff --git a/addons/web/static/test/search.js b/addons/web/static/test/search.js index 340784e657d..f180a97357d 100644 --- a/addons/web/static/test/search.js +++ b/addons/web/static/test/search.js @@ -1163,34 +1163,35 @@ openerp.testing.section('search.invisible', { rpc: 'mock', templates: true, }, function (test) { - // Invisible fields should not auto-complete - test('invisible-no-autocomplete', {asserts: 1}, function (instance, $fix, mock) { - instance.web.search.fields.add('test', 'instance.test.TestWidget'); - instance.test = { - TestWidget: instance.web.search.Field.extend({ - complete: function () { - return $.when([{label: this.attrs.string}]); - }, - }), - }; - var fields = { - field0: {type: 'test', string: 'Field 0'}, - field1: {type: 'test', string: 'Field 1'}, + var registerTestField = function (instance, methods) { + instance.web.search.fields.add('test', 'instance.testing.TestWidget'); + instance.testing = { + TestWidget: instance.web.search.Field.extend(methods), }; + }; + var makeView = function (instance, mock, fields, arch, defaults) { mock('ir.filters:get_filters', function () { return []; }); mock('test.model:fields_get', function () { return fields; }); mock('test.model:fields_view_get', function () { - return { - type: 'search', - fields: fields, - arch: '' + - '' + - '' + - '' - }; + return { type: 'search', fields: fields, arch: arch }; }); var ds = new instance.web.DataSet(null, 'test.model'); - var view = new instance.web.SearchView(null, ds, false); + return new instance.web.SearchView(null, ds, false, defaults); + }; + // Invisible fields should not auto-complete + test('invisible-field-no-autocomplete', {asserts: 1}, function (instance, $fix, mock) { + registerTestField(instance, { + complete: function () { + return $.when([{label: this.attrs.string}]); + }, + }); + var view = makeView(instance, mock, { + field0: {type: 'test', string: 'Field 0'}, + field1: {type: 'test', string: 'Field 1'}, + }, ['', + '', + '', + ''].join()); return view.appendTo($fix) .then(function () { var done = $.Deferred(); @@ -1204,6 +1205,112 @@ openerp.testing.section('search.invisible', { }); }); // Invisible filters should not appear in the drawer + test('invisible-filter-no-drawer', {asserts: 4}, function (instance, $fix, mock) { + var view = makeView(instance, mock, {}, [ + '', + '', + '', + ''].join()); + return view.appendTo($fix) + .then(function () { + var $fs = $fix.find('.oe_searchview_filters ul'); + strictEqual($fs.children().length, + 1, + "should only display one filter"); + strictEqual(_.str.trim($fs.children().text()), + "filter 0", + "should only display filter 0"); + var done = $.Deferred(); + view.complete_global_search({term: 'filter'}, function (comps) { + done.resolve(); + strictEqual(comps.length, 1, "should only complete visible filter"); + strictEqual(comps[0].label, "Filter on: filter 0", + "should complete filter 0"); + }); + return done; + }); + }); // Invisible filter groups should not appear in the drawer // Group invisibility should be inherited by children + test('group-invisibility', {asserts: 6}, function (instance, $fix, mock) { + registerTestField(instance, { + complete: function () { + return $.when([{label: this.attrs.string}]); + }, + }); + var view = makeView(instance, mock, { + field0: {type: 'test', string: 'Field 0'}, + field1: {type: 'test', string: 'Field 1'}, + }, [ + '', + '', + '', + '', + '', + '', + '', + '', + '', + '' + ].join('')); + return view.appendTo($fix) + .then(function () { + strictEqual($fix.find('.oe_searchview_filters h3').length, + 1, + "should only display one group"); + strictEqual($fix.find('.oe_searchview_filters h3').text(), + 'w Visibles', + "should only display the Visibles group (and its icon char)"); + + var $fs = $fix.find('.oe_searchview_filters ul'); + strictEqual($fs.children().length, 1, + "should only have one filter in the drawer"); + strictEqual(_.str.trim($fs.text()), "Filter 0", + "should have filter 0 as sole filter"); + + var done = $.Deferred(); + view.complete_global_search({term: 'filter'}, function (compls) { + done.resolve(); + strictEqual(compls.length, 2, + "should have 2 completions"); + deepEqual(_.pluck(compls, 'label'), + ['Field 0', 'Filter on: Filter 0'], + "should complete on field 0 and filter 0"); + }); + return done; + }); + }); + // Default on invisible fields should still work, for fields and filters both + test('invisible-defaults', {asserts: 1}, function (instance, $fix, mock) { + var view = makeView(instance, mock, { + field: {type: 'char', string: "Field"}, + field2: {type: 'char', string: "Field 2"}, + }, [ + '', + '', + '', + '', + '', + '', + '', + '' + ].join(''), {field: "foo", filter: true}); + + return view.appendTo($fix) + .then(function () { + deepEqual(view.build_search_data(), { + errors: [], + groupbys: [], + contexts: [], + domains: [ + // Generated from field + [['field', 'ilike', 'foo']], + // generated from filter + "[['whee', '=', '42']]" + ], + }, "should yield invisible fields selected by defaults"); + }); + }); }); From ab816b74fb007dd19c28dcb00281a2555ae7f2ee Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 14 Feb 2013 08:59:06 +0100 Subject: [PATCH 009/305] [FIX] correctly handle invisible or empty groups in total filters count computation bzr revid: xmo@openerp.com-20130214075906-kg83zi0pan61au1d --- addons/web/static/src/js/search.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index c57dec67229..bb846726b93 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -1740,17 +1740,19 @@ instance.web.search.Filters = instance.web.search.Input.extend({ var running_count = 0; // get total filters count var is_group = function (i) { return i instanceof instance.web.search.FilterGroup; }; - var filters_count = _(this.view.controls).chain() + var visible_filters = _(this.view.controls).chain().reject(function (group) { + return _(_(group.children).filter(is_group)).isEmpty() + || group.modifiers.invisible; + }); + var filters_count = visible_filters + .pluck('children') .flatten() .filter(is_group) .map(function (i) { return i.filters.length; }) .sum() .value(); - var col1 = [], col2 = _(this.view.controls).chain() - .reject(function (group) { - return _(group.children).isEmpty() || group.modifiers.invisible; - }).map(function (group) { + var col1 = [], col2 = visible_filters.map(function (group) { var filters = _(group.children).filter(is_group); return { name: _.str.sprintf("%s %s", From ecde0aa380867bfcdd8636d757681a15a4279a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 14 Feb 2013 09:55:06 +0100 Subject: [PATCH 010/305] [IMP] Chatter: now displays the 'To' that is the notified people of a given message. bzr revid: tde@openerp.com-20130214085506-5akyw06ho9vq1xd9 --- addons/mail/mail_message.py | 6 +++--- addons/mail/static/src/js/mail.js | 8 ++++++++ addons/mail/static/src/xml/mail.xml | 17 ++++++++++++++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 77573f0d2f8..91d46098b4b 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -301,8 +301,8 @@ class mail_message(osv.Model): for key, message in message_tree.iteritems(): if message.author_id: partner_ids |= set([message.author_id.id]) - if message.partner_ids: - partner_ids |= set([partner.id for partner in message.partner_ids]) + if message.notified_partner_ids: + partner_ids |= set([partner.id for partner in message.notified_partner_ids]) if message.attachment_ids: attachment_ids |= set([attachment.id for attachment in message.attachment_ids]) # Read partners as SUPERUSER -> display the names like classic m2o even if no access @@ -322,7 +322,7 @@ class mail_message(osv.Model): else: author = (0, message.email_from) partner_ids = [] - for partner in message.partner_ids: + for partner in message.notified_partner_ids: if partner.id in partner_tree: partner_ids.append(partner_tree[partner.id]) attachment_ids = [] diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 0872d049162..2c5b8d77dd1 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -262,6 +262,14 @@ openerp.mail = function (session) { this.author_id.push(_.str.escapeHTML(email[0]), _.str.trim(email[1]), email[2]); } } + if (this.partner_ids && this.partner_ids.length > 3) { + this.extra_partners_nbr = this.partner_ids.length - 3; + this.extra_partners_str = '' + var extra_partners = this.partner_ids.slice(3); + for (var key in extra_partners) { + this.extra_partners_str += extra_partners[key][1]; + } + } }, diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 4693adeb1ee..574d65a6458 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -243,7 +243,22 @@
    - + From: + + To: + + Nobody + + + + + + + , + + + and more + From 4bcd2325dd35fd96d230aae21d08b0cbc7717843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 14 Feb 2013 10:39:23 +0100 Subject: [PATCH 011/305] [IMP] Chatter: File -> Attach a file. bzr revid: tde@openerp.com-20130214093923-64459u87eda0yr47 --- addons/mail/static/src/xml/mail.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 574d65a6458..738a9d5d0e7 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -57,7 +57,7 @@ - File + Attach a File /web/binary/upload_attachment From f33db4543c8624f2bb098ff8482354af4e293ede Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 14 Feb 2013 12:19:25 +0100 Subject: [PATCH 012/305] [IMP] mail: 'Advanced wizard' in upper-right corner bzr revid: chm@openerp.com-20130214111925-dnknlv4jdkpxx8se --- addons/mail/static/src/css/mail.css | 20 +++++++++++--------- addons/mail/static/src/xml/mail.xml | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index e4e112cdfaf..5c0817b3ff0 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -458,18 +458,20 @@ .openerp .oe_mail .oe_hidden_input_file, .openerp .oe_mail .oe_hidden_input_file form{ display:inline; } -.openerp .oe_mail .oe_msg_footer button.oe_full{ - width:24px; - overflow:hidden; +.openerp .oe_mail .oe_msg_center button.oe_full{ + width: 24px; + height: 22px; + overflow: hidden; float: right; - filter:none; + filter: none; } -.openerp .oe_mail .oe_msg_footer button.oe_full .oe_e{ +.openerp .oe_mail .oe_msg_center button.oe_full .oe_e{ position: relative; - top: -4px; - margin-left: -9px; - vertical-align: middle; - filter:none; + top: -9px; + margin-left: -5px; + vertical-align: top; + filter: none; + height: 14px; } .openerp .oe_mail button.oe_attach, .openerp .oe_mail button.oe_full{ background: transparent; diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 738a9d5d0e7..9a98f52d779 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -26,6 +26,7 @@
    +
    @@ -34,7 +35,6 @@ -
    From ca76cb8d32a12217cffd50a445565312e4beb0c1 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 14 Feb 2013 12:20:36 +0100 Subject: [PATCH 013/305] [IMP] mail: 'Post' to 'Send to followers' bzr revid: chm@openerp.com-20130214112036-qgekbxegb8a3ihjh --- addons/mail/static/src/xml/mail.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 9a98f52d779..4a5473981d5 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -32,7 +32,7 @@ From 6f1f3f8a03ab4edc3dddd874786bc167b834f229 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 14 Feb 2013 12:37:02 +0100 Subject: [PATCH 014/305] [IMP] mail: add 'Log a note' button right after 'Send to followers' button bzr revid: chm@openerp.com-20130214113702-ste98tekdcj4kt4w --- addons/mail/static/src/css/mail.css | 3 ++- addons/mail/static/src/js/mail.js | 17 ++++++++++++++--- addons/mail/static/src/xml/mail.xml | 1 + 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 5c0817b3ff0..72ecb5d4d07 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -439,7 +439,8 @@ line-height: 12px; vertical-align: middle; } -.openerp .oe_mail .oe_msg_footer button.oe_post{ +.openerp .oe_mail .oe_msg_footer button.oe_post, +.openerp .oe_mail .oe_msg_footer button.oe_log{ position: relative; z-index: 2; } diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 2c5b8d77dd1..5c36c32c15a 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -495,6 +495,7 @@ openerp.mail = function (session) { this.$('.oe_cancel').on('click', _.bind( this.on_cancel, this) ); this.$('.oe_post').on('click', _.bind( this.on_message_post, this) ); + this.$('.oe_log').on('click', _.bind( this.on_message_log, this) ); this.$('.oe_full').on('click', _.bind( this.on_compose_fullmail, this, this.id ? 'reply' : 'comment') ); /* stack for don't close the compose form if the user click on a button */ this.$('.oe_msg_left, .oe_msg_center').on('mousedown', _.bind( function () { this.stay_open = true; }, this)); @@ -632,17 +633,27 @@ openerp.mail = function (session) { } }, + on_message_log: function (event) { + if (this.do_check_attachment_upload() && (this.attachment_ids.length || this.$('textarea').val().match(/\S+/))) { + this.do_send_message_post([], true); + } + }, + /*do post a message and fetch the message*/ - do_send_message_post: function (partner_ids) { + do_send_message_post: function (partner_ids, log) { var self = this; - this.parent_thread.ds_thread._model.call('message_post_user_api', [this.context.default_res_id], { + var values = { 'body': this.$('textarea').val(), 'subject': false, 'parent_id': this.context.default_parent_id, 'attachment_ids': _.map(this.attachment_ids, function (file) {return file.id;}), 'partner_ids': partner_ids, 'context': this.parent_thread.context, - }).done(function (message_id) { + }; + if (log) { + values['subtype'] = false; + } + this.parent_thread.ds_thread._model.call('message_post_user_api', [this.context.default_res_id], values).done(function (message_id) { var thread = self.parent_thread; var root = thread == self.options.root_thread; if (self.options.display_indented_thread < self.thread_level && thread.parent_message) { diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 4a5473981d5..bc8a8dc30ed 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -33,6 +33,7 @@ From 58de3dfad7ace01ba81d2ab52e80a094e342de17 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 14 Feb 2013 12:47:55 +0100 Subject: [PATCH 015/305] [IMP] mail: background color lighter for log message bzr revid: chm@openerp.com-20130214114755-esrkjiay6rcb00f3 --- addons/mail/static/src/css/mail.css | 3 +++ addons/mail/static/src/xml/mail.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 72ecb5d4d07..c441499ddd8 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -54,6 +54,9 @@ min-height: 42px; border: solid 1px rgba(0,0,0,0.03); } +.openerp .oe_mail .oe_msg.oe_msg_nobody{ + background: #F8F8F8; +} .openerp .oe_mail .oe_msg .oe_msg_left{ position: absolute; left:0; top: 0; bottom: 0; width: 40px; diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index bc8a8dc30ed..e9c5f7bc424 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -213,7 +213,7 @@ -
    +
    From b419e8b6c2f81980604a47c6cab9c6b5f673fe86 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 14 Feb 2013 15:34:17 +0100 Subject: [PATCH 016/305] [IMP] mail: _get_message_data: display only number of new messages bzr revid: chm@openerp.com-20130214143417-suf0tyonrewh2a7l --- addons/mail/mail_thread.py | 21 ++++++++++----------- addons/project/project_view.xml | 2 +- addons/project_issue/project_issue_view.xml | 4 ++++ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 46052b07fdf..03f681ce501 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -35,6 +35,7 @@ from openerp import SUPERUSER_ID from openerp.addons.mail.mail_message import decode from openerp.osv import fields, osv from openerp.tools.safe_eval import safe_eval as eval +from openerp.tools.translate import _ _logger = logging.getLogger(__name__) @@ -92,23 +93,21 @@ class mail_thread(osv.AbstractModel): """ Computes: - message_unread: has uid unread message for the document - message_summary: html snippet summarizing the Chatter for kanban views """ - res = dict((id, dict(message_unread=False, message_summary='')) for id in ids) + res = dict((id, dict(message_unread=False, message_summary=' ')) for id in ids) user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] # search for unread messages, directly in SQL to improve performances - cr.execute(""" SELECT m.res_id FROM mail_message m + cr.execute(""" SELECT m.res_id, COUNT(n.message_id) as nb FROM mail_message m RIGHT JOIN mail_notification n ON (n.message_id = m.id AND n.partner_id = %s AND (n.read = False or n.read IS NULL)) - WHERE m.model = %s AND m.res_id in %s""", + WHERE m.model = %s AND m.res_id in %s + GROUP BY m.res_id""", (user_pid, self._name, tuple(ids),)) - msg_ids = [result[0] for result in cr.fetchall()] - for msg_id in msg_ids: - res[msg_id]['message_unread'] = True - - for thread in self.browse(cr, uid, ids, context=context): - cls = res[thread.id]['message_unread'] and ' class="oe_kanban_mail_new"' or '' - res[thread.id]['message_summary'] = "9 %d + %d" % (cls, len(thread.message_ids), len(thread.message_follower_ids)) - + for result in cr.fetchall(): + res[result[0]]['message_unread'] = True + if result[1]: + title = result[1] > 1 and _("There are %d messages unread") % result[1] or _("There is 1 message unread") + res[result[0]]['message_summary'] = "9 %d %s" % (title, result[1], _("New")) return res def _get_subscription_data(self, cr, uid, ids, name, args, context=None): diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index b102c01193d..b49d61c8e98 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -476,7 +476,6 @@ - @@ -523,6 +522,7 @@
    diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 77375450e9a..60afb7a90f3 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -201,6 +201,7 @@ +
      @@ -251,6 +252,9 @@
    +
    From fd315b66f27665418ac2c24bf44d4cfc40330cc3 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 14 Feb 2013 15:39:38 +0100 Subject: [PATCH 017/305] [IMP] mail: You have one unread message bzr revid: chm@openerp.com-20130214143938-aeemq70wl5gnf3k1 --- addons/mail/mail_thread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 03f681ce501..d026f62667a 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -106,7 +106,7 @@ class mail_thread(osv.AbstractModel): for result in cr.fetchall(): res[result[0]]['message_unread'] = True if result[1]: - title = result[1] > 1 and _("There are %d messages unread") % result[1] or _("There is 1 message unread") + title = result[1] > 1 and _("You have %d unread messages") % result[1] or _("You have one unread message") res[result[0]]['message_summary'] = "9 %d %s" % (title, result[1], _("New")) return res From d374ffba59d64408098f4931be10a70e6a05cb51 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 14 Feb 2013 16:09:49 +0100 Subject: [PATCH 018/305] [IMP] mail_followers: When unfollowing: confirmation box bzr revid: chm@openerp.com-20130214150949-hgdlxvd4g18734mu --- addons/mail/static/src/js/mail_followers.js | 28 +++++++++++++-------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js index 03351c24ac2..8cc85bf1449 100644 --- a/addons/mail/static/src/js/mail_followers.js +++ b/addons/mail/static/src/js/mail_followers.js @@ -106,9 +106,12 @@ openerp_mail_followers = function(session, mail) { on_remove_follower: function (event) { var partner_id = $(event.target).data('id'); - var context = new session.web.CompoundContext(this.build_context(), {}); - return this.ds_model.call('message_unsubscribe', [[this.view.datarecord.id], [partner_id], context]) - .then(this.proxy('read_value')); + var name = $(event.target).parent().find("a").html(); + if (confirm(_.str.sprintf(_t("Warning! \n %s won't be notified of any email or discussion on this document. Do you really want to remove him from the followers ?"), name))) { + var context = new session.web.CompoundContext(this.build_context(), {}); + return this.ds_model.call('message_unsubscribe', [[this.view.datarecord.id], [partner_id], context]) + .then(this.proxy('read_value')); + } }, read_value: function () { @@ -246,12 +249,15 @@ openerp_mail_followers = function(session, mail) { }, do_unfollow: function () { - _(this.$('.oe_msg_subtype_check')).each(function (record) { - $(record).attr('checked',false); - }); - var context = new session.web.CompoundContext(this.build_context(), {}); - return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context]) - .then(this.proxy('read_value')); + if (confirm(_t("Warning! \nYou won't be notified of any email or discussion on this document. Do you really want to unfollow this document ?"))) { + _(this.$('.oe_msg_subtype_check')).each(function (record) { + $(record).attr('checked',false); + }); + var context = new session.web.CompoundContext(this.build_context(), {}); + return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context]) + .then(this.proxy('read_value')); + } + return false; }, do_update_subscription: function (event) { @@ -265,7 +271,9 @@ openerp_mail_followers = function(session, mail) { }); if (!checklist.length) { - this.do_unfollow(); + if (!this.do_unfollow()) { + $(event.target).attr("checked", "checked") + } } else { var context = new session.web.CompoundContext(this.build_context(), {}); return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], checklist, context]) From 336f7cb9a5d439dbb12f52f6b379bdd30042be5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 14 Feb 2013 17:19:50 +0100 Subject: [PATCH 019/305] [FIX] Chatter: fixed from/to that werebadly placed. bzr revid: tde@openerp.com-20130214161950-gnmtyktm1p88ge8t --- addons/mail/static/src/xml/mail.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index e9c5f7bc424..4c003cbeae1 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -245,6 +245,7 @@
    From: + To: @@ -260,7 +261,6 @@ and more - From 3a8426e5c5b1ae91904d7333ed91f48044da45cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 14 Feb 2013 17:20:11 +0100 Subject: [PATCH 020/305] [IMP] mail: message_new in mailgateway is done with nolog context: no 'document created' message. bzr revid: tde@openerp.com-20130214162011-0xhjo7l5y8733usr --- addons/mail/mail_thread.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index d026f62667a..4539d3357a8 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -605,6 +605,7 @@ class mail_thread(osv.AbstractModel): if thread_id and hasattr(model_pool, 'message_update'): model_pool.message_update(cr, user_id, [thread_id], msg, context=nosub_ctx) else: + nosub_ctx = dict(nosub_ctx, mail_create_nolog=True) thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=nosub_ctx) else: assert thread_id == 0, "Posting a message without model should be with a null res_id, to create a private message." From 6d19450457ded5b9a57cb88b7eb4dc6d90dfff02 Mon Sep 17 00:00:00 2001 From: "DBR (OpenERP)" Date: Mon, 18 Feb 2013 18:21:17 +0530 Subject: [PATCH 021/305] [IMP]account_analytic_analysis: Add Recurrent virtual invoice functionality Try to set cron job but and test it's function with temp invoice btn bzr revid: dbr@tinyerp.com-20130218125117-4sqbhxb1vro2nzhp --- .../account_analytic_analysis.py | 96 ++++++++++++++++++- .../account_analytic_analysis_cron.xml | 13 +++ .../account_analytic_analysis_view.xml | 53 ++++++++++ .../cron_account_analytic_account.py | 57 +++++++++++ .../hr_timesheet_final_invoice_create.py | 2 +- 5 files changed, 219 insertions(+), 2 deletions(-) diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 23d8faaffce..809b4e2f3fb 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -25,7 +25,9 @@ import openerp.tools from openerp.tools.translate import _ from openerp.addons.decimal_precision import decimal_precision as dp - +import time +import datetime +from dateutil.relativedelta import relativedelta class account_analytic_account(osv.osv): _name = "account.analytic.account" @@ -391,6 +393,44 @@ class account_analytic_account(osv.osv): res[account.id]['toinvoice_total'] = self._get_total_toinvoice(account) return res + def _calc_virtual_invoice(self, cr, uid, ids, name, arg, context=None): + res = {} + analytic_line_obj = self.pool.get('account.analytic.line') + product_obj = self.pool.get('product.product') + if ids: + journal_types = {} + timesheets1 = [] + for account in self.browse(cr, uid, ids, context=context): + timesheets1 = analytic_line_obj.search(cr, uid,[('account_id','=', account.id),('invoice_id','=', None)]) + res[account.id]= {'product_id': 0, 'user_id': 0, 'qty': 0.0 , 'uom': 0, 'description':'', 'unit_price': 0.0, 'sub_total':0.0} + for line in analytic_line_obj.browse(cr, uid, timesheets1, context=context): + if line.journal_id.type not in journal_types: + journal_types[line.journal_id.type] = set() + for journal_type in journal_types: + cr.execute("""SELECT product_id, user_id, to_invoice, unit_amount, product_uom_id, date + FROM account_analytic_line as line LEFT JOIN account_analytic_journal journal ON (line.journal_id = journal.id) + WHERE account_id = %s + AND invoice_id IS NULL AND journal.type = %s AND to_invoice IS NOT NULL + GROUP BY product_id, user_id, to_invoice, unit_amount, product_uom_id, date""", (account.id, journal_type)) + + for product_id, user_id, to_invoice, unit_amount, product_uom_id, date in cr.fetchall(): + product = product_obj.browse(cr, uid, product_id) + description = product.description + unit_price = product.list_price + sub_total = product.list_price * unit_amount + print"--------------------------------->",description, unit_price, sub_total + res[account.id]['product_id']= product_id, + res[account.id]['user_id'] = user_id, + res[account.id]['qty'] = unit_amount, + res[account.id]['uom'] = product_uom_id, + res[account.id]['date'] = date, + res[account.id]['descriptions'] = description, + res[account.id]['unit_price'] = unit_price, + res[account.id]['sub_total'] = sub_total, + + print"\n\n**********************************>",res + return res + _columns = { 'is_overdue_quantity' : fields.function(_is_overdue_quantity, method=True, type='boolean', string='Overdue Quantity', store={ @@ -452,6 +492,26 @@ class account_analytic_account(osv.osv): 'invoiced_total' : fields.function(_sum_of_fields, type="float",multi="sum_of_all", string="Total Invoiced"), 'remaining_total' : fields.function(_sum_of_fields, type="float",multi="sum_of_all", string="Total Remaining", help="Expectation of remaining income for this contract. Computed as the sum of remaining subtotals which, in turn, are computed as the maximum between '(Estimation - Invoiced)' and 'To Invoice' amounts"), 'toinvoice_total' : fields.function(_sum_of_fields, type="float",multi="sum_of_all", string="Total to Invoice", help=" Sum of everything that could be invoiced for this contract."), + # New Fields added + 'recurring_invoices' : fields.boolean('Recurring Invoices'), + 'product_id': fields.function(_calc_virtual_invoice, type='many2one',relation="product.product",string='Product(s)', multi="vinvline"), + 'user_id': fields.function(_calc_virtual_invoice, type='many2one', relation="res.users",string='User(s)', multi="vinvline"), + 'descriptions': fields.function(_calc_virtual_invoice, type='char', string='Description', multi="vinvline"), + 'qty': fields.function(_calc_virtual_invoice, type='char', string='Quantity', multi="vinvline"), + 'uom': fields.function(_calc_virtual_invoice, type='many2one', string='Unit of Measure', relation="product.uom",multi="vinvline"), + 'date': fields.function(_calc_virtual_invoice, type='date', string='Date', multi="vinvline"), + 'unit_price':fields.function(_calc_virtual_invoice, type='char', string='Unit Price', multi="vinvline"), + 'sub_total':fields.function(_calc_virtual_invoice, type='char', string='Sub Total', multi="vinvline"), + 'rrule_type': fields.selection([ + ('daily', 'Day(s)'), + ('weekly', 'Week(s)'), + ('monthly', 'Month(s)') + ], 'Recurrency', help="Let the event automatically repeat at that interval"), + 'interval': fields.integer('Repeat Every', help="Repeat every (Days/Week/Month/Year)"), + 'next_date': fields.date('Next Date'), + 'amount_tax': fields.float('Tax'), + 'amount_total': fields.float('Total'), + } def open_sale_order_lines(self,cr,uid,ids,context=None): @@ -484,6 +544,40 @@ class account_analytic_account(osv.osv): res['value']['to_invoice'] = template.to_invoice.id res['value']['pricelist_id'] = template.pricelist_id.id return res + + def onchange_recurring_invoices(self, cr, uid, ids, date_start=False, parent_id=False, context=None): + result = {} + if date_start: + result = {'value': { + 'next_date': date_start, + 'rrule_type':'monthly' + } + } + return result + + + def cron_create_invoice1(self, cr, uid, ids, context=None): + res = {} + inv_obj = self.pool.get('account.invoice') + journal_obj = self.pool.get('account.journal') + inv_lines = [] + + contract_ids = self.search(cr, uid, [('next_date','<=',time.strftime("%Y-%m-%d")), ('state','=', 'open'), ('recurring_invoices','=', True)], context=context, order='name asc') + context.update({'active_ids':ids}) + contracts = self.browse(cr, uid, contract_ids, context=context) + for contract in contracts: + next_date = datetime.datetime.strptime(contract.next_date, "%Y-%m-%d") + interval = contract.interval + if contract.rrule_type == 'monthly': + new_date = next_date+relativedelta(months=+interval) + if contract.rrule_type == 'daily': + new_date = next_date+relativedelta(days=+interval) + if contract.rrule_type == 'weekly': + new_date = next_date+relativedelta(weeks=+interval) + # Need to Link this new invoice to related contract + contract.write({'next_date':new_date}, context=context) + return self.pool.get('hr.timesheet.invoice.create.final').do_create(cr, uid, contract_ids, context=context) + account_analytic_account() class account_analytic_account_summary_user(osv.osv): diff --git a/addons/account_analytic_analysis/account_analytic_analysis_cron.xml b/addons/account_analytic_analysis/account_analytic_analysis_cron.xml index 8564b1606eb..53ecc0c8c55 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis_cron.xml +++ b/addons/account_analytic_analysis/account_analytic_analysis_cron.xml @@ -11,5 +11,18 @@ + + + Recurrent Report for Invoice + + + 1 + minutes + -1 + + + + + diff --git a/addons/account_analytic_analysis/account_analytic_analysis_view.xml b/addons/account_analytic_analysis/account_analytic_analysis_view.xml index f8f718882af..104e66d5d47 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis_view.xml +++ b/addons/account_analytic_analysis/account_analytic_analysis_view.xml @@ -138,6 +138,59 @@ attrs="{'required': [('invoice_on_timesheets', '=', True)]}"/> + + From 94c07f4d6ec798634abeb6227b19361133f262ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 26 Feb 2013 11:02:06 +0100 Subject: [PATCH 058/305] [IMP] mail: mail_group chatter placeholder improvement. bzr revid: tde@openerp.com-20130226100206-yqzmnn02yfupd438 --- addons/mail/mail_group_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index fba192b18bc..77c0b8f6c86 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -106,7 +106,7 @@
    - +
    From 6c3aa520c71e30be0b7a436c43c915f395d7a67b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 26 Feb 2013 11:57:55 +0100 Subject: [PATCH 059/305] [IMP] mail_followers: improved _notify, to better manage notifications. An optional (to provide compatibility) parameter has been added, that allows to notify partners of a given messages. It checks whether a notification already exists, setting it back as unread, or create a notification pushed by email. bzr revid: tde@openerp.com-20130226105755-gvkn1ocpwkcndzpl --- addons/mail/mail_followers.py | 36 +++++++++++++++++++++++++++-------- addons/mail/mail_message.py | 5 ++--- addons/mail/mail_thread.py | 8 +------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index a9d33f4585d..25772d7bb33 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -75,16 +75,21 @@ class mail_notification(osv.Model): if not cr.fetchone(): cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, read, starred, message_id)') - def get_partners_to_notify(self, cr, uid, message, context=None): + def get_partners_to_notify(self, cr, uid, message, partners_to_notify=None, context=None): """ Return the list of partners to notify, based on their preferences. :param browse_record message: mail.message to notify + :param list partners_to_notify: optional list of partner ids restricting + the notifications to process """ notify_pids = [] for notification in message.notification_ids: if notification.read: continue partner = notification.partner_id + # If partners_to_notify specified: restrict to them + if partners_to_notify and partner.id not in partners_to_notify: + continue # Do not send to partners without email address defined if not partner.email: continue @@ -100,16 +105,35 @@ class mail_notification(osv.Model): notify_pids.append(partner.id) return notify_pids - def _notify(self, cr, uid, msg_id, context=None): - """ Send by email the notification depending on the user preferences """ + def _notify(self, cr, uid, msg_id, partners_to_notify=None, context=None): + """ Send by email the notification depending on the user preferences + + :param list partners_to_notify: optional list of partner ids restricting + the notifications to process + """ if context is None: context = {} + mail_message_obj = self.pool.get('mail.message') + + # optional list of partners to notify: subscribe them if not already done or update the notification + if partners_to_notify: + notifications_to_update = [] + notified_partners = [] + notif_ids = self.search(cr, SUPERUSER_ID, [('message_id', '=', msg_id), ('partner_id', 'in', partners_to_notify)], context=context) + for notification in self.browse(cr, SUPERUSER_ID, notif_ids, context=context): + notified_partners.append(notification.partner_id.id) + notifications_to_update.append(notification.id) + partners_to_notify = filter(lambda item: item not in notified_partners, partners_to_notify) + if notifications_to_update: + self.write(cr, SUPERUSER_ID, notifications_to_update, {'read': False}, context=context) + mail_message_obj.write(cr, uid, msg_id, {'notified_partner_ids': [(4, id) for id in partners_to_notify]}, context=context) + # mail_notify_noemail (do not send email) or no partner_ids: do not send, return if context.get('mail_notify_noemail'): return True # browse as SUPERUSER_ID because of access to res_partner not necessarily allowed msg = self.pool.get('mail.message').browse(cr, SUPERUSER_ID, msg_id, context=context) - notify_partner_ids = self.get_partners_to_notify(cr, uid, msg, context=context) + notify_partner_ids = self.get_partners_to_notify(cr, uid, msg, partners_to_notify=partners_to_notify, context=context) if not notify_partner_ids: return True @@ -136,16 +160,12 @@ class mail_notification(osv.Model): mail_values = { 'mail_message_id': msg.id, - 'email_to': [], 'auto_delete': True, 'body_html': body_html, 'email_from': email_from, - 'state': 'outgoing', } - mail_values['email_to'] = ', '.join(mail_values['email_to']) email_notif_id = mail_mail.create(cr, uid, mail_values, context=context) try: return mail_mail.send(cr, uid, [email_notif_id], recipient_ids=notify_partner_ids, context=context) except Exception: return False - diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 91d46098b4b..2f56e620543 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -861,7 +861,7 @@ class mail_message(osv.Model): # message has no subtype_id: pure log message -> no partners, no one notified if not message.subtype_id: return True - + # all followers of the mail.message document have to be added as partners and notified if message.model and message.res_id: fol_obj = self.pool.get("mail.followers") @@ -884,8 +884,7 @@ class mail_message(osv.Model): # notify if partners_to_notify: - self.write(cr, SUPERUSER_ID, [newid], {'notified_partner_ids': [(4, p.id) for p in partners_to_notify]}, context=context) - notification_obj._notify(cr, uid, newid, context=context) + notification_obj._notify(cr, uid, newid, partners_to_notify=[p.id for p in partners_to_notify], context=context) message.refresh() # An error appear when a user receive a notification without notifying diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index e4b282060cd..1c7ec4bf4fb 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -1135,7 +1135,6 @@ class mail_thread(osv.AbstractModel): # find first email message, set it as unread for auto_subscribe fields for them to have a notification if user_id_partner_ids: - notification_obj = self.pool.get('mail.notification') msg_ids = self.pool.get('mail.message').search(cr, uid, [ ('model', '=', self._name), ('res_id', '=', record.id), @@ -1143,12 +1142,7 @@ class mail_thread(osv.AbstractModel): if not msg_ids and record.message_ids: msg_ids = [record.message_ids[-1].id] if msg_ids: - for partner_id in user_id_partner_ids: - notif_ids = notification_obj.search(cr, uid, [('partner_id', '=', partner_id), ('message_id', '=', msg_ids[0])], context=context) - if notif_ids: - notification_obj.write(cr, uid, notif_ids, {'read': False}, context=context) - else: - notification_obj.create(cr, uid, {'partner_id': partner_id, 'message_id': msg_ids[0], 'read': False}, context=context) + self.pool.get('mail.notification')._notify(cr, uid, msg_ids[0], partners_to_notify=user_id_partner_ids, context=context) return True From 49061b5f309f4fd251f334d87ba39f414e876310 Mon Sep 17 00:00:00 2001 From: "DBR (OpenERP)" Date: Tue, 26 Feb 2013 16:54:26 +0530 Subject: [PATCH 060/305] [IMP]improve warning msg bzr revid: dbr@tinyerp.com-20130226112426-dthza6gha0klsqqo --- .../account_analytic_analysis.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 485bf8d6f73..c0f451ef28d 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -267,12 +267,12 @@ class account_analytic_account(osv.osv): GROUP BY product_id, user_id, to_invoice, product_uom_id, line.name""", (account.id,)) res[account.id][f] = 0.0 - for product_id, price, user_id, factor_id, quantity, uom, line_name in cr.fetchall(): + for product_id, price, user_id, factor_id, qty, uom, line_name in cr.fetchall(): price = -price if product_id: - price = self.pool.get('account.analytic.line')._get_invoice_price(cr, uid, account, product_id, user_id, quantity, context) + price = self.pool.get('account.analytic.line')._get_invoice_price(cr, uid, account, product_id, user_id, qty, context) factor = self.pool.get('hr_timesheet_invoice.factor').browse(cr, uid, factor_id, context=context) - res[account.id][f] += price * quantity * (100-factor.factor or 0.0) / 100.0 + res[account.id][f] += price * qty * (100-factor.factor or 0.0) / 100.0 # sum both result on account_id for id in ids: @@ -787,9 +787,11 @@ class account_analytic_account(osv.osv): journal_obj = self.pool.get('account.journal') if context is None: context = {} - contract_ids = self.search(cr, uid, [('next_date','=',time.strftime("%Y-%m-%d")), ('state','=', 'open'), ('recurring_invoices','=', True)], context=context, order='name asc') + contract_ids = self.search(cr, uid, [('next_date','=',time.strftime("%Y-%m-%d")), ('state','=', 'open'), ('recurring_invoices','=', True)]) if contract_ids: for contract in self.browse(cr, uid, contract_ids): + if not contract.partner_id: + raise osv.except_osv(_('No Partner Defined !'),_("You must first select a Customer for Contract %s!") % contract.name ) journal_ids = journal_obj.search(cr, uid, [('type', '=','sale'),('company_id', '=', contract.company_id.id)], limit=1) if not journal_ids: raise osv.except_osv(_('Error!'), From e4baaa666eb05ae6db9897c4229d741de2bd7354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 26 Feb 2013 12:33:27 +0100 Subject: [PATCH 061/305] [CLEAN] Chatter: removed log. bzr revid: tde@openerp.com-20130226113327-ig0n4ou44nh82pm9 --- addons/mail/static/src/js/mail.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 4449a73b1a8..bb200aedf95 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -606,14 +606,6 @@ openerp.mail = function (session) { var names_to_remove = []; var recipient_ids_to_remove = []; - // some debug - console.group('check_recipient_partners'); - console.log('recipients', recipients); - console.log('recipient_ids', recipient_ids); - console.log('recipients_to_find', recipients_to_find); - console.log('recipients_to_check', recipients_to_check); - console.log('names_to_find', names_to_find); - // have unknown names -> call message_get_partner_info_from_emails to try to find partner_id var find_done = $.Deferred(); if (names_to_find.length > 0) { @@ -660,7 +652,6 @@ openerp.mail = function (session) { }); $.when.apply($, emails_deferred).then(function () { - console.log('final call', names_to_remove, recipient_ids_to_remove); var new_names_to_find = _.difference(names_to_find, names_to_remove); find_done = $.Deferred(); if (new_names_to_find.length > 0) { @@ -678,7 +669,6 @@ openerp.mail = function (session) { }); }).pipe(function () { check_done.resolve(recipient_ids); - console.groupEnd(); }); }); }); @@ -748,17 +738,11 @@ openerp.mail = function (session) { var email_addresses = _.pluck(this.recipients, 'email_address'); var suggested_partners = $.Deferred(); - // some debug - console.group('on_toggle_quick_composer'); - console.log('event', event); - console.log('computed recipients', this.recipients); - // if clicked: call for suggested recipients if (event.type == 'click') { this.is_log = $input.hasClass('oe_compose_log'); suggested_partners = this.parent_thread.ds_thread.call('message_get_suggested_recipients', [[this.context.default_res_id]]).done(function (additional_recipients) { var thread_recipients = additional_recipients[self.context.default_res_id]; - console.log('message_get_suggested_recipients:', thread_recipients); _.each(thread_recipients, function (recipient) { var parsed_email = mail.ChatterUtils.parse_email(recipient[1]); if (_.indexOf(email_addresses, parsed_email[1]) == -1) { @@ -779,8 +763,6 @@ openerp.mail = function (session) { // when call for suggested partners finished: re-render the widget $.when(suggested_partners).pipe(function (additional_recipients) { - console.log('recipients after toogle', self.recipients); - console.groupEnd(); if ((!self.stay_open || (event && event.type == 'click')) && (!self.show_composer || !self.$('textarea:not(.oe_compact)').val().match(/\S+/) && !self.attachment_ids.length)) { self.show_composer = !self.show_composer || self.stay_open; self.reinit(); From c5e395ec499688a566d203551ff02c470bd6b6bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 26 Feb 2013 12:36:24 +0100 Subject: [PATCH 062/305] [REM] Crm: removed 'send mail' button and related function. Cleaned poportunity basic template. bzr revid: tde@openerp.com-20130226113624-0znlq3eihuzfbutm --- addons/crm/crm_data.xml | 3 +-- addons/crm/crm_lead.py | 35 ----------------------------------- addons/crm/crm_lead_view.xml | 1 - 3 files changed, 1 insertion(+), 38 deletions(-) diff --git a/addons/crm/crm_data.xml b/addons/crm/crm_data.xml index 3726c744b2e..613141b5818 100644 --- a/addons/crm/crm_data.xml +++ b/addons/crm/crm_data.xml @@ -80,8 +80,7 @@ basis for email recipients, name and to ease the definition of a further elaborated template. --> Opportunity - Send Emails - ${object.user_id.email or ''} - Opportunity ${object.name | h}) + ${object.name} ${object.partner_id.id} diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 6d00ac19b51..9911a412a9c 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -917,41 +917,6 @@ class crm_lead(base_stage, format_address, osv.osv): vals['probability'] = stage.probability return super(crm_lead, self).write(cr, uid, ids, vals, context=context) - def new_mail_send(self, cr, uid, ids, context=None): - ''' - This function opens a window to compose an email, with the edi sale template message loaded by default - ''' - assert len(ids) == 1, 'This option should only be used for a single id at a time.' - ir_model_data = self.pool.get('ir.model.data') - try: - template_id = ir_model_data.get_object_reference(cr, uid, 'crm', 'email_template_opportunity_mail')[1] - except ValueError: - template_id = False - try: - compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1] - except ValueError: - compose_form_id = False - if context is None: - context = {} - ctx = context.copy() - ctx.update({ - 'default_model': 'crm.lead', - 'default_res_id': ids[0], - 'default_use_template': bool(template_id), - 'default_template_id': template_id, - 'default_composition_mode': 'comment', - }) - return { - 'type': 'ir.actions.act_window', - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'mail.compose.message', - 'views': [(compose_form_id, 'form')], - 'view_id': compose_form_id, - 'target': 'new', - 'context': ctx, - } - # ---------------------------------------- # Mail Gateway # ---------------------------------------- diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 87c130547cd..843c00c4f4d 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -377,7 +377,6 @@ states="draft,open,pending" class="oe_highlight"/> - - - - + + + +
    From 53dc52a5c152d0c30953b67c3a31ce72cfb7110f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 26 Feb 2013 16:07:07 +0100 Subject: [PATCH 069/305] [CLEAN] mail_thread: cleaned _get_message_data method. Maybe not the best way, but we tries to avoid using group_by and count because of spurious timing results. Also added some comments. bzr revid: tde@openerp.com-20130226150707-hzyb8unhsz1i2hh3 --- addons/mail/mail_thread.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 1c7ec4bf4fb..56c6351142e 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -93,21 +93,23 @@ class mail_thread(osv.AbstractModel): """ Computes: - message_unread: has uid unread message for the document - message_summary: html snippet summarizing the Chatter for kanban views """ - res = dict((id, dict(message_unread=False, message_summary=' ')) for id in ids) + res = dict((id, dict(message_unread=False, message_unread_count=0, message_summary=' ')) for id in ids) user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] # search for unread messages, directly in SQL to improve performances - cr.execute(""" SELECT m.res_id, COUNT(n.message_id) as nb FROM mail_message m + cr.execute(""" SELECT m.res_id FROM mail_message m RIGHT JOIN mail_notification n ON (n.message_id = m.id AND n.partner_id = %s AND (n.read = False or n.read IS NULL)) - WHERE m.model = %s AND m.res_id in %s - GROUP BY m.res_id""", + WHERE m.model = %s AND m.res_id in %s""", (user_pid, self._name, tuple(ids),)) for result in cr.fetchall(): res[result[0]]['message_unread'] = True - if result[1]: - title = result[1] > 1 and _("You have %d unread messages") % result[1] or _("You have one unread message") - res[result[0]]['message_summary'] = "9 %d %s" % (title, result[1], _("New")) + res[result[0]]['message_unread_count'] += 1 + + for id in ids: + if res[id]['message_unread_count']: + title = res[id]['message_unread_count'] > 1 and _("You have %d unread messages") % res[id]['message_unread_count'] or _("You have one unread message") + res[id]['message_summary'] = "9 %d %s" % (title, res[id].pop('message_unread_count'), _("New")) return res def _get_subscription_data(self, cr, uid, ids, name, args, context=None): @@ -816,6 +818,9 @@ class mail_thread(osv.AbstractModel): self.message_post(cr, uid, [id], message, context=context) def _message_add_suggested_recipient(self, result, obj, partner=None, email=None, reason='', context=None): + """ Called by message_get_suggested_recipients, to add a suggested + recipient in the result dictionary. The form is : + partner_id, partner_name or partner_name, reason """ if partner and partner in obj.message_follower_ids: return result if partner and partner in [val[0] for val in result[obj.id]]: @@ -831,9 +836,11 @@ class mail_thread(osv.AbstractModel): return result def message_get_suggested_recipients(self, cr, uid, ids, context=None): + """ Returns suggested recipients for ids. Those are a list of + tuple (partner_id, partner_name, reason), to be managed by Chatter. """ result = dict.fromkeys(ids, list()) if self._all_columns.get('user_id'): - for obj in self.browse(cr, SUPERUSER_ID, ids, context=context): # SUPERUSER because of a read on res.users that would crash otherwise IMHO + for obj in self.browse(cr, SUPERUSER_ID, ids, context=context): # SUPERUSER because of a read on res.users that would crash otherwise if not obj.user_id or not obj.user_id.partner_id: continue self._message_add_suggested_recipient(result, obj, partner=obj.user_id.partner_id, reason=self._all_columns['user_id'].column.string, context=context) From 68545cf6aed2a0fc52f32c978387fd1c2ce0a2ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 26 Feb 2013 16:15:31 +0100 Subject: [PATCH 070/305] [DOC] mail: added some comments. bzr revid: tde@openerp.com-20130226151531-3zmtcdodmpnh74zh --- addons/mail/mail_thread.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 56c6351142e..67a61dab7f7 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -821,17 +821,17 @@ class mail_thread(osv.AbstractModel): """ Called by message_get_suggested_recipients, to add a suggested recipient in the result dictionary. The form is : partner_id, partner_name or partner_name, reason """ - if partner and partner in obj.message_follower_ids: + if partner and partner in obj.message_follower_ids: # recipient already in the followers -> skip return result - if partner and partner in [val[0] for val in result[obj.id]]: + if partner and partner in [val[0] for val in result[obj.id]]: # already existing partner ID -> skip return result - if email and email in [val[1] for val in result[obj.id]]: + if email and email in [val[1] for val in result[obj.id]]: # already existing email -> skip return result - if partner and partner.email: + if partner and partner.email: # complete profile: id, name result[obj.id].append((partner.id, '%s<%s>' % (partner.name, partner.email), reason)) - elif partner: + elif partner: # incomplete profile: id, name result[obj.id].append((partner.id, '%s' % (partner.name), reason)) - else: + else: # unknown partner, we are probably managing an email address result[obj.id].append((False, email, reason)) return result From ce688b93e426c6f2edb0ad87181d0e937439b243 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 26 Feb 2013 16:16:22 +0100 Subject: [PATCH 071/305] [IMP] Code lint bzr revid: fme@openerp.com-20130226151622-frhyc73lwtc56tee --- addons/auth_signup/static/src/js/auth_signup.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index 0b11f3ea446..4cff913253e 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -21,20 +21,13 @@ openerp.auth_signup = function(instance) { start: function() { var self = this; this.set({ 'login-mode': 'default' }); - var d = this._super(); - d.done(function() { + return this._super().then(function() { // to switch between the signup and regular login form self.$('a.oe_signup_signup').click(function(ev) { - if (ev) { - ev.preventDefault(); - } self.set({ 'login-mode': 'signup' }); return false; }); self.$('a.oe_signup_back').click(function(ev) { - if (ev) { - ev.preventDefault(); - } self.set({ 'login-mode': 'default' }); delete self.params.token; return false; @@ -53,7 +46,7 @@ openerp.auth_signup = function(instance) { if (dbname && self.params.token) { self.rpc("/auth_signup/retrieve", {dbname: dbname, token: self.params.token}) .done(self.on_token_loaded) - .fail(self.on_token_failed) + .fail(self.on_token_failed); } if (dbname && self.params.login) { self.$("form input[name=login]").val(self.params.login); @@ -74,7 +67,6 @@ openerp.auth_signup = function(instance) { }); } }); - return d; }, on_token_loaded: function(result) { @@ -169,7 +161,7 @@ openerp.auth_signup = function(instance) { this.do_warn(_t("Login"), _t("No database selected !")); return false; } else if (!login) { - this.do_warn(_t("Login"), _t("Please enter a username or email address.")) + this.do_warn(_t("Login"), _t("Please enter a username or email address.")); return false; } var params = { From 4d84340eae2c1418bf45e69d32e7b510715b0d27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 26 Feb 2013 16:22:02 +0100 Subject: [PATCH 072/305] [IMP] hr_recruitment: display message_summary in kanban views. bzr revid: tde@openerp.com-20130226152202-ev92h06rvzp7niey --- addons/hr_recruitment/hr_recruitment_view.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index b6a0bdd4ccf..233f977e528 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -237,6 +237,7 @@ +
      @@ -282,6 +283,7 @@ From ed5012ffb6ef0888893e4623da8ee45230f4a4b9 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 26 Feb 2013 17:03:33 +0100 Subject: [PATCH 073/305] [FIX] ... my own error bzr revid: fme@openerp.com-20130226160333-nxshvoq8ydf4gfcp --- .../auth_signup/static/src/js/auth_signup.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index 4cff913253e..d292febcc87 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -5,6 +5,8 @@ openerp.auth_signup = function(instance) { instance.web.Login.include({ init: function() { this._super.apply(this, arguments); + this.signup_enabled = false; + this.reset_password_enabled = false; this.on('change:login-mode', this, function() { /* * Switches the login box to the select mode @@ -16,6 +18,8 @@ openerp.auth_signup = function(instance) { var modes = $(this).data('modes').split(/\s+/); $(this).toggle(modes.indexOf(mode) > -1); }); + self.$('a.oe_signup_signup:visible').toggle(this.signup_enabled); + self.$('a.oe_signup_reset_password:visible').toggle(this.reset_password_enabled); }); }, start: function() { @@ -56,15 +60,10 @@ openerp.auth_signup = function(instance) { self.$('a.oe_signup_reset_password').click(self.do_reset_password); if (dbname) { - self.rpc("/auth_signup/get_config", {dbname: dbname}) - .done(function(result) { - if (result.signup) { - self.set({ 'login-mode': 'signup' }); - } - if (result.reset_password) { - self.set({ 'login-mode': 'reset' }); - } - }); + self.rpc("/auth_signup/get_config", {dbname: dbname}).done(function(result) { + self.signup_enabled = result.signup; + self.reset_password_enabled = result.reset_password; + }); } }); }, @@ -97,6 +96,7 @@ openerp.auth_signup = function(instance) { this.show_error(_t("Invalid signup token")); delete this.params.db; delete this.params.token; + this.set({ 'login-mode': 'default' }); }, on_submit: function(ev) { From 977eaed2c852bbe9aa6c60cc8a36861f4967ae7d Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 26 Feb 2013 17:19:25 +0100 Subject: [PATCH 074/305] [FIX] make a json request out of reset_password bzr revid: fme@openerp.com-20130226161925-v8sr2r1zgcki6n3z --- addons/auth_signup/controllers/main.py | 11 +++------- .../auth_signup/static/src/js/auth_signup.js | 20 ++++++++++--------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index baf1a8f0b28..73e2f0e3fa0 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -19,9 +19,6 @@ # ############################################################################## import logging -import urllib - -import werkzeug import openerp from openerp.modules.registry import RegistryManager @@ -69,7 +66,7 @@ class Controller(openerp.addons.web.http.Controller): res_users = registry.get('res.users') res_users.signup(cr, openerp.SUPERUSER_ID, values, token) - @openerp.addons.web.http.httprequest + @openerp.addons.web.http.jsonrequest def reset_password(self, req, dbname, login): """ retrieve user, and perform reset password """ registry = RegistryManager.get(dbname) @@ -78,12 +75,10 @@ class Controller(openerp.addons.web.http.Controller): res_users = registry.get('res.users') res_users.reset_password(cr, openerp.SUPERUSER_ID, login) cr.commit() - message = 'An email has been sent with credentials to reset your password' except Exception as e: # signup error _logger.exception('error when resetting password') - message = e.message - params = [('action', 'login'), ('error_message', message)] - return werkzeug.utils.redirect("/#" + urllib.urlencode(params)) + raise(e) + return True # vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index d292febcc87..4303ea5b6cf 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -74,7 +74,7 @@ openerp.auth_signup = function(instance) { this.on_db_loaded([result.db]); if (result.token) { // switch to signup mode, set user name and login - this.set({ 'login-mode': 'signup' }); + this.set({ 'login-mode': this.params.type === 'reset' ? 'reset' : 'signup' }); this.$("form input[name=name]").val(result.name).attr("readonly", "readonly"); if (result.login) { this.$("form input[name=login]").val(result.login).attr("readonly", "readonly"); @@ -155,21 +155,23 @@ openerp.auth_signup = function(instance) { if (ev) { ev.preventDefault(); } + var self = this; var db = this.$("form [name=db]").val(); var login = this.$("form input[name=login]").val(); if (!db) { this.do_warn(_t("Login"), _t("No database selected !")); - return false; + return $.Deferred.reject(); } else if (!login) { this.do_warn(_t("Login"), _t("Please enter a username or email address.")); - return false; + return $.Deferred.reject(); } - var params = { - dbname : db, - login: login, - }; - var url = "/auth_signup/reset_password?" + $.param(params); - window.location = url; + return self.rpc("/auth_signup/reset_password", { dbname: db, login: login }).done(function(result) { + self.show_error(_t("An email has been sent with credentials to reset your password")); + self.set({ 'login-mode': 'default' }); + }).fail(function(result, ev) { + ev.preventDefault(); + self.show_error(result.message); + }); }, }); }; From ab4e67a786c733bfa9d1c923bcfc8e4b17b70dc6 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 26 Feb 2013 17:39:20 +0100 Subject: [PATCH 075/305] [FIX] set login-mode on time bzr revid: fme@openerp.com-20130226163920-7m4ye86xv1c06owo --- addons/auth_signup/static/src/js/auth_signup.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index 4303ea5b6cf..0ce7f04e590 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -24,7 +24,6 @@ openerp.auth_signup = function(instance) { }, start: function() { var self = this; - this.set({ 'login-mode': 'default' }); return this._super().then(function() { // to switch between the signup and regular login form self.$('a.oe_signup_signup').click(function(ev) { @@ -63,7 +62,11 @@ openerp.auth_signup = function(instance) { self.rpc("/auth_signup/get_config", {dbname: dbname}).done(function(result) { self.signup_enabled = result.signup; self.reset_password_enabled = result.reset_password; + self.set({ 'login-mode': 'default' }); }); + } else { + // TODO: support multiple database mode + this.set({ 'login-mode': 'default' }); } }); }, From 790262cbe4a23ec012932913aaf16c3ba0ee1475 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 26 Feb 2013 17:39:41 +0100 Subject: [PATCH 076/305] [FIX] Bad usage of deferreds bzr revid: fme@openerp.com-20130226163941-fky6s1zwrvavf8f6 --- addons/auth_signup/static/src/js/auth_signup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index 0ce7f04e590..82e0806ab14 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -163,10 +163,10 @@ openerp.auth_signup = function(instance) { var login = this.$("form input[name=login]").val(); if (!db) { this.do_warn(_t("Login"), _t("No database selected !")); - return $.Deferred.reject(); + return $.Deferred().reject(); } else if (!login) { this.do_warn(_t("Login"), _t("Please enter a username or email address.")); - return $.Deferred.reject(); + return $.Deferred().reject(); } return self.rpc("/auth_signup/reset_password", { dbname: db, login: login }).done(function(result) { self.show_error(_t("An email has been sent with credentials to reset your password")); From 4c3d4f36a425228297e690a657af475307b7c027 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 26 Feb 2013 17:55:22 +0100 Subject: [PATCH 077/305] [IMP] Hide "manage database" link when login mode is not default bzr revid: fme@openerp.com-20130226165522-930dndel17no11m1 --- addons/auth_signup/static/src/xml/auth_signup.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/auth_signup/static/src/xml/auth_signup.xml b/addons/auth_signup/static/src/xml/auth_signup.xml index 5b4e246b448..9ef4f394e27 100644 --- a/addons/auth_signup/static/src/xml/auth_signup.xml +++ b/addons/auth_signup/static/src/xml/auth_signup.xml @@ -28,6 +28,9 @@ + + this.attr('data-modes', 'default'); + From fe20d5197711fe6f997b9cefa7a6349be3d01270 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 26 Feb 2013 18:14:13 +0100 Subject: [PATCH 078/305] [IMP] use new api for events bzr revid: fme@openerp.com-20130226171413-1h3lyhm79gw3c3rv --- addons/auth_signup/static/src/js/auth_signup.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index 82e0806ab14..78c62749d8a 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -27,11 +27,11 @@ openerp.auth_signup = function(instance) { return this._super().then(function() { // to switch between the signup and regular login form self.$('a.oe_signup_signup').click(function(ev) { - self.set({ 'login-mode': 'signup' }); + self.set('login-mode', 'signup'); return false; }); self.$('a.oe_signup_back').click(function(ev) { - self.set({ 'login-mode': 'default' }); + self.set('login-mode', 'default'); delete self.params.token; return false; }); @@ -62,11 +62,11 @@ openerp.auth_signup = function(instance) { self.rpc("/auth_signup/get_config", {dbname: dbname}).done(function(result) { self.signup_enabled = result.signup; self.reset_password_enabled = result.reset_password; - self.set({ 'login-mode': 'default' }); + self.set('login-mode', 'default'); }); } else { // TODO: support multiple database mode - this.set({ 'login-mode': 'default' }); + this.set('login-mode', 'default'); } }); }, @@ -77,7 +77,7 @@ openerp.auth_signup = function(instance) { this.on_db_loaded([result.db]); if (result.token) { // switch to signup mode, set user name and login - this.set({ 'login-mode': this.params.type === 'reset' ? 'reset' : 'signup' }); + this.set('login-mode', (this.params.type === 'reset' ? 'reset' : 'signup')); this.$("form input[name=name]").val(result.name).attr("readonly", "readonly"); if (result.login) { this.$("form input[name=login]").val(result.login).attr("readonly", "readonly"); @@ -87,7 +87,7 @@ openerp.auth_signup = function(instance) { } else { // remain in login mode, set login if present delete this.params.token; - this.set({ 'login-mode': 'default' }); + this.set('login-mode', 'default'); this.$("form input[name=login]").val(result.login || ""); } }, @@ -99,7 +99,7 @@ openerp.auth_signup = function(instance) { this.show_error(_t("Invalid signup token")); delete this.params.db; delete this.params.token; - this.set({ 'login-mode': 'default' }); + this.set('login-mode', 'default'); }, on_submit: function(ev) { @@ -170,7 +170,7 @@ openerp.auth_signup = function(instance) { } return self.rpc("/auth_signup/reset_password", { dbname: db, login: login }).done(function(result) { self.show_error(_t("An email has been sent with credentials to reset your password")); - self.set({ 'login-mode': 'default' }); + self.set('login-mode', 'default'); }).fail(function(result, ev) { ev.preventDefault(); self.show_error(result.message); From 0ba8ca07a5c13440cbf9cd7f18243e47fc97ebc5 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 26 Feb 2013 21:57:45 +0100 Subject: [PATCH 079/305] [FIX] s/this/self/ bzr revid: fme@openerp.com-20130226205745-h48pyc1qdkjj2uie --- addons/auth_signup/static/src/js/auth_signup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index 78c62749d8a..72ab85ada32 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -66,7 +66,7 @@ openerp.auth_signup = function(instance) { }); } else { // TODO: support multiple database mode - this.set('login-mode', 'default'); + self.set('login-mode', 'default'); } }); }, From 2d18a009f1e1614fdda2b9e4437249dfb534d4b1 Mon Sep 17 00:00:00 2001 From: "DBR (OpenERP)" Date: Wed, 27 Feb 2013 15:31:15 +0530 Subject: [PATCH 080/305] [IMP]Remove extra button and set grey color for a para. bzr revid: dbr@tinyerp.com-20130227100115-panqiupw1j2yzvdy --- .../account_analytic_analysis/account_analytic_analysis.py | 4 ++-- .../account_analytic_analysis_view.xml | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 17ec3dec35f..152450d1d9c 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -123,7 +123,7 @@ class account_analytic_invoice_line(osv.osv): result['account_id'] = a taxes = res.taxes_id and res.taxes_id or (a and self.pool.get('account.account').browse(cr, uid, a, context=context).tax_ids or False) - result.update( {'name':res.partner_ref,'uom_id': uom_id or res.uom_id.id, 'price_unit': res.list_price or res.standard_price,'tax_ids': [x.id for x in taxes]} ) + result.update({'name':res.partner_ref,'uom_id': uom_id or res.uom_id.id, 'price_unit': res.list_price or res.standard_price,'tax_ids': [x.id for x in taxes]}) if res.description: result['name'] += '\n'+res.description @@ -781,7 +781,7 @@ class account_analytic_account(osv.osv): inv_line_id.append(line_id) return inv_line_id - def cron_create_invoice(self, cr, uid, context=None): + def cron_create_invoice(self, cr, ids, uid, context=None): inv_obj = self.pool.get('account.invoice') obj_invoice_line = self.pool.get('account.invoice.line') journal_obj = self.pool.get('account.journal') diff --git a/addons/account_analytic_analysis/account_analytic_analysis_view.xml b/addons/account_analytic_analysis/account_analytic_analysis_view.xml index f14a0fc50a4..b15e9592fec 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis_view.xml +++ b/addons/account_analytic_analysis/account_analytic_analysis_view.xml @@ -152,7 +152,7 @@ -

      +

      Following Invoice will be generated on .

      @@ -164,7 +164,7 @@ - + @@ -181,9 +181,6 @@ - - +

      + Fill in this form to create an OpenERP database. You can + create databases for different companies or for different + goals (testing, production). Once the database is created, + you will be able to install your first application. +

      +

      + By default, the master password is 'admin' if you did not changed it. +

      +
      - + - - + + - + - - + + + + +
      + +
      - + + Check this box to evaluate OpenERP.
      - +