From fe3c208bf7405a3a715eb448955e0e4c55a0d97c Mon Sep 17 00:00:00 2001 From: sngohodoo Date: Mon, 25 May 2026 15:10:20 +0530 Subject: [PATCH 1/3] [ADD] sale_renting_deposit: initialize module structure Renting items or properties often requires capturing a safety deposit beforehand. This commit initializes the foundational structure for the new sale_renting_deposit module, setting up the environment required to build and integrate deposit tracking directly into the rental application flow. Project: [PSIN]INTERNSHIP ONBOARDING Task: Deposit in Rental App --- sale_renting_deposit/__init__.py | 0 sale_renting_deposit/__manifest__.py | 9 +++++++++ 2 files changed, 9 insertions(+) create mode 100644 sale_renting_deposit/__init__.py create mode 100644 sale_renting_deposit/__manifest__.py diff --git a/sale_renting_deposit/__init__.py b/sale_renting_deposit/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/sale_renting_deposit/__manifest__.py b/sale_renting_deposit/__manifest__.py new file mode 100644 index 00000000000..a28c466ffc2 --- /dev/null +++ b/sale_renting_deposit/__manifest__.py @@ -0,0 +1,9 @@ +{ + "name": "Renting Deposit", + "application": False, + "installable": True, + "author": "sngoh", + "depends": ["base", "sale_renting"], + "license": "LGPL-3", + "category": "Renting, Sale", +} From 608814c974373dd931f7d7e9d4670b7871cf68f4 Mon Sep 17 00:00:00 2001 From: sngohodoo Date: Tue, 26 May 2026 15:14:52 +0530 Subject: [PATCH 2/3] [IMP] sale_renting_deposit: automate deposit tracking on rental orders Managing rental deposits manually often leads to forgotten charges or incorrect totals when order quantities change. This update introduces an automated deposit system to ensure financial accuracy and save time during checkout. Added global settings to define the default deposit product, alongside a toggle on individual products to activate and set specific deposit amounts. The system now automatically adds, updates, or removes deposit lines on the sales order in real-time whenever the associated rental products are added, modified in quantity, or deleted. Project: [PSIN]INTERNSHIP ONBOARDING Task: Deposit in Rental App --- sale_renting_deposit/__init__.py | 1 + sale_renting_deposit/__manifest__.py | 4 ++ sale_renting_deposit/models/__init__.py | 4 ++ .../models/product_template.py | 8 +++ sale_renting_deposit/models/res_company.py | 12 ++++ .../models/res_config_settings.py | 14 +++++ .../models/sale_order_line.py | 60 +++++++++++++++++++ .../views/product_template_view.xml | 16 +++++ .../views/res_config_settings_views.xml | 29 +++++++++ 9 files changed, 148 insertions(+) create mode 100644 sale_renting_deposit/models/__init__.py create mode 100644 sale_renting_deposit/models/product_template.py create mode 100644 sale_renting_deposit/models/res_company.py create mode 100644 sale_renting_deposit/models/res_config_settings.py create mode 100644 sale_renting_deposit/models/sale_order_line.py create mode 100644 sale_renting_deposit/views/product_template_view.xml create mode 100644 sale_renting_deposit/views/res_config_settings_views.xml diff --git a/sale_renting_deposit/__init__.py b/sale_renting_deposit/__init__.py index e69de29bb2d..0650744f6bc 100644 --- a/sale_renting_deposit/__init__.py +++ b/sale_renting_deposit/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sale_renting_deposit/__manifest__.py b/sale_renting_deposit/__manifest__.py index a28c466ffc2..19ce20f658e 100644 --- a/sale_renting_deposit/__manifest__.py +++ b/sale_renting_deposit/__manifest__.py @@ -6,4 +6,8 @@ "depends": ["base", "sale_renting"], "license": "LGPL-3", "category": "Renting, Sale", + "data": [ + "views/res_config_settings_views.xml", + "views/product_template_view.xml", + ], } diff --git a/sale_renting_deposit/models/__init__.py b/sale_renting_deposit/models/__init__.py new file mode 100644 index 00000000000..3e5f6d1c5d6 --- /dev/null +++ b/sale_renting_deposit/models/__init__.py @@ -0,0 +1,4 @@ +from . import res_company +from . import res_config_settings +from . import product_template +from . import sale_order_line diff --git a/sale_renting_deposit/models/product_template.py b/sale_renting_deposit/models/product_template.py new file mode 100644 index 00000000000..5ba4327b441 --- /dev/null +++ b/sale_renting_deposit/models/product_template.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + is_deposit_required = fields.Boolean(default=False) + deposit_amount = fields.Float() diff --git a/sale_renting_deposit/models/res_company.py b/sale_renting_deposit/models/res_company.py new file mode 100644 index 00000000000..d4a36a85d5a --- /dev/null +++ b/sale_renting_deposit/models/res_company.py @@ -0,0 +1,12 @@ +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + deposit_product = fields.Many2one( + comodel_name="product.product", + string="Product", + help="The product is used to add the deposit to the sales order", + domain="[('type', '=', 'service')]", + ) diff --git a/sale_renting_deposit/models/res_config_settings.py b/sale_renting_deposit/models/res_config_settings.py new file mode 100644 index 00000000000..faa713f7fb1 --- /dev/null +++ b/sale_renting_deposit/models/res_config_settings.py @@ -0,0 +1,14 @@ +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + deposit_product = fields.Many2one( + string="Deposit Product", + help="This product will be used to add deposit in the Rental Order.", + comodel_name="product.product", + related="company_id.deposit_product", + readonly=False, + domain=[("type", "=", "service")], + ) diff --git a/sale_renting_deposit/models/sale_order_line.py b/sale_renting_deposit/models/sale_order_line.py new file mode 100644 index 00000000000..34eebb82a6b --- /dev/null +++ b/sale_renting_deposit/models/sale_order_line.py @@ -0,0 +1,60 @@ +from odoo import api, fields, models + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + is_deposit_line = fields.Boolean(default=False) + linked_line_id = fields.Many2one( + "sale.order.line", string="Linked Line", ondelete="cascade" + ) + + @api.model_create_multi + def create(self, vals_list): + lines = super().create(vals_list) + rental_lines = lines.filtered( + lambda l: l.product_id.is_deposit_required and not l.is_deposit_line + ) + deposit_vals = [] + for line in rental_lines: + deposit_vals.append( + { + "order_id": line.order_id.id, + "product_id": line.company_id.deposit_product.id, + "product_uom_qty": 1, + "price_unit": line.product_id.deposit_amount * line.product_uom_qty, + "name": f"This amount is deposit for {line.product_id.name} product", + "is_deposit_line": True, + "linked_line_id": line.id, + } + ) + if deposit_vals: + self.env["sale.order.line"].create(deposit_vals) + return lines + + def write(self, vals): + res = super().write(vals) + if "product_uom_qty" in vals or "product_id" in vals: + for line in self: + deposit = self.env["sale.order.line"].search( + [("linked_line_id", "=", line.id)] + ) + if deposit: + if line.product_id.is_deposit_required: + deposit.write( + { + "price_unit": line.product_id.deposit_amount + * line.product_uom_qty + } + ) + else: + deposit.unlink() + return res + + @api.ondelete(at_uninstall=False) + def _unlink_order_line(self): + deposits = self.env["sale.order.line"].search( + [("linked_line_id", "in", self.ids)] + ) + if deposits: + deposits.unlink() diff --git a/sale_renting_deposit/views/product_template_view.xml b/sale_renting_deposit/views/product_template_view.xml new file mode 100644 index 00000000000..5de4d7f93e1 --- /dev/null +++ b/sale_renting_deposit/views/product_template_view.xml @@ -0,0 +1,16 @@ + + + + + product.template.form.inherit.rental.deposit + product.template + + + + + + + + + + \ No newline at end of file diff --git a/sale_renting_deposit/views/res_config_settings_views.xml b/sale_renting_deposit/views/res_config_settings_views.xml new file mode 100644 index 00000000000..f157472835d --- /dev/null +++ b/sale_renting_deposit/views/res_config_settings_views.xml @@ -0,0 +1,29 @@ + + + + + res.config.settings.view.form.inherit.rental.deposit + res.config.settings + + + +
+
+
+
+
+ + + Settings + res.config.settings + + form + {'module' : 'sale_renting_deposit', 'bin_size': False} + + +
\ No newline at end of file From 07ca0d3359da4a062f82c1b0c6cf1a37e6c8fc38 Mon Sep 17 00:00:00 2001 From: sngohodoo Date: Thu, 28 May 2026 11:57:30 +0530 Subject: [PATCH 3/3] [ADD] website_sale_sale_deposit: enforce rental deposits on eCommerce This auto-installing bridge module brings our rental deposit logic to the eCommerce. - Displays deposit amounts directly on product pages so customers see the full cost upfront. - Locks a non-removable deposit line into the cart at checkout to guarantee mandatory security fees are always captured online. Project: [PSIN]INTERNSHIP ONBOARDING Task: Deposit in Rental App --- website_sale_sale_renting_deposit/__init__.py | 0 .../__manifest__.py | 18 +++++++++++ .../static/src/js/variant_configurator.js | 18 +++++++++++ .../views/template.xml | 30 +++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 website_sale_sale_renting_deposit/__init__.py create mode 100644 website_sale_sale_renting_deposit/__manifest__.py create mode 100644 website_sale_sale_renting_deposit/static/src/js/variant_configurator.js create mode 100644 website_sale_sale_renting_deposit/views/template.xml diff --git a/website_sale_sale_renting_deposit/__init__.py b/website_sale_sale_renting_deposit/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/website_sale_sale_renting_deposit/__manifest__.py b/website_sale_sale_renting_deposit/__manifest__.py new file mode 100644 index 00000000000..42b8dbfad71 --- /dev/null +++ b/website_sale_sale_renting_deposit/__manifest__.py @@ -0,0 +1,18 @@ +{ + "name": "Website_sale/Sale_renting_deposit Bridge", + "application": False, + "installable": True, + "author": "sngoh", + "depends": ["website_sale", "sale_renting_deposit", "website_sale_renting"], + "auto_install": True, + "license": "LGPL-3", + "category": "Sale", + "data": [ + "views/template.xml", + ], + "assets": { + "web.assets_frontend": [ + "website_sale_sale_renting_deposit_bridge/static/src/js/variant_configurator.js" + ], + }, +} diff --git a/website_sale_sale_renting_deposit/static/src/js/variant_configurator.js b/website_sale_sale_renting_deposit/static/src/js/variant_configurator.js new file mode 100644 index 00000000000..36bda5c2001 --- /dev/null +++ b/website_sale_sale_renting_deposit/static/src/js/variant_configurator.js @@ -0,0 +1,18 @@ +document.addEventListener('change', function (ev) { + const input = ev.target; + if (!input.matches('.js_main_product input[name="add_qty"]')) { + return; + } + const productEl = input.closest('.js_product'); + const depositEl = productEl?.querySelector('.o_deposit_wrapper'); + if (!depositEl) { + return; + } + const depositUnit = parseFloat(depositEl.dataset.depositUnit) || 0; + const quantity = parseFloat(input.value) || 0; + const total = (depositUnit * quantity).toFixed(2); + const target = depositEl.querySelector('.o_deposit_amount_value'); + if (target) { + target.textContent = total; + } +}); diff --git a/website_sale_sale_renting_deposit/views/template.xml b/website_sale_sale_renting_deposit/views/template.xml new file mode 100644 index 00000000000..24a93ec252b --- /dev/null +++ b/website_sale_sale_renting_deposit/views/template.xml @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file