# -*- coding: utf-8 -*- from datetime import datetime, date from odoo import models, fields, api, _ from odoo.exceptions import ValidationError, AccessError class ProductBooking(models.TransientModel): _name = "product.booking" _rec_name = "book_number" _description = "Book" @api.depends( "product_line_ids", "total_days", "extra_charges", "price_based", "from_date", "to_date", ) def compute_rate(self): for record in self: record.total = 0 record.sub_total = 0 record.total_days = 0 record.total_hours = 0 if record.from_date and record.to_date: date_from = datetime.strptime( str(record.from_date), "%Y-%m-%d %H:%M:%S" ) date_to = datetime.strptime(str(record.to_date), "%Y-%m-%d %H:%M:%S") delta = date_from - date_to record.total_days = abs(delta.days) record.total_hours = abs(delta.total_seconds() / 3600.0) sub_total = 0.0 with_tax_total = 0.0 for product in record.product_line_ids.filtered( lambda fv: fv.selected_product ): if record.price_based == "per_day": taxes = product.taxes_id.compute_all( product.rental_qyt, product.currency_id, product.rental_amount * record.total_days, ) sub_total += taxes["total_excluded"] with_tax_total += taxes["total_included"] elif record.price_based == "per_hour": taxes = product.taxes_id.compute_all( product.rental_qyt, product.currency_id, product.rental_amount_per_hour * record.total_hours, ) sub_total += taxes["total_excluded"] with_tax_total += taxes["total_included"] else: taxes = product.taxes_id.compute_all( product.rental_qyt, product.currency_id, product.rental_amount_per_session, ) sub_total += taxes["total_excluded"] with_tax_total += taxes["total_included"] record.sub_total = sub_total record.total = with_tax_total + record.extra_charges @api.model def _get_based_on_selections(self): enabled_day_rent = ( self.env["ir.config_parameter"].sudo().get_param("enabled_day_rent") ) enabled_hour_rent = ( self.env["ir.config_parameter"].sudo().get_param("enabled_hour_rent") ) enabled_session_rent = ( self.env["ir.config_parameter"].sudo().get_param("enabled_session_rent") ) selection = [] if enabled_day_rent: selection.append(("per_day", "Day")) if enabled_hour_rent: selection.append(("per_hour", "Hour")) if enabled_session_rent: selection.append(("per_session", "Session")) return selection book_number = fields.Char(string="Book Number") from_date = fields.Datetime(string="From Date") to_date = fields.Datetime(string="To Date") product_line_ids = fields.Many2many( "product.product", "product_search_table", "product_search_id", "product_book_id", string="Available Product", ) is_search = fields.Boolean(string="Is Search", default=False) total_days = fields.Float(string="Total Days", compute="compute_rate") extra_charges = fields.Monetary(string="Extra Charges") sub_total = fields.Float(string="Sub Total(Exclusive Tax)", compute="compute_rate") total = fields.Float(string="Total", compute="compute_rate") categ_id = fields.Many2one( "product.category", required=True, string="Product Category" ) company_id = fields.Many2one( "res.company", string="Company", default=lambda self: self.env.user.company_id ) currency_id = fields.Many2one("res.currency", related="company_id.currency_id") location_id = fields.Many2one("stock.location", string="Location") price_based = fields.Selection( selection=_get_based_on_selections, default="per_day", string="Based On" ) total_hours = fields.Float(string="Total Hours", compute="compute_rate") session_id = fields.Many2one("session.config", string="Session") @api.constrains("from_date", "to_date") def check_date(self): for record in self: if ( date.strftime( datetime.strptime(str(record.from_date), "%Y-%m-%d %H:%M:%S"), "%Y-%m-%d", ) < str(date.today()) ): raise ValidationError(_("You cannot enter past date")) if date.strftime( datetime.strptime(str(record.to_date), "%Y-%m-%d %H:%M:%S"), "%Y-%m-%d" ) < str(date.today()): raise ValidationError(_("You cannot enter past date")) @api.model def convert_float_to_hh_mm(self, session_id, from_date): start_date = "{0:02.0f}:{1:02.0f}".format( *divmod(session_id.start_time * 60, 60) ) end_date = "{0:02.0f}:{1:02.0f}".format(*divmod(session_id.end_time * 60, 60)) start_date_fmt = ( "%Y-%m-%d " + start_date.split(":")[0] + ":" + start_date.split(":")[1] + ":" + "00" ) end_date_fmt = ( "%Y-%m-%d " + end_date.split(":")[0] + ":" + end_date.split(":")[1] + ":" + "00" ) start_str_datetime = from_date.strftime(start_date_fmt) end_str_datetime = from_date.strftime(end_date_fmt) start_date = self.env["rental.product.order"].convert_TZ_UTC(start_str_datetime) end_date = self.env["rental.product.order"].convert_TZ_UTC(end_str_datetime) return start_date, end_date @api.onchange("price_based", "from_date", "session_id") def onchange_date_from_price_based(self): if self.price_based == "per_session" and self.from_date and self.session_id: start_time, end_time = self.convert_float_to_hh_mm( self.session_id, self.from_date ) self.to_date = end_time self.from_date = start_time @api.model def create(self, vals): vals.update( { "book_number": self.env["ir.sequence"].next_by_code("product_booking") or _("Product Booking") } ) return super(ProductBooking, self).create(vals) def search_product(self): product_id = [] if self.from_date and self.to_date: if self.from_date > self.to_date: raise ValidationError("To Date must be greater than From date") elif not self.env.user.has_group("stock.group_stock_multi_locations"): raise AccessError( _("Manage Multiple Stock Locations need to be assign.") ) else: categ_ids = self.env["product.category"].search( [("parent_id", "child_of", self.categ_id.id)] ) self.env.cr.execute( """ SELECT propro.id FROM product_product propro JOIN product_template protmp ON protmp.id = propro.product_tmpl_id WHERE protmp.categ_id IN %s AND propro.is_rental='t' AND propro.id IN ( Select product_id from stock_quant where location_id = %s); """, ( tuple(categ_ids.ids), self.location_id.id, ), ) product_data = self.env.cr.fetchall() if product_data: for products in product_data: product = ( self.env["product.product"] .sudo() .search([("id", "in", products)]) ) available_product_stock = self.env["stock.quant"].search( [ ("product_id", "=", product.id), ("quantity", ">", 0), ("location_id", "=", self.location_id.id), ] ) available_product_stock.product_id.rental_qyt = 1.0 available_product_stock.product_id.selected_product = False product_id.extend(available_product_stock.product_id.ids) self.update( { "product_line_ids": [(6, 0, product_id)], "total": self.total, "is_search": True, } ) else: raise ValidationError("Sorry!!! No any Product Available!!!") def book_product(self): product = False for product_line in self.product_line_ids: if product_line.selected_product: product = True if product: product_order_id = [] for product_id in self.product_line_ids.filtered( lambda fv: fv.selected_product ): # available_product_stock = self.env['stock.quant'].search([('product_id', '=', product_id.id), # ('location_id', '=', # self.location_id.id)]) # if product_id.rental_qyt > available_product_stock.quantity: # raise ValidationError(_('Available quantity for %s is %d') % (product_id.name, # available_product_stock.quantity)) order_line_obj = self.env["product.order.line"] start_date = self.from_date end_date = self.to_date order_line_ids = order_line_obj.sudo().search( [ ("product_order_id.state", "=", "confirm"), ("product_id", "=", product_id.id), ] ) total_in_order_qty = 0 for order_line in order_line_ids: if ( ( start_date <= order_line.product_order_id.from_date <= end_date ) or ( start_date <= order_line.product_order_id.to_date <= end_date ) and order_line.product_order_id.picking_count == 1 ): total_in_order_qty += ( order_line.qty_needed if order_line.product_order_id.picking_count == 1 else 0 ) elif ( ( order_line.product_order_id.from_date <= start_date <= order_line.product_order_id.to_date ) or ( order_line.product_order_id.from_date <= end_date <= order_line.product_order_id.to_date ) and order_line.product_order_id.picking_count == 1 ): total_in_order_qty += ( order_line.qty_needed if order_line.product_order_id.picking_count == 1 else 0 ) if product_id.sudo().qty_available and total_in_order_qty: qty_available = product_id.sudo().qty_available - total_in_order_qty else: total_in_order_qty = order_line_ids.filtered( lambda x: x.qty_needed if x.product_order_id.picking_count > 1 else 0 ) qty_available = product_id.sudo().qty_available + sum( total_in_order_qty.mapped("qty_needed") ) if ( not product_id.rental_qyt or not qty_available >= product_id.rental_qyt ): raise ValidationError( _("Available quantity for %s is %d") % (product_id.name, qty_available) ) else: if self.price_based == "per_day": rate_total = product_id.rental_amount else: if self.price_based == "per_hour": rate_total = product_id.rental_amount_per_hour else: rate_total = product_id.rental_amount_per_session product_order_id.append( ( 0, 0, { "product_id": product_id.id, "name": product_id.name, "price_based": self.price_based, "enter_days": self.total_days if self.price_based == "per_day" else 0, "enter_hour": self.total_hours if self.price_based in ["per_hour", "per_session"] else 0, "price": rate_total, "qty_needed": product_id.rental_qyt, "tax_id": [(6, 0, product_id.taxes_id.ids)], }, ) ) return { "name": "product order", "view_type": "form", "view_mode": "form", "res_model": "rental.product.order", "type": "ir.actions.act_window", "context": { "default_from_date": self.from_date, "default_to_date": self.to_date, "default_extra_charges": self.extra_charges, "default_is_days": True if self.price_based == "per_day" else False, "default_is_hours": True if self.price_based in ["per_hour", "per_session"] else False, "default_product_order_lines_ids": product_order_id, "default_location_id": self.location_id.id, "default_price_based": self.price_based, }, } else: raise ValidationError(_("First Please Select the Product"))