# -*- coding: utf-8 -*- from odoo.tests.common import TransactionCase from odoo.fields import Datetime from datetime import datetime, timedelta from odoo.exceptions import UserError class TestEventRental(TransactionCase): @classmethod def setUpClass(cls): super(TestEventRental, cls).setUpClass() # Create a rental product template first, then get variant cls.rental_product = cls.env['product.product'].create({ 'name': 'Wedding Chair', 'type': 'product', 'is_rental': True, 'rental_price_per_day': 10.0, }) # Set quantity on hand using Odoo stock.quant warehouse = cls.env['stock.warehouse'].search([], limit=1) if warehouse: location = warehouse.lot_stock_id cls.env['stock.quant'].with_context(inventory_mode=True).create({ 'product_id': cls.rental_product.id, 'location_id': location.id, 'inventory_quantity': 50.0, }).action_apply_inventory() # Create a customer partner cls.partner = cls.env['res.partner'].create({ 'name': 'Test Client', 'email': 'client@test.com', 'phone': '1234567890', }) def test_01_rental_flow(self): """ Test the complete rental reservation, pricing, approval, payment, and delivery flow """ # Define dates: 3 days rental duration start_date = datetime.now() + timedelta(days=1) end_date = start_date + timedelta(days=3) # Create request request = self.env['event.rental.request'].create({ 'partner_id': self.partner.id, 'start_date': start_date, 'end_date': end_date, 'location': 'Grand Palace Hall', 'event_type': 'wedding', 'delivery_charge': 50.0, 'setup_charge': 30.0, }) # Create line: booking 10 chairs line = self.env['event.rental.line'].create({ 'request_id': request.id, 'product_id': self.rental_product.id, 'quantity': 10.0, }) # Verify duration-based pricing: 3 days * $10 per day = $30 per chair unit self.assertEqual(line.price_unit, 30.0, "Line unit price should be rental_price_per_day * duration") self.assertEqual(line.price_subtotal, 300.0, "Subtotal should be quantity * price_unit") # Verify grand total: $300 (subtotal) + $50 (delivery) + $30 (setup) = $380 self.assertEqual(request.amount_total, 380.0, "Grand total is incorrect") # Check availability avail = request.check_availability(start_date, end_date, self.rental_product) self.assertEqual(avail, 50.0, "Before approval, all 50 chairs should be available") # Approve request request.action_approve() # Status should become quotation_sent self.assertEqual(request.status, 'quotation_sent') self.assertTrue(request.sale_order_id, "Sales Order should be generated") self.assertEqual(request.sale_order_id.amount_untaxed, 380.0, "Sales order untaxed total should match rental request total") # Check availability again: since approved request blocks inventory, available should drop by 10 avail_after_approval = request.check_availability(start_date, end_date, self.rental_product) self.assertEqual(avail_after_approval, 40.0, "Approved booking should block inventory") # Confirm Sale Order (simulating payment) request.sale_order_id.action_confirm() # Request status should automatically become confirmed self.assertEqual(request.status, 'confirmed') # Deliver products request.action_deliver() self.assertEqual(request.status, 'delivered') self.assertEqual(request.delivery_status, 'delivered') # Return products request.action_pickup() self.assertEqual(request.status, 'returned') self.assertEqual(request.delivery_status, 'picked_up') def test_02_overbooking_prevention(self): """ Test that overbooking raises a UserError during approval """ start_date = datetime.now() + timedelta(days=1) end_date = start_date + timedelta(days=3) # Request 60 chairs (only 50 are on hand) request = self.env['event.rental.request'].create({ 'partner_id': self.partner.id, 'start_date': start_date, 'end_date': end_date, 'location': 'Venue Hall', }) self.env['event.rental.line'].create({ 'request_id': request.id, 'product_id': self.rental_product.id, 'quantity': 60.0, }) # Approving should raise UserError with self.assertRaises(UserError): request.action_approve()