From c54383d4e5b0681acdad0c9ebf854f8fc131c9cc Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 23 May 2012 12:49:59 +0200 Subject: [PATCH 001/367] [IMP] extract key-creation in custom filters, store complete filter descriptor as well (in a separate mapping) bzr revid: xmo@openerp.com-20120523104959-859cuucade83u11o --- addons/web/static/src/js/search.js | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index e569e44df28..6d4892dc3c0 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -1540,6 +1540,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ var self = this; this.model = new instance.web.Model('ir.filters'); this.filters = {}; + this.$filters = {}; this.$element.on('submit', 'form', this.proxy('save_current')); this.$element.on('click', 'h4', function () { self.$element.toggleClass('oe_opened'); @@ -1549,16 +1550,34 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ model: this.view.model }).pipe(this.proxy('set_filters')); }, + /** + * Generates a mapping key (in the filters and $filter mappings) for the + * filter descriptor object provided (as returned by ``get_filters``). + * + * The mapping key is guaranteed to be unique for a given (user_id, name) + * pair. + * + * @param {Object} filter + * @param {String} filter.name + * @param {Number|Pair} [filter.user_id] + * @return {String} mapping key corresponding to the filter + */ + key_for: function (filter) { + var user_id = filter.user_id; + var uid = (user_id instanceof Array) ? user_id[0] : user_id; + return _.str.sprintf('(%s)%s', uid, filter.name); + }, append_filter: function (filter) { var self = this; - var key = _.str.sprintf('(%s)%s', filter.user_id, filter.name); + var key = this.key_for(filter); var $filter; - if (key in this.filters) { - $filter = this.filters[key]; + if (key in this.$filters) { + $filter = this.$filters[key]; } else { var id = filter.id; - $filter = this.filters[key] = $('
  • ') + this.filters[key] = filter; + $filter = this.$filters[key] = $('
  • ') .appendTo(this.$element.find('.oe_searchview_custom_list')) .addClass(filter.user_id ? 'oe_searchview_custom_private' : 'oe_searchview_custom_public') @@ -1570,6 +1589,8 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ e.stopPropagation(); self.model.call('unlink', [id]).then(function () { $filter.remove(); + delete self.$filters[key]; + delete self.filters[key]; }); }) .appendTo($filter); From 9168a66d6a5403495d74bb9136b85438004540ed Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 23 May 2012 13:23:14 +0200 Subject: [PATCH 002/367] [ADD] is_default custom filters * Second checkbox when creating a custom filter, unchecked by default * Global is_default filter overrides action defaults * Personal is_default filter overrides global is_default * Extracted sections of CustomFilter's behavior into methods to make them usable from the SearchView and easier to understand Hack note: CustomFilters#facet_for_defaults is weird stuff, it never fetches an actual facet, but it forces the SearchView's defaults handling to wait until CustomFilters is ready before executing, meaning SearchView#setup_default_query can run safe in the knowledge that custom filters are filled and everything. That this hidden dependency exists bugs me, though there's a comment. bzr revid: xmo@openerp.com-20120523112314-618hfttpegn39qey --- addons/web/static/src/js/search.js | 95 ++++++++++++++++++++++++------ addons/web/static/src/xml/base.xml | 2 + 2 files changed, 80 insertions(+), 17 deletions(-) diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 6d4892dc3c0..31650632850 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -632,7 +632,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea // add Filters to this.inputs, need view.controls filled (new instance.web.search.Filters(this)); // add custom filters to this.inputs - (new instance.web.search.CustomFilters(this)); + this.custom_filters = new instance.web.search.CustomFilters(this); // add Advanced to this.inputs (new instance.web.search.Advanced(this)); @@ -643,13 +643,40 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea // load defaults var defaults_fetched = $.when.apply(null, _(this.inputs).invoke( - 'facet_for_defaults', this.defaults)).then(function () { - self.query.reset(_(arguments).compact(), {preventSearch: true}); - }); + 'facet_for_defaults', this.defaults)) + .pipe(this.proxy('setup_default_query')); return $.when(drawer_started, defaults_fetched) .then(function () { self.ready.resolve(); }) }, + setup_default_query: function () { + // Hacky implementation of CustomFilters#facet_for_defaults ensure + // CustomFilters will be ready (and CustomFilters#filters will be + // correctly filled) by the time this method executes. + var custom_filters = this.custom_filters.filters; + if (!_(custom_filters).isEmpty()) { + // Check for any is_default custom filter + var personal_filter = _(custom_filters).find(function (filter) { + return filter.user_id && filter.is_default; + }); + if (personal_filter) { + this.query.reset([this.custom_filters.facet_for(personal_filter)], + {preventSearch: true}); + return; + } + + var global_filter = _(custom_filters).find(function (filter) { + return !filter.user_id && filter.is_default; + }); + if (global_filter) { + this.query.reset([this.custom_filters.facet_for(global_filter)], + {preventSearch: true}); + return; + } + } + // No custom filter, or no is_default custom filter, apply view defaults + this.query.reset(_(arguments).compact(), {preventSearch: true}); + }, /** * Handle event when the user make a selection in the filters management select box. */ @@ -1536,6 +1563,10 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({ instance.web.search.CustomFilters = instance.web.search.Input.extend({ template: 'SearchView.CustomFilters', _in_drawer: true, + init: function () { + this.is_ready = $.Deferred(); + this._super.apply(this, arguments); + }, start: function () { var self = this; this.model = new instance.web.Model('ir.filters'); @@ -1548,7 +1579,18 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ // FIXME: local eval of domain and context to get rid of special endpoint return this.rpc('/web/searchview/get_filters', { model: this.view.model - }).pipe(this.proxy('set_filters')); + }).pipe(this.proxy('set_filters')) + .then(function () { + self.is_ready.resolve(null); + }, function () { + self.is_ready.reject(); + }); + }, + /** + * Special implementation delaying defaults until CustomFilters is loaded + */ + facet_for_defaults: function () { + return this.is_ready; }, /** * Generates a mapping key (in the filters and $filter mappings) for the @@ -1567,6 +1609,30 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ var uid = (user_id instanceof Array) ? user_id[0] : user_id; return _.str.sprintf('(%s)%s', uid, filter.name); }, + /** + * Generates a :js:class:`~instance.web.search.Facet` descriptor from a + * filter descriptor + * + * @param {Object} filter + * @param {String} filter.name + * @param {Object} [filter.context] + * @param {Array} [filter.domain] + * @return {Object} + */ + facet_for: function (filter) { + return { + category: _("Custom Filter"), + icon: 'M', + field: { + get_context: function () { return filter.context; }, + get_groupby: function () { return [filter.context]; }, + get_domain: function () { return filter.domain; } + }, + values: [ + {label: filter.name, value: null} + ] + }; + }, append_filter: function (filter) { var self = this; var key = this.key_for(filter); @@ -1597,16 +1663,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ } $filter.unbind('click').click(function () { - self.view.query.reset([{ - category: _("Custom Filter"), - icon: 'M', - field: { - get_context: function () { return filter.context; }, - get_groupby: function () { return [filter.context]; }, - get_domain: function () { return filter.domain; } - }, - values: [{label: filter.name, value: null}] - }]); + self.view.query.reset([self.facet_for(filter)]); }); }, set_filters: function (filters) { @@ -1615,7 +1672,10 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ save_current: function () { var self = this; var $name = this.$element.find('input:first'); - var private_filter = !this.$element.find('input:last').prop('checked'); + var private_filter = !this.$element.find( + 'input#oe_searchview_custom_public').prop('checked'); + var set_as_default = this.$element.find( + 'input#oe_searchview_custom_default').prop('checked'); var search = this.view.build_search_data(); this.rpc('/web/session/eval_domain_and_context', { @@ -1631,7 +1691,8 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ user_id: private_filter ? instance.connection.uid : false, model_id: self.view.model, context: results.context, - domain: results.domain + domain: results.domain, + is_default: set_as_default }; // FIXME: current context? return self.model.call('create_or_replace', [filter]).then(function (id) { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 86b2f67cf62..7047b722462 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1487,6 +1487,8 @@
    + +
    From 4a8e00cac7ba39a99f2a4fac9cdd4a4f171370ed Mon Sep 17 00:00:00 2001 From: "Mayur Maheshwari (OpenERP)" Date: Mon, 25 Jun 2012 12:21:09 +0530 Subject: [PATCH 003/367] [FIX]base:fix the issue convert to quote is not working via Scheduled Calls lp bug: https://launchpad.net/bugs/1017353 fixed bzr revid: mma@tinyerp.com-20120625065109-5ciy82syodgtbcg9 --- addons/sale_crm/wizard/crm_make_sale.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/sale_crm/wizard/crm_make_sale.py b/addons/sale_crm/wizard/crm_make_sale.py index 9484b87652e..fd84e33610f 100644 --- a/addons/sale_crm/wizard/crm_make_sale.py +++ b/addons/sale_crm/wizard/crm_make_sale.py @@ -64,7 +64,9 @@ class crm_make_sale(osv.osv_memory): """ if context is None: context = {} - + # update context: if come from phonecall, default state values can make the quote crash lp:1017353 + context.pop('default_state', False) + case_obj = self.pool.get('crm.lead') sale_obj = self.pool.get('sale.order') partner_obj = self.pool.get('res.partner') From e46502580a5c9e19c31e15dd308e99307bbb68ed Mon Sep 17 00:00:00 2001 From: "Bharat Devnani (OpenERP)" Date: Wed, 25 Jul 2012 16:43:27 +0530 Subject: [PATCH 004/367] [IMP] changed the condition in def create_move of account_asset/account_asset.py bzr revid: bde@tinyerp.com-20120725111327-rd1se9de0yoj8wgg --- addons/account_asset/account_asset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account_asset/account_asset.py b/addons/account_asset/account_asset.py index 8cd73c60aa7..fe0ee8b3922 100644 --- a/addons/account_asset/account_asset.py +++ b/addons/account_asset/account_asset.py @@ -366,8 +366,6 @@ class account_asset_depreciation_line(osv.osv): currency_obj = self.pool.get('res.currency') created_move_ids = [] for line in self.browse(cr, uid, ids, context=context): - if currency_obj.is_zero(cr, uid, line.asset_id.currency_id, line.remaining_value): - can_close = True depreciation_date = line.asset_id.prorata and line.asset_id.purchase_date or time.strftime('%Y-%m-%d') period_ids = period_obj.find(cr, uid, depreciation_date, context=context) company_currency = line.asset_id.company_id.currency_id.id @@ -419,6 +417,8 @@ class account_asset_depreciation_line(osv.osv): }) self.write(cr, uid, line.id, {'move_id': move_id}, context=context) created_move_ids.append(move_id) + if currency_obj.is_zero(cr, uid, line.asset_id.currency_id, line.asset_id.value_residual): + can_close = True if can_close: asset_obj.write(cr, uid, [line.asset_id.id], {'state': 'close'}, context=context) return created_move_ids From 3f51a37d3b263e9e30b34292817b3c5fd9a44718 Mon Sep 17 00:00:00 2001 From: Paramjit Singh Sahota Date: Wed, 3 Oct 2012 18:46:11 +0530 Subject: [PATCH 005/367] [IMP]Improved code for the kanban of EVENT module. In which date is not getting the shadow effect. bzr revid: psa@tinyerp.com-20121003131611-5i0fvvwjdm7hqety --- addons/event/static/src/css/event.css | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/event/static/src/css/event.css b/addons/event/static/src/css/event.css index 8602ea24d53..dd6cc21ffc9 100644 --- a/addons/event/static/src/css/event.css +++ b/addons/event/static/src/css/event.css @@ -19,6 +19,7 @@ color: #8A89BA; -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); background-color: #FFFFFF; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); } .oe_event_month_year{ border-bottom-left-radius:3px; From 7baf04078665cdd1a185eaff4685cf64d9984a8c Mon Sep 17 00:00:00 2001 From: Paramjit Singh Sahota Date: Thu, 4 Oct 2012 12:13:18 +0530 Subject: [PATCH 006/367] [IMP]Improved code for the kanban of Home >> My Groups >> JOIN GROUP which was not properly visible in IE9. bzr revid: psa@tinyerp.com-20121004064318-d91ei034jros4btw --- addons/mail/static/src/css/mail_group.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/mail/static/src/css/mail_group.css b/addons/mail/static/src/css/mail_group.css index b700cb8c80f..9ca0055c17f 100644 --- a/addons/mail/static/src/css/mail_group.css +++ b/addons/mail/static/src/css/mail_group.css @@ -48,10 +48,11 @@ -o-border-radius: 3px; -ms-border-radius: 3px; border-radius: 3px; + border-collapse: separate; -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); -o-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); - -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); } .oe_group_photo { From bb6bc1a91b6149d96667fff5ece590e39d645345 Mon Sep 17 00:00:00 2001 From: Paramjit Singh Sahota Date: Wed, 10 Oct 2012 12:03:35 +0530 Subject: [PATCH 007/367] [IMP]Improved code for the kanban css of HR. bzr revid: psa@tinyerp.com-20121010063335-n7n0j6cytjinavxs --- addons/hr/static/src/css/hr.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/hr/static/src/css/hr.css b/addons/hr/static/src/css/hr.css index 00ef92c0fd8..e0eb8c198a5 100644 --- a/addons/hr/static/src/css/hr.css +++ b/addons/hr/static/src/css/hr.css @@ -18,10 +18,11 @@ -o-border-radius: 3px; -ms-border-radius: 3px; border-radius: 3px; + border-collapse: separate; -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); -o-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); - -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + box-shadow: 0 1px 4px 1px rgba(0, 0, 0, 0.4); } .oe_employee_picture { From 54c0c6e9ca85cb3375e259395847199fc58a1895 Mon Sep 17 00:00:00 2001 From: Ronald Portier Date: Tue, 16 Oct 2012 10:51:41 +0200 Subject: [PATCH 008/367] [FIX] l10n_nl - vat in the Netherlands went up from 19 to 21%. lp bug: https://launchpad.net/bugs/1067059 fixed bzr revid: ronald@therp.nl-20121016085141-zfa3ymjqmp57j1vg --- addons/l10n_nl/account_chart_netherlands.xml | 126 +++++++++---------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/addons/l10n_nl/account_chart_netherlands.xml b/addons/l10n_nl/account_chart_netherlands.xml index 96100d9a442..f736c6f4895 100644 --- a/addons/l10n_nl/account_chart_netherlands.xml +++ b/addons/l10n_nl/account_chart_netherlands.xml @@ -1424,7 +1424,7 @@ - + Btw af te dragen laag 1601 other @@ -1432,7 +1432,7 @@ - + Btw af te dragen hoog 1602 other @@ -1448,7 +1448,7 @@ - + Btw te vorderen laag 1611 other @@ -1456,7 +1456,7 @@ - + Btw te vorderen hoog 1612 other @@ -3909,7 +3909,7 @@ 1a - Leveringen/diensten belast met 19% (BTW) + Leveringen/diensten belast met 21% (BTW) 1b @@ -4021,7 +4021,7 @@ 1a - Leveringen/diensten belast met 19% (omzet) + Leveringen/diensten belast met 21% (omzet) 1b @@ -4149,22 +4149,22 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge 6% BTW percent - - + + sale - + Verkopen/omzet hoog - 19% BTW - + 21% BTW + percent - - + + @@ -4176,10 +4176,10 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge Verkopen/omzet overig variabel BTW - + percent - - + + @@ -4194,20 +4194,20 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge 6% BTW percent - - + + purchase - + BTW te vorderen hoog (inkopen) - 19% BTW - + 21% BTW + percent - - + + purchase @@ -4216,10 +4216,10 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge BTW te vorderen overig (inkopen) variabel BTW - + percent - - + + purchase @@ -4240,8 +4240,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge BTW af te dragen verlegd (inkopen) - 19% BTW verlegd - + 21% BTW verlegd + percent @@ -4277,8 +4277,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge BTW te vorderen verlegd (inkopen) - 19% BTW verlegd - + 21% BTW verlegd + percent @@ -4330,8 +4330,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge percent - - + + purchase @@ -4342,43 +4342,43 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge percent - - + + purchase - + Inkopen import binnen EU hoog - 19% BTW import binnen EU - + 21% BTW import binnen EU + percent purchase - + Inkopen import binnen EU hoog(1) percent - - - + + + purchase - + Inkopen import binnen EU hoog(2) percent - - - + + + purchase @@ -4400,8 +4400,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge percent - - + + purchase @@ -4412,8 +4412,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge percent - - + + purchase @@ -4463,8 +4463,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge percent - - + + purchase @@ -4475,8 +4475,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge percent - - + + purchase @@ -4485,7 +4485,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge Inkopen import buiten EU hoog BTW import buiten EU - + percent @@ -4498,8 +4498,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge percent - - + + purchase @@ -4510,8 +4510,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge percent - - + + purchase @@ -4520,7 +4520,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge Inkopen import buiten EU overig BTW import buiten EU - + percent @@ -4533,8 +4533,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge percent - - + + purchase @@ -4545,8 +4545,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge percent - - + + purchase From 83a0a252b9400dc152381ff9d0c66409477be857 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Fri, 19 Oct 2012 12:12:59 +0200 Subject: [PATCH 009/367] [IMP] database management: added `duplicate` operation. bzr revid: vmt@openerp.com-20121019101259-f32m2wlf6xugk2vi --- addons/web/controllers/main.py | 11 +++++++++++ addons/web/static/src/js/chrome.js | 13 +++++++++++++ addons/web/static/src/xml/base.xml | 27 +++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 72ffd9567f6..3980fc34aed 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -816,6 +816,17 @@ class Database(openerpweb.Controller): return req.session.proxy("db").create_database(*create_attrs) + @openerpweb.jsonrequest + def duplicate(self, req, fields): + params = dict(map(operator.itemgetter('name', 'value'), fields)) + duplicate_attrs = ( + params['super_admin_pwd'], + params['db_original_name'], + params['db_name'], + ) + + return req.session.proxy("db").duplicate_database(*duplicate_attrs) + @openerpweb.jsonrequest def drop(self, req, fields): password, db = operator.itemgetter( diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index a298ac67159..cbff398f0a3 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -326,6 +326,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({ self.$el.find("tr td:first-child").addClass("oe_form_group_cell_label"); self.$el.find("label").addClass("oe_form_label"); self.$el.find("form[name=create_db_form]").validate({ submitHandler: self.do_create }); + self.$el.find("form[name=duplicate_db_form]").validate({ submitHandler: self.do_duplicate }); self.$el.find("form[name=drop_db_form]").validate({ submitHandler: self.do_drop }); self.$el.find("form[name=backup_db_form]").validate({ submitHandler: self.do_backup }); self.$el.find("form[name=restore_db_form]").validate({ submitHandler: self.do_restore }); @@ -411,6 +412,18 @@ instance.web.DatabaseManager = instance.web.Widget.extend({ self.do_action(client_action); }); }, + do_duplicate: function(form) { + var self = this; + var fields = $(form).serializeArray(); + self.rpc("/web/database/duplicate", {'fields': fields}).then(function(result) { + if (result.error) { + self.display_error(result); + return; + } + self.do_notify("Duplicating database", "The database has been duplicated."); + self.start(); + }); + }, do_drop: function(form) { var self = this; var $form = $(form), diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index b031ee172e2..f57e5e47c47 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -152,6 +152,32 @@ +