def pricelist(self, promo, **post): order = request.website.sale_get_order() coupon_status = request.env['sale.coupon.apply.code'].sudo( ).apply_coupon(order, promo) if coupon_status.get('not_found'): return super(WebsiteSale, self).pricelist(promo, **post) elif coupon_status.get('error'): request.session['error_promo_code'] = coupon_status['error'] return request.redirect(post.get('r', '/shop/cart'))
def survey_start(self, survey_token, answer_token=None, email=False, **post): """ Start a survey by providing * a token linked to a survey; * a token linked to an answer or generate a new token if access is allowed; """ # Get the current answer token from cookie answer_from_cookie = False if not answer_token: answer_token = request.httprequest.cookies.get('survey_%s' % survey_token) answer_from_cookie = bool(answer_token) access_data = self._get_access_data(survey_token, answer_token, ensure_token=False) if answer_from_cookie and access_data['validity_code'] in ( 'answer_wrong_user', 'token_wrong'): # If the cookie had been generated for another user or does not correspond to any existing answer object # (probably because it has been deleted), ignore it and redo the check. # The cookie will be replaced by a legit value when resolving the URL, so we don't clean it further here. access_data = self._get_access_data(survey_token, None, ensure_token=False) if access_data['validity_code'] is not True: return self._redirect_with_error(access_data, access_data['validity_code']) survey_sudo, answer_sudo = access_data['survey_sudo'], access_data[ 'answer_sudo'] if not answer_sudo: try: answer_sudo = survey_sudo._create_answer(user=request.env.user, email=email) except UserError: answer_sudo = False if not answer_sudo: try: survey_sudo.with_user( request.env.user).check_access_rights('read') survey_sudo.with_user( request.env.user).check_access_rule('read') except: return werkzeug.utils.redirect("/") else: return request.render("survey.survey_403_page", {'survey': survey_sudo}) return request.redirect( '/survey/%s/%s' % (survey_sudo.access_token, answer_sudo.access_token))
def return_confirmation(self, **post): return_request_id = request.session.get('request_last_return_id') if return_request_id: return_request = request.env['rma.request'].sudo().browse( return_request_id) return request.render("website_rma.return_confirmation", {'return_req_id': return_request}) else: return request.redirect('/shop')
def payment_token(self, order_id, pm_id=None, **kwargs): order = request.env['sale.order'].sudo().browse(order_id) if not order or not order.order_line or pm_id is None: return request.redirect("/quote/%s" % order_id) # try to convert pm_id into an integer, if it doesn't work redirect the user to the quote try: pm_id = int(pm_id) except ValueError: return request.redirect('/quote/%s' % order_id) # retrieve the token from its id token = request.env['payment.token'].sudo().browse(pm_id) if not token: return request.redirect('/quote/%s' % order_id) # find an already existing transaction tx = request.env['payment.transaction'].sudo().search( [('reference', '=', order.name)], limit=1) # set the transaction type to server2server tx_type = 'server2server' # check if the transaction exists, if not then it create one tx = tx._check_or_create_sale_tx( order, token.acquirer_id, payment_token=token, tx_type=tx_type, add_tx_values={ 'callback_model_id': request.env['ir.model'].sudo().search( [('model', '=', order._name)], limit=1).id, 'callback_res_id': order.id, 'callback_method': '_confirm_online_quote', }) # set the transaction id into the session request.session['quote_%s_transaction_id' % order_id] = tx.id # proceed to the payment tx.confirm_sale_token() # redirect the user to the online quote return request.redirect('/quote/%s/%s' % (order_id, order.access_token))
def blog(self, blog=None, tag=None, page=1, search=None, **opt): Blog = request.env['blog.blog'] if blog and not blog.can_access_from_current_website(): raise werkzeug.exceptions.NotFound() blogs = Blog.search(request.website.website_domain(), order="create_date asc, id asc") if not blog and len(blogs) == 1: return werkzeug.utils.redirect('/blog/%s' % slug(blogs[0]), code=302) date_begin, date_end, state = opt.get('date_begin'), opt.get( 'date_end'), opt.get('state') if tag and request.httprequest.method == 'GET': # redirect get tag-1,tag-2 -> get tag-1 tags = tag.split(',') if len(tags) > 1: url = QueryURL('' if blog else '/blog', ['blog', 'tag'], blog=blog, tag=tags[0], date_begin=date_begin, date_end=date_end, search=search)() return request.redirect(url, code=302) values = self._prepare_blog_values(blogs=blogs, blog=blog, date_begin=date_begin, date_end=date_end, tags=tag, state=state, page=page, search=search) # in case of a redirection need by `_prepare_blog_values` we follow it if isinstance(values, werkzeug.wrappers.Response): return values if blog: values['main_object'] = blog values['edit_in_backend'] = True values['blog_url'] = QueryURL('', ['blog', 'tag'], blog=blog, tag=tag, date_begin=date_begin, date_end=date_end, search=search) else: values['blog_url'] = QueryURL('/blog', ['tag'], date_begin=date_begin, date_end=date_end, search=search) return request.render("website_blog.blog_post_short", values)
def portal_order_page(self, order=None, access_token=None, **kw): try: order_sudo = self._order_check_access(order, access_token=access_token) except AccessError: return request.redirect('/my') values = self._order_get_page_view_values(order_sudo, access_token, **kw) return request.render("sale.portal_order_page", values)
def portal_my_home_ticket(self, ticket_id=None, access_token=None, **kw): try: project_sudo = self._document_check_access('helpdesk.ticket', ticket_id, access_token) except (AccessError, MissingError): return request.redirect('/my') values = self._ticket_get_page_view_values(project_sudo, access_token, **kw) return request.render("website_helpdesk.portal_my_home_ticket", values)
def post_toggle_correct(self, forum, post, **kwargs): if post.parent_id is False: return request.redirect('/') if not request.session.uid: return {'error': 'anonymous_user'} # set all answers to False, only one can be accepted (post.parent_id.child_ids - post).write(dict(is_correct=False)) post.is_correct = not post.is_correct return post.is_correct
def sale_pay_token(self, order_id, pm_id=None, **kwargs): """ Use a token to perform a s2s transaction """ error_url = kwargs.get('error_url', '/my') success_url = kwargs.get('success_url', '/my') access_token = kwargs.get('access_token') params = {} if access_token: params['access_token'] = access_token order_sudo = request.env['sale.order'].sudo().browse(order_id).exists() if not order_sudo: params['error'] = 'pay_sale_invalid_doc' return request.redirect(_build_url_w_params(error_url, params)) try: token = request.env['payment.token'].sudo().browse(int(pm_id)) except (ValueError, TypeError): token = False token_owner = order_sudo.partner_id if request.env.user == request.env.ref('base.public_user') else request.env.user.partner_id if not token or token.partner_id != token_owner: params['error'] = 'pay_sale_invalid_token' return request.redirect(_build_url_w_params(error_url, params)) # find an existing tx or create a new one tx = request.env['payment.transaction'].sudo()._check_or_create_sale_tx( order_sudo, token.acquirer_id, payment_token=token, tx_type='server2server', ) # set the transaction id into the session request.session['portal_sale_%s_transaction_id' % order_sudo.id] = tx.id # proceed to the payment res = tx.confirm_sale_token() if tx.state != 'authorized' or not tx.acquirer_id.capture_manually: if res is not True: params['error'] = res return request.redirect(_build_url_w_params(error_url, params)) params['success'] = 'pay_sale' return request.redirect(_build_url_w_params(success_url, params))
def payment_token(self, pm_id, reference, amount, currency_id, return_url=None, **kwargs): token = request.env['payment.token'].browse(int(pm_id)) if not token: return request.redirect('/website_payment/pay?error_msg=%s' % _('Cannot setup the payment.')) partner_id = request.env.user.partner_id.id if not request.env.user._is_public( ) else False values = { 'acquirer_id': token.acquirer_id.id, 'reference': reference, 'amount': float(amount), 'currency_id': int(currency_id), 'partner_id': partner_id, 'payment_token_id': pm_id } tx = request.env['payment.transaction'].sudo().create(values) request.session['website_payment_tx_id'] = tx.id try: res = tx.s2s_do_transaction() except Exception as e: return request.redirect('/website_payment/pay?error_msg=%s' % _('Payment transaction failed.')) valid_state = 'authorized' if tx.acquirer_id.capture_manually else 'done' if not res or tx.state != valid_state: return request.redirect('/website_payment/pay?error_msg=%s' % _('Payment transaction failed.')) return request.redirect( return_url if return_url else '/website_payment/confirm?tx_id=%d' % tx.id)
def portal_my_task(self, task_id, access_token=None, **kw): try: task_sudo = self._document_check_access('project.task', task_id, access_token) except (AccessError, MissingError): return request.redirect('/my') # ensure attachment are accessible with access token inside template for attachment in task_sudo.attachment_ids: attachment.generate_access_token() values = self._task_get_page_view_values(task_sudo, access_token, **kw) return request.render("project.portal_my_task", values)
def payment(self, **post): order = request.website.sale_get_order() carrier_id = post.get('carrier_id') if carrier_id: carrier_id = int(carrier_id) if order: order._check_carrier_quotation(force_carrier_id=carrier_id) if carrier_id: return request.redirect("/shop/payment") return super(WebsiteSaleDelivery, self).payment(**post)
def old_blog_post(self, blog, blog_post, tag_id=None, page=1, enable_editor=None, **post): # Compatibility pre-v14 return request.redirect(_build_url_w_params( "/blog/%s/%s" % (slug(blog), slug(blog_post)), request.params), code=301)
def print_saleorder(self): sale_order_id = request.session.get('sale_last_order_id') if sale_order_id: pdf, _ = request.env.ref( 'sale.action_report_saleorder').sudo().render_qweb_pdf( [sale_order_id]) pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', u'%s' % len(pdf))] return request.make_response(pdf, headers=pdfhttpheaders) else: return request.redirect('/shop')
def reset_template(self, view_id, mode='soft', redirect='/', **kwargs): """ This method will try to reset a broken view. Given the mode, the view can either be: - Soft reset: restore to previous architeture. - Hard reset: it will read the original `arch` from the XML file if the view comes from an XML file (arch_fs). """ view = request.env['ir.ui.view'].browse(int(view_id)) # Deactivate COW to not fix a generic view by creating a specific view.with_context(website_id=None).reset_arch(mode) return request.redirect(redirect)
def cart(self, access_token=None, revive='', **post): """ Main cart management + abandoned cart revival access_token: Abandoned cart SO access token revive: Revival method when abandoned cart. Can be 'merge' or 'squash' """ order = request.website.sale_get_order() values = {} if access_token: abandoned_order = request.env['sale.order'].sudo().search( [('access_token', '=', access_token)], limit=1) if not abandoned_order: # wrong token (or SO has been deleted) return request.render('website.404') if abandoned_order.state != 'draft': # abandoned cart already finished values.update({'abandoned_proceed': True}) elif revive == 'squash' or ( revive == 'merge' and not request.session['sale_order_id'] ): # restore old cart or merge with unexistant request.session['sale_order_id'] = abandoned_order.id return request.redirect('/shop/cart') elif revive == 'merge': abandoned_order.order_line.write( {'order_id': request.session['sale_order_id']}) abandoned_order.action_cancel() elif abandoned_order.id != request.session[ 'sale_order_id']: # abandoned cart found, user have to choose what to do values.update({'access_token': abandoned_order.access_token}) if order: from_currency = order.company_id.currency_id to_currency = order.pricelist_id.currency_id compute_currency = lambda price: from_currency.compute( price, to_currency) else: compute_currency = lambda price: price values.update({ 'website_sale_order': order, 'compute_currency': compute_currency, 'suggested_products': [], }) if order: _order = order if not request.env.context.get('pricelist'): _order = order.with_context(pricelist=order.pricelist_id.id) values['suggested_products'] = _order._cart_accessories() if post.get('type') == 'popover': # force no-cache so IE11 doesn't cache this XHR return request.render("website_sale.cart_popover", values, headers={'Cache-Control': 'no-cache'}) return request.render("website_sale.cart", values)
def _serve_page(cls): req_page = request.httprequest.path page_domain = [('url', '=', req_page)] + request.website.website_domain() published_domain = page_domain # specific page first page = request.env['website.page'].sudo().search(published_domain, order='website_id asc', limit=1) # redirect withtout trailing / if not page and req_page != "/" and req_page.endswith("/"): return request.redirect(req_page[:-1]) if page: # prefetch all menus (it will prefetch website.page too) request.website.menu_id if page and (request.website.is_publisher() or page.is_visible): need_to_cache = False cache_key = page._get_cache_key(request) if ( page.cache_time # cache > 0 and request.httprequest.method == "GET" and request.env.user._is_public() # only cache for unlogged user and 'nocache' not in request.params # allow bypass cache / debug and not request.session.debug and len(cache_key) and cache_key[-1] is not None # nocache via expr ): need_to_cache = True try: r = page._get_cache_response(cache_key) if r['time'] + page.cache_time > time.time(): response = werkzeug.Response(r['content'], mimetype=r['contenttype']) response._cached_template = r['template'] response._cached_page = page return response except KeyError: pass _, ext = os.path.splitext(req_page) response = request.render(page.view_id.id, { 'deletable': True, 'main_object': page, }, mimetype=_guess_mimetype(ext)) if need_to_cache and response.status_code == 200: r = response.render() page._set_cache_response(cache_key, { 'content': r, 'contenttype': response.headers['Content-Type'], 'time': time.time(), 'template': getattr(response, 'qcontext', {}).get('response_template') }) return response return False
def post_toc_ok(self, forum, post_id, toc_id, **kwargs): assert request.env.user.karma >= 200, 'Not enough karma, you need 200 to promote a documentation.' stage = request.env['forum.documentation.toc'].search([], limit=1) request.env['forum.post'].browse(int(post_id)).write({ 'documentation_toc_id': toc_id and int(toc_id) or False, 'documentation_stage_id': stage and stage.id }) return request.redirect('/forum/' + str(forum.id) + '/question/' + str(post_id))
def event(self, event, **post): if not event.can_access_from_current_website(): raise werkzeug.exceptions.NotFound() if event.menu_id and event.menu_id.child_id: target_url = event.menu_id.child_id[0].url else: target_url = '/event/%s/register' % str(event.id) if post.get('enable_editor') == '1': target_url += '?enable_editor=1' return request.redirect(target_url)
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 and request.httprequest.method == '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}) for field in set(['country_id', 'state_id']) & set(values.keys()): try: values[field] = int(values[field]) except: values[field] = False 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 add(self, order_id, option_id, access_token=None, **post): try: order_sudo = self._document_check_access('sale.order', order_id, access_token=access_token) except (AccessError, MissingError): return request.redirect('/my') option_sudo = request.env['sale.order.option'].sudo().browse(option_id) if order_sudo != option_sudo.order_id: return request.redirect(order_sudo.get_portal_url()) option_sudo.add_option_to_order() results = self._get_portal_order_details(order_sudo) results['sale_template'] = request.env['ir.ui.view']._render_template( "sale.sale_order_portal_content", { 'sale_order': option_sudo.order_id, 'report_type': "html" }) return results
def decline(self, order_id, access_token=None, **post): try: order_sudo = self._document_check_access('sale.order', order_id, access_token=access_token) except (AccessError, MissingError): return request.redirect('/my') message = post.get('decline_message') query_string = False if order_sudo.has_to_be_signed() and message: order_sudo.action_cancel() _message_post_helper( 'sale.order', order_id, message, **{'token': access_token} if access_token else {}) else: query_string = "&message=cant_reject" return request.redirect( order_sudo.get_portal_url(query_string=query_string))
def get_wishlist(self, count=False, **kw): values = request.env['product.wishlist'].with_context( display_default_code=False).current() if count: return request.make_response( json.dumps(values.mapped('product_id').ids)) if not len(values): return request.redirect("/shop") return request.render("website_sale_wishlist.product_wishlist", dict(wishes=values))
def survey_test(self, survey_token, **kwargs): """ Test mode for surveys: create a test answer, only for managers or officers testing their surveys """ survey_sudo, dummy = self._fetch_from_access_token(survey_token, False) try: answer_sudo = survey_sudo._create_answer(user=request.env.user, test_entry=True) except: return werkzeug.utils.redirect('/') return request.redirect( '/survey/start/%s?%s' % (survey_sudo.access_token, keep_query('*', answer_token=answer_sudo.access_token)))
def portal_my_purchase_order(self, order_id=None, **kw): order = request.env['purchase.order'].browse(order_id) try: order.check_access_rights('read') order.check_access_rule('read') except AccessError: return request.redirect('/my') history = request.session.get('my_purchases_history', []) values = { 'order': order.sudo(), } values.update(get_records_pager(history, order)) return request.render("purchase.portal_my_purchase_order", values)
def invoice_pay_token(self, invoice_id, pm_id=None, **kwargs): """ Use a token to perform a s2s transaction """ error_url = kwargs.get('error_url', '/my') access_token = kwargs.get('access_token') params = {} if access_token: params['access_token'] = access_token invoice_sudo = request.env['account.move'].sudo().browse( invoice_id).exists() if not invoice_sudo: params['error'] = 'pay_invoice_invalid_doc' return request.redirect(_build_url_w_params(error_url, params)) success_url = kwargs.get( 'success_url', "%s?%s" % (invoice_sudo.access_url, url_encode( {'access_token': access_token}) if access_token else '')) try: token = request.env['payment.token'].sudo().browse(int(pm_id)) except (ValueError, TypeError): token = False token_owner = invoice_sudo.partner_id if request.env.user._is_public( ) else request.env.user.partner_id if not token or token.partner_id != token_owner: params['error'] = 'pay_invoice_invalid_token' return request.redirect(_build_url_w_params(error_url, params)) vals = { 'payment_token_id': token.id, 'type': 'server2server', 'return_url': _build_url_w_params(success_url, params), } tx = invoice_sudo._create_payment_transaction(vals) PaymentProcessing.add_payment_transaction(tx) params['success'] = 'pay_invoice' return request.redirect('/payment/process')
def payment_validate(self, transaction_id=None, sale_order_id=None, **post): """ Method that should be called by the server when receiving an update for a transaction. State at this point : - UDPATE ME """ if transaction_id is None: tx = request.website.sale_get_transaction() else: tx = request.env['payment.transaction'].browse(transaction_id) if sale_order_id is None: order = request.website.sale_get_order() else: order = request.env['sale.order'].sudo().browse(sale_order_id) assert order.id == request.session.get('sale_last_order_id') if not order or (order.amount_total and not tx): return request.redirect('/shop') if (not order.amount_total and not tx) or tx.state in ['pending', 'done', 'authorized']: if (not order.amount_total and not tx): # Orders are confirmed by payment transactions, but there is none for free orders, # (e.g. free events), so confirm immediately order.with_context(send_email=True).action_confirm() elif tx and tx.state == 'cancel': # cancel the quotation order.action_cancel() # clean context and session, then redirect to the confirmation page request.website.sale_reset() if tx and tx.state == 'draft': return request.redirect('/shop') return request.redirect('/shop/confirmation')
def payment_token(self, pm_id=None, **kwargs): """ Method that handles payment using saved tokens :param int pm_id: id of the payment.token that we want to use to pay. """ order = request.website.sale_get_order() # do not crash if the user has already paid and try to pay again if not order: return request.redirect('/shop/?error=no_order') assert order.partner_id.id != request.website.partner_id.id try: pm_id = int(pm_id) except ValueError: return request.redirect('/shop/?error=invalid_token_id') # We retrieve the token the user want to use to pay token = request.env['payment.token'].sudo().browse(pm_id) if not token: return request.redirect('/shop/?error=token_not_found') # we retrieve an existing transaction (if it exists obviously) tx = request.website.sale_get_transaction( ) or request.env['payment.transaction'].sudo() # we check if the transaction is Ok, if not then we create it tx = tx._check_or_create_sale_tx(order, token.acquirer_id, payment_token=token, tx_type='server2server') # we set the transaction id into the session (so `sale_get_transaction` can retrieve it ) request.session['sale_transaction_id'] = tx.id # we proceed the s2s payment res = tx.confirm_sale_token() # we then redirect to the page that validates the payment by giving it error if there's one if res is not True: return request.redirect( '/shop/payment/validate?success=False&error=%s' % res) return request.redirect('/shop/payment/validate?success=True')
def product_compare(self, **post): values = {} product_ids = [ int(i) for i in post.get('products', '').split(',') if i.isdigit() ] if not product_ids: return request.redirect("/shop") # use search to check read access on each record/ids products = request.env['product.product'].search([('id', 'in', product_ids)]) values['products'] = products.with_context(display_default_code=False) return request.render("website_sale_comparison.product_compare", values)
def slide_download(self, slide, **kw): slide = slide.sudo() if slide.download_security == 'public' or (slide.download_security == 'user' and request.env.user and request.env.user != request.website.user_id): filecontent = base64.b64decode(slide.datas) disposition = 'attachment; filename=%s.pdf' % werkzeug.urls.url_quote(slide.name) return request.make_response( filecontent, [('Content-Type', 'application/pdf'), ('Content-Length', len(filecontent)), ('Content-Disposition', disposition)]) elif not request.session.uid and slide.download_security == 'user': return request.redirect('/web/login?redirect=/slides/slide/%s' % (slide.id)) return request.render("website.403")