diff --git a/rental_deposit/__init__.py b/rental_deposit/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/rental_deposit/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/rental_deposit/__manifest__.py b/rental_deposit/__manifest__.py new file mode 100644 index 00000000000..119f6714ddb --- /dev/null +++ b/rental_deposit/__manifest__.py @@ -0,0 +1,18 @@ +{ + 'author': 'Aditi (adpaw)', + 'name': 'Deposit Rental App', + 'license': 'LGPL-3', + 'depends': ['sale_renting', 'website_sale'], + 'data': [ + 'views/res_config_settings_view.xml', + 'views/product_template_view.xml', + 'views/template_view.xml' + ], + 'assets': { + 'web.assets_frontend': [ + 'rental_deposit/static/src/deposit_amount.js', + ], + }, + 'installable': True, + 'auto_install': True, +} diff --git a/rental_deposit/models/__init__.py b/rental_deposit/models/__init__.py new file mode 100644 index 00000000000..90b06a9bf48 --- /dev/null +++ b/rental_deposit/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/rental_deposit/models/product_template.py b/rental_deposit/models/product_template.py new file mode 100644 index 00000000000..76d32810754 --- /dev/null +++ b/rental_deposit/models/product_template.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class ProductTemplate(models.Model): + _inherit = 'product.template' + + requires_deposit = fields.Boolean(string="Requires Deposit") + deposit_amount = fields.Float() diff --git a/rental_deposit/models/res_company.py b/rental_deposit/models/res_company.py new file mode 100644 index 00000000000..20d11bd60a1 --- /dev/null +++ b/rental_deposit/models/res_company.py @@ -0,0 +1,7 @@ +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + deposit_product = fields.Many2one("product.product") diff --git a/rental_deposit/models/res_config_settings.py b/rental_deposit/models/res_config_settings.py new file mode 100644 index 00000000000..35ef96e4b71 --- /dev/null +++ b/rental_deposit/models/res_config_settings.py @@ -0,0 +1,12 @@ +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + deposit_product = fields.Many2one( + "product.product", + related="company_id.deposit_product", + string="Deposit", + readonly=False + ) diff --git a/rental_deposit/models/sale_order_line.py b/rental_deposit/models/sale_order_line.py new file mode 100644 index 00000000000..32eb8c8a121 --- /dev/null +++ b/rental_deposit/models/sale_order_line.py @@ -0,0 +1,103 @@ +from odoo import api, fields, models +from odoo.exceptions import UserError, MissingError +from odoo.tools import _ + + +class SaleOrderLine(models.Model): + _inherit = 'sale.order.line' + + is_deposit_line = fields.Boolean(default=False) + + @api.model_create_multi + def create(self, vals_list): + lines = super().create(vals_list) + rental_lines = lines.filtered( + lambda l: l.product_id.rent_ok + and l.product_id.requires_deposit + and l.product_id.deposit_amount > 0 + ) + if not rental_lines: + return lines + company_map = {} + for line in rental_lines: + company = line.company_id + if company not in company_map: + if not company.deposit_product: + raise UserError(_("Please set deposit product in settings.")) + company_map[company] = company.deposit_product + deposit_vals = [] + for line in rental_lines: + deposit_product = company_map[line.company_id] + deposit_vals.append({ + 'order_id': line.order_id.id, + 'product_id': deposit_product.id, + 'product_uom_qty': line.product_uom_qty, + 'price_unit': line.product_id.deposit_amount, + 'name': f"This amount is deposit for {line.product_id.name} product", + 'is_deposit_line': True, + }) + self.create(deposit_vals) + return lines + + @api.ondelete(at_uninstall=False) + def _unlink_deposit_fee(self): + if not self.env.context.get("bypass_deposit_protection_for_delete"): + for record in self: + if record.is_deposit_line: + raise UserError(_("You can't delete a Deposit Product line directly.")) + rental_lines = self.filtered( + lambda l: l.product_id.rent_ok + and l.product_id.requires_deposit + and l.product_id.deposit_amount > 0 + and not l.is_deposit_line + ) + if not rental_lines: + return + deposit_lines = self.search([ + ('order_id', 'in', rental_lines.mapped('order_id').ids), + ('is_deposit_line', '=', True), + ]) + deposit_map = {} + for line in deposit_lines: + deposit_map[line.name] = line + for line in rental_lines: + deposit_line = deposit_map.get( + f"This amount is deposit for {line.product_id.name} product" + ) + if not deposit_line: + raise MissingError(_("Deposit fee is not present.")) + deposit_line.with_context(bypass_deposit_protection_for_delete=True).unlink() + + def write(self, vals): + if not self.env.context.get("bypass_deposit_protection_for_write"): + for record in self: + if record.is_deposit_line: + raise UserError(_("You can't edit Deposit Product line directly.")) + res = super().write(vals) + if 'product_uom_qty' not in vals: + return res + rental_lines = self.filtered( + lambda l: l.product_id.rent_ok + and l.product_id.requires_deposit + and l.product_id.deposit_amount > 0 + ) + if not rental_lines: + return res + deposit_lines = self.search([ + ('order_id', 'in', rental_lines.mapped('order_id').ids), + ('is_deposit_line', '=', True), + ]) + deposit_map = {} + for line in deposit_lines: + deposit_map[line.name] = line + for line in rental_lines: + deposit_line = deposit_map.get( + f"This amount is deposit for {line.product_id.name} product" + ) + if not deposit_line: + raise MissingError(_("Deposit product line not found.")) + deposit_line.with_context(bypass_deposit_protection_for_write=True).write({ + 'product_uom_qty': line.product_uom_qty, + 'price_unit': line.product_id.deposit_amount, + }) + return res diff --git a/rental_deposit/static/src/deposit_amount.js b/rental_deposit/static/src/deposit_amount.js new file mode 100644 index 00000000000..15a51b0a716 --- /dev/null +++ b/rental_deposit/static/src/deposit_amount.js @@ -0,0 +1,22 @@ +import publicWidget from "@web/legacy/js/public/public_widget"; + +publicWidget.registry.DepositRental = publicWidget.Widget.extend({ + selector: "#product_detail", + events: { + 'change input[name="add_qty"]': '_updateDepositAmount', + }, + start: function () { + this._super.apply(this, arguments); + if ($("#deposit_amount").length) { + this._updateDepositAmount(); + } + else { + this.$el.off('change input[name="add_qty"]'); + } + }, + _updateDepositAmount: function () { + var qty = parseFloat($("#o_wsale_cta_wrapper").find("input[name='add_qty']").val()); + var depositAmount = parseFloat($("#deposit_amount").attr("data-base-amount")) || 0; + $("#deposit_amount").text(depositAmount * qty); + } +}) diff --git a/rental_deposit/views/product_template_view.xml b/rental_deposit/views/product_template_view.xml new file mode 100644 index 00000000000..2b699cf135c --- /dev/null +++ b/rental_deposit/views/product_template_view.xml @@ -0,0 +1,13 @@ + + + product.template.view.form.inherit.rental.deposit + product.template + + + + + + + + + diff --git a/rental_deposit/views/res_config_settings_view.xml b/rental_deposit/views/res_config_settings_view.xml new file mode 100644 index 00000000000..0688dc17ab3 --- /dev/null +++ b/rental_deposit/views/res_config_settings_view.xml @@ -0,0 +1,13 @@ + + + res.config.settings.view.form.inherit.deposit + res.config.settings + + + + + + + diff --git a/rental_deposit/views/template_view.xml b/rental_deposit/views/template_view.xml new file mode 100644 index 00000000000..a7685c71b48 --- /dev/null +++ b/rental_deposit/views/template_view.xml @@ -0,0 +1,12 @@ + + +