diff --git a/sale_renting_deposit/__init__.py b/sale_renting_deposit/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ 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 new file mode 100644 index 00000000000..19ce20f658e --- /dev/null +++ b/sale_renting_deposit/__manifest__.py @@ -0,0 +1,13 @@ +{ + "name": "Renting Deposit", + "application": False, + "installable": True, + "author": "sngoh", + "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 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