Skip to content

[ADD] product_warranty: add warranty management feature and validations #745

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: 18.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions product_warranty/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizard
18 changes: 18 additions & 0 deletions product_warranty/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
'name': 'Product Warranty',
'version': '1.0',
'depends': ['sale_management'],
'description': """
This module adds warranty availabe feature for products.
""",
'data': [
'security/ir.model.access.csv',
'wizard/add_warranty_wizard_views.xml',
'views/product_template_views.xml',
'views/warranty_config_views.xml',
'views/warranty_config_menu.xml',
'views/sale_order_views.xml',
],
'installable': True,
'license': 'LGPL-3'
}
4 changes: 4 additions & 0 deletions product_warranty/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import product_template
from . import warranty_config
from . import sale_order
from . import sale_order_line
7 changes: 7 additions & 0 deletions product_warranty/models/product_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from odoo import models, fields


class ProductTemplate(models.Model):
_inherit = 'product.template'

is_warranty_available = fields.Boolean(string="Is Warranty Available")
16 changes: 16 additions & 0 deletions product_warranty/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from odoo import models


class SaleOrder(models.Model):
_inherit = "sale.order"

def open_warranty_wizard(self):

return {
'type': 'ir.actions.act_window',
'name': 'Add Warranty',
'res_model': 'add.warranty.wizard',
'view_mode': 'form',
'target': 'new',
'context': {'default_sale_order_id': self.id},
}
39 changes: 39 additions & 0 deletions product_warranty/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from odoo import api, fields, models
from odoo.exceptions import ValidationError


class SaleOrderLine(models.Model):
_inherit = "sale.order.line"

has_warranty = fields.Boolean(string="Has Warranty")
order_line_linked_to_warranty = fields.Many2one(comodel_name="sale.order.line", copy=False, string="Warranty Product", ondelete="cascade")
is_warranty = fields.Boolean('Is Warranty')

@api.constrains('product_uom_qty')
def _check_warranty_qty_limit(self):
for line in self:
if line.is_warranty and line.order_line_linked_to_warranty:
linked_line = line.order_line_linked_to_warranty
if line.product_uom_qty > linked_line.product_uom_qty:
raise ValidationError(
f"The warranty quantity ({line.product_uom_qty}) cannot be more than the linked product quantity ({linked_line.product_uom_qty})."
)

elif not line.is_warranty:
warranty_line = self.search([
('order_line_linked_to_warranty', '=', line.id),
('is_warranty', '=', True)
], limit=1)

if warranty_line:
if line.product_uom_qty < warranty_line.product_uom_qty:
raise ValidationError(
f"The quantity of the product ({line.product_uom_qty}) cannot be less than the linked warranty quantity ({warranty_line.product_uom_qty})."
)

@api.ondelete(at_uninstall=False)
def _unlink_except_confirmed(self):
super()._unlink_except_confirmed()
for line in self:
if line.is_warranty:
line.order_line_linked_to_warranty.write({'has_warranty': False})
11 changes: 11 additions & 0 deletions product_warranty/models/warranty_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from odoo import models, fields


class WarrantyConfig(models.Model):
_name = 'warranty.config'
_description = 'Warranty Configuration'

name = fields.Char(string="Name")
product = fields.Many2one('product.product', string="Warranty Product")
period = fields.Float(string='Period (in years)', default=1)
percentage = fields.Float(string="Percentage (%)")
4 changes: 4 additions & 0 deletions product_warranty/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_warranty_config,access.warranty.config,model_warranty_config,base.group_user,1,1,1,1
access_add_warranty_wizard,access.add.warranty.wizard,model_add_warranty_wizard,base.group_user,1,1,1,1
access_add_warranty_lines_wizard,access.add.warranty.lines.wizard,model_add_warranty_lines_wizard,base.group_user,1,1,1,1
13 changes: 13 additions & 0 deletions product_warranty/views/product_template_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_product_form_inherit_warranty" model="ir.ui.view">
<field name="name">product.template.form.warranty.inherit</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_only_form_view"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='extra_info']" position="inside">
<field name="is_warranty_available"/>
</xpath>
</field>
</record>
</odoo>
16 changes: 16 additions & 0 deletions product_warranty/views/sale_order_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<odoo>
<record id="sale_order_list_view_add_warranty" model="ir.ui.view">
<field name="name">sale.order.list.view.inherit.add.warranty</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form" />
<field name="arch" type="xml">
<xpath expr="//button[@name='action_open_discount_wizard']" position="before">
<button name="open_warranty_wizard"
type="object"
string="Add warranty"
class="btn btn-secondary"/>
</xpath>
</field>
</record>
</odoo>
7 changes: 7 additions & 0 deletions product_warranty/views/warranty_config_menu.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<menuitem id="menu_warranty_config"
name="Warranty Configuration"
parent="sale.menu_sale_config"
action="action_warranty_config"/>
</odoo>
21 changes: 21 additions & 0 deletions product_warranty/views/warranty_config_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_warranty_config_list" model="ir.ui.view">
<field name="name">warranty.config.list</field>
<field name="model">warranty.config</field>
<field name="arch" type="xml">
<list string="Warranty Configurations" editable="bottom">
<field name="name"/>
<field name="product"/>
<field name="period"/>
<field name="percentage"/>
</list>
</field>
</record>

