diff --git a/addons/account/account.py b/addons/account/account.py index aa5d507b3d2..51700d30731 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -2055,6 +2055,8 @@ class account_tax(osv.osv): amount = amount2 child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, product, partner, quantity) res.extend(child_tax) + for child in child_tax: + amount2 += child.get('amount', 0.0) if tax.child_depend: for r in res: for name in ('base','ref_base'): diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 785d7cd6db0..744c2ef61bf 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -62,22 +62,28 @@ class account_analytic_invoice_line(osv.osv): context = context or {} uom_obj = self.pool.get('product.uom') company_id = company_id or False - context.update({'company_id': company_id, 'force_company': company_id, 'pricelist_id': pricelist_id}) + local_context = dict(context, company_id=company_id, force_company=company_id, pricelist=pricelist_id) if not product: return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}} if partner_id: - part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) + part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=local_context) if part.lang: context.update({'lang': part.lang}) result = {} - res = self.pool.get('product.product').browse(cr, uid, product, context=context) - result.update({'name': name or res.description or False,'uom_id': uom_id or res.uom_id.id or False, 'price_unit': price_unit or res.list_price or 0.0}) + res = self.pool.get('product.product').browse(cr, uid, product, context=local_context) + if price_unit is not False: + price = price_unit + elif pricelist_id: + price = res.price + else: + price = res.list_price + result.update({'name': name or res.description or False,'uom_id': uom_id or res.uom_id.id or False, 'price_unit': price}) res_final = {'value':result} if result['uom_id'] != res.uom_id.id: - selected_uom = uom_obj.browse(cr, uid, result['uom_id'], context=context) + selected_uom = uom_obj.browse(cr, uid, result['uom_id'], context=local_context) new_price = uom_obj._compute_price(cr, uid, res.uom_id.id, res_final['value']['price_unit'], result['uom_id']) res_final['value']['price_unit'] = new_price return res_final diff --git a/addons/auth_oauth/auth_oauth_data.xml b/addons/auth_oauth/auth_oauth_data.xml index 6abdab41b7c..fe64f42b2f6 100644 --- a/addons/auth_oauth/auth_oauth_data.xml +++ b/addons/auth_oauth/auth_oauth_data.xml @@ -2,13 +2,13 @@ - OpenERP.com Accounts - https://accounts.openerp.com/oauth2/auth + Odoo.com Accounts + https://accounts.odoo.com/oauth2/auth userinfo - https://accounts.openerp.com/oauth2/tokeninfo + https://accounts.odoo.com/oauth2/tokeninfo zocial openerp - Log in with OpenERP.com + Log in with Odoo.com diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py index 40654f464f4..7964e4a4a3a 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity.py +++ b/addons/crm/wizard/crm_lead_to_opportunity.py @@ -276,7 +276,7 @@ class crm_lead2opportunity_mass_convert(osv.osv_memory): active_ids = active_ids.difference(merged_lead_ids) active_ids = active_ids.union(remaining_lead_ids) ctx['active_ids'] = list(active_ids) - ctx['no_force_assignation'] = not data.force_assignation + ctx['no_force_assignation'] = context.get('no_force_assignation', not data.force_assignation) return self.action_apply(cr, uid, ids, context=ctx) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/gamification/models/challenge.py b/addons/gamification/models/challenge.py index 9612cf4a05e..ed6016b9665 100644 --- a/addons/gamification/models/challenge.py +++ b/addons/gamification/models/challenge.py @@ -324,7 +324,7 @@ class gamification_challenge(osv.Model): ('end_date', '<=', challenge.last_report_date) ]) - if fields.date.today() >= challenge.next_report_date: + if challenge.next_report_date and fields.date.today() >= challenge.next_report_date: self.report_progress(cr, uid, challenge, context=context) elif len(closed_goals_to_report) > 0: diff --git a/addons/gamification/models/goal.py b/addons/gamification/models/goal.py index b4a47a0d07a..904e6fafa36 100644 --- a/addons/gamification/models/goal.py +++ b/addons/gamification/models/goal.py @@ -349,8 +349,8 @@ class gamification_goal(osv.Model): goal = all_goals[goal_id] # check goal target reached - if (goal.definition_condition == 'higher' and value.get('current', goal.current) >= goal.target_goal) \ - or (goal.definition_condition == 'lower' and value.get('current', goal.current) <= goal.target_goal): + if (goal.definition_id.condition == 'higher' and value.get('current', goal.current) >= goal.target_goal) \ + or (goal.definition_id.condition == 'lower' and value.get('current', goal.current) <= goal.target_goal): value['state'] = 'reached' # check goal failure diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 0059df90530..0985b16f5ac 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -183,16 +183,20 @@ class mail_notification(osv.Model): references = message.parent_id.message_id if message.parent_id else False # create email values - mail_values = { - 'mail_message_id': message.id, - 'auto_delete': True, - 'body_html': body_html, - 'recipient_ids': [(4, id) for id in email_pids], - 'references': references, - } - email_notif_id = self.pool.get('mail.mail').create(cr, uid, mail_values, context=context) - if force_send: - self.pool.get('mail.mail').send(cr, uid, [email_notif_id], context=context) + max_recipients = 100 + chunks = [email_pids[x:x + max_recipients] for x in xrange(0, len(email_pids), max_recipients)] + email_ids = [] + for chunk in chunks: + mail_values = { + 'mail_message_id': message.id, + 'auto_delete': True, + 'body_html': body_html, + 'recipient_ids': [(4, id) for id in chunk], + 'references': references, + } + email_ids.append(self.pool.get('mail.mail').create(cr, uid, mail_values, context=context)) + if force_send and len(chunks) < 6: # for more than 500 followers, use the queue system + self.pool.get('mail.mail').send(cr, uid, email_ids, context=context) return True def _notify(self, cr, uid, message_id, partners_to_notify=None, context=None, diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 9811fcd6aef..65970265ce3 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -149,19 +149,9 @@ class mail_mail(osv.Model): link to action_mail_redirect action that will redirect to doc or Inbox """ if partner and partner.user_ids: base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') - # the parameters to encode for the query and fragment part of url - query = {'db': cr.dbname} - fragment = { - 'login': partner.user_ids[0].login, - 'action': 'mail.action_mail_redirect', - } - if mail.notification: - fragment['message_id'] = mail.mail_message_id.id - elif mail.model and mail.res_id: - fragment.update(model=mail.model, res_id=mail.res_id) - - url = urljoin(base_url, "/web?%s#%s" % (urlencode(query), urlencode(fragment))) - return _("""Access your messages and documents in OpenERP""") % url + mail_model = mail.model or 'mail.thread' + url = urljoin(base_url, self.pool[mail_model]._get_access_link(cr, uid, mail, partner, context=context)) + return _("""Access your messages and documents in Odoo""") % url else: return None diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index d2dcef4d85e..8cb735b047f 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -35,6 +35,7 @@ import socket import time import xmlrpclib from email.message import Message +from urllib import urlencode from openerp import tools from openerp import SUPERUSER_ID @@ -641,6 +642,20 @@ class mail_thread(osv.AbstractModel): }) return action + def _get_access_link(self, cr, uid, mail, partner, context=None): + # the parameters to encode for the query and fragment part of url + query = {'db': cr.dbname} + fragment = { + 'login': partner.user_ids[0].login, + 'action': 'mail.action_mail_redirect', + } + if mail.notification: + fragment['message_id'] = mail.mail_message_id.id + elif mail.model and mail.res_id: + fragment.update(model=mail.model, res_id=mail.res_id) + + return "/web?%s#%s" % (urlencode(query), urlencode(fragment)) + #------------------------------------------------------ # Email specific #------------------------------------------------------ diff --git a/addons/marketing_campaign/marketing_campaign.py b/addons/marketing_campaign/marketing_campaign.py index e434090ad76..22073ea6389 100644 --- a/addons/marketing_campaign/marketing_campaign.py +++ b/addons/marketing_campaign/marketing_campaign.py @@ -491,11 +491,9 @@ class marketing_campaign_activity(osv.osv): active_ids=[workitem.res_id], active_model=workitem.object_id.model, workitem=workitem) - res = server_obj.run(cr, uid, [activity.server_action_id.id], + server_obj.run(cr, uid, [activity.server_action_id.id], context=action_context) - # server action return False if the action is performed - # except client_action, other and python code - return res == False and True or res + return True def process(self, cr, uid, act_id, wi_id, context=None): activity = self.browse(cr, uid, act_id, context=context) diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index f188af8aa82..f18edf39e85 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -1167,8 +1167,8 @@ class pos_order(osv.osv): return self.write(cr, uid, ids, {'state': 'payment'}, context=context) def action_paid(self, cr, uid, ids, context=None): - self.write(cr, uid, ids, {'state': 'paid'}, context=context) self.create_picking(cr, uid, ids, context=context) + self.write(cr, uid, ids, {'state': 'paid'}, context=context) return True def action_cancel(self, cr, uid, ids, context=None): diff --git a/addons/point_of_sale/static/src/js/db.js b/addons/point_of_sale/static/src/js/db.js index 077a2399c55..0c7ba8f9e1e 100644 --- a/addons/point_of_sale/static/src/js/db.js +++ b/addons/point_of_sale/static/src/js/db.js @@ -146,6 +146,9 @@ function openerp_pos_db(instance, module){ var product = products[i]; var search_string = this._product_search_string(product); var categ_id = product.public_categ_id ? product.public_categ_id[0] : this.root_category_id; + if (product.variants){ + product.name = product.name+" ("+product.variants+")"; + } if(!stored_categories[categ_id]){ stored_categories[categ_id] = []; } diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 8de82b4dc0a..f65a7afa439 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -219,7 +219,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal return self.fetch( 'product.product', - ['name', 'list_price','price','public_categ_id', 'taxes_id', 'ean13', 'default_code', + ['name', 'list_price','price','public_categ_id', 'taxes_id', 'ean13', 'default_code', 'variants', 'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description'], [['sale_ok','=',true],['available_in_pos','=',true]], {pricelist: self.pricelist.id} // context for price diff --git a/addons/survey/controllers/main.py b/addons/survey/controllers/main.py index 0e708d96f43..2f65d92cd4c 100644 --- a/addons/survey/controllers/main.py +++ b/addons/survey/controllers/main.py @@ -234,7 +234,7 @@ class WebsiteSurvey(http.Controller): # AJAX submission of a page @http.route(['/survey/submit/'], - type='http', auth='public', multilang=True, website=True) + type='http', methods=['POST'], auth='public', multilang=True, website=True) def submit(self, survey, **post): _logger.debug('Incoming data: %s', post) page_id = int(post['page_id']) diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index 5068d7d0e74..460f64147e8 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -1,24 +1,18 @@ # -*- coding: utf-8 -*- import cStringIO -import contextlib -import hashlib +import datetime +from itertools import islice import json import logging -import os -import datetime import re from sys import maxint -import werkzeug -import werkzeug.exceptions import werkzeug.utils import werkzeug.wrappers from PIL import Image import openerp -from openerp.osv import fields -from openerp.addons.website.models import website from openerp.addons.web import http from openerp.http import request, Response @@ -27,6 +21,7 @@ logger = logging.getLogger(__name__) # Completely arbitrary limits MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768) LOC_PER_SITEMAP = 45000 +SITEMAP_CACHE_TIME = datetime.timedelta(hours=12) class Website(openerp.addons.web.controllers.main.Home): #------------------------------------------------------ @@ -75,33 +70,64 @@ class Website(openerp.addons.web.controllers.main.Home): @http.route('/sitemap.xml', type='http', auth="public", website=True) def sitemap_xml_index(self): - pages = list(request.website.enumerate_pages()) - if len(pages)<=LOC_PER_SITEMAP: - return self.__sitemap_xml(pages, 0) - # Sitemaps must be split in several smaller files with a sitemap index - values = { - 'pages': range(len(pages)/LOC_PER_SITEMAP+1), - 'url_root': request.httprequest.url_root - } - headers = { - 'Content-Type': 'application/xml;charset=utf-8', - } - return request.render('website.sitemap_index_xml', values, headers=headers) + cr, uid, context = request.cr, openerp.SUPERUSER_ID, request.context + ira = request.registry['ir.attachment'] + iuv = request.registry['ir.ui.view'] + mimetype ='application/xml;charset=utf-8' + content = None - @http.route('/sitemap-.xml', type='http', auth="public", website=True) - def sitemap_xml(self, page): - pages = list(request.website.enumerate_pages()) - return self.__sitemap_xml(pages, page) + def create_sitemap(url, content): + ira.create(cr, uid, dict( + datas=content.encode('base64'), + mimetype=mimetype, + type='binary', + name=url, + url=url, + ), context=context) - def __sitemap_xml(self, pages, index=0): - values = { - 'pages': pages[index*LOC_PER_SITEMAP:(index+1)*LOC_PER_SITEMAP], - 'url_root': request.httprequest.url_root.rstrip('/') - } - headers = { - 'Content-Type': 'application/xml;charset=utf-8', - } - return request.render('website.sitemap_xml', values, headers=headers) + sitemap = ira.search_read(cr, uid, [('url', '=' , '/sitemap.xml'), ('type', '=', 'binary')], ('datas', 'create_date'), context=context) + if sitemap: + # Check if stored version is still valid + server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT + create_date = datetime.datetime.strptime(sitemap[0]['create_date'], server_format) + delta = datetime.datetime.now() - create_date + if delta < SITEMAP_CACHE_TIME: + content = sitemap[0]['datas'].decode('base64') + + if not content: + # Remove all sitemaps in ir.attachments as we're going to regenerated them + sitemap_ids = ira.search(cr, uid, [('url', '=like' , '/sitemap%.xml'), ('type', '=', 'binary')], context=context) + if sitemap_ids: + ira.unlink(cr, uid, sitemap_ids, context=context) + + pages = 0 + first_page = None + locs = request.website.enumerate_pages() + while True: + start = pages * LOC_PER_SITEMAP + loc_slice = islice(locs, start, start + LOC_PER_SITEMAP) + urls = iuv.render(cr, uid, 'website.sitemap_locs', dict(locs=loc_slice), context=context) + if urls.strip(): + page = iuv.render(cr, uid, 'website.sitemap_xml', dict(content=urls), context=context) + if not first_page: + first_page = page + pages += 1 + create_sitemap('/sitemap-%d.xml' % pages, page) + else: + break + if not pages: + return request.not_found() + elif pages == 1: + content = first_page + else: + # Sitemaps must be split in several smaller files with a sitemap index + content = iuv.render(cr, uid, 'website.sitemap_index_xml', dict( + pages=range(1, pages + 1), + url_root=request.httprequest.url_root, + ), context=context) + create_sitemap('/sitemap.xml', content) + + return request.make_response(content, [('Content-Type', mimetype)]) #------------------------------------------------------ # Edit @@ -329,13 +355,7 @@ class Website(openerp.addons.web.controllers.main.Home): return request.website.kanban_col(**post) def placeholder(self, response): - # file_open may return a StringIO. StringIO can be closed but are - # not context managers in Python 2 though that is fixed in 3 - with contextlib.closing(openerp.tools.misc.file_open( - os.path.join('web', 'static', 'src', 'img', 'placeholder.png'), - mode='rb')) as f: - response.data = f.read() - return response.make_conditional(request.httprequest) + return request.registry['website']._image_placeholder(response) @http.route([ '/website/image', @@ -359,74 +379,10 @@ class Website(openerp.addons.web.controllers.main.Home): The requested field is assumed to be base64-encoded image data in all cases. """ - Model = request.registry[model] - response = werkzeug.wrappers.Response() + return request.registry['website']._image( + request.cr, request.uid, model, id, field, response) - id = int(id) - - ids = Model.search(request.cr, request.uid, - [('id', '=', id)], context=request.context) - if not ids and 'website_published' in Model._all_columns: - ids = Model.search(request.cr, openerp.SUPERUSER_ID, - [('id', '=', id), ('website_published', '=', True)], context=request.context) - - if not ids: - return self.placeholder(response) - - presized = '%s_big' % field - concurrency = '__last_update' - [record] = Model.read(request.cr, openerp.SUPERUSER_ID, [id], - [concurrency, field, presized], - context=request.context) - - if concurrency in record: - server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT - try: - response.last_modified = datetime.datetime.strptime( - record[concurrency], server_format + '.%f') - except ValueError: - # just in case we have a timestamp without microseconds - response.last_modified = datetime.datetime.strptime( - record[concurrency], server_format) - - # Field does not exist on model or field set to False - if not record.get(field): - # FIXME: maybe a field which does not exist should be a 404? - return self.placeholder(response) - - response.set_etag(hashlib.sha1(record[field]).hexdigest()) - response.make_conditional(request.httprequest) - - # conditional request match - if response.status_code == 304: - return response - - data = (record.get(presized) or record[field]).decode('base64') - - image = Image.open(cStringIO.StringIO(data)) - response.mimetype = Image.MIME[image.format] - - # record provides a pre-resized version of the base field, use that - # directly - if record.get(presized): - response.data = data - return response - - fit = int(max_width), int(max_height) - w, h = image.size - max_w, max_h = fit - - if w < max_w and h < max_h: - response.data = data - else: - image.thumbnail(fit, Image.ANTIALIAS) - image.save(response.stream, image.format) - # invalidate content-length computed by make_conditional as - # writing to response.stream does not do it (as of werkzeug 0.9.3) - del response.headers['Content-Length'] - - return response #------------------------------------------------------ # Server actions diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 584dc4b6105..e0aac6dbbbf 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -1,13 +1,20 @@ # -*- coding: utf-8 -*- +import cStringIO +import contextlib +import datetime import hashlib import inspect import itertools import logging import math import mimetypes +import os import re import urlparse +from PIL import Image +from sys import maxint + import werkzeug import werkzeug.exceptions import werkzeug.utils @@ -472,6 +479,99 @@ class website(osv.osv): html += request.website._render(template, {'object_id': object_id}) return html + def _image_placeholder(self, response): + # file_open may return a StringIO. StringIO can be closed but are + # not context managers in Python 2 though that is fixed in 3 + with contextlib.closing(openerp.tools.misc.file_open( + os.path.join('web', 'static', 'src', 'img', 'placeholder.png'), + mode='rb')) as f: + response.data = f.read() + return response.make_conditional(request.httprequest) + + def _image(self, cr, uid, model, id, field, response, max_width=maxint, max_height=maxint, context=None): + """ Fetches the requested field and ensures it does not go above + (max_width, max_height), resizing it if necessary. + + Resizing is bypassed if the object provides a $field_big, which will + be interpreted as a pre-resized version of the base field. + + If the record is not found or does not have the requested field, + returns a placeholder image via :meth:`~._image_placeholder`. + + Sets and checks conditional response parameters: + * :mailheader:`ETag` is always set (and checked) + * :mailheader:`Last-Modified is set iif the record has a concurrency + field (``__last_update``) + + The requested field is assumed to be base64-encoded image data in + all cases. + """ + Model = self.pool[model] + id = int(id) + + ids = Model.search(cr, uid, + [('id', '=', id)], context=context) + if not ids and 'website_published' in Model._all_columns: + ids = Model.search(cr, openerp.SUPERUSER_ID, + [('id', '=', id), ('website_published', '=', True)], context=context) + if not ids: + return self._image_placeholder(response) + + presized = '%s_big' % field + concurrency = '__last_update' + [record] = Model.read(cr, openerp.SUPERUSER_ID, [id], + [concurrency, field, presized], + context=context) + + if concurrency in record: + server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT + try: + response.last_modified = datetime.datetime.strptime( + record[concurrency], server_format + '.%f') + except ValueError: + # just in case we have a timestamp without microseconds + response.last_modified = datetime.datetime.strptime( + record[concurrency], server_format) + + # Field does not exist on model or field set to False + if not record.get(field): + # FIXME: maybe a field which does not exist should be a 404? + return self._image_placeholder(response) + + response.set_etag(hashlib.sha1(record[field]).hexdigest()) + response.make_conditional(request.httprequest) + + # conditional request match + if response.status_code == 304: + return response + + data = (record.get(presized) or record[field]).decode('base64') + + image = Image.open(cStringIO.StringIO(data)) + response.mimetype = Image.MIME[image.format] + + # record provides a pre-resized version of the base field, use that + # directly + if record.get(presized): + response.data = data + return response + + fit = int(max_width), int(max_height) + w, h = image.size + max_w, max_h = fit + + if w < max_w and h < max_h: + response.data = data + else: + image.thumbnail(fit, Image.ANTIALIAS) + image.save(response.stream, image.format) + # invalidate content-length computed by make_conditional as + # writing to response.stream does not do it (as of werkzeug 0.9.3) + del response.headers['Content-Length'] + + return response + + class website_menu(osv.osv): _name = "website.menu" _description = "Website Menu" diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index 94de70dc7dc..209d431ae8f 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -121,7 +121,7 @@ header a.navbar-brand img { /* ----- EDITOR ----- */ .css_non_editable_mode_hidden { - display: none; + display: none !important; } /* ----- BOOTSTRAP FIX ----- */ diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index b9f33ef1c6b..c322528764a 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -697,14 +697,18 @@ User-agent: * Sitemap: sitemap.xml + + diff --git a/addons/website_blog/controllers/main.py b/addons/website_blog/controllers/main.py index 04cb84eafef..ceeaab46a69 100644 --- a/addons/website_blog/controllers/main.py +++ b/addons/website_blog/controllers/main.py @@ -122,7 +122,7 @@ class WebsiteBlog(http.Controller): blog_url = QueryURL('', ['blog', 'tag'], blog=blog, tag=tag, date_begin=date_begin, date_end=date_end) post_url = QueryURL('', ['blogpost'], tag_id=tag and tag.id or None, date_begin=date_begin, date_end=date_end) - blog_post_ids = blog_post_obj.search(cr, uid, domain, order="create_date asc", context=context) + blog_post_ids = blog_post_obj.search(cr, uid, domain, order="create_date desc", context=context) blog_posts = blog_post_obj.browse(cr, uid, blog_post_ids, context=context) pager = request.website.pager( diff --git a/addons/website_crm_partner_assign/controllers/main.py b/addons/website_crm_partner_assign/controllers/main.py index bf54968e9ae..faae5593a9b 100644 --- a/addons/website_crm_partner_assign/controllers/main.py +++ b/addons/website_crm_partner_assign/controllers/main.py @@ -1,115 +1,172 @@ # -*- coding: utf-8 -*- - +import logging +import re import werkzeug -import openerp +_logger = logging.getLogger(__name__) +try: + import GeoIP +except ImportError: + GeoIP = None + _logger.warn("Please install GeoIP python module to use events localisation.") + +from openerp import SUPERUSER_ID from openerp.addons.web import http -from openerp.tools.translate import _ from openerp.addons.web.http import request -from openerp.addons.website_partner.controllers import main as website_partner +from openerp.addons.website.models.website import slug +from openerp.tools.translate import _ + class WebsiteCrmPartnerAssign(http.Controller): - _references_per_page = 20 + _references_per_page = 40 + + def _get_current_country_code(self): + if not GeoIP: + return False + GI = GeoIP.open('/usr/share/GeoIP/GeoIP.dat', 0) + return GI.country_code_by_addr(request.httprequest.remote_addr) @http.route([ '/partners', '/partners/page/', - '/partners/grade/', - '/partners/grade//page/', + '/partners/grade/', + '/partners/grade//page/', - '/partners/country/', - '/partners/country/-', - '/partners/country//page/', - '/partners/country/-/page/', - - '/partners/grade//country/', - '/partners/grade//country/-', - '/partners/grade//country//page/', - '/partners/grade//country/-/page/', + '/partners/country/', + '/partners/country//page/', + '/partners/grade//country/', + '/partners/grade//country//page/', ], type='http', auth="public", website=True, multilang=True) - def partners(self, country_id=0, grade_id=0, page=0, country_name='', **post): - country_obj = request.registry['res.country'] + def partners(self, country=None, grade=None, page=0, **post): + country_all = post.pop('country_all', False) partner_obj = request.registry['res.partner'] - post_name = post.get('search', '') - country = None + country_obj = request.registry['res.country'] + search = post.get('search', '') - # format displayed membership lines domain base_partner_domain = [('is_company', '=', True), ('grade_id.website_published', '=', True), ('website_published', '=', True)] - partner_domain = list(base_partner_domain) - if post_name: - partner_domain += ['|', ('name', 'ilike', post_name), ('website_description', 'ilike', post_name)] - if grade_id and grade_id != "all": - partner_domain += [('grade_id', '=', int(grade_id))] # try/catch int - - # group by country - countries = partner_obj.read_group( - request.cr, openerp.SUPERUSER_ID, partner_domain, ["id", "country_id"], - groupby="country_id", orderby="country_id", context=request.context) - countries_partners = partner_obj.search( - request.cr, openerp.SUPERUSER_ID, partner_domain, - context=request.context, count=True) - - if country_id: - country = country_obj.browse(request.cr, request.uid, country_id, request.context) - partner_domain += [('country_id', '=', country_id)] - if not any(x['country_id'][0] == country_id for x in countries): - countries.append({ - 'country_id_count': 0, - 'country_id': (country_id, country.name) - }) - countries.sort(key=lambda d: d['country_id'][1]) - - countries.insert(0, { - 'country_id_count': countries_partners, - 'country_id': (0, _("All Countries")) - }) - - # format pager - partner_count = partner_obj.search_count( - request.cr, openerp.SUPERUSER_ID, partner_domain, - context=request.context) - pager = request.website.pager(url="/partners", total=partner_count, page=page, step=self._references_per_page, scope=7, url_args=post) - - partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, partner_domain, - offset=pager['offset'], limit=self._references_per_page, - order="grade_id DESC, partner_weight DESC", - context=request.context) - google_map_partner_ids = ','.join(map(str, partner_ids)) - partners = partner_obj.browse(request.cr, openerp.SUPERUSER_ID, partner_ids, request.context) + if search: + base_partner_domain += ['|', ('name', 'ilike', search), ('website_description', 'ilike', search)] # group by grade + grade_domain = list(base_partner_domain) + if not country and not country_all: + country_code = self._get_current_country_code() + if country_code: + country_ids = country_obj.search(request.cr, request.uid, [('code', '=', country_code)], context=request.context) + if country_ids: + country = country_obj.browse(request.cr, request.uid, country_ids[0], context=request.context) + if country: + grade_domain += [('country_id', '=', country.id)] grades = partner_obj.read_group( - request.cr, openerp.SUPERUSER_ID, base_partner_domain, ["id", "grade_id"], + request.cr, SUPERUSER_ID, grade_domain, ["id", "grade_id"], groupby="grade_id", orderby="grade_id DESC", context=request.context) grades_partners = partner_obj.search( - request.cr, openerp.SUPERUSER_ID, base_partner_domain, + request.cr, SUPERUSER_ID, grade_domain, context=request.context, count=True) + # flag active grade + for grade_dict in grades: + grade_dict['active'] = grade and grade_dict['grade_id'][0] == grade.id grades.insert(0, { 'grade_id_count': grades_partners, - 'grade_id': (0, _("All Categories")) + 'grade_id': (0, _("All Categories")), + 'active': bool(grade is None), }) + # group by country + country_domain = list(base_partner_domain) + if grade: + country_domain += [('grade_id', '=', grade.id)] + countries = partner_obj.read_group( + request.cr, SUPERUSER_ID, country_domain, ["id", "country_id"], + groupby="country_id", orderby="country_id", context=request.context) + countries_partners = partner_obj.search( + request.cr, SUPERUSER_ID, country_domain, + context=request.context, count=True) + # flag active country + for country_dict in countries: + country_dict['active'] = country and country_dict['country_id'] and country_dict['country_id'][0] == country.id + countries.insert(0, { + 'country_id_count': countries_partners, + 'country_id': (0, _("All Countries")), + 'active': bool(country is None), + }) + + # current search + if grade: + base_partner_domain += [('grade_id', '=', grade.id)] + if country: + base_partner_domain += [('country_id', '=', country.id)] + + # format pager + if grade and not country: + url = '/partners/grade/' + slug(grade) + elif country and not grade: + url = '/partners/country/' + slug(country) + elif country and grade: + url = '/partners/grade/' + slug(grade) + '/country/' + slug(country) + else: + url = '/partners' + url_args = {} + if search: + url_args['search'] = search + partner_count = partner_obj.search_count( + request.cr, SUPERUSER_ID, base_partner_domain, + context=request.context) + pager = request.website.pager( + url=url, total=partner_count, page=page, step=self._references_per_page, scope=7, + url_args=url_args) + + # search partners matching current search parameters + partner_ids = partner_obj.search( + request.cr, SUPERUSER_ID, base_partner_domain, + order="grade_id DESC", + context=request.context) # todo in trunk: order="grade_id DESC, implemented_count DESC", offset=pager['offset'], limit=self._references_per_page + partners = partner_obj.browse(request.cr, SUPERUSER_ID, partner_ids, request.context) + # remove me in trunk + partners.sort(key=lambda x: (-1 * (x.grade_id and x.grade_id.id or 0), len(x.implemented_partner_ids)), reverse=True) + partners = partners[pager['offset']:pager['offset'] + self._references_per_page] + + google_map_partner_ids = ','.join(map(str, [p.id for p in partners])) + values = { 'countries': countries, - 'current_country_id': country_id, 'current_country': country, 'grades': grades, - 'grade_id': grade_id, + 'current_grade': grade, 'partners': partners, 'google_map_partner_ids': google_map_partner_ids, 'pager': pager, 'searches': post, - 'search_path': "?%s" % werkzeug.url_encode(post), + 'search_path': "%s" % werkzeug.url_encode(post), } return request.website.render("website_crm_partner_assign.index", values) # Do not use semantic controller due to SUPERUSER_ID - @http.route(['/partners/', '/partners/-'], type='http', auth="public", website=True, multilang=True) - def partners_ref(self, partner_id, partner_name='', **post): - values = website_partner.get_partner_template_value(partner_id) - if not values: - return self.partners(**post) - values['main_object'] = values['partner'] - return request.website.render("website_crm_partner_assign.partner", values) + @http.route(['/partners/'], type='http', auth="public", website=True, multilang=True) + def partners_detail(self, partner_id, partner_name='', **post): + mo = re.search('-([-0-9]+)$', str(partner_id)) + current_grade, current_country = None, None + grade_id = post.get('grade_id') + country_id = post.get('country_id') + if grade_id: + grade_ids = request.registry['res.partner.grade'].exists(request.cr, request.uid, int(grade_id), context=request.context) + if grade_ids: + current_grade = request.registry['res.partner.grade'].browse(request.cr, request.uid, grade_ids[0], context=request.context) + if country_id: + country_ids = request.registry['res.country'].exists(request.cr, request.uid, int(country_id), context=request.context) + if country_ids: + current_country = request.registry['res.country'].browse(request.cr, request.uid, country_ids[0], context=request.context) + if mo: + partner_id = int(mo.group(1)) + partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context) + if partner.exists() and partner.website_published: + values = { + 'main_object': partner, + 'partner': partner, + 'current_grade': current_grade, + 'current_country': current_country + } + return request.website.render("website_crm_partner_assign.partner", values) + return self.partners(**post) diff --git a/addons/website_crm_partner_assign/views/website_crm_partner_assign.xml b/addons/website_crm_partner_assign/views/website_crm_partner_assign.xml index 3e96bc12443..6d5b93793ad 100644 --- a/addons/website_crm_partner_assign/views/website_crm_partner_assign.xml +++ b/addons/website_crm_partner_assign/views/website_crm_partner_assign.xml @@ -15,7 +15,7 @@ Resellers
-
+
@@ -36,13 +36,13 @@
-
+
-
-
+