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 @@ + + + + +