<record id="action_warranty_config" model="ir.actions.act_window">
<field name="name">Warranty Configuration</field>
<field name="res_model">warranty.config</field>
<field name="view_mode">list</field>
</record>
</odoo>
1 change: 1 addition & 0 deletions product_warranty/wizard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import warranty_wizard
30 changes: 30 additions & 0 deletions product_warranty/wizard/add_warranty_wizard_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="add_warranty_wizard_form" model="ir.ui.view">
<field name="name">add.warranty.wizard.form</field>
<field name="model">add.warranty.wizard</field>
<field name="arch" type="xml">
<form>
<field name="wizard_line_ids">
<list editable="bottom" create="false" delete="false">
<field name="sale_order_line_id" column_invisible="1"/>
<field name="product_id" readonly="1"/>
<field name="warranty_name"/>
<field name="end_date"/>
</list>
</field>
<footer>
<button name="action_add" string="Add Warranty" type="object" class="btn-primary"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>

<record id="add_warranty_wizard_action" model="ir.actions.act_window">
<field name="name">Add Warranty</field>
<field name="res_model">add.warranty.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>
68 changes: 68 additions & 0 deletions product_warranty/wizard/warranty_wizard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models


class AddWarrantyLinesWizard(models.TransientModel):
_name = "add.warranty.lines.wizard"
_description = "Warranty Line Wizard"

wizard_id = fields.Many2one('add.warranty.wizard', string="Wizard Reference", ondelete="cascade")
product_id = fields.Many2one(comodel_name="product.product", string="Product")
sale_order_line_id = fields.Many2one(comodel_name="sale.order.line", string="Sale Order Line")
warranty_name = fields.Many2one(comodel_name="warranty.config", string="Warranty Configuration")
end_date = fields.Date(readonly=True, string="End Date", compute="_compute_end_date")

@api.depends('warranty_name')
def _compute_end_date(self):
for record in self:
if record.warranty_name:
record.end_date = fields.Date.today() + relativedelta(years=record.warranty_name.period)
else:
record.end_date = False


class AddWarrantyWizard(models.TransientModel):
_name = "add.warranty.wizard"
_description = "Add Warranty Wizard"

wizard_line_ids = fields.One2many(
comodel_name='add.warranty.lines.wizard',
inverse_name='wizard_id',
string="Warranty Lines"
)

def default_get(self, fields_list):
res = super().default_get(fields_list)

sale_order = self.env['sale.order'].browse(self.env.context.get("default_sale_order_id"))

sale_order_lines = sale_order.order_line.filtered(
lambda line: line.product_id.is_warranty_available and not line.has_warranty
)

res.update({
'wizard_line_ids': [(0, 0, {
'sale_order_line_id': line.id,
'product_id': line.product_id.id,
}) for line in sale_order_lines]
})
return res

def action_add(self):
sale_order_line = self.env['sale.order.line']
warranty_lines = self.wizard_line_ids

for line in warranty_lines:
if line.warranty_name:
sale_order_line.create({
'order_id': line.sale_order_line_id.order_id.id,
'product_id': line.warranty_name.product.id,
'product_uom_qty': 1,
'price_unit': line.warranty_name.percentage / 100 * line.sale_order_line_id.price_unit,
'name': f"{line.sale_order_line_id.product_id.name} Warranty, End Date: {line.end_date}",
'order_line_linked_to_warranty': line.sale_order_line_id.id,
'is_warranty': True,
})

line.sale_order_line_id.write({'has_warranty': True})
return True