1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import math
from werkzeug import urls
from odoo import fields as odoo_fields, tools, _
from odoo.osv import expression
from odoo.exceptions import ValidationError
from odoo.http import Controller, request, route
from odoo.addons.web.controllers.main import WebClient
# --------------------------------------------------
# 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': odoo_fields.Date.to_string(odoo_fields.Date.from_string(date_begin)),
'date_end': odoo_fields.Date.to_string(odoo_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
|