Skip to content

Commit 8b6372c

Browse files
committed
[ADD] rental,sale: implement mandatory rental deposit system
This commit introduces a mandatory deposit system for rental products to secure rentals against potential damages or late returns. The implementation includes: 1. System configuration to set a deposit product that will be used across all rental deposits 2. Product-level settings to enable deposits and specify amounts per unit 3. Automatic addition of deposit lines to rental orders 4. Frontend display of deposit information for customer transparency The deposit system works by: - Allowing administrators to configure a deposit product in Settings - Enabling per-product deposit requirements with customizable amounts - Automatically adding deposit lines when rental products are ordered - Calculating deposit totals based on product quantity - Maintaining clear relationships between rentals and their deposits This feature was implemented to reduce financial risk for rental businesses while maintaining a smooth customer experience. The system ensures deposits are always collected when required without manual intervention from sales staff. task- Add rental deposit functionality
1 parent 4c06dbd commit 8b6372c

11 files changed

+191
-0
lines changed

rental_deposit/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models

rental_deposit/__manifest__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
'name': 'Rental Deposit',
3+
'version': '1.0',
4+
'depends': ['sale_renting', 'website_sale'],
5+
'category': 'Sales',
6+
'Summary': 'Add deposit logic to rental products on sale order and webshop',
7+
'data': [
8+
'views/res_config_settings_views.xml',
9+
'views/product_template_views.xml',
10+
'views/product_webiste_template_views.xml',
11+
],
12+
'assets': {
13+
'web.assets_frontend': {
14+
'deposit_rental/static/src/website_deposit_amount.js',
15+
}
16+
},
17+
}

