225 lines
8.3 KiB
Python
225 lines
8.3 KiB
Python
# -*- 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
|