Skip to content

[ADD] product_warranty: add warranty config and wizard to add warranty #894

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 1 commit 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
4 changes: 4 additions & 0 deletions product_warranty/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from . import models
from . import wizard
22 changes: 22 additions & 0 deletions product_warranty/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

{
"name": "Warranty Management for Products",
"version": "1.0",
"category": "Sales",
"summary": "Manage product warranties",
"description": """
This module allows you to manage product warranties, including warranty periods and conditions.
""",
"depends": ["sale_management"],
"data": [
"security/ir.model.access.csv",
"views/product_warranty.xml",
"views/product_warranty_config_views.xml",
"views/product_warranty_config_menus.xml",
"views/sale_order_views.xml",
"wizard/product_warranty_wizard.xml",
],
"installable": True,
"license": "LGPL-3",
}
6 changes: 6 additions & 0 deletions product_warranty/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from . import product_warranty
from . import product_warranty_config
from . import sale_order
from . import sale_order_line
9 changes: 9 additions & 0 deletions product_warranty/models/product_warranty.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import fields, models


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

warranty = fields.Boolean(string="Warranty", help="Is warranty available for this product?")
19 changes: 19 additions & 0 deletions product_warranty/models/product_warranty_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import fields, models


class ProductWarrantyConfig(models.Model):
_name = "product.warranty.config"
_description = "Product Warranty Configuration"

name = fields.Char(string="Warranty Name", required=True)
percentage = fields.Float(string="Percentage", help="Warranty percentage")
year = fields.Integer(
string="Warranty Period (Year)", required=True,
help="Warranty period in years"
)
product_id = fields.Many2one(
comodel_name="product.product", string="Product",
help="Warranty product name"
)
28 changes: 28 additions & 0 deletions product_warranty/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models


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

has_warranty_product = fields.Boolean(
string="Has Warranty Product", store=True,
compute='_compute_has_warranty_product'
)

@api.depends('order_line.product_id')
def _compute_has_warranty_product(self):
for order in self:
order.has_warranty_product = any(line.product_id.warranty for line in order.order_line)

def open_add_warranty_wizard(self):
self.ensure_one()
return {
'type': 'ir.actions.act_window',
'name': 'Add Warranty Products',
'res_model': 'product.warranty.wizard',
'view_mode': 'form',
'target': 'new',
'context': {'default_sale_order_id': self.id}
}
32 changes: 32 additions & 0 deletions product_warranty/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import fields, models


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

source_order_line_id = fields.Many2one(
comodel_name="sale.order.line",
string="Source Order Line", ondelete="cascade",
)

def write(self, vals):
res = super().write(vals)
if "price_unit" in vals:
source_lines = self.filtered(lambda l: not l.source_order_line_id)
warranty_lines = self.env["sale.order.line"].search([
("source_order_line_id", "in", source_lines.ids)
])

configs = self.env["product.warranty.config"].search([
("product_id", "in", warranty_lines.mapped("product_id").ids)
]).mapped(lambda c: (c.product_id.id, c))
config_map = dict(configs)

for wl in warranty_lines:
source = wl.source_order_line_id
config = config_map.get(wl.product_id.id)
if source in source_lines and config:
wl.price_unit = source.price_unit * (config.percentage / 100.0)
return res
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
product_warranty.access_product_warranty_config,access_product_warranty_config,product_warranty.model_product_warranty_config,base.group_user,1,1,1,1
product_warranty.access_product_warranty_wizard,access_product_warranty_wizard,product_warranty.model_product_warranty_wizard,base.group_user,1,1,1,1
product_warranty.access_product_warranty_wizard_lines,access_product_warranty_wizard_lines,product_warranty.model_product_warranty_wizard_lines,base.group_user,1,1,1,1
3 changes: 3 additions & 0 deletions product_warranty/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from . import test_product_warranty_wizard
64 changes: 64 additions & 0 deletions product_warranty/tests/test_product_warranty_wizard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo.exceptions import UserError
from odoo.tests.common import TransactionCase


class TestProductWarrantyWizard(TransactionCase):

def setUp(self):
super().setUp()
self.partner = self.env['res.partner'].create({
'name': 'Test Customer'
})
ProductTemplate = self.env['product.template']
warranty_template = ProductTemplate.create({
'name': 'Warranty Product',
'warranty': True,
})
no_warranty_template = ProductTemplate.create({
'name': 'Non-Warranty Product',
'warranty': False,
})
self.warranty_product = warranty_template.product_variant_id
self.no_warranty_product = no_warranty_template.product_variant_id

self.sale_order = self.env['sale.order'].create({
'partner_id': self.partner.id,
})
self.sol_with_warranty = self.env['sale.order.line'].create({
'order_id': self.sale_order.id,
'product_id': self.warranty_product.id,
'product_uom_qty': 1,
'price_unit': 100,
})
self.sol_without_warranty = self.env['sale.order.line'].create({
'order_id': self.sale_order.id,
'product_id': self.no_warranty_product.id,
'product_uom_qty': 1,
'price_unit': 100,
})

def test_default_get_populates_wizard_lines(self):
"""Wizard should only include products with warranty=True"""

wizard = self.env['product.warranty.wizard'].with_context(
default_sale_order_id=self.sale_order.id
).new({})

wizard.default_get(['wizard_line_ids'])
self.assertEqual(len(wizard.wizard_line_ids), 1)