rental_deposit/models/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from . import res_config_settings
2+
from . import product_template
3+
from . import sale_order
4+
from . import sale_order_line
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from odoo import api, fields, models
2+
3+
class ProductTemplate(models.Model):
4+
_inherit = 'product.template'
5+
6+
deposit_require = fields.Boolean(string='Require Deposit')
7+
deposit_amount = fields.Monetary(string='Deposit Amount')
8+
currency_id = fields.Many2one(
9+
'res.currency', string='Currency',
10+
required=True,
11+
default=lambda self: self.env.company.currency_id,
12+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from odoo import api, fields, models
2+
3+
class ResConfigSettings(models.TransientModel):
4+
_inherit = 'res.config.settings'
5+
6+
deposit_product = fields.Many2one('product.product', string='Deposit Product')
7+
8+
def set_values(self):
9+
super().set_values()
10+
# Save the product ID or 0 if no product is selected
11+
self.env['ir.config_parameter'].sudo().set_param(
12+
'rental_deposit.deposit_product',
13+
self.deposit_product.id if self.deposit_product else 0
14+
)
15+
16+
@api.model
17+
def get_values(self):
18+
res = super().get_values()
19+
# Read the deposit product ID as string
20+
config_value = self.env['ir.config_parameter'].sudo().get_param(
21+
'rental_deposit.deposit_product', default=0
22+
)
23+
# Convert to int and browse the product; if empty or invalid, return empty recordset
24+
product = self.env['product.product'].browse(int(config_value)) if config_value else self.env['product.product']
25+
res.update(deposit_product=product)
26+
return res
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from odoo import _, api, fields, models
2+
from odoo.exceptions import UserError
3+
4+
class SaleOrder(models.Model):
5+
_inherit = 'sale.order'
6+
7+
def _add_deposit_product(self, order_line):
8+
config_product = int(self.env['ir.config_parameter'].sudo().get_param('rental_deposit.deposit_product', default=0))
9+
if not config_product:
10+
raise UserError(_("No deposit product configured. Please configure it in Rental Settings."))
11+
12+
deposit_product = self.env['product.product'].browse(config_product)
13+
if not deposit_product.exists():
14+
raise UserError(_("The configured deposit product does not exist."))
15+
16+
existing_line = self.order_line.filtered(lambda l: l.linked_line_id.id == order_line.id)
17+
amount = order_line.product_id.deposit_amount * order_line.product_uom_qty
18+
19+
if existing_line:
20+
existing_line.write({
21+
'product_uom_qty': order_line.product_uom_qty,
22+
'price_unit': order_line.product_id.deposit_amount,
23+
'name': f"Deposit for {order_line.product_id.display_name}"
24+
})
25+
else:
26+
self.env['sale.order.line'].create({
27+
'order_id': self.id,
28+
'product_id': deposit_product.id,
29+
'product_uom_qty': order_line.product_uom_qty,
30+
'product_uom': deposit_product.uom_id.id,
31+
'price_unit': order_line.product_id.deposit_amount,
32+
'linked_line_id': order_line.id,
33+
'name': f"Deposit for {order_line.product_id.display_name}",
34+
})
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from odoo import api, fields, models
2+
3+
class SaleOrderLine(models.Model):
4+
_inherit = 'sale.order.line'
5+
6+
linked_line_id = fields.Many2one('sale.order.line', string='Linked Product Line')
7+
8+
@api.model_create_multi
9+
def create(self, vals):
10+
lines = super().create(vals)
11+
for line in lines:
12+
deposit_product_id = int(self.env['ir.config_parameter'].sudo().get_param('rental_deposit.deposit_product', default=0))
13+
if line.product_id.id != deposit_product_id and line.product_id.rent_ok and line.product_id.deposit_require:
14+
line.order_id._add_deposit_product(line)
15+
return lines
16+
17+
def write(self, vals):
18+
res = super().write(vals)
19+
for line in self:
20+
if 'product_uom_qty' in vals and line.product_id.deposit_require:
21+
line.order_id._add_deposit_product(line)
22+
return res
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import publicWidget from "@web/legacy/js/public/public_widget";
2+
3+
publicWidget.registry.DepositRental = publicWidget.Widget.extend({
4+
selector: "#product_detail",
5+
events: {
6+
'change input[name="add_qty"]': '_updateDepositAmount',
7+
},
8+
start: function () {
9+
this._super.apply(this, arguments);
10+
if ($("#deposit_amount").length && $("#deposit_amount").data("base-amount") > 0) {
11+
this._updateDepositAmount();
12+
} else {
13+
this.$el.off('change input[name="add_qty"]');
14+
}
15+
},
16+
_updateDepositAmount: function () {
17+
var qty = parseFloat($("#o_wsale_cta_wrapper").find("input[name='add_qty']").val()) || 1;
18+
var depositAmount = parseFloat($("#deposit_amount").data("base-amount")) || 0;
19+
$("#deposit_amount").text(depositAmount * qty);
20+
}
21+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<odoo>
2+
<record id="product_template_form_view_deposit_rental" model="ir.ui.view">
3+
<field name="name">product.template.form.deposit.rental</field>
4+
<field name="model">product.template</field>
5+
<field name="inherit_id" ref="product.product_template_only_form_view" />
6+
<field name="arch" type="xml">
7+
<xpath expr="//field[@name='extra_daily']" position="after">
8+
<group string="Rental Deposit">
9+
<field name="deposit_require"/>
10+
<field name="deposit_amount" invisible="deposit_require == False"/>
11+
</group>
12+
</xpath>
13+
</field>
14+
</record>
15+
</odoo>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0"?>
2+
<odoo>
3+
<template id="deposit_rental_website_view" inherit_id="website_sale.product">
4+
<xpath expr="//div[@id='product_option_block']" position="after">
5+
<div t-if="product.deposit_require">
6+
<strong>Deposit Required:</strong>
7+
<span id="deposit_amount" t-esc="product.deposit_amount"/>
8+
<t t-esc="product.currency_id.symbol"/>
9+
</div>
10+
</xpath>
11+
</template>
12+
13+
<template id="deposit_amount_cart_line" inherit_id="website_sale.cart_lines">
14+
<xpath expr="//div[@name='o_wsale_cart_line_button_container']" position="after">
15+
<t t-if="line.linked_line_id">
16+
<p>
17+
Deposit for <t t-esc="line.linked_line_id.product_id.display_name"/>:
18+
<t t-esc="line.currency_id.symbol"/><t t-esc="line.price_unit * line.product_uom_qty"/>
19+
</p>
20+
</t>
21+
</xpath>
22+
</template>
23+
</odoo>

0 commit comments

Comments
 (0)