# -*- coding: utf-8 -*- # Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details. import math from werkzeug import urls from flectra import fields as flectra_fields, tools, _ from flectra.exceptions import ValidationError from flectra.http import Controller, request, route # -------------------------------------------------- # Misc tools # -------------------------------------------------- def pager(url, total, page=1, step=30, scope=5, url_args=None): """ Generate a dict with required value to render `website.pager` template. This method compute url, page range to display, ... in the pager. :param url : base url of the page link :param total : number total of item to be splitted into pages :param page : current page :param step : item per page :param scope : number of page to display on pager :param url_args : additionnal parameters to add as query params to page url :type url_args : dict :returns dict """ # Compute Pager page_count = int(math.ceil(float(total) / step)) page = max(1, min(int(page if str(page).isdigit() else 1), page_count)) scope -= 1 pmin = max(page - int(math.floor(scope/2)), 1) pmax = min(pmin + scope, page_count) if pmax - pmin < scope: pmin = pmax - scope if pmax - scope > 0 else 1 def get_url(page): _url = "%s/page/%s" % (url, page) if page > 1 else url if url_args: _url = "%s?%s" % (_url, urls.url_encode(url_args)) return _url return { "page_count": page_count, "offset": (page - 1) * step, "page": { 'url': get_url(page), 'num': page }, "page_start": { 'url': get_url(pmin), 'num': pmin }, "page_previous": { 'url': get_url(max(pmin, page - 1)), 'num': max(pmin, page - 1) }, "page_next": { 'url': get_url(min(pmax, page + 1)), 'num': min(pmax, page + 1) }, "page_end": { 'url': get_url(pmax), 'num': pmax }, "pages": [ {'url': get_url(page_num), 'num': page_num} for page_num in range(pmin, pmax+1) ] } def get_records_pager(ids, current): if current.id in ids and (hasattr(current, 'website_url') or hasattr(current, 'portal_url')): attr_name = 'portal_url' if hasattr(current, 'portal_url') else 'website_url' idx = ids.index(current.id) return { 'prev_record': idx != 0 and getattr(current.browse(ids[idx - 1]), attr_name), 'next_record': idx < len(ids) - 1 and getattr(current.browse(ids[idx + 1]), attr_name), } return {} def _build_url_w_params(url_string, query_params, remove_duplicates=True): """ Rebuild a string url based on url_string and correctly compute query parameters using those present in the url and those given by query_params. Having duplicates in the final url is optional. For example: * url_string = '/my?foo=bar&error=pay' * query_params = {'foo': 'bar2', 'alice': 'bob'} * if remove duplicates: result = '/my?foo=bar2&error=pay&alice=bob' * else: result = '/my?foo=bar&foo=bar2&error=pay&alice=bob' """ url = urls.url_parse(url_string) url_params = url.decode_query() if remove_duplicates: # convert to standard dict instead of werkzeug multidict to remove duplicates automatically url_params = url_params.to_dict() url_params.update(query_params) return url.replace(query=urls.url_encode(url_params)).to_url() class CustomerPortal(Controller): MANDATORY_BILLING_FIELDS = ["name", "phone", "email", "street", "city", "country_id"] OPTIONAL_BILLING_FIELDS = ["zipcode", "state_id", "vat", "company_name"] _items_per_page = 20 def _get_archive_groups(self, model, domain=None, fields=None, groupby="create_date", order="create_date desc"): if not model: return [] if domain is None: domain = [] if fields is None: fields = ['name', 'create_date'] groups = [] for group in request.env[model]._read_group_raw(domain, fields=fields, groupby=groupby, orderby=order): dates, label = group[groupby] date_begin, date_end = dates.split('/') groups.append({ 'date_begin': flectra_fields.Date.to_string(flectra_fields.Date.from_string(date_begin)), 'date_end': flectra_fields.Date.to_string(flectra_fields.Date.from_string(date_end)), 'name': label, 'item_count': group[groupby + '_count'] }) return groups def _prepare_portal_layout_values(self): # get customer sales rep sales_user = False partner = request.env.user.partner_id if partner.user_id and not partner.user_id._is_public(): sales_user = partner.user_id return { 'sales_user': sales_user, 'page_name': 'home', 'archive_groups': [], } @route(['/my', '/my/home'], type='http', auth="user", website=True) def home(self, **kw): values = self._prepare_portal_layout_values() return request.render("portal.portal_my_home", values) @route(['/my/account'], type='http', auth='user', website=True) def account(self, redirect=None, **post): values = self._prepare_portal_layout_values() partner = request.env.user.partner_id values.update({ 'error': {}, 'error_message': [], }) if post: error, error_message = self.details_form_validate(post) values.update({'error': error, 'error_message': error_message}) values.update(post) if not error: values = {key: post[key] for key in self.MANDATORY_BILLING_FIELDS} values.update({key: post[key] for key in self.OPTIONAL_BILLING_FIELDS if key in post}) values.update({'zip': values.pop('zipcode', '')}) partner.sudo().write(values) if redirect: return request.redirect(redirect) return request.redirect('/my/home') countries = request.env['res.country'].sudo().search([]) states = request.env['res.country.state'].sudo().search([]) values.update({ 'partner': partner, 'countries': countries, 'states': states, 'has_check_vat': hasattr(request.env['res.partner'], 'check_vat'), 'redirect': redirect, 'page_name': 'my_details', }) response = request.render("portal.portal_my_details", values) response.headers['X-Frame-Options'] = 'DENY' return response def details_form_validate(self, data): error = dict() error_message = [] # Validation for field_name in self.MANDATORY_BILLING_FIELDS: if not data.get(field_name): error[field_name] = 'missing' # email validation if data.get('email') and not tools.single_email_re.match(data.get('email')): error["email"] = 'error' error_message.append(_('Invalid Email! Please enter a valid email address.')) # vat validation partner = request.env["res.partner"] if data.get("vat") and hasattr(partner, "check_vat"): if data.get("country_id"): data["vat"] = request.env["res.partner"].fix_eu_vat_number(int(data.get("country_id")), data.get("vat")) partner_dummy = partner.new({ 'vat': data['vat'], 'country_id': (int(data['country_id']) if data.get('country_id') else False), }) try: partner_dummy.check_vat() except ValidationError: error["vat"] = 'error' # error message for empty required fields if [err for err in error.values() if err == 'missing']: error_message.append(_('Some required fields are empty.')) unknown = [k for k in data if k not in self.MANDATORY_BILLING_FIELDS + self.OPTIONAL_BILLING_FIELDS] if unknown: error['common'] = 'Unknown field' error_message.append("Unknown field '%s'" % ','.join(unknown)) return error, error_message