# -*- coding: utf-8 -*- import base64 import logging from datetime import datetime from odoo import http, fields, _, exceptions from odoo.http import request from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager _logger = logging.getLogger(__name__) def parse_html_datetime(dt_str): if not dt_str: return None for fmt in ( "%Y-%m-%dT%H:%M", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%d", "%m/%d/%Y %I:%M %p", "%d/%m/%Y %I:%M %p", "%m/%d/%Y %H:%M", "%d/%m/%Y %H:%M", "%m/%d/%Y", "%d/%m/%Y" ): try: return datetime.strptime(dt_str.strip(), fmt) except ValueError: continue try: return fields.Datetime.to_datetime(dt_str) except Exception: return None class EventRentalController(http.Controller): @http.route('/rentals', type='http', auth='public', website=True) def rental_catalog(self, search='', category=None, **post): domain = [('is_rental', '=', True)] if search: domain.append(('name', 'ilike', search)) # Fetch products matching domain products = request.env['product.template'].sudo().search(domain) # Extract categories represented by these products to display in the filter sidebar categories = products.mapped('categ_id') if category: domain.append(('categ_id', '=', int(category))) products = request.env['product.template'].sudo().search(domain) values = { 'products': products, 'categories': categories, 'search': search, 'current_category': int(category) if category else None, } return request.render('event_rental.rental_catalog_template', values) @http.route('/rentals/', type='http', auth='public', website=True) def rental_product_detail(self, product_slug, **post): try: if '-' in product_slug: product_id = int(product_slug.split('-')[-1]) else: product_id = int(product_slug) except (ValueError, IndexError): return request.not_found() product = request.env['product.template'].sudo().browse(product_id) if not product or not product.exists() or not product.is_rental: return request.not_found() return request.render('event_rental.rental_product_detail_template', {'product': product}) @http.route('/rental/request', type='http', auth='public', website=True, methods=['GET', 'POST']) def rental_request(self, product_id=None, **post): if request.httprequest.method == 'GET': selected_product = None if product_id: # Browse product product variant product_tmpl = request.env['product.template'].sudo().browse(int(product_id)) selected_product = product_tmpl.product_variant_id all_products = request.env['product.product'].sudo().search([('is_rental', '=', True)]) return request.render('event_rental.rental_request_form_template', { 'all_products': all_products, 'selected_product': selected_product, 'error_message': None, 'post': post }) # Process POST request try: _logger.info("RENTAL REQUEST POST PARAMS (product_id=%s): %s", product_id, post) customer_name = post.get('customer_name') customer_email = post.get('customer_email') customer_phone = post.get('customer_phone') company_name = post.get('company_name') customer_address = post.get('customer_address') start_date_str = post.get('start_date') end_date_str = post.get('end_date') location = post.get('location') event_type = post.get('event_type') req_product_id = int(product_id or post.get('product_id') or 0) quantity = float(post.get('quantity') or 1.0) doc_type = post.get('doc_type', 'aadhaar') id_proof_file = request.httprequest.files.get('id_proof') # Simple UI checks if not customer_name or not customer_email or not customer_phone or not start_date_str or not end_date_str or not location or not req_product_id: raise ValueError(_("All required fields must be filled.")) if not id_proof_file or id_proof_file.filename == '': raise ValueError(_("Please upload a valid Government ID Proof document.")) start_date = parse_html_datetime(start_date_str) end_date = parse_html_datetime(end_date_str) if not start_date or not end_date: raise ValueError(_("Invalid start or end date format.")) if start_date >= end_date: raise ValueError(_("Event Start Date must be earlier than End Date.")) product = request.env['product.product'].sudo().browse(req_product_id) if not product: raise ValueError(_("Invalid rental product selected.")) # Availability checking bypassed per request # dummy_request = request.env['event.rental.request'].sudo() # available_qty = dummy_request.check_availability(start_date, end_date, product) # # if available_qty < quantity: # raise ValueError(_("Product '%s' is not available in the quantity requested (%s) for the selected dates. Only %s units are currently available.") % ( # product.name, quantity, available_qty # )) # Find or create partner partner = request.env['res.partner'].sudo().search([('email', '=', customer_email)], limit=1) if not partner: partner = request.env['res.partner'].sudo().create({ 'name': customer_name, 'email': customer_email, 'phone': customer_phone, 'company_name': company_name, 'street': customer_address, }) # Create rental request record rental_request = request.env['event.rental.request'].sudo().create({ 'partner_id': partner.id, 'customer_name': customer_name, 'customer_email': customer_email, 'customer_phone': customer_phone, 'company_name': company_name, 'customer_address': customer_address, 'start_date': start_date, 'end_date': end_date, 'location': location, 'event_type': event_type, 'status': 'under_review', }) # Create request line request.env['event.rental.line'].sudo().create({ 'request_id': rental_request.id, 'product_id': product.id, 'quantity': quantity, }) # Store ID proof attachment file_content = id_proof_file.read() file_base64 = base64.b64encode(file_content) attachment = request.env['ir.attachment'].sudo().create({ 'name': id_proof_file.filename, 'type': 'binary', 'datas': file_base64, 'res_model': 'event.rental.request', 'res_id': rental_request.id, }) # Create document record request.env['event.document'].sudo().create({ 'request_id': rental_request.id, 'partner_id': partner.id, 'doc_type': doc_type, 'attachment_id': attachment.id, 'verification_status': 'pending', }) # Post submission trace rental_request.message_post(body=_("Rental request submitted successfully from public website.")) return request.redirect(f'/rental/request/success?name={rental_request.name}') except Exception as e: selected_product = None if post.get('product_id'): selected_product = request.env['product.product'].sudo().browse(int(post.get('product_id'))) all_products = request.env['product.product'].sudo().search([('is_rental', '=', True)]) return request.render('event_rental.rental_request_form_template', { 'all_products': all_products, 'selected_product': selected_product, 'error_message': str(e), 'post': post }) @http.route('/rental/request/success', type='http', auth='public', website=True) def rental_request_success(self, name, **post): return request.render('event_rental.rental_request_success_template', {'name': name}) class CustomerPortalRental(CustomerPortal): def _prepare_home_portal_values(self, counters): values = super()._prepare_home_portal_values(counters) if 'rental_count' in counters: partner = request.env.user.partner_id rental_count = request.env['event.rental.request'].search_count([ ('partner_id', '=', partner.id) ]) values['rental_count'] = rental_count return values @http.route(['/my/rentals', '/my/rentals/page/'], type='http', auth="user", website=True) def portal_my_rentals(self, page=1, date_begin=None, date_end=None, sortby=None, **kw): values = self._prepare_portal_layout_values() partner = request.env.user.partner_id RentalRequest = request.env['event.rental.request'] domain = [('partner_id', '=', partner.id)] # Count for pagination rental_count = RentalRequest.search_count(domain) pager = portal_pager( url="/my/rentals", total=rental_count, page=page, step=10 ) requests = RentalRequest.search(domain, limit=10, offset=pager['offset']) values.update({ 'requests': requests, 'page_name': 'rental_requests', 'pager': pager, 'default_url': '/my/rentals', }) return request.render("event_rental.portal_my_rental_requests", values) @http.route(['/my/rentals/'], type='http', auth="user", website=True) def portal_rental_request_detail(self, request_id, **kw): rental_request = request.env['event.rental.request'].browse(request_id) try: # Enforce native record rules / access rights rental_request.check_access_rights('read') rental_request.check_access_rule('read') except exceptions.AccessError: return request.redirect('/my') values = { 'rental_request': rental_request, 'page_name': 'rental_request_detail', } return request.render("event_rental.portal_rental_request_detail_template", values)