diff --git a/deposit_rental_app/__init__.py b/deposit_rental_app/__init__.py
new file mode 100644
index 00000000000..0650744f6bc
--- /dev/null
+++ b/deposit_rental_app/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/deposit_rental_app/__manifest__.py b/deposit_rental_app/__manifest__.py
new file mode 100644
index 00000000000..10d9fea9153
--- /dev/null
+++ b/deposit_rental_app/__manifest__.py
@@ -0,0 +1,21 @@
+{
+ "name": "Deposit Rental App",
+ "description": """
+Adds a deposit to a rental product on the webshop
+ """,
+ "category": "Sales/Sales",
+ "depends": ["account", "sale_renting", "website_sale"],
+ "data": [
+ "views/res_config_settings_views.xml",
+ "views/product_template_views.xml",
+ "views/template.xml",
+ ],
+ "assets": {
+ "web.assets_frontend": [
+ "deposit_rental_app/static/src/js/sale_product_field.js",
+ ]
+ },
+ "application": True,
+ "installable": True,
+ "license": "LGPL-3",
+}
diff --git a/deposit_rental_app/models/__init__.py b/deposit_rental_app/models/__init__.py
new file mode 100644
index 00000000000..90b06a9bf48
--- /dev/null
+++ b/deposit_rental_app/models/__init__.py
@@ -0,0 +1,4 @@
+from . import res_config_settings
+from . import product_template
+from . import sale_order_line
+from . import res_company
diff --git a/deposit_rental_app/models/product_template.py b/deposit_rental_app/models/product_template.py
new file mode 100644
index 00000000000..35299481be4
--- /dev/null
+++ b/deposit_rental_app/models/product_template.py
@@ -0,0 +1,32 @@
+from odoo import fields, models
+
+
+class ProductTemplate(models.Model):
+ _inherit = "product.template"
+
+ required_deposit = fields.Boolean(default=False, string="Required Deposit")
+ amount = fields.Monetary(string="Amount", default="0.0")
+
+ def _get_combination_info(
+ self,
+ combination=False,
+ product_id=False,
+ add_qty=1.0,
+ parent_combination=False,
+ only_template=False,
+ ):
+ combination_info = super()._get_combination_info(
+ combination=combination,
+ product_id=product_id,
+ add_qty=add_qty,
+ parent_combination=parent_combination,
+ only_template=only_template,
+ )
+
+ combination_info.update(
+ {
+ "required_deposit": self.required_deposit,
+ "amount": self.amount,
+ }
+ )
+ return combination_info
diff --git a/deposit_rental_app/models/res_company.py b/deposit_rental_app/models/res_company.py
new file mode 100644
index 00000000000..ecea3d91613
--- /dev/null
+++ b/deposit_rental_app/models/res_company.py
@@ -0,0 +1,14 @@
+from odoo import fields, models
+
+
+class ResCompany(models.Model):
+ _inherit = "res.company"
+
+ # RENTAL company defaults :
+
+ # Deposit product configured in settings
+ deposit_product_id = fields.Many2one(
+ "product.product",
+ string="Deposit",
+ help="This product will be used to add deposit in the Rental Order.",
+ )
diff --git a/deposit_rental_app/models/res_config_settings.py b/deposit_rental_app/models/res_config_settings.py
new file mode 100644
index 00000000000..eba2f8eb648
--- /dev/null
+++ b/deposit_rental_app/models/res_config_settings.py
@@ -0,0 +1,13 @@
+from odoo import fields, models
+
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = "res.config.settings"
+
+ deposit_product_id = fields.Many2one(
+ string="Deposit",
+ help="This product will be used to add deposit in the Rental Order.",
+ comodel_name="product.product",
+ related="company_id.deposit_product_id",
+ readonly=False,
+ )
diff --git a/deposit_rental_app/models/sale_order_line.py b/deposit_rental_app/models/sale_order_line.py
new file mode 100644
index 00000000000..49bd0a92ed8
--- /dev/null
+++ b/deposit_rental_app/models/sale_order_line.py
@@ -0,0 +1,76 @@
+from odoo import api, fields, models
+
+
+class SaleOrderLine(models.Model):
+ _inherit = "sale.order.line"
+
+ is_deposit = fields.Boolean(string="Is Deposit", default=False)
+
+ def _handle_deposit_product(self):
+ for line in self:
+ order = line.order_id
+
+ deposit_product = order.company_id.deposit_product_id
+
+ if not deposit_product:
+ continue
+
+ if line.is_deposit or not line.product_id.required_deposit:
+ continue
+
+ deposit_amount = line.product_id.amount * line.product_uom_qty
+
+ deposit_line = order.order_line.filtered(
+ lambda l: l.is_deposit
+ and l.name == f"Deposit for {line.product_id.name}"
+ )
+
+ if deposit_amount > 0:
+ if deposit_line:
+ deposit_line.with_context(no_update_deposit=True).write(
+ {
+ "price_unit": deposit_amount,
+ "product_uom_qty": 1,
+ }
+ )
+ else:
+ order.order_line.with_context(no_update_deposit=True).create(
+ {
+ "order_id": order.id,
+ "product_id": deposit_product.id,
+ "name": f"Deposit for {line.product_id.name}",
+ "price_unit": deposit_amount,
+ "product_uom_qty": 1,
+ "is_deposit": True,
+ "product_uom": deposit_product.uom_id.id,
+ }
+ )
+ elif deposit_line:
+ deposit_line.unlink()
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ """Create a product line along with its deposit line"""
+ records = super().create(vals_list)
+ if not self.env.context.get("no_update_deposit"):
+ records._handle_deposit_product()
+ return records
+
+ def write(self, vals):
+ """Update the product with its deposit line"""
+ res = super().write(vals)
+ if not self.env.context.get("no_update_deposit"):
+ self._handle_deposit_product()
+ return res
+
+ @api.ondelete(at_uninstall=False)
+ def _unlink_related_deposits(self):
+ """Remove deposit lines when the product is removed from recordset"""
+ for line in self:
+ if line.is_deposit or not line.product_id.required_deposit:
+ continue
+ deposit_lines = line.order_id.order_line.filtered(
+ lambda l: l.is_deposit
+ and l.name == f"Deposit for {line.product_id.name}"
+ )
+ deposit_lines.unlink()
diff --git a/deposit_rental_app/static/src/js/sale_product_field.js b/deposit_rental_app/static/src/js/sale_product_field.js
new file mode 100644
index 00000000000..ce7e01059b2
--- /dev/null
+++ b/deposit_rental_app/static/src/js/sale_product_field.js
@@ -0,0 +1,19 @@
+import { WebsiteSale } from '@website_sale/js/website_sale';
+
+
+const oldOnChangeCombination = WebsiteSale.prototype._onChangeCombination;
+
+WebsiteSale.include({
+ _onChangeCombination(ev, $parent, combination) {
+ oldOnChangeCombination.call(this, ev, $parent, combination);
+
+
+ if (combination && combination.amount !== undefined) {
+ const $amount = $parent.find("#total_amount");
+ const quantity = parseFloat($parent.find("input[name='add_qty']").val()) || 1;
+ console.log("amount and quantity", $amount, quantity);
+ $amount.text(this._priceToStr(combination.amount * quantity));
+ console.log("combitnation", combination, $amount.text());
+ }
+ },
+});
\ No newline at end of file
diff --git a/deposit_rental_app/views/product_template_views.xml b/deposit_rental_app/views/product_template_views.xml
new file mode 100644
index 00000000000..ac2f837dbda
--- /dev/null
+++ b/deposit_rental_app/views/product_template_views.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ product.template.form.view.rental.inherit
+ product.template
+
+
+
+
+
+
+
+
+
+
+ product.template.form.view.rental.extended
+ product.template
+
+ form
+
+
+
diff --git a/deposit_rental_app/views/res_config_settings_views.xml b/deposit_rental_app/views/res_config_settings_views.xml
new file mode 100644
index 00000000000..ad46776d4a1
--- /dev/null
+++ b/deposit_rental_app/views/res_config_settings_views.xml
@@ -0,0 +1,27 @@
+
+
+
+
+ res.config.settings.view.form.inherit.rental
+ res.config.settings
+
+
+
+
+
+
+
+
+
+
+
+
+ Deposit Settings Extended
+ res.config.settings
+
+ form
+ inline
+ {'module' : 'sale_renting', 'bin_size': False}
+
+
+
diff --git a/deposit_rental_app/views/template.xml b/deposit_rental_app/views/template.xml
new file mode 100644
index 00000000000..0f3ad46a39e
--- /dev/null
+++ b/deposit_rental_app/views/template.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+ Deposit Required : $
+
+
+
+
+