-
-
- this.attr('data-modes', 'default');
-
-
-
-
diff --git a/addons/auth_signup/views/auth_signup_login.xml b/addons/auth_signup/views/auth_signup_login.xml
new file mode 100644
index 00000000000..bdc093e3c27
--- /dev/null
+++ b/addons/auth_signup/views/auth_signup_login.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+ Sign up
+ Reset Password
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/portal_crm/__init__.py b/addons/base_geolocalize/__init__.py
similarity index 92%
rename from addons/portal_crm/__init__.py
rename to addons/base_geolocalize/__init__.py
index 92b3ac62321..9afb820ae15 100644
--- a/addons/portal_crm/__init__.py
+++ b/addons/base_geolocalize/__init__.py
@@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2011 OpenERP S.A ().
+# Copyright (C) 2013_Today OpenERP SA ().
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -19,4 +19,4 @@
#
##############################################################################
-import contact
+import models
diff --git a/addons/portal_event/__openerp__.py b/addons/base_geolocalize/__openerp__.py
similarity index 66%
rename from addons/portal_event/__openerp__.py
rename to addons/base_geolocalize/__openerp__.py
index 05d84875647..f978680210b 100644
--- a/addons/portal_event/__openerp__.py
+++ b/addons/base_geolocalize/__openerp__.py
@@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-TODAY OpenERP SA ()
+# Copyright (C) 2013_Today OpenERP SA ().
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -21,26 +21,24 @@
{
- 'name': 'Portal Event',
- 'version': '0.1',
- 'category': 'Tools',
- 'complexity': 'easy',
+ 'name': 'Partners Geo-Localization',
+ 'version': '1.0',
+ 'category': 'Customer Relationship Management',
'description': """
-This module adds event menu and features to your portal if event and portal are installed.
-==========================================================================================
+Partners geolocalization
+========================
""",
'author': 'OpenERP SA',
- 'depends': [
- 'event',
- 'portal',
+ 'depends': ['crm'],
+ 'demo': [
],
'data': [
- 'portal_event_view.xml',
- 'security/portal_security.xml',
- 'security/ir.model.access.csv',
+ 'views/res_partner_view.xml',
],
+ 'test': [],
'installable': True,
- 'auto_install': True,
- 'category': 'Hidden',
+ 'auto_install': False,
+ 'images': [],
}
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/base_geolocalize/models/__init__.py b/addons/base_geolocalize/models/__init__.py
new file mode 100644
index 00000000000..4e5bc1614d1
--- /dev/null
+++ b/addons/base_geolocalize/models/__init__.py
@@ -0,0 +1 @@
+import res_partner
diff --git a/addons/base_geolocalize/models/res_partner.py b/addons/base_geolocalize/models/res_partner.py
new file mode 100644
index 00000000000..af43b990526
--- /dev/null
+++ b/addons/base_geolocalize/models/res_partner.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2013_Today OpenERP SA ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+try:
+ import simplejson as json
+except ImportError:
+ import json # noqa
+import urllib
+
+from openerp.osv import osv, fields
+from openerp import tools
+from openerp.tools.translate import _
+
+
+def geo_find(addr):
+ url = 'https://maps.googleapis.com/maps/api/geocode/json?sensor=false&address='
+ url += urllib.quote(addr.encode('utf8'))
+
+ try:
+ result = json.load(urllib.urlopen(url))
+ except Exception, e:
+ raise osv.except_osv(_('Network error'),
+ _('Cannot contact geolocation servers. Please make sure that your internet connection is up and running (%s).') % e)
+ if result['status'] != 'OK':
+ return None
+
+ try:
+ geo = result['results'][0]['geometry']['location']
+ return float(geo['lat']), float(geo['lng'])
+ except (KeyError, ValueError):
+ return None
+
+
+def geo_query_address(street=None, zip=None, city=None, state=None, country=None):
+ if country and ',' in country and (country.endswith(' of') or country.endswith(' of the')):
+ # put country qualifier in front, otherwise GMap gives wrong results,
+ # e.g. 'Congo, Democratic Republic of the' => 'Democratic Republic of the Congo'
+ country = '{1} {0}'.format(*country.split(',', 1))
+ return tools.ustr(', '.join(filter(None, [street,
+ ("%s %s" % (zip or '', city or '')).strip(),
+ state,
+ country])))
+
+
+class res_partner(osv.osv):
+ _inherit = "res.partner"
+
+ _columns = {
+ 'partner_latitude': fields.float('Geo Latitude'),
+ 'partner_longitude': fields.float('Geo Longitude'),
+ 'date_localization': fields.date('Geo Localization Date'),
+ }
+
+ def geo_localize(self, cr, uid, ids, context=None):
+ # Don't pass context to browse()! We need country names in english below
+ for partner in self.browse(cr, uid, ids):
+ if not partner:
+ continue
+ result = geo_find(geo_query_address(street=partner.street,
+ zip=partner.zip,
+ city=partner.city,
+ state=partner.state_id.name,
+ country=partner.country_id.name))
+ if result:
+ self.write(cr, uid, [partner.id], {
+ 'partner_latitude': result[0],
+ 'partner_longitude': result[1],
+ 'date_localization': fields.date.context_today(self, cr, uid, context=context)
+ }, context=context)
+ return True
diff --git a/addons/base_geolocalize/views/res_partner_view.xml b/addons/base_geolocalize/views/res_partner_view.xml
new file mode 100644
index 00000000000..24cc8d2d21d
--- /dev/null
+++ b/addons/base_geolocalize/views/res_partner_view.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+ res.partner.geo.inherit
+ res.partner
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/base_import/__openerp__.py b/addons/base_import/__openerp__.py
index e044930a242..8c87e779b3b 100644
--- a/addons/base_import/__openerp__.py
+++ b/addons/base_import/__openerp__.py
@@ -31,11 +31,9 @@ Re-implement openerp's file import system:
'security/ir.model.access.csv',
],
'css': [
- 'static/lib/select2/select2.css',
'static/src/css/import.css',
],
'js': [
- 'static/lib/select2/select2.js',
'static/lib/javascript-state-machine/state-machine.js',
'static/src/js/import.js',
],
diff --git a/addons/base_import/static/lib/select2/LICENSE b/addons/base_import/static/lib/select2/LICENSE
deleted file mode 100644
index 627fddef413..00000000000
--- a/addons/base_import/static/lib/select2/LICENSE
+++ /dev/null
@@ -1,12 +0,0 @@
-Copyright 2012 Igor Vaynberg
-
-Version: @@ver@@ Timestamp: @@timestamp@@
-
-Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
-compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software distributed under the License is
-distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and limitations under the License.
\ No newline at end of file
diff --git a/addons/base_import/static/lib/select2/README.md b/addons/base_import/static/lib/select2/README.md
deleted file mode 100644
index 3a6dcaf1ba4..00000000000
--- a/addons/base_import/static/lib/select2/README.md
+++ /dev/null
@@ -1,68 +0,0 @@
-Select2
-=================
-
-Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results. Look and feel of Select2 is based on the excellent [Chosen](http://harvesthq.github.com/chosen/) library.
-
-To get started -- checkout http://ivaynberg.github.com/select2!
-
-What Does Select2 Support That Chosen Does Not?
--------------------------------------------------
-
-* Working with large datasets: Chosen requires the entire dataset to be loaded as `option` tags in the DOM, which limits
-it to working with small-ish datasets. Select2 uses a function to find results on-the-fly, which allows it to partially
-load results.
-* Paging of results: Since Select2 works with large datasets and only loads a small amount of matching results at a time
-it has to support paging. Select2 will call the search function when the user scrolls to the bottom of currently loaded
-result set allowing for the 'infinite scrolling' of results.
-* Custom markup for results: Chosen only supports rendering text results because that is the only markup supported by
-`option` tags. Select2 provides an extension point which can be used to produce any kind of markup to represent results.
-* Ability to add results on the fly: Select2 provides the ability to add results from the search term entered by the user, which allows it to be used for
-tagging.
-
-Browser Compatibility
---------------------
-* IE 8+ (7 mostly works except for [issue with z-index](https://github.com/ivaynberg/select2/issues/37))
-* Chrome 8+
-* Firefox 3.5+
-* Safari 3+
-* Opera 10.6+
-
-Integrations
-------------
-
-* [Wicket-Select2](https://github.com/ivaynberg/wicket-select2) (Java / Apache Wicket)
-* [select2-rails](https://github.com/argerim/select2-rails) (Ruby on Rails)
-* [AngularUI](http://angular-ui.github.com/#directives-select2) ([AngularJS](angularjs.org))
-* [Django](https://github.com/applegrew/django-select2)
-
-Bug tracker
------------
-
-Have a bug? Please create an issue here on GitHub!
-
-https://github.com/ivaynberg/select2/issues
-
-
-Mailing list
-------------
-
-Have a question? Ask on our mailing list!
-
-select2@googlegroups.com
-
-https://groups.google.com/d/forum/select2
-
-
-Copyright and License
----------------------
-
-Copyright 2012 Igor Vaynberg
-
-Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
-compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software distributed under the License is
-distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and limitations under the License.
\ No newline at end of file
diff --git a/addons/base_import/static/lib/select2/release.sh b/addons/base_import/static/lib/select2/release.sh
deleted file mode 100755
index 79eaabd621d..00000000000
--- a/addons/base_import/static/lib/select2/release.sh
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/bin/bash
-set -e
-
-echo -n "Enter the version for this release: "
-
-read ver
-
-if [ ! $ver ]; then
- echo "Invalid version."
- exit
-fi
-
-name="select2"
-js="$name.js"
-mini="$name.min.js"
-css="$name.css"
-release="$name-$ver"
-releasedir="/tmp/$release"
-tag="release-$ver"
-branch="build-$ver"
-curbranch=`git branch | grep "*" | sed "s/* //"`
-timestamp=$(date)
-tokens="s/@@ver@@/$ver/g;s/\@@timestamp@@/$timestamp/g"
-remote="github"
-
-git branch "$branch"
-git checkout "$branch"
-
-echo "Tokenizing..."
-
-find . -name "$js" | xargs sed -i -e "$tokens"
-find . -name "$css" | xargs sed -i -e "$tokens"
-
-git add "$js"
-git add "$css"
-
-echo "Minifying..."
-
-echo "/*" > "$mini"
-cat LICENSE | sed "$tokens" >> "$mini"
-echo "*/" >> "$mini"
-
-curl -s \
- -d compilation_level=SIMPLE_OPTIMIZATIONS \
- -d output_format=text \
- -d output_info=compiled_code \
- --data-urlencode "js_code@$js" \
- http://closure-compiler.appspot.com/compile \
- >> "$mini"
-
-git add "$mini"
-
-git commit -m "release $ver"
-
-echo "Tagging..."
-
-git tag -a "$tag" -m "tagged version $ver"
-git push "$remote" --tags
-
-echo "Archiving..."
-
-rm -rf "$releasedir"
-mkdir "$releasedir"
-
-cp $name.* "$releasedir"
-cp spinner.gif "$releasedir"
-cp README.* "$releasedir"
-
-zip -r "$releasedir.zip" "$releasedir"
-rm -rf "$releasedir"
-
-echo "Cleaning Up..."
-
-git checkout "$curbranch"
-git branch -D "$branch"
-
-echo "Done. Release archive created: $releasedir.zip"
diff --git a/addons/base_import/static/lib/select2/select2.css b/addons/base_import/static/lib/select2/select2.css
deleted file mode 100644
index f0bdfb82f04..00000000000
--- a/addons/base_import/static/lib/select2/select2.css
+++ /dev/null
@@ -1,524 +0,0 @@
-/*
-Version: @@ver@@ Timestamp: @@timestamp@@
-*/
-.select2-container {
- position: relative;
- display: inline-block;
- /* inline-block for ie7 */
- zoom: 1;
- *display: inline;
- vertical-align: top;
-}
-
-.select2-container,
-.select2-drop,
-.select2-search,
-.select2-search input{
- /*
- Force border-box so that % widths fit the parent
- container without overlap because of margin/padding.
-
- More Info : http://www.quirksmode.org/css/box.html
- */
- -moz-box-sizing: border-box; /* firefox */
- -ms-box-sizing: border-box; /* ie */
- -webkit-box-sizing: border-box; /* webkit */
- -khtml-box-sizing: border-box; /* konqueror */
- box-sizing: border-box; /* css3 */
-}
-
-.select2-container .select2-choice {
- background-color: #fff;
- background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white));
- background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%);
- background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%);
- background-image: -o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%);
- background-image: -ms-linear-gradient(top, #eeeeee 0%, #ffffff 50%);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#ffffff', GradientType = 0);
- background-image: linear-gradient(top, #eeeeee 0%, #ffffff 50%);
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- -moz-background-clip: padding;
- -webkit-background-clip: padding-box;
- background-clip: padding-box;
- border: 1px solid #aaa;
- display: block;
- overflow: hidden;
- white-space: nowrap;
- position: relative;
- height: 26px;
- line-height: 26px;
- padding: 0 0 0 8px;
- color: #444;
- text-decoration: none;
-}
-
-.select2-container.select2-drop-above .select2-choice
-{
- border-bottom-color: #aaa;
- -webkit-border-radius:0px 0px 4px 4px;
- -moz-border-radius:0px 0px 4px 4px;
- border-radius:0px 0px 4px 4px;
- background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.9, white));
- background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 90%);
- background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 90%);
- background-image: -o-linear-gradient(bottom, #eeeeee 0%, white 90%);
- background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 90%);
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 );
- background-image: linear-gradient(top, #eeeeee 0%,#ffffff 90%);
-}
-
-.select2-container .select2-choice span {
- margin-right: 26px;
- display: block;
- overflow: hidden;
- white-space: nowrap;
- -o-text-overflow: ellipsis;
- -ms-text-overflow: ellipsis;
- text-overflow: ellipsis;
-}
-
-.select2-container .select2-choice abbr {
- display: block;
- position: absolute;
- right: 26px;
- top: 8px;
- width: 12px;
- height: 12px;
- font-size: 1px;
- background: url('select2.png') right top no-repeat;
- cursor: pointer;
- text-decoration: none;
- border:0;
- outline: 0;
-}
-.select2-container .select2-choice abbr:hover {
- background-position: right -11px;
- cursor: pointer;
-}
-
-.select2-drop {
- background: #fff;
- color: #000;
- border: 1px solid #aaa;
- border-top: 0;
- position: absolute;
- top: 100%;
- -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
- -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
- -o-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
- box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
- z-index: 9999;
- width:100%;
- margin-top:-1px;
-
- -webkit-border-radius: 0 0 4px 4px;
- -moz-border-radius: 0 0 4px 4px;
- border-radius: 0 0 4px 4px;
-}
-
-.select2-drop.select2-drop-above {
- -webkit-border-radius: 4px 4px 0px 0px;
- -moz-border-radius: 4px 4px 0px 0px;
- border-radius: 4px 4px 0px 0px;
- margin-top:1px;
- border-top: 1px solid #aaa;
- border-bottom: 0;
-
- -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
- -moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
- -o-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
- box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
-}
-
-.select2-container .select2-choice div {
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
- -moz-background-clip: padding;
- -webkit-background-clip: padding-box;
- background-clip: padding-box;
- background: #ccc;
- background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
- background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
- background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
- background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%);
- background-image: -ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#cccccc', endColorstr = '#eeeeee', GradientType = 0);
- background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%);
- border-left: 1px solid #aaa;
- position: absolute;
- right: 0;
- top: 0;
- display: block;
- height: 100%;
- width: 18px;
-}
-
-.select2-container .select2-choice div b {
- background: url('select2.png') no-repeat 0 1px;
- display: block;
- width: 100%;
- height: 100%;
-}
-
-.select2-search {
- display: inline-block;
- white-space: nowrap;
- z-index: 10000;
- min-height: 26px;
- width: 100%;
- margin: 0;
- padding-left: 4px;
- padding-right: 4px;
-}
-
-.select2-search-hidden {
- display: block;
- position: absolute;
- left: -10000px;
-}
-
-.select2-search input {
- background: #fff url('select2.png') no-repeat 100% -22px;
- background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
- background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
- background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
- background: url('select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
- background: url('select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
- background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
- padding: 4px 20px 4px 5px;
- outline: 0;
- border: 1px solid #aaa;
- font-family: sans-serif;
- font-size: 1em;
- width:100%;
- margin:0;
- height:auto !important;
- min-height: 26px;
- -webkit-box-shadow: none;
- -moz-box-shadow: none;
- box-shadow: none;
- border-radius: 0;
- -moz-border-radius: 0;
- -webkit-border-radius: 0;
-}
-
-.select2-drop.select2-drop-above .select2-search input
-{
- margin-top:4px;
-}
-
-.select2-search input.select2-active {
- background: #fff url('spinner.gif') no-repeat 100%;
- background: url('spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
- background: url('spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
- background: url('spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
- background: url('spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
- background: url('spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
- background: url('spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
-}
-
-
-.select2-container-active .select2-choice,
-.select2-container-active .select2-choices {
- -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
- -moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
- -o-box-shadow : 0 0 5px rgba(0,0,0,.3);
- box-shadow : 0 0 5px rgba(0,0,0,.3);
- border: 1px solid #5897fb;
- outline: none;
-}
-
-.select2-dropdown-open .select2-choice {
- border: 1px solid #aaa;
- border-bottom-color: transparent;
- -webkit-box-shadow: 0 1px 0 #fff inset;
- -moz-box-shadow : 0 1px 0 #fff inset;
- -o-box-shadow : 0 1px 0 #fff inset;
- box-shadow : 0 1px 0 #fff inset;
- background-color: #eee;
- background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee));
- background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%);
- background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%);
- background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%);
- background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%);
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
- background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%);
- -webkit-border-bottom-left-radius : 0;
- -webkit-border-bottom-right-radius: 0;
- -moz-border-radius-bottomleft : 0;
- -moz-border-radius-bottomright: 0;
- border-bottom-left-radius : 0;
- border-bottom-right-radius: 0;
-}
-
-.select2-dropdown-open .select2-choice div {
- background: transparent;
- border-left: none;
-}
-.select2-dropdown-open .select2-choice div b {
- background-position: -18px 1px;
-}
-
-/* results */
-.select2-results {
- margin: 4px 4px 4px 0;
- padding: 0 0 0 4px;
- position: relative;
- overflow-x: hidden;
- overflow-y: auto;
- max-height: 200px;
-}
-
-.select2-results ul.select2-result-sub {
- margin: 0 0 0 0;
-}
-
-.select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px }
-.select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 40px }
-.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 60px }
-.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 80px }
-.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 100px }
-.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 110px }
-.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px }
-
-.select2-results li {
- list-style: none;
- display: list-item;
-}
-
-.select2-results li.select2-result-with-children > .select2-result-label {
- font-weight: bold;
-}
-
-.select2-results .select2-result-label {
- padding: 3px 7px 4px;
- margin: 0;
- cursor: pointer;
-}
-
-.select2-results .select2-highlighted {
- background: #3875d7;
- color: #fff;
-}
-.select2-results li em {
- background: #feffde;
- font-style: normal;
-}
-.select2-results .select2-highlighted em {
- background: transparent;
-}
-.select2-results .select2-no-results,
-.select2-results .select2-searching,
-.select2-results .select2-selection-limit {
- background: #f4f4f4;
- display: list-item;
-}
-
-/*
-disabled look for already selected choices in the results dropdown
-.select2-results .select2-disabled.select2-highlighted {
- color: #666;
- background: #f4f4f4;
- display: list-item;
- cursor: default;
-}
-.select2-results .select2-disabled {
- background: #f4f4f4;
- display: list-item;
- cursor: default;
-}
-*/
-.select2-results .select2-disabled {
- display: none;
-}
-
-.select2-more-results.select2-active {
- background: #f4f4f4 url('spinner.gif') no-repeat 100%;
-}
-
-.select2-more-results {
- background: #f4f4f4;
- display: list-item;
-}
-
-/* disabled styles */
-
-.select2-container.select2-container-disabled .select2-choice {
- background-color: #f4f4f4;
- background-image: none;
- border: 1px solid #ddd;
- cursor: default;
-}
-
-.select2-container.select2-container-disabled .select2-choice div {
- background-color: #f4f4f4;
- background-image: none;
- border-left: 0;
-}
-
-
-/* multiselect */
-
-.select2-container-multi .select2-choices {
- background-color: #fff;
- background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
- background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
- background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
- background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
- background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
- background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%);
- border: 1px solid #aaa;
- margin: 0;
- padding: 0;
- cursor: text;
- overflow: hidden;
- height: auto !important;
- height: 1%;
- position: relative;
-}
-
-.select2-container-multi .select2-choices {
- min-height: 26px;
-}
-
-.select2-container-multi.select2-container-active .select2-choices {
- -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
- -moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
- -o-box-shadow : 0 0 5px rgba(0,0,0,.3);
- box-shadow : 0 0 5px rgba(0,0,0,.3);
- border: 1px solid #5897fb;
- outline: none;
-}
-.select2-container-multi .select2-choices li {
- float: left;
- list-style: none;
-}
-.select2-container-multi .select2-choices .select2-search-field {
- white-space: nowrap;
- margin: 0;
- padding: 0;
-}
-
-.select2-container-multi .select2-choices .select2-search-field input {
- color: #666;
- background: transparent !important;
- font-family: sans-serif;
- font-size: 100%;
- height: 15px;
- padding: 5px;
- margin: 1px 0;
- outline: 0;
- border: 0;
- -webkit-box-shadow: none;
- -moz-box-shadow : none;
- -o-box-shadow : none;
- box-shadow : none;
-}
-
-.select2-container-multi .select2-choices .select2-search-field input.select2-active {
- background: #fff url('spinner.gif') no-repeat 100% !important;
-}
-
-.select2-default {
- color: #999 !important;
-}
-
-.select2-container-multi .select2-choices .select2-search-choice {
- -webkit-border-radius: 3px;
- -moz-border-radius : 3px;
- border-radius : 3px;
- -moz-background-clip : padding;
- -webkit-background-clip: padding-box;
- background-clip : padding-box;
- background-color: #e4e4e4;
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
- background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
- background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
- -moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
- box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
- color: #333;
- border: 1px solid #aaaaaa;
- line-height: 13px;
- padding: 3px 5px 3px 18px;
- margin: 3px 0 3px 5px;
- position: relative;
- cursor: default;
-}
-.select2-container-multi .select2-choices .select2-search-choice span {
- cursor: default;
-}
-.select2-container-multi .select2-choices .select2-search-choice-focus {
- background: #d4d4d4;
-}
-
-.select2-search-choice-close {
- display: block;
- position: absolute;
- right: 3px;
- top: 4px;
- width: 12px;
- height: 13px;
- font-size: 1px;
- background: url('select2.png') right top no-repeat;
- outline: none;
-}
-
-.select2-container-multi .select2-search-choice-close {
- left: 3px;
-}
-
-
-.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover {
- background-position: right -11px;
-}
-.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close {
- background-position: right -11px;
-}
-
-/* disabled styles */
-
-.select2-container-multi.select2-container-disabled .select2-choices{
- background-color: #f4f4f4;
- background-image: none;
- border: 1px solid #ddd;
- cursor: default;
-}
-
-.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
- background-image: none;
- background-color: #f4f4f4;
- border: 1px solid #ddd;
- padding: 3px 5px 3px 5px;
-}
-
-.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close {
- display: none;
-}
-/* end multiselect */
-
-.select2-result-selectable .select2-match,
-.select2-result-unselectable .select2-result-selectable .select2-match { text-decoration: underline; }
-.select2-result-unselectable .select2-match { text-decoration: none; }
-
-.select2-offscreen { position: absolute; left: -10000px; }
-
-/* Retina-ize icons */
-
-@media only screen and (-webkit-min-device-pixel-ratio: 1.5) {
- .select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice div b {
- background-image: url(select2x2.png) !important;
- background-repeat: no-repeat !important;
- background-size: 60px 40px !important;
- }
- .select2-search input {
- background-position: 100% -21px !important;
- }
-}
\ No newline at end of file
diff --git a/addons/base_import/static/lib/select2/select2.js b/addons/base_import/static/lib/select2/select2.js
deleted file mode 100644
index 42ce0a928b8..00000000000
--- a/addons/base_import/static/lib/select2/select2.js
+++ /dev/null
@@ -1,2407 +0,0 @@
-/*
- Copyright 2012 Igor Vaynberg
-
- Version: @@ver@@ Timestamp: @@timestamp@@
-
- Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
- compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software distributed under the License is
- distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and limitations under the License.
- */
- (function ($) {
- if(typeof $.fn.each2 == "undefined"){
- $.fn.extend({
- /*
- * 4-10 times faster .each replacement
- * use it carefully, as it overrides jQuery context of element on each iteration
- */
- each2 : function (c) {
- var j = $([0]), i = -1, l = this.length;
- while (
- ++i < l
- && (j.context = j[0] = this[i])
- && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
- );
- return this;
- }
- });
- }
-})(jQuery);
-
-(function ($, undefined) {
- "use strict";
- /*global document, window, jQuery, console */
-
- if (window.Select2 !== undefined) {
- return;
- }
-
- var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer;
-
- KEY = {
- TAB: 9,
- ENTER: 13,
- ESC: 27,
- SPACE: 32,
- LEFT: 37,
- UP: 38,
- RIGHT: 39,
- DOWN: 40,
- SHIFT: 16,
- CTRL: 17,
- ALT: 18,
- PAGE_UP: 33,
- PAGE_DOWN: 34,
- HOME: 36,
- END: 35,
- BACKSPACE: 8,
- DELETE: 46,
- isArrow: function (k) {
- k = k.which ? k.which : k;
- switch (k) {
- case KEY.LEFT:
- case KEY.RIGHT:
- case KEY.UP:
- case KEY.DOWN:
- return true;
- }
- return false;
- },
- isControl: function (e) {
- var k = e.which;
- switch (k) {
- case KEY.SHIFT:
- case KEY.CTRL:
- case KEY.ALT:
- return true;
- }
-
- if (e.metaKey) return true;
-
- return false;
- },
- isFunctionKey: function (k) {
- k = k.which ? k.which : k;
- return k >= 112 && k <= 123;
- }
- };
-
- nextUid=(function() { var counter=1; return function() { return counter++; }; }());
-
- function indexOf(value, array) {
- var i = 0, l = array.length, v;
-
- if (typeof value === "undefined") {
- return -1;
- }
-
- if (value.constructor === String) {
- for (; i < l; i = i + 1) if (value.localeCompare(array[i]) === 0) return i;
- } else {
- for (; i < l; i = i + 1) {
- v = array[i];
- if (v.constructor === String) {
- if (v.localeCompare(value) === 0) return i;
- } else {
- if (v === value) return i;
- }
- }
- }
- return -1;
- }
-
- /**
- * Compares equality of a and b taking into account that a and b may be strings, in which case localeCompare is used
- * @param a
- * @param b
- */
- function equal(a, b) {
- if (a === b) return true;
- if (a === undefined || b === undefined) return false;
- if (a === null || b === null) return false;
- if (a.constructor === String) return a.localeCompare(b) === 0;
- if (b.constructor === String) return b.localeCompare(a) === 0;
- return false;
- }
-
- /**
- * Splits the string into an array of values, trimming each value. An empty array is returned for nulls or empty
- * strings
- * @param string
- * @param separator
- */
- function splitVal(string, separator) {
- var val, i, l;
- if (string === null || string.length < 1) return [];
- val = string.split(separator);
- for (i = 0, l = val.length; i < l; i = i + 1) val[i] = $.trim(val[i]);
- return val;
- }
-
- function getSideBorderPadding(element) {
- return element.outerWidth() - element.width();
- }
-
- function installKeyUpChangeEvent(element) {
- var key="keyup-change-value";
- element.bind("keydown", function () {
- if ($.data(element, key) === undefined) {
- $.data(element, key, element.val());
- }
- });
- element.bind("keyup", function () {
- var val= $.data(element, key);
- if (val !== undefined && element.val() !== val) {
- $.removeData(element, key);
- element.trigger("keyup-change");
- }
- });
- }
-
- $(document).delegate("*", "mousemove", function (e) {
- $.data(document, "select2-lastpos", {x: e.pageX, y: e.pageY});
- });
-
- /**
- * filters mouse events so an event is fired only if the mouse moved.
- *
- * filters out mouse events that occur when mouse is stationary but
- * the elements under the pointer are scrolled.
- */
- function installFilteredMouseMove(element) {
- element.bind("mousemove", function (e) {
- var lastpos = $.data(document, "select2-lastpos");
- if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
- $(e.target).trigger("mousemove-filtered", e);
- }
- });
- }
-
- /**
- * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made
- * within the last quietMillis milliseconds.
- *
- * @param quietMillis number of milliseconds to wait before invoking fn
- * @param fn function to be debounced
- * @param ctx object to be used as this reference within fn
- * @return debounced version of fn
- */
- function debounce(quietMillis, fn, ctx) {
- ctx = ctx || undefined;
- var timeout;
- return function () {
- var args = arguments;
- window.clearTimeout(timeout);
- timeout = window.setTimeout(function() {
- fn.apply(ctx, args);
- }, quietMillis);
- };
- }
-
- /**
- * A simple implementation of a thunk
- * @param formula function used to lazily initialize the thunk
- * @return {Function}
- */
- function thunk(formula) {
- var evaluated = false,
- value;
- return function() {
- if (evaluated === false) { value = formula(); evaluated = true; }
- return value;
- };
- };
-
- function installDebouncedScroll(threshold, element) {
- var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);});
- element.bind("scroll", function (e) {
- if (indexOf(e.target, element.get()) >= 0) notify(e);
- });
- }
-
- function killEvent(event) {
- event.preventDefault();
- event.stopPropagation();
- }
-
- function measureTextWidth(e) {
- if (!sizer){
- var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
- sizer = $("").css({
- position: "absolute",
- left: "-10000px",
- top: "-10000px",
- display: "none",
- fontSize: style.fontSize,
- fontFamily: style.fontFamily,
- fontStyle: style.fontStyle,
- fontWeight: style.fontWeight,
- letterSpacing: style.letterSpacing,
- textTransform: style.textTransform,
- whiteSpace: "nowrap"
- });
- $("body").append(sizer);
- }
- sizer.text(e.val());
- return sizer.width();
- }
-
- function markMatch(text, term, markup) {
- var match=text.toUpperCase().indexOf(term.toUpperCase()),
- tl=term.length;
-
- if (match<0) {
- markup.push(text);
- return;
- }
-
- markup.push(text.substring(0, match));
- markup.push("");
- markup.push(text.substring(match, match + tl));
- markup.push("");
- markup.push(text.substring(match + tl, text.length));
- }
-
- /**
- * Produces an ajax-based query function
- *
- * @param options object containing configuration paramters
- * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax
- * @param options.url url for the data
- * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url.
- * @param options.dataType request data type: ajax, jsonp, other datatatypes supported by jQuery's $.ajax function or the transport function if specified
- * @param options.traditional a boolean flag that should be true if you wish to use the traditional style of param serialization for the ajax request
- * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often
- * @param options.results a function(remoteData, pageNumber) that converts data returned form the remote request to the format expected by Select2.
- * The expected format is an object containing the following keys:
- * results array of objects that will be used as choices
- * more (optional) boolean indicating whether there are more results available
- * Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true}
- */
- function ajax(options) {
- var timeout, // current scheduled but not yet executed request
- requestSequence = 0, // sequence used to drop out-of-order responses
- handler = null,
- quietMillis = options.quietMillis || 100;
-
- return function (query) {
- window.clearTimeout(timeout);
- timeout = window.setTimeout(function () {
- requestSequence += 1; // increment the sequence
- var requestNumber = requestSequence, // this request's sequence number
- data = options.data, // ajax data function
- transport = options.transport || $.ajax,
- traditional = options.traditional || false,
- type = options.type || 'GET'; // set type of request (GET or POST)
-
- data = data.call(this, query.term, query.page, query.context);
-
- if( null !== handler) { handler.abort(); }
-
- handler = transport.call(null, {
- url: options.url,
- dataType: options.dataType,
- data: data,
- type: type,
- traditional: traditional,
- success: function (data) {
- if (requestNumber < requestSequence) {
- return;
- }
- // TODO 3.0 - replace query.page with query so users have access to term, page, etc.
- var results = options.results(data, query.page);
- query.callback(results);
- }
- });
- }, quietMillis);
- };
- }
-
- /**
- * Produces a query function that works with a local array
- *
- * @param options object containing configuration parameters. The options parameter can either be an array or an
- * object.
- *
- * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys.
- *
- * If the object form is used ti is assumed that it contains 'data' and 'text' keys. The 'data' key should contain
- * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text'
- * key can either be a String in which case it is expected that each element in the 'data' array has a key with the
- * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract
- * the text.
- */
- function local(options) {
- var data = options, // data elements
- dataText,
- text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search
-
- if (!$.isArray(data)) {
- text = data.text;
- // if text is not a function we assume it to be a key name
- if (!$.isFunction(text)) {
- dataText = data.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available
- text = function (item) { return item[dataText]; };
- }
- data = data.results;
- }
-
- return function (query) {
- var t = query.term, filtered = { results: [] }, process;
- if (t === "") {
- query.callback({results: data});
- return;
- }
-
- process = function(datum, collection) {
- var group, attr;
- datum = datum[0];
- if (datum.children) {
- group = {};
- for (attr in datum) {
- if (datum.hasOwnProperty(attr)) group[attr]=datum[attr];
- }
- group.children=[];
- $(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); });
- if (group.children.length) {
- collection.push(group);
- }
- } else {
- if (query.matcher(t, text(datum))) {
- collection.push(datum);
- }
- }
- };
-
- $(data).each2(function(i, datum) { process(datum, filtered.results); });
- query.callback(filtered);
- };
- }
-
- // TODO javadoc
- function tags(data) {
- // TODO even for a function we should probably return a wrapper that does the same object/string check as
- // the function for arrays. otherwise only functions that return objects are supported.
- if ($.isFunction(data)) {
- return data;
- }
-
- // if not a function we assume it to be an array
-
- return function (query) {
- var t = query.term, filtered = {results: []};
- $(data).each(function () {
- var isObject = this.text !== undefined,
- text = isObject ? this.text : this;
- if (t === "" || query.matcher(t, text)) {
- filtered.results.push(isObject ? this : {id: this, text: this});
- }
- });
- query.callback(filtered);
- };
- }
-
- /**
- * Checks if the formatter function should be used.
- *
- * Throws an error if it is not a function. Returns true if it should be used,
- * false if no formatting should be performed.
- *
- * @param formatter
- */
- function checkFormatter(formatter, formatterName) {
- if ($.isFunction(formatter)) return true;
- if (!formatter) return false;
- throw new Error("formatterName must be a function or a falsy value");
- }
-
- function evaluate(val) {
- return $.isFunction(val) ? val() : val;
- }
-
- function countResults(results) {
- var count = 0;
- $.each(results, function(i, item) {
- if (item.children) {
- count += countResults(item.children);
- } else {
- count++;
- }
- });
- return count;
- }
-
- /**
- * Default tokenizer. This function uses breaks the input on substring match of any string from the
- * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those
- * two options have to be defined in order for the tokenizer to work.
- *
- * @param input text user has typed so far or pasted into the search field
- * @param selection currently selected choices
- * @param selectCallback function(choice) callback tho add the choice to selection
- * @param opts select2's opts
- * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value
- */
- function defaultTokenizer(input, selection, selectCallback, opts) {
- var original = input, // store the original so we can compare and know if we need to tell the search to update its text
- dupe = false, // check for whether a token we extracted represents a duplicate selected choice
- token, // token
- index, // position at which the separator was found
- i, l, // looping variables
- separator; // the matched separator
-
- if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined;
-
- while (true) {
- index = -1;
-
- for (i = 0, l = opts.tokenSeparators.length; i < l; i++) {
- separator = opts.tokenSeparators[i];
- index = input.indexOf(separator);
- if (index >= 0) break;
- }
-
- if (index < 0) break; // did not find any token separator in the input string, bail
-
- token = input.substring(0, index);
- input = input.substring(index + separator.length);
-
- if (token.length > 0) {
- token = opts.createSearchChoice(token, selection);
- if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) {
- dupe = false;
- for (i = 0, l = selection.length; i < l; i++) {
- if (equal(opts.id(token), opts.id(selection[i]))) {
- dupe = true; break;
- }
- }
-
- if (!dupe) selectCallback(token);
- }
- }
- }
-
- if (original.localeCompare(input) != 0) return input;
- }
-
- /**
- * blurs any Select2 container that has focus when an element outside them was clicked or received focus
- *
- * also takes care of clicks on label tags that point to the source element
- */
- $(document).ready(function () {
- $(document).delegate("*", "mousedown touchend", function (e) {
- var target = $(e.target).closest("div.select2-container").get(0), attr;
- if (target) {
- $(document).find("div.select2-container-active").each(function () {
- if (this !== target) $(this).data("select2").blur();
- });
- } else {
- target = $(e.target).closest("div.select2-drop").get(0);
- $(document).find("div.select2-drop-active").each(function () {
- if (this !== target) $(this).data("select2").blur();
- });
- }
-
- target=$(e.target);
- attr = target.attr("for");
- if ("LABEL" === e.target.tagName && attr && attr.length > 0) {
- target = $("#"+attr);
- target = target.data("select2");
- if (target !== undefined) { target.focus(); e.preventDefault();}
- }
- });
- });
-
- /**
- * Creates a new class
- *
- * @param superClass
- * @param methods
- */
- function clazz(SuperClass, methods) {
- var constructor = function () {};
- constructor.prototype = new SuperClass;
- constructor.prototype.constructor = constructor;
- constructor.prototype.parent = SuperClass.prototype;
- constructor.prototype = $.extend(constructor.prototype, methods);
- return constructor;
- }
-
- AbstractSelect2 = clazz(Object, {
-
- // abstract
- bind: function (func) {
- var self = this;
- return function () {
- func.apply(self, arguments);
- };
- },
-
- // abstract
- init: function (opts) {
- var results, search, resultsSelector = ".select2-results";
-
- // prepare options
- this.opts = opts = this.prepareOpts(opts);
-
- this.id=opts.id;
-
- // destroy if called on an existing component
- if (opts.element.data("select2") !== undefined &&
- opts.element.data("select2") !== null) {
- this.destroy();
- }
-
- this.enabled=true;
- this.container = this.createContainer();
-
- this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid());
- this.containerSelector="#"+this.containerId.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
- this.container.attr("id", this.containerId);
-
- // cache the body so future lookups are cheap
- this.body = thunk(function() { return opts.element.closest("body"); });
-
- if (opts.element.attr("class") !== undefined) {
- this.container.addClass(opts.element.attr("class").replace(/validate\[[\S ]+] ?/, ''));
- }
-
- this.container.css(evaluate(opts.containerCss));
- this.container.addClass(evaluate(opts.containerCssClass));
-
- // swap container for the element
- this.opts.element
- .data("select2", this)
- .hide()
- .before(this.container);
- this.container.data("select2", this);
-
- this.dropdown = this.container.find(".select2-drop");
- this.dropdown.addClass(evaluate(opts.dropdownCssClass));
- this.dropdown.data("select2", this);
-
- this.results = results = this.container.find(resultsSelector);
- this.search = search = this.container.find("input.select2-input");
-
- search.attr("tabIndex", this.opts.element.attr("tabIndex"));
-
- this.resultsPage = 0;
- this.context = null;
-
- // initialize the container
- this.initContainer();
- this.initContainerWidth();
-
- installFilteredMouseMove(this.results);
- this.dropdown.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent));
-
- installDebouncedScroll(80, this.results);
- this.dropdown.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded));
-
- // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
- if ($.fn.mousewheel) {
- results.mousewheel(function (e, delta, deltaX, deltaY) {
- var top = results.scrollTop(), height;
- if (deltaY > 0 && top - deltaY <= 0) {
- results.scrollTop(0);
- killEvent(e);
- } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) {
- results.scrollTop(results.get(0).scrollHeight - results.height());
- killEvent(e);
- }
- });
- }
-
- installKeyUpChangeEvent(search);
- search.bind("keyup-change", this.bind(this.updateResults));
- search.bind("focus", function () { search.addClass("select2-focused"); if (search.val() === " ") search.val(""); });
- search.bind("blur", function () { search.removeClass("select2-focused");});
-
- this.dropdown.delegate(resultsSelector, "mouseup", this.bind(function (e) {
- if ($(e.target).closest(".select2-result-selectable:not(.select2-disabled)").length > 0) {
- this.highlightUnderEvent(e);
- this.selectHighlighted(e);
- } else {
- this.focusSearch();
- }
- killEvent(e);
- }));
-
- // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening
- // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's
- // dom it will trigger the popup close, which is not what we want
- this.dropdown.bind("click mouseup mousedown", function (e) { e.stopPropagation(); });
-
- if ($.isFunction(this.opts.initSelection)) {
- // initialize selection based on the current value of the source element
- this.initSelection();
-
- // if the user has provided a function that can set selection based on the value of the source element
- // we monitor the change event on the element and trigger it, allowing for two way synchronization
- this.monitorSource();
- }
-
- if (opts.element.is(":disabled") || opts.element.is("[readonly='readonly']")) this.disable();
- },
-
- // abstract
- destroy: function () {
- var select2 = this.opts.element.data("select2");
- if (select2 !== undefined) {
- select2.container.remove();
- select2.dropdown.remove();
- select2.opts.element
- .removeData("select2")
- .unbind(".select2")
- .show();
- }
- },
-
- // abstract
- prepareOpts: function (opts) {
- var element, select, idKey, ajaxUrl;
-
- element = opts.element;
-
- if (element.get(0).tagName.toLowerCase() === "select") {
- this.select = select = opts.element;
- }
-
- if (select) {
- // these options are not allowed when attached to a select because they are picked up off the element itself
- $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
- if (this in opts) {
- throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a
diff --git a/addons/crm/crm_lead_menu.xml b/addons/crm/crm_lead_menu.xml
index 7ee3287fd75..2f1a2ca16cd 100644
--- a/addons/crm/crm_lead_menu.xml
+++ b/addons/crm/crm_lead_menu.xml
@@ -12,6 +12,7 @@
{
'default_type':'lead',
'stage_type':'lead',
+ 'empty_list_help_model': 'crm.case.section',
'needaction_menu_ref': 'crm.menu_crm_opportunities',
'search_default_unassigned':1,
}
@@ -79,8 +80,7 @@
+ action="crm_case_category_act_leads_all"/>
diff --git a/addons/crm/report/crm_phonecall_report.py b/addons/crm/report/crm_phonecall_report.py
index fba095f1174..6a3e3bf208b 100644
--- a/addons/crm/report/crm_phonecall_report.py
+++ b/addons/crm/report/crm_phonecall_report.py
@@ -40,20 +40,12 @@ class crm_phonecall_report(osv.osv):
_auto = False
_columns = {
- 'name': fields.char('Year', size=64, required=False, readonly=True),
'user_id':fields.many2one('res.users', 'User', readonly=True),
'section_id':fields.many2one('crm.case.section', 'Section', readonly=True),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
'nbr': fields.integer('# of Cases', readonly=True),
'state': fields.selection(AVAILABLE_STATES, 'Status', size=16, readonly=True),
- 'month':fields.selection([('01', 'January'), ('02', 'February'), \
- ('03', 'March'), ('04', 'April'),\
- ('05', 'May'), ('06', 'June'), \
- ('07', 'July'), ('08', 'August'),\
- ('09', 'September'), ('10', 'October'),\
- ('11', 'November'), ('12', 'December')], 'Month', readonly=True),
'create_date': fields.datetime('Create Date', readonly=True, select=True),
- 'day': fields.char('Day', size=128, readonly=True),
'delay_close': fields.float('Delay to close', digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to close the case"),
'duration': fields.float('Duration', digits=(16,2),readonly=True, group_operator="avg"),
'delay_open': fields.float('Delay to open',digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to open the case"),
@@ -77,9 +69,6 @@ class crm_phonecall_report(osv.osv):
create or replace view crm_phonecall_report as (
select
id,
- to_char(c.date, 'YYYY') as name,
- to_char(c.date, 'MM') as month,
- to_char(c.date, 'YYYY-MM-DD') as day,
to_char(c.create_date, 'YYYY-MM-DD') as creation_date,
to_char(c.date_open, 'YYYY-MM-DD') as opening_date,
to_char(c.date_closed, 'YYYY-mm-dd') as date_closed,
diff --git a/addons/crm/report/crm_phonecall_report_view.xml b/addons/crm/report/crm_phonecall_report_view.xml
index 9de9adeade3..32ae9704064 100644
--- a/addons/crm/report/crm_phonecall_report_view.xml
+++ b/addons/crm/report/crm_phonecall_report_view.xml
@@ -12,6 +12,7 @@
+
@@ -52,9 +53,9 @@
-
-
-
+
+
+
diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py
index 21a553a59ff..f779237ea71 100644
--- a/addons/crm/wizard/crm_lead_to_opportunity.py
+++ b/addons/crm/wizard/crm_lead_to_opportunity.py
@@ -175,7 +175,6 @@ class crm_lead2opportunity_partner(osv.osv_memory):
ctx['active_id'] = lead_id
partner_id = self._find_matching_partner(cr, uid, context=ctx)
action = 'create'
- print partner_id
res = lead.handle_partner_assignation(cr, uid, [lead_id], action, partner_id, context=context)
return res.get(lead_id)
diff --git a/addons/crm_partner_assign/__openerp__.py b/addons/crm_partner_assign/__openerp__.py
index 3c75ed35911..eae57624970 100644
--- a/addons/crm_partner_assign/__openerp__.py
+++ b/addons/crm_partner_assign/__openerp__.py
@@ -21,7 +21,7 @@
{
- 'name': 'CRM Geolocation',
+ 'name': 'Partner Assignation & Geolocation',
'version': '1.0',
'category': 'Customer Relationship Management',
'description': """
@@ -34,8 +34,7 @@ Once the coordinates of the Lead/Opportunity is known, they can be automatically
to an appropriate local partner, based on the distance and the weight that was assigned to the partner.
""",
'author': 'OpenERP SA',
- 'depends': ['crm', 'account', 'portal'],
- 'demo': ['res_partner_demo.xml', 'crm_lead_demo.xml'],
+ 'depends': ['base_geolocalize', 'crm', 'account', 'portal'],
'data': [
'security/ir.model.access.csv',
'res_partner_view.xml',
@@ -48,6 +47,10 @@ to an appropriate local partner, based on the distance and the weight that was a
'report/crm_lead_report_view.xml',
'report/crm_partner_report_view.xml',
],
+ 'demo': [
+ 'res_partner_demo.xml',
+ 'crm_lead_demo.xml'
+ ],
'js': [
'static/src/js/next.js',
],
diff --git a/addons/crm_partner_assign/crm_partner_assign.py b/addons/crm_partner_assign/crm_partner_assign.py
index c5329696b32..046babccf73 100644
--- a/addons/crm_partner_assign/crm_partner_assign.py
+++ b/addons/crm_partner_assign/crm_partner_assign.py
@@ -19,46 +19,12 @@
#
##############################################################################
-import urllib
import random
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
-
+from openerp.addons.base_geolocalize.models.res_partner import geo_find, geo_query_address
from openerp.osv import osv
from openerp.osv import fields
-from openerp.tools.translate import _
-from openerp import tools
-def geo_find(addr):
- url = 'https://maps.googleapis.com/maps/api/geocode/json?sensor=false&address='
- url += urllib.quote(addr.encode('utf8'))
-
- try:
- result = json.load(urllib.urlopen(url))
- except Exception, e:
- raise osv.except_osv(_('Network error'),
- _('Cannot contact geolocation servers. Please make sure that your internet connection is up and running (%s).') % e)
- if result['status'] != 'OK':
- return None
-
- try:
- geo = result['results'][0]['geometry']['location']
- return float(geo['lat']), float(geo['lng'])
- except (KeyError, ValueError):
- return None
-
-def geo_query_address(street=None, zip=None, city=None, state=None, country=None):
- if country and ',' in country and (country.endswith(' of') or country.endswith(' of the')):
- # put country qualifier in front, otherwise GMap gives wrong results,
- # e.g. 'Congo, Democratic Republic of the' => 'Democratic Republic of the Congo'
- country = '{1} {0}'.format(*country.split(',',1))
- return tools.ustr(', '.join(filter(None, [street,
- ("%s %s" % (zip or '', city or '')).strip(),
- state,
- country])))
class res_partner_grade(osv.osv):
_order = 'sequence'
@@ -88,9 +54,6 @@ class res_partner_activation(osv.osv):
class res_partner(osv.osv):
_inherit = "res.partner"
_columns = {
- 'partner_latitude': fields.float('Geo Latitude'),
- 'partner_longitude': fields.float('Geo Longitude'),
- 'date_localization': fields.date('Geo Localization Date'),
'partner_weight': fields.integer('Grade Weight',
help="Gives the probability to assign a lead to this partner. (0 means no assignation.)"),
'opportunity_assigned_ids': fields.one2many('crm.lead', 'partner_assigned_id',\
@@ -100,6 +63,14 @@ class res_partner(osv.osv):
'date_partnership' : fields.date('Partnership Date'),
'date_review' : fields.date('Latest Partner Review'),
'date_review_next' : fields.date('Next Partner Review'),
+ # customer implementation
+ 'assigned_partner_id': fields.many2one(
+ 'res.partner', 'Implementedy by',
+ ),
+ 'implemented_partner_ids': fields.one2many(
+ 'res.partner', 'assigned_partner_id',
+ string='Implementation References',
+ ),
}
_defaults = {
'partner_weight': lambda *args: 0
@@ -111,23 +82,7 @@ class res_partner(osv.osv):
partner_grade = self.pool.get('res.partner.grade').browse(cr, uid, grade_id)
res['value']['partner_weight'] = partner_grade.partner_weight
return res
- def geo_localize(self, cr, uid, ids, context=None):
- # Don't pass context to browse()! We need country names in english below
- for partner in self.browse(cr, uid, ids):
- if not partner:
- continue
- result = geo_find(geo_query_address(street=partner.street,
- zip=partner.zip,
- city=partner.city,
- state=partner.state_id.name,
- country=partner.country_id.name))
- if result:
- self.write(cr, uid, [partner.id], {
- 'partner_latitude': result[0],
- 'partner_longitude': result[1],
- 'date_localization': fields.date.context_today(self,cr,uid,context=context)
- }, context=context)
- return True
+
class crm_lead(osv.osv):
_inherit = "crm.lead"
diff --git a/addons/crm_partner_assign/portal_data.xml b/addons/crm_partner_assign/portal_data.xml
index 0d4a5b30abd..da0660c3446 100644
--- a/addons/crm_partner_assign/portal_data.xml
+++ b/addons/crm_partner_assign/portal_data.xml
@@ -3,25 +3,10 @@
-
- openerp.portal.res.partner
-
-
-
-
-
-
-
-
- openerp.portal.res.partner
-
-
- [('id','child_of',user.commercial_partner_id.id)]
- openerp.portal.res.partner.grade
-
+
@@ -32,13 +17,13 @@
openerp.portal.crm.lead
-
+ [('partner_assigned_id','child_of',user.commercial_partner_id.id)]openerp.portal.crm.lead
-
+
diff --git a/addons/crm_partner_assign/report/crm_lead_report.py b/addons/crm_partner_assign/report/crm_lead_report.py
index c7a6b76ee44..572e1796b00 100644
--- a/addons/crm_partner_assign/report/crm_lead_report.py
+++ b/addons/crm_partner_assign/report/crm_lead_report.py
@@ -30,22 +30,14 @@ class crm_lead_report_assign(osv.osv):
_auto = False
_description = "CRM Lead Report"
_columns = {
- 'year': fields.char('Year', size=64, required=False, readonly=True),
'partner_assigned_id':fields.many2one('res.partner', 'Partner', readonly=True),
'grade_id':fields.many2one('res.partner.grade', 'Grade', readonly=True),
'user_id':fields.many2one('res.users', 'User', readonly=True),
'country_id':fields.many2one('res.country', 'Country', readonly=True),
'section_id':fields.many2one('crm.case.section', 'Sales Team', readonly=True),
- 'month':fields.selection([('01', 'January'), ('02', 'February'), \
- ('03', 'March'), ('04', 'April'),\
- ('05', 'May'), ('06', 'June'), \
- ('07', 'July'), ('08', 'August'),\
- ('09', 'September'), ('10', 'October'),\
- ('11', 'November'), ('12', 'December')], 'Month', readonly=True),
'company_id': fields.many2one('res.company', 'Company', readonly=True),
- 'date_assign': fields.date('Partner Date', readonly=True),
+ 'date_assign': fields.date('Assign Date', readonly=True),
'create_date': fields.datetime('Create Date', readonly=True),
- 'day': fields.char('Day', size=128, readonly=True),
'delay_open': fields.float('Delay to Assign',digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to open the case"),
'delay_close': fields.float('Delay to Close',digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to close the case"),
'delay_expected': fields.float('Overpassed Deadline',digits=(16,2),readonly=True, group_operator="avg"),
@@ -77,9 +69,6 @@ class crm_lead_report_assign(osv.osv):
CREATE OR REPLACE VIEW crm_lead_report_assign AS (
SELECT
c.id,
- to_char(c.date_assign, 'YYYY') as year,
- to_char(c.date_assign, 'MM') as month,
- to_char(c.date_assign, 'YYYY-MM-DD') as day,
to_char(c.create_date, 'YYYY-MM-DD') as creation_date,
to_char(c.date_open, 'YYYY-MM-DD') as opening_date,
to_char(c.date_closed, 'YYYY-mm-dd') as date_closed,
diff --git a/addons/crm_partner_assign/report/crm_lead_report_view.xml b/addons/crm_partner_assign/report/crm_lead_report_view.xml
index a7ff8d80f0f..271c9b2016f 100644
--- a/addons/crm_partner_assign/report/crm_lead_report_view.xml
+++ b/addons/crm_partner_assign/report/crm_lead_report_view.xml
@@ -35,10 +35,9 @@
-
-
-
-
+
+
+
@@ -50,9 +49,11 @@
crm.lead.assign.graphcrm.lead.report.assign
-
-
+
+
+
+
@@ -63,7 +64,7 @@
Opp. Assignment Analysiscrm.lead.report.assignform
- {'default_type': 'opportunity', "search_default_group_partner_date": 1, "search_default_group_grade":1,'group_by_no_leaf':1,'group_by':[]}
+ {'default_type': 'opportunity', 'group_by_no_leaf':1,'group_by':[]}graph[('type', '=', 'opportunity')]
diff --git a/addons/crm_partner_assign/report/crm_partner_report_view.xml b/addons/crm_partner_assign/report/crm_partner_report_view.xml
index 4e7e13afb15..02320219218 100644
--- a/addons/crm_partner_assign/report/crm_partner_report_view.xml
+++ b/addons/crm_partner_assign/report/crm_partner_report_view.xml
@@ -24,7 +24,7 @@
domain="[]" context="{'group_by':'grade_id'}" />
-
@@ -35,25 +35,15 @@
-
-
-
- crm.partner.assign.report.tree
+
+ crm.partner.assign.report.graphcrm.partner.report.assign
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
@@ -62,8 +52,8 @@
Partnership Analysiscrm.partner.report.assign
- {'search_default_group_grade': 1, 'group_by_no_leaf':1,'group_by':[]}
- tree
+ {'group_by_no_leaf':1,'group_by':[]}
+ graph[('grade_id', '!=', False)]
diff --git a/addons/crm_partner_assign/res_partner_demo.xml b/addons/crm_partner_assign/res_partner_demo.xml
index 8e9088165d1..a0711657810 100644
--- a/addons/crm_partner_assign/res_partner_demo.xml
+++ b/addons/crm_partner_assign/res_partner_demo.xml
@@ -1,13 +1,109 @@
-
- First
+
+ Platinum
+ 4
+
+
+ Gold
+ 3
+
+
+ Silver
+ 2
+
+
+ Bronze1
+
-
+ 10
+
+
+
+
+
+ 10
+
+
+
+ 10
+
+
+
+
+ 10
+
+
+
+ 10
+
+
+
+ 10
+
+
+
+
+ 10
+
+
+
+ 10
+
+
+
+ 10
+
+
+
+
+ 10
+
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/crm_partner_assign/res_partner_view.xml b/addons/crm_partner_assign/res_partner_view.xml
index 734d5a31e95..c298832a2e6 100644
--- a/addons/crm_partner_assign/res_partner_view.xml
+++ b/addons/crm_partner_assign/res_partner_view.xml
@@ -98,13 +98,14 @@
-
- res.partner.geo.inherit
+
+ res.partner.assign.inheritres.partner
-
+
-
-
+
+
+
diff --git a/addons/crm_partner_assign/security/ir.model.access.csv b/addons/crm_partner_assign/security/ir.model.access.csv
index 81d74bf43e3..6a26c574a1c 100644
--- a/addons/crm_partner_assign/security/ir.model.access.csv
+++ b/addons/crm_partner_assign/security/ir.model.access.csv
@@ -5,4 +5,4 @@ access_crm_partner_report,crm.partner.report.assign.all,model_crm_partner_report
access_res_partner_grade,res.partner.grade,model_res_partner_grade,base.group_sale_salesman,1,1,1,0
access_res_partner_grade_manager,res.partner.grade.manager,model_res_partner_grade,base.group_sale_manager,1,1,1,1
"access_partner_activation_manager","res.partner.activation.manager","model_res_partner_activation","base.group_partner_manager",1,1,1,1
-partner_access_crm_lead,crm.lead,model_crm_lead,portal.group_portal,1,1,0,0
\ No newline at end of file
+partner_access_crm_lead,crm.lead,model_crm_lead,base.group_portal,1,1,0,0
\ No newline at end of file
diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py
index fca2f9b746f..a854da9459d 100644
--- a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py
+++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py
@@ -87,7 +87,7 @@ class crm_lead_forward_to_partner(osv.TransientModel):
raise osv.except_osv(_('Email Template Error'),
_('The Forward Email Template is not in the database'))
try:
- portal_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal')[1]
+ portal_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_portal')[1]
except ValueError:
raise osv.except_osv(_('Portal Group Error'),
_('The Portal group cannot be found'))
diff --git a/addons/decimal_precision/decimal_precision.py b/addons/decimal_precision/decimal_precision.py
index 0e72035651b..9af09704b61 100644
--- a/addons/decimal_precision/decimal_precision.py
+++ b/addons/decimal_precision/decimal_precision.py
@@ -22,10 +22,10 @@
import openerp
from openerp import SUPERUSER_ID
from openerp import tools
-from openerp.osv import osv, fields
+from openerp.osv import orm, fields
from openerp.modules.registry import RegistryManager
-class decimal_precision(osv.osv):
+class decimal_precision(orm.Model):
_name = 'decimal.precision'
_columns = {
'name': fields.char('Usage', size=50, select=True, required=True),
@@ -77,4 +77,29 @@ def get_precision(application):
return (16, res)
return change_digit
+class DecimalPrecisionFloat(orm.AbstractModel):
+ """ Override qweb.field.float to add a `decimal_precision` domain option
+ and use that instead of the column's own value if it is specified
+ """
+ _inherit = 'ir.qweb.field.float'
+
+
+ def precision(self, cr, uid, column, options=None, context=None):
+ dp = options and options.get('decimal_precision')
+ if dp:
+ return self.pool['decimal.precision'].precision_get(
+ cr, uid, dp)
+
+ return super(DecimalPrecisionFloat, self).precision(
+ cr, uid, column, options=options, context=context)
+
+class DecimalPrecisionTestModel(orm.Model):
+ _name = 'decimal.precision.test'
+
+ _columns = {
+ 'float': fields.float(),
+ 'float_2': fields.float(digits=(16, 2)),
+ 'float_4': fields.float(digits=(16, 4)),
+ }
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/decimal_precision/security/ir.model.access.csv b/addons/decimal_precision/security/ir.model.access.csv
index aa203eae141..d55da432c57 100644
--- a/addons/decimal_precision/security/ir.model.access.csv
+++ b/addons/decimal_precision/security/ir.model.access.csv
@@ -1,3 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_decimal_precision_config,decimal.precision configuration,model_decimal_precision,base.group_system,1,1,1,1
access_decimal_precision_all,decimal.precision,model_decimal_precision,,1,0,0,0
+access_decimal_precision_test_all,decimal.precision.test,model_decimal_precision_test,,1,1,1,1
diff --git a/addons/decimal_precision/tests/__init__.py b/addons/decimal_precision/tests/__init__.py
new file mode 100644
index 00000000000..feee24c8f15
--- /dev/null
+++ b/addons/decimal_precision/tests/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+import test_qweb_float
+
+checks = [
+ test_qweb_float
+]
diff --git a/addons/decimal_precision/tests/test_qweb_float.py b/addons/decimal_precision/tests/test_qweb_float.py
new file mode 100644
index 00000000000..93334170f37
--- /dev/null
+++ b/addons/decimal_precision/tests/test_qweb_float.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+from openerp.tests import common
+
+class TestFloatExport(common.TransactionCase):
+ def setUp(self):
+ super(TestFloatExport, self).setUp()
+ self.Model = self.registry('decimal.precision.test')
+
+ def get_converter(self, name):
+ converter = self.registry('ir.qweb.field.float')
+ column = self.Model._all_columns[name].column
+
+ return lambda value, options=None: converter.value_to_html(
+ self.cr, self.uid, value, column, options=options, context=None)
+
+ def test_basic_float(self):
+ converter = self.get_converter('float')
+ self.assertEqual(
+ converter(42.0),
+ "42.0")
+ self.assertEqual(
+ converter(42.12345),
+ "42.12345")
+
+ converter = self.get_converter('float_2')
+ self.assertEqual(
+ converter(42.0),
+ "42.00")
+ self.assertEqual(
+ converter(42.12345),
+ "42.12")
+
+ converter = self.get_converter('float_4')
+ self.assertEqual(
+ converter(42.0),
+ '42.0000')
+ self.assertEqual(
+ converter(42.12345),
+ '42.1234')
+
+ def test_precision_domain(self):
+ DP = self.registry('decimal.precision')
+ DP.create(self.cr, self.uid, {
+ 'name': 'A',
+ 'digits': 2,
+ })
+ DP.create(self.cr, self.uid, {
+ 'name': 'B',
+ 'digits': 6,
+ })
+
+ converter = self.get_converter('float')
+ self.assertEqual(
+ converter(42.0, {'decimal_precision': 'A'}),
+ '42.00')
+ self.assertEqual(
+ converter(42.0, {'decimal_precision': 'B'}),
+ '42.000000')
+
+ converter = self.get_converter('float_4')
+ self.assertEqual(
+ converter(42.12345, {'decimal_precision': 'A'}),
+ '42.12')
+ self.assertEqual(
+ converter(42.12345, {'decimal_precision': 'B'}),
+ '42.123450')
diff --git a/addons/delivery/__openerp__.py b/addons/delivery/__openerp__.py
index 0ea891bb6ae..1ee6bf413ec 100644
--- a/addons/delivery/__openerp__.py
+++ b/addons/delivery/__openerp__.py
@@ -32,7 +32,7 @@ You can define your own carrier and delivery grids for prices. When creating
invoices from picking, OpenERP is able to add and compute the shipping line.
""",
'author': 'OpenERP SA',
- 'depends': ['sale', 'purchase', 'stock'],
+ 'depends': ['sale_stock'],
'data': [
'security/ir.model.access.csv',
'delivery_report.xml',
diff --git a/addons/delivery/delivery.py b/addons/delivery/delivery.py
index 76750212edd..8f474461cf7 100644
--- a/addons/delivery/delivery.py
+++ b/addons/delivery/delivery.py
@@ -110,11 +110,11 @@ class delivery_carrier(osv.osv):
# not using advanced pricing per destination: override grid
grid_id = grid_pool.search(cr, uid, [('carrier_id', '=', record.id)], context=context)
-
if grid_id and not (record.normal_price or record.free_if_more_than):
grid_pool.unlink(cr, uid, grid_id, context=context)
- if not (record.normal_price or record.free_if_more_than):
+ # Check that float, else 0.0 is False
+ if not (isinstance(record.normal_price,float) or record.free_if_more_than):
continue
if not grid_id:
@@ -141,7 +141,7 @@ class delivery_carrier(osv.osv):
'list_price': 0.0,
}
grid_line_pool.create(cr, uid, line_data, context=context)
- if record.normal_price:
+ if isinstance(record.normal_price,float):
line_data = {
'grid_id': grid_id and grid_id[0],
'name': _('Default price'),
@@ -192,7 +192,7 @@ class delivery_grid(osv.osv):
weight = 0
volume = 0
for line in order.order_line:
- if not line.product_id:
+ if not line.product_id or line.is_delivery:
continue
total += line.price_subtotal or 0.0
weight += (line.product_id.weight or 0.0) * line.product_uom_qty
@@ -205,9 +205,8 @@ class delivery_grid(osv.osv):
grid = self.browse(cr, uid, id, context=context)
price = 0.0
ok = False
-
+ price_dict = {'price': total, 'volume':volume, 'weight': weight, 'wv':volume*weight}
for line in grid.line_ids:
- price_dict = {'price': total, 'volume':volume, 'weight': weight, 'wv':volume*weight}
test = eval(line.type+line.operator+str(line.max_value), price_dict)
if test:
if line.price_type=='variable':
diff --git a/addons/delivery/delivery_demo.xml b/addons/delivery/delivery_demo.xml
index 9c412b24957..f090947d57f 100644
--- a/addons/delivery/delivery_demo.xml
+++ b/addons/delivery/delivery_demo.xml
@@ -32,13 +32,15 @@
The Poste
+ 20
+ Free delivery charges
- 10
+ 0True1000
diff --git a/addons/delivery/sale.py b/addons/delivery/sale.py
index 1b797290632..dbebd01ef1e 100644
--- a/addons/delivery/sale.py
+++ b/addons/delivery/sale.py
@@ -20,21 +20,37 @@
##############################################################################
import time
-from openerp.osv import fields,osv
+from openerp.osv import fields, osv
from openerp.tools.translate import _
-# Overloaded sale_order to manage carriers :
-class sale_order(osv.osv):
+
+class sale_order_line(osv.Model):
+ _inherit = 'sale.order.line'
+
+ _columns = {
+ 'is_delivery': fields.boolean("Is a Delivery"),
+ }
+
+ _defaults = {
+ 'is_delivery': False
+ }
+
+
+class sale_order(osv.Model):
_inherit = 'sale.order'
_columns = {
- 'carrier_id':fields.many2one("delivery.carrier", "Delivery Method", help="Complete this field if you plan to invoice the shipping based on picking."),
+ 'carrier_id': fields.many2one(
+ "delivery.carrier", string="Delivery Method",
+ help="Complete this field if you plan to invoice the shipping based on picking."),
}
def onchange_partner_id(self, cr, uid, ids, part, context=None):
result = super(sale_order, self).onchange_partner_id(cr, uid, ids, part, context=context)
if part:
dtype = self.pool.get('res.partner').browse(cr, uid, part, context=context).property_delivery_carrier.id
- result['value']['carrier_id'] = dtype
+ # TDE NOTE: not sure the aded 'if dtype' is valid
+ if dtype:
+ result['value']['carrier_id'] = dtype
return result
def _prepare_order_picking(self, cr, uid, order, context=None):
@@ -42,18 +58,25 @@ class sale_order(osv.osv):
result.update(carrier_id=order.carrier_id.id)
return result
+ def _delivery_unset(self, cr, uid, order, context=None):
+ line_ids = [line.id for line in order.order_line if line.is_delivery]
+ self.pool['sale.order.line'].unlink(cr, uid, line_ids, context=context)
+ order.refresh()
+ return True
+
def delivery_set(self, cr, uid, ids, context=None):
- order_obj = self.pool.get('sale.order')
line_obj = self.pool.get('sale.order.line')
grid_obj = self.pool.get('delivery.grid')
carrier_obj = self.pool.get('delivery.carrier')
acc_fp_obj = self.pool.get('account.fiscal.position')
+
for order in self.browse(cr, uid, ids, context=context):
+ self._delivery_unset(cr, uid, order, context=context)
grid_id = carrier_obj.grid_get(cr, uid, [order.carrier_id.id], order.partner_shipping_id.id)
if not grid_id:
raise osv.except_osv(_('No Grid Available!'), _('No grid matching for this carrier!'))
- if not order.state in ('draft'):
+ if order.state != 'draft':
raise osv.except_osv(_('Order not in Draft State!'), _('The order state have to be draft to add delivery lines.'))
grid = grid_obj.browse(cr, uid, grid_id, context=context)
@@ -69,13 +92,7 @@ class sale_order(osv.osv):
'product_uom': grid.carrier_id.product_id.uom_id.id,
'product_id': grid.carrier_id.product_id.id,
'price_unit': grid_obj.get_price(cr, uid, grid.id, order, time.strftime('%Y-%m-%d'), context),
- 'tax_id': [(6,0,taxes_ids)],
- 'type': 'make_to_stock'
+ 'tax_id': [(6, 0, taxes_ids)],
+ 'type': 'make_to_stock',
+ 'is_delivery': True
})
- #remove the value of the carrier_id field on the sale order
- return self.write(cr, uid, ids, {'carrier_id': False}, context=context)
- #return {'type': 'ir.actions.act_window_close'} action reload?
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
diff --git a/addons/document_page/document_page.py b/addons/document_page/document_page.py
deleted file mode 100644
index 7f885018001..00000000000
--- a/addons/document_page/document_page.py
+++ /dev/null
@@ -1,133 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-
-from openerp.osv import fields, osv
-from openerp.tools.translate import _
-import difflib
-from openerp import tools
-
-class document_page(osv.osv):
- _name = "document.page"
- _description = "Document Page"
- _order = 'name'
-
- def _get_page_index(self, cr, uid, page, link=True):
- index = []
- for subpage in page.child_ids:
- index += ["
"+ self._get_page_index(cr, uid, subpage) +"
"]
- r = ''
- if link:
- r = '%s'%(page.id,page.name)
- if index:
- r += "
" + "".join(index) + "
"
- return r
-
- def _get_display_content(self, cr, uid, ids, name, args, context=None):
- res = {}
- for page in self.browse(cr, uid, ids, context=context):
- if page.type == "category":
- content = self._get_page_index(cr, uid, page, link=False)
- else:
- content = page.content
- res[page.id] = content
- return res
-
- _columns = {
- 'name': fields.char('Title', required=True),
- 'type':fields.selection([('content','Content'), ('category','Category')], 'Type', help="Page type"),
-
- 'parent_id': fields.many2one('document.page', 'Category', domain=[('type','=','category')]),
- 'child_ids': fields.one2many('document.page', 'parent_id', 'Children'),
-
- 'content': fields.text("Content"),
- 'display_content': fields.function(_get_display_content, string='Displayed Content', type='text'),
-
- 'history_ids': fields.one2many('document.page.history', 'page_id', 'History'),
- 'menu_id': fields.many2one('ir.ui.menu', "Menu", readonly=True),
-
- 'create_date': fields.datetime("Created on", select=True, readonly=True),
- 'create_uid': fields.many2one('res.users', 'Author', select=True, readonly=True),
- 'write_date': fields.datetime("Modification Date", select=True, readonly=True),
- 'write_uid': fields.many2one('res.users', "Last Contributor", select=True, readonly=True),
- }
- _defaults = {
- 'type':'content',
- }
-
- def onchange_parent_id(self, cr, uid, ids, parent_id, content, context=None):
- res = {}
- if parent_id and not content:
- parent = self.browse(cr, uid, parent_id, context=context)
- if parent.type == "category":
- res['value'] = {
- 'content': parent.content,
- }
- return res
-
- def create_history(self, cr, uid, ids, vals, context=None):
- for i in ids:
- history = self.pool.get('document.page.history')
- if vals.get('content'):
- res = {
- 'content': vals.get('content', ''),
- 'page_id': i,
- }
- history.create(cr, uid, res)
-
- def create(self, cr, uid, vals, context=None):
- page_id = super(document_page, self).create(cr, uid, vals, context)
- self.create_history(cr, uid, [page_id], vals, context)
- return page_id
-
- def write(self, cr, uid, ids, vals, context=None):
- result = super(document_page, self).write(cr, uid, ids, vals, context)
- self.create_history(cr, uid, ids, vals, context)
- return result
-
-class document_page_history(osv.osv):
- _name = "document.page.history"
- _description = "Document Page History"
- _order = 'id DESC'
- _rec_name = "create_date"
-
- _columns = {
- 'page_id': fields.many2one('document.page', 'Page'),
- 'summary': fields.char('Summary', size=256, select=True),
- 'content': fields.text("Content"),
- 'create_date': fields.datetime("Date"),
- 'create_uid': fields.many2one('res.users', "Modified By"),
- }
-
- def getDiff(self, cr, uid, v1, v2, context=None):
- history_pool = self.pool.get('document.page.history')
- text1 = history_pool.read(cr, uid, [v1], ['content'])[0]['content']
- text2 = history_pool.read(cr, uid, [v2], ['content'])[0]['content']
- line1 = line2 = ''
- if text1:
- line1 = text1.splitlines(1)
- if text2:
- line2 = text2.splitlines(1)
- if (not line1 and not line2) or (line1 == line2):
- raise osv.except_osv(_('Warning!'), _('There are no changes in revisions.'))
- diff = difflib.HtmlDiff()
- return diff.make_table(line1, line2, "Revision-%s" % (v1), "Revision-%s" % (v2), context=True)
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/document_page/document_page_data.xml b/addons/document_page/document_page_data.xml
deleted file mode 100644
index 1cef65694d8..00000000000
--- a/addons/document_page/document_page_data.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
- The OpenERP wiki
- help, quick start, wiki, formatting
- 0
- 1
- Initial Page
- ==The OpenERP wiki==
-
-[[File:http://www.openerp.com/sites/all/themes/openerp/logo.png OpenERP]]
-
-The OpenERP wiki allows you to manage your enterprise's contents using wiki
-restructured texts. This module provides a collaborative way to manage internal
-FAQs, quality manuals, technical references, etc.
-
-==Keypoints==
-* Same formating style than MediaWiki,
-* Any number of wiki group for different purposes,
-* Detailed history on all pages,
-* Integrated with the document management system.
-
-==Why you should use the OpenERP integrated wiki than a separate wiki system ?==
-* Allows links to any document of the system,
-* Uses the access controls of OpenERP for uniq access rights management,
-* Use it to describe projects, tasks, products,
-* Integrated with customer portal to provide restricted external accesses,
-* Linked to users processes for quality manuals.
-
-==To get more information==
-* [[Basic Wiki Editing]]
-* [[Wiki Documentation]]
-* [http://openerp.com The OpenERP website]
-
-
-
-
-
-
-
diff --git a/addons/document_page/document_page_demo.xml b/addons/document_page/document_page_demo.xml
deleted file mode 100644
index f779ea8313f..00000000000
--- a/addons/document_page/document_page_demo.xml
+++ /dev/null
@@ -1,130 +0,0 @@
-
-
-
-
-
-
-
- OpenERP Features
- category
-
-Summary of the feature
-
-Long explanation
-
-Conclusion
-
-Additional ressources
-
-
-
-
-
- OpenERP 6.1. Functional Demo
-
-
-
-
-The news is out, OpenERP's latest version 6.1. is here. It's more
-user-friendly, even more business oriented and efficient to manage your company
-
-How to discover the latest version 6.1.?
-
-Demo :
-Online:
-Download:
-
-We have also put together a functional demo that presents 6.1. Watch this video
-to learn directly from us what OpenERP 6.1. can do for you. Share it in your
-company, with your clients and implement it now for your business.
-
-
Watch on Youtube!
-
-
-
-
-]]>
-
-
-
-
- Personalise Dashboards
-
-
-
-You like OpenERP, but feel like you want to personalise it more? Now, OpenERP
-goes a step further and lets you customize your dashboard. Thanks to a new
-feature that allows you to customize your dashboard by adding new boards of any
-search view.
-
-
How is it done?
-
-Step 1: access one search view
-
-Step 2: apply the filter you want to see at each connection to the application
-(eg. on sales, manufacturing, etc)
-
-Step 3: add it into the dashboard in the same space where you can save the filter
-
-Step 4: choose the application you want it visible on and the name of the array
-
-Look at this simple example below from Purchase, where I want to put on the
-application's dashboard "Purchases to Approve". After I access the search view
-and apply the filter for "Purchases to Approve", I can add it immediately to my
-Purchase dashboard.
-
-
-
-In less than a minute, the search view is visible on the dashboard
-
-
-
-Of course, you are free to delete what you don't need or like, but just in case
-you change your mind there is a reset button to return to the default view.
-
-
-]]>
-
-
-
-
- Touchscreen Point of Sale
-
-
-
-The brand new OpenERP touchscreen point of sale available with 6.1 allows you
-to manage your shop sales very easily. It's fully web based so that you don't
-have to install or deploy any software and all the sales shops can be easily
-consolidated. It works in connected and disconnected modes so that you can
-continue to sell if you lose your internet connection.
-
-
-
-
Here's a summary of its main features and benefits:
-
-100% WEB based
-
-
available for any touchscreen device (ipod, ipad, any tablet)mobile (with portable devices)
no installation required
no synchronization needed, completely integrated
continue working even when your connection is down if you close your browser, data won't be lost
fully web based with a clean interface smart interface
-
-You have different options to select your products. You can do it through the
-barcode reader, just browse through the categories you have put in place (ie.
-drinks, snacks, meals, etc.), or text search in case neither of the other
-options work for you. If you need to use the POS for your restaurant, for
-example, your employees can record at the same time multiple tickets without
-having to wait to do one transaction at a time. Along, to facilitate payment,
-the application allows multiple payment methods.
-
-The POS application is so simple and accessible to use that your shop or
-restaurant will never need any other tool to manage orders. Due to its smart
-and user-friendly interface you don't need any training to learn how to use it.
-Think of it as an out-of-the-box solution to boost your business' productivity.
-
-]]>
-
-
-
-
-
diff --git a/addons/document_page/document_page_view.xml b/addons/document_page/document_page_view.xml
deleted file mode 100644
index 9afb29c787a..00000000000
--- a/addons/document_page/document_page_view.xml
+++ /dev/null
@@ -1,162 +0,0 @@
-
-
-
-
-
-
-
-
- document.page.tree
- document.page
- child_ids
- 100
-
-
-
-
-
-
-
-
-
-
- document.page.list
- document.page
-
-
-
-
-
-
-
-
-
-
-
-
- document.page.form
- document.page
-
-
-
-
-
-
- document.page.search
- document.page
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Pages
- document.page
- [('type','=','content')]
- {'default_type': 'content'}
- form
- tree,form
-
-
-
-
Join us to our main event of the year: the Open Days
+
+
Every year we invite our community, partners and end-users to come and meet us! It's the ideal event to get together and present new features, roadmap of future versions, achievements of the software, workshops, training sessions, etc.... This event is also an opportunity to showcase our partners' case studies, methodology or developments. Be there and see directly from the source the features of the version 8!
+
+
This event and all the conferences are in english!
+
+
What's new for this year?
+
+
The Open Days are preceded by 2 days of optional training sessions for experts! We propose 3 different training sessions, 2 days each.
+
The whole event is open to all public! We ask a participation fee of 49.50€ for the costs for the 3 days (morning coffee, coffee breaks, drinks, sandwiches for lunch and the surprising beer party of Wednesday evening) but it's optional. For those who do not want to contribute, there is a free ticket, therefore, catering is not inclued.
+
The plenary sessions in the morning will be shorter and we will give more time for thematical meetings, conferences, workshops and tutorial sessions in the afternoon.
+
+
+
Program:
+
Conferences, workshops and trainings will be organized in 6 rooms:
+
+
2 technical rooms: one dedicated to advanced OpenERP developers, one for new developers.
+
2 technical rooms: one dedicated to advanced OpenERP developers, one for new developers.
+
1 business room: to discuss implementation methodologies, best sales practices, etc.
+
1 workshop room: mainly for developers.
+
+
If you wish to make a presentation, please send your topic proposal as soon as possible for approval to Mr. Famke Jenssens at ngh (a) yourcompany (dot) com. The presentations should be, for example, a presentation of a community module, a case study, methodology feedback, technical, etc. Each presentation must be in English.
+
+
Where to find us:
+
OpenElec Applications 23 Rockwell Lane, Los Angeles, CA 90001, United States
(OpenElec Applications reserves the right to cancel, re-name or re-locate the event or change the dates on which it is held.)
+
+]]>
- Opera of Verdi
-
-
-
- 50
- 350
+ Functional Webinar
+
+
+
+
+ 350
+
+
Functional Webinar
+
+
Webinars are online demonstrations where one of our team members explains the main features and benefits of our online offer through an online conference. We can therefore directly answer any questions you may have through a Q&A.
+
Each session lasts approximately one hour and is free, we just ask you to register to receive access codes.
+
+
Objective:
+
These webinars allow companies interested in our software, to assess whether the solution meets their needs, and can adapt to the scope of their project.
+
This webinar helps participants to:
+
+
Discover how to navigate in our software;
+
View full flow: purchasing, sales, project management, accounting;
+
Ask your questions to our expert;
+
Assess whether your expectations are met;
+
+
+
After registering, you will receive a link and password by email before the start of the session. If you have a problem to connect, please contact us at events@yourcompany.com.
+
+
We are looking forward to meeting you online,
+
Best regards,
+
Luigi Roni, Senior Event Manager
+
+
(YourCompany reserves the right to cancel, re-name or re-locate the event or change the dates on which it is held.)
+
+]]>
+
- Conference on ERP Business
-
-
+ Conference on Business Applications
+
+
- 200
+
+ 200
+
+
Conference on Business Applications
+
+
During this conference, our team will give a detailed overview of our business applications. You’ll know all the benefits of using it.
+
+
Objectives:
+
Having attended this conference, participants should be able to:
Point of Sale (POS), Introduction to report customization.
+
+
+
Where to find us:
+
Chamber Works 60, Rosewood Court Detroit, MI 48212 (United States)
+
For any additional information, please contact us at events@openerp.com.
+
+
Best regards,
+
Luigi Roni, Senior Event Manager
+
+
(Chamber Works reserves the right to cancel, re-name or re-locate the event or change the dates on which it is held.)
+
+]]>
-
+
+
+ Technical Training
+
+
+
+
+
+
+
5-days Technical Training
+
+
Course summary:
+
This course is dedicated to partners, integrators and developers who need to grasp knowledge about the business applications development process and for new developers or for IT professionals eager to learn more about technical aspects.
+
+
Objectives:
+
Having attended this course, participants should be able to:
+
+
Understand the development concepts and architecture;
+
Install and administer your own server;
+
Develop a new module for a particular application.
+
+
+
Our prices include:
+
+
drinks and lunch;
+
training material.
+
+
+
Requirements
+
+
Bring your own laptop.
+
Participants are expected to have some knowledge in programming. A basic knowledge of the Python programming is recommended.
+
Participants preferably have a functional knowledge of our software (see Functional Training).
If you have a question concerning the content of the training, please contact events@yourcompany.com.
+
+
Where to find us:
+
Chaussée de Namur 69, 1300 Wavre, Belgium
+
More information about our Headquarter office (directions, transports, parking, hotels, ...), please have a look at http://bit.ly/VD8J67.
+
+
Cancellation Policy:
+
The organization of the training session has related costs. Due to these costs, cancellations made less than 2 weeks (14 calendar days) prior to the start of the training session is a subject to a fee. This fee can be up to a maximum of 1000€ per cancellation request.
+
We strongly recommend to book your flight tickets and/or hotel reservations 2 weeks prior to the training. If the training is cancelled 2 weeks in advance, you'll be notified by email.
+
+
For any additional information, please contact us at events@openerp.com.
+
+
Best regards,
+
Luigi Roni, Senior Event Manager
+
+
(YourCompany reserves the right to cancel, re-name or re-locate the event or change the dates on which it is held.)