wizard_line = wizard.wizard_line_ids[0]
self.assertEqual(wizard_line.product_id.id, self.warranty_product.id)
self.assertEqual(wizard_line.sale_order_line_id.id, self.sol_with_warranty.id)

def test_action_add_warranty_raises_if_config_missing(self):
"""Should raise UserError if any wizard line has no warranty_config_id"""

wizard = self.env['product.warranty.wizard'].with_context(
default_sale_order_id=self.sale_order.id
).create({})

with self.assertRaises(UserError):
wizard.action_add_warranty()
15 changes: 15 additions & 0 deletions product_warranty/views/product_warranty.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<record id="product_template_warranty_inherit_view_form" model="ir.ui.view">
<field name="name">product.template.inherit.view.form</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view" />
<field name="arch" type="xml">
<xpath expr="//page[@name='sales']//group[@name='extra_info']" position="inside">
<group name="product_warranty" string="Product Warranty">
<field name="warranty"></field>
</group>
</xpath>
</field>
</record>
</odoo>
13 changes: 13 additions & 0 deletions product_warranty/views/product_warranty_config_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>

<odoo>
<menuitem id="product_warranty_menu_root"
name="Warranty"
parent="sale.menu_sale_config"
sequence="40" />
<menuitem id="product_warranty_config_menu"
name="Warranty Configuration"
parent="product_warranty_menu_root"
action="product_warranty_config_action"
sequence="10" />
</odoo>
25 changes: 25 additions & 0 deletions product_warranty/views/product_warranty_config_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="product_warranty_config_action" model="ir.actions.act_window">
<field name="name">Product Warranty Configuration</field>
<field name="res_model">product.warranty.config</field>
<field name="view_mode">list</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Configure the product warranty here.
</p>
</field>
</record>
<record id="product_warranty_view_list" model="ir.ui.view">
<field name="name">product.warranty.config.list</field>
<field name="model">product.warranty.config</field>
<field name="arch" type="xml">
<list editable="bottom">
<field name="name" />
<field name="product_id" />
<field name="percentage" />
<field name="year" />
</list>
</field>
</record>
</odoo>
19 changes: 19 additions & 0 deletions product_warranty/views/sale_order_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="add_warranty_to_sale_order" model="ir.ui.view">
<field name="name">sale.order.warranty.inherit</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form" />
<field name="arch" type="xml">
<div name="so_button_below_order_lines" position="inside">
<button
string="Add Warranty"
type="object"
class="btn-primary"
name="open_add_warranty_wizard"
invisible="not has_warranty_product">
</button>
</div>
</field>
</record>
</odoo>
4 changes: 4 additions & 0 deletions product_warranty/wizard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from . import product_warranty_wizard
from . import product_warranty_wizard_lines
55 changes: 55 additions & 0 deletions product_warranty/wizard/product_warranty_wizard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import _, Command, api, fields, models
from odoo.exceptions import UserError


class ProductWarrantyWizard(models.TransientModel):
_name = "product.warranty.wizard"
_description = "Product Warranty Wizard"

wizard_line_ids = fields.One2many(
comodel_name="product.warranty.wizard.lines",
inverse_name="wizard_id", string="Warranty Lines",
help="Lines for adding warranty products"
)
sale_order_id = fields.Many2one(
comodel_name="sale.order", string="Sale Order",
help="Reference to the sale order for which warranty products are being added"
)

@api.model
def default_get(self, fields):
res = super().default_get(fields)
sale_order = self.env["sale.order"].browse(
self.env.context.get("default_sale_order_id")
)
sale_order_line_id = sale_order.order_line.filtered(
lambda l: l.product_template_id.warranty
)
res["wizard_line_ids"] = [
Command.create({"product_id": line.product_id.id, "sale_order_line_id": line.id})
for line in sale_order_line_id
]
return res

def action_add_warranty(self):
for line in self.wizard_line_ids:
if not line.warranty_config_id:
raise UserError(_("Warranty Configuration is required for all warranty lines."))

warranty_lines = []
for line in self.wizard_line_ids:
base_price = line.sale_order_line_id.price_unit
warranty_price = base_price * (line.warranty_config_id.percentage / 100.0)
warranty_lines.append({
"order_id": self.sale_order_id.id,
"product_id": line.warranty_config_id.product_id.id,
"name": "End Date:" + line.end_date,
"price_unit": warranty_price,
"product_uom_qty": 1.0,
"source_order_line_id": line.sale_order_line_id.id,
"sequence": line.sale_order_line_id.sequence,
})
if warranty_lines:
self.env["sale.order.line"].create(warranty_lines)
24 changes: 24 additions & 0 deletions product_warranty/wizard/product_warranty_wizard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="product_warranty_wizard_view_list" model="ir.ui.view">
<field name="name">product.warranty.wizard.form</field>
<field name="model">product.warranty.wizard</field>
<field name="arch" type="xml">
<form string="Add Warranty">
<field name="wizard_line_ids">
<list editable="bottom" create="0">
<field name="sale_order_line_id" column_invisible="1" />
<field name="product_id"/>
<field name="warranty_config_id"/>
<field name="end_date"/>
</list>
</field>
<footer>
<button string="Add Warranty" type="object" name="action_add_warranty"
class="btn-primary"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
</odoo>
Loading