Skip to content

[ADD] new_product_type: Added sub-products to a particular product #833

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 new_producttype/__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 new_producttype/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
'name': "New Product Type",
'category': '',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shoud not be empty.you can either remove it.

'version': '0.1',
'depends': ['sale'],
'sequence': 1,
'application': True,
'installable': True,
'data': [
"security/ir.model.access.csv",
"report/sub_product_report.xml",
"wizard/sub_product_kit_wizard_view.xml",
"views/product_views.xml",
"views/sale_order_view.xml",
"views/sub_product_customer_preview.xml",
],
'license': 'AGPL-3'
}
3 changes: 3 additions & 0 deletions new_producttype/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import product_template
from . import sale_order_line
from . import sale_order
25 changes: 25 additions & 0 deletions new_producttype/models/product_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from odoo import fields, models, api
from odoo.exceptions import ValidationError


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

is_kit = fields.Boolean(default=False, string='Is kit')
sub_products_ids = fields.Many2many('product.product', string='Sub products')

@api.constrains("sub_products_ids")
def check_sub_product_not_itself(self):
for record in self:
if record.id in record.sub_products_ids.mapped("product_tmpl_id.id"):
raise ValidationError(
"A product cannot be added as its own sub-product"
)

@api.constrains("sub_products_ids", "is_kit")
def check_sub_product(self):
for record in self:
if record.is_kit and not record.sub_products_ids:
raise ValidationError(
"A kit product must have at least one sub-product"
)
7 changes: 7 additions & 0 deletions new_producttype/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from odoo import models, fields


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

print_report = fields.Boolean(string="Print in Report")
23 changes: 23 additions & 0 deletions new_producttype/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from odoo import fields, models


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

is_kit = fields.Boolean(string='Is kit', related='product_id.is_kit')
main_product_line_id = fields.Many2one("sale.order.line", ondelete="cascade")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow the same quote in the file. if you are using ' then you have to apply it to all fields.

sub_products_ids = fields.Many2many(
"product.product",
related="product_template_id.sub_products_ids",
string="Sub Products",
)
last_price = fields.Float()

def action_open_kit_wizard(self):
return {
'type': 'ir.actions.act_window',
'name': 'sub.product.kit.wizard.view',
'res_model': 'sub.product.kit.wizard',
'view_mode': 'form',
'target': 'new',
}
8 changes: 8 additions & 0 deletions new_producttype/report/sub_product_report.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="report_sub_product_template" inherit_id="sale.report_saleorder_document">
<xpath expr="//tbody[@class='sale_tbody']/t/tr" position="attributes">
<attribute name="t-if">(doc.print_report) or (not line.main_product_line_id)</attribute>
</xpath>
</template>
</odoo>
3 changes: 3 additions & 0 deletions new_producttype/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sub_product_kit_wizard,access_sub_product_kit_wizard,model_sub_product_kit_wizard,base.group_user,1,1,1,1
access_sub_product_line_kit_wizard,access_sub_product_line_kit_wizard,model_sub_product_line_kit_wizard,base.group_user,1,1,1,1
14 changes: 14 additions & 0 deletions new_producttype/views/product_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="product_template_form_view_inherit" model="ir.ui.view">
<field name="name">product.template.form.view.inherit</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//group[@name='group_general']" position="inside">
<field name="is_kit"/>
<field name="sub_products_ids" widget="many2many_tags" invisible="is_kit == False"/>
</xpath>
</field>
</record>
</odoo>
35 changes: 35 additions & 0 deletions new_producttype/views/sale_order_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_order_form_inherit_sub_product" model="ir.ui.view">
<field name="name">view.order.form.inherit.sub.product</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//list//field[@name='product_template_id']" position="after">
<button name="action_open_kit_wizard"
type="object"
string="Add Sub-Products"
invisible="not is_kit or state=='sale' or main_product_line_id"
/>
</xpath>
<xpath expr="//field[@name='order_line']/list" position="attributes">
<attribute name="decoration-warning">main_product_line_id</attribute>
</xpath>
<xpath expr="//list//field[@name='product_template_id']" position="attributes">
<attribute name="readonly">main_product_line_id</attribute>
</xpath>
<xpath expr="//list//field[@name='product_uom_qty']" position="attributes">
<attribute name="readonly">main_product_line_id</attribute>
</xpath>
<xpath expr="//list//field[@name='price_unit']" position="attributes">
<attribute name="readonly">main_product_line_id</attribute>
</xpath>
<xpath expr="//list//field[@name='tax_id']" position="attributes">
<attribute name="readonly">main_product_line_id</attribute>
</xpath>
<xpath expr="//group//field[@name='payment_term_id']" position="after">
<field name="print_report"/>
</xpath>
</field>
</record>
</odoo>
8 changes: 8 additions & 0 deletions new_producttype/views/sub_product_customer_preview.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="sale_portal_templates_report_inherited" inherit_id="sale.sale_order_portal_content">
<xpath expr="//table[@id='sales_order_table']/tbody/t/tr" position="attributes">
<attribute name="t-if">(sale_order.print_report) or (not line.main_product_line_id)</attribute>
</xpath>
</template>
</odoo>
2 changes: 2 additions & 0 deletions new_producttype/wizard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import sub_product_kit_wizard
from . import sub_product_line_kit_wizard
76 changes: 76 additions & 0 deletions new_producttype/wizard/sub_product_kit_wizard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from odoo import fields, models, api, Command


class SubProductKitWizard(models.TransientModel):
_name = 'sub.product.kit.wizard'

sale_order_line_id = fields.Many2one('sale.order.line', required=True)
sub_products_ids = fields.One2many('sub.product.line.kit.wizard', 'sub_products_line_id')

@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
sale_order_line_id = self._context.get("active_id")
if sale_order_line_id:
sale_order_line = self.env["sale.order.line"].browse(sale_order_line_id)
existing_lines = self.env["sale.order.line"].search(
[("main_product_line_id", "=", sale_order_line_id)]
)
line_values = []
if existing_lines:
for sub_product in existing_lines:
curr_product = self.env["sub.product.line.kit.wizard"].create(
{
"product_id": sub_product.product_id.id,
"price": sub_product.price_unit,
"quantity": sub_product.product_uom_qty,
}
)
line_values.append(Command.link(curr_product.id))
else:
for sub_product in sale_order_line.product_id.sub_products_ids:
curr_product = self.env["sub.product.line.kit.wizard"].create(
{
"product_id": sub_product.id,
"price": sub_product.list_price,
"quantity": 1,
}
)
line_values.append(Command.link(curr_product.id))
res["sub_products_ids"] = line_values
res["sale_order_line_id"] = sale_order_line_id
Comment on lines +15 to +41

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sale_order_line = self.env["sale.order.line"].browse(sale_order_line_id)
existing_lines = self.env["sale.order.line"].search(
[("main_product_line_id", "=", sale_order_line_id)]
)
line_values = []
if existing_lines:
for sub_product in existing_lines:
curr_product = self.env["sub.product.line.kit.wizard"].create(
{
"product_id": sub_product.product_id.id,
"price": sub_product.price_unit,
"quantity": sub_product.product_uom_qty,
}
)
line_values.append(Command.link(curr_product.id))
else:
for sub_product in sale_order_line.product_id.sub_products_ids:
curr_product = self.env["sub.product.line.kit.wizard"].create(
{
"product_id": sub_product.id,
"price": sub_product.list_price,
"quantity": 1,
}
)
line_values.append(Command.link(curr_product.id))
res["sub_products_ids"] = line_values
res["sale_order_line_id"] = sale_order_line_id
sale_order_line = self.env["sale.order.line"].browse(sale_order_line_id)
existing_lines = self.env["sale.order.line"].search([
("main_product_line_id", "=", sale_order_line_id)
])
sub_product_line_wizard = self.env["sub.product.line.kit.wizard"]
line_values = []
if existing_lines:
vals_list = [
{
"product_id": line.product_id.id,
"price": line.price_unit,
"quantity": line.product_uom_qty,
}
for line in existing_lines
]
else:
vals_list = [
{
"product_id": sub_product.id,
"price": sub_product.list_price,
"quantity": 1,
}
for sub_product in sale_order_line.product_id.sub_products_ids
]
if vals_list:
curr_products = sub_product_line_wizard.create(vals_list)
line_values = [Command.link(product.id) for product in curr_products]
res.update({
"sub_products_ids": line_values,
"sale_order_line_id": sale_order_line_id,
})

return res

def action_confirm(self):
total_of_sub_product_price = 0
for rec in self.sub_products_ids:
existing_lines = self.env["sale.order.line"].search(
[
("main_product_line_id", "=", self.sale_order_line_id.id),
("product_id", "=", rec.product_id.id),
("order_id", "=", self.sale_order_line_id.order_id.id)
],
limit=1,
Comment on lines +47 to +53

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use a search outside of the loop.

)
if existing_lines:
existing_lines.product_uom_qty = rec.quantity
existing_lines.last_price = rec.price
existing_lines.price_unit = 0
Comment on lines +56 to +58

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should make a database call.You can use the code below to reduce database calls.

Suggested change
existing_lines.product_uom_qty = rec.quantity
existing_lines.last_price = rec.price
existing_lines.price_unit = 0
existing_lines.write({
"product_uom_qty": rec.quantity,
"last_price": rec.price,
"price_unit": 0,
})

else:
self.env["sale.order.line"].create(
{
"order_id": self.sale_order_line_id.order_id.id,
"product_id": rec.product_id.id,
"product_uom_qty": rec.quantity,
"last_price": rec.price,
"price_unit": 0,
"main_product_line_id": self.sale_order_line_id.id,
}
)
total_of_sub_product_price += (rec.quantity * rec.price)
total_of_sub_product_price += (
self.sale_order_line_id.product_uom_qty
* self.sale_order_line_id.product_template_id.list_price
)
self.sale_order_line_id.price_unit = total_of_sub_product_price
return True
24 changes: 24 additions & 0 deletions new_producttype/wizard/sub_product_kit_wizard_view.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="sub_product_kit_wizard_view" model="ir.ui.view">
<field name="name">sub.product.kit.wizard.view</field>
<field name="model">sub.product.kit.wizard</field>
<field name="arch" type="xml">
<form string="Sub-Products">
<sheet>
<field name="sub_products_ids">
<list create="false" editable="bottom">
<field name="product_id" readonly="1"/>
<field name="quantity"/>
<field name="price"/>
</list>
</field>
<footer>
<button name="action_confirm" class="btn btn-primary" type="object" string="Confirm"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
</odoo>
10 changes: 10 additions & 0 deletions new_producttype/wizard/sub_product_line_kit_wizard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from odoo import fields, models


class SubProductLineKitWizard(models.TransientModel):
_name = 'sub.product.line.kit.wizard'

sub_products_line_id = fields.Many2one('sub.product.kit.wizard')
product_id = fields.Many2one('product.product', required=True)
quantity = fields.Float(default=0.0)
price = fields.Float(default=0)