Skip to content

[ADD] product_type_kit: kit product added with sub product selection … #884

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
2 changes: 2 additions & 0 deletions product_type_kit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizards
17 changes: 17 additions & 0 deletions product_type_kit/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
'name': 'Product Type Kit',
'version': '1.0',
'category': 'Sales',
'license': 'LGPL-3',
'summary': 'Custom product kit functionality without using BoM',
'depends': ['sale', 'product'],
'data': [
'security/ir.model.access.csv',
'views/product_template_views.xml',
'views/sale_order_views.xml',
'views/sale_order_line_views.xml',
'views/kit_subproduct_wizards.xml',
],
'installable': True,
'application': True,
}
3 changes: 3 additions & 0 deletions product_type_kit/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
from . import sale_order_line
16 changes: 16 additions & 0 deletions product_type_kit/models/product_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from odoo import fields, models


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

is_kit = fields.Boolean(string='Is Kit')
sub_product_ids = fields.Many2many(
'product.product',
'product_kit_sub_products_rel',
'kit_id',
'sub_product_id',
string='Sub Products',
domain="[('type', '=', 'product')]"
)
show_subproducts_on_report = fields.Boolean(string='Print Subproducts on Report')
18 changes: 18 additions & 0 deletions product_type_kit/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from odoo import api, fields, models


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

print_kit_details = fields.Boolean(string="Print Kit Details")
display_in_report = fields.Boolean(string="Print in report?", default=True)

@api.model
def unlink(self):
for order in self:
for line in order.order_line:
if not line.is_subproduct:
# delete sub lines linked to this main line
sub_lines = order.order_line.filtered(lambda l: l.kit_parent_line_id == line.id)
sub_lines.unlink()
return super().unlink()
42 changes: 42 additions & 0 deletions product_type_kit/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# models/sale_order_line.py

Choose a reason for hiding this comment

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

?

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


class SaleOrder(models.Model):

Choose a reason for hiding this comment

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

Name your class SaleOrderLine.

_inherit = 'sale.order.line'

kit_parent_line_id = fields.Many2one('sale.order.line', string='Parent Kit Line', ondelete='cascade')
is_kit_product = fields.Boolean(
string="Is Kit Product", compute="_compute_is_kit", store=True
)
is_subproduct = fields.Boolean(string="Is Sub Product", default=False)

@api.depends("product_id")
def _compute_is_kit(self):
for line in self:
line.is_kit_product = line.product_id.product_tmpl_id.is_kit

def write(self, vals):
for line in self:
if line.kit_parent_line_id:
raise UserError("Sub product lines cannot be edited manually.")
return super().write(vals)

def open_kit_wizard(self):
self.ensure_one()
return {
'name': f'Configure Kit: {self.product_id.name}',
'type': 'ir.actions.act_window',
'res_model': 'kit.sub.product.wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_order_line_id': self.id,
}
}

@api.onchange('kit_parent_line_id')
def _onchange_disable_edit(self):
if self.kit_parent_line_id:
self.update({'price_unit': 0})
6 changes: 6 additions & 0 deletions product_type_kit/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_product_template,access_product_template,model_product_template,base.group_user,1,1,1,1
access_sale_order_line,access_sale_order_line,model_sale_order_line,base.group_user,1,1,1,1
access_sale_order,access_sale_order,model_sale_order,base.group_user,1,1,1,1
access_kit_sub_product_wizard,access_kit_sub_product_wizard,model_kit_sub_product_wizard,,1,1,1,1
access_kit_sub_product_line,access_kit_sub_product_line,model_kit_sub_product_line,,1,1,1,1
25 changes: 25 additions & 0 deletions product_type_kit/views/kit_subproduct_wizards.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="view_kit_sub_product_wizard" model="ir.ui.view">
<field name="name">kit.sub.product.wizard</field>
<field name="model">kit.sub.product.wizard</field>
<field name="arch" type="xml">
<form>
<group>
<field name="order_line_id" readonly="1"/>
</group>
<field name="sub_product_lines">
<list editable="bottom">
<field name="product_id"/>
<field name="quantity"/>
<field name="price_unit"/>
</list>
</field>
<footer>
<button name="action_confirm" string="Confirm" type="object" class="btn-primary"/>
<button string="Cancel" special="cancel" class="btn-secondary"/>
</footer>
</form>
</field>
</record>
</odoo>
14 changes: 14 additions & 0 deletions product_type_kit/views/product_template_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="view_product_template_form_kit" model="ir.ui.view">
<field name="name">product.template.form.kit</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="//field[@name='type']" position="after">
<field name="is_kit"/>
<field name="sub_product_ids" widget="many2many_tags" invisible="not is_kit"/>
</xpath>
</field>
</record>
</odoo>
18 changes: 18 additions & 0 deletions product_type_kit/views/sale_order_line_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_order_line_readonly_kit" model="ir.ui.view">
<field name="name">sale.order.line.tree.readonly.kit</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='order_line']/list/field[@name='product_id']" position="attributes">
<attribute name="readonly">1</attribute>
<attribute name="attrs">{'readonly': [('is_subproduct', '=', True)]}</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/list/field[@name='price_unit']" position="attributes">
<attribute name="readonly">1</attribute>
<attribute name="attrs">{'readonly': [('is_subproduct', '=', True)]}</attribute>
</xpath>
</field>
</record>
</odoo>
16 changes: 16 additions & 0 deletions product_type_kit/views/sale_order_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_order_line_kit_button" model="ir.ui.view">
<field name="name">sale.order.line.kit.button</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='order_line']//list/field[@name='product_id']" position="after">
<button name="open_kit_wizard" type="object" string="Add Kit" class="btn-primary" invisible="(not is_kit_product) or (state != 'draft')" />
</xpath>
<xpath expr="//field[@name='payment_term_id']" position="after">
<field name="display_in_report"/>
</xpath>
</field>
</record>
</odoo>
2 changes: 2 additions & 0 deletions product_type_kit/wizards/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import kit_sub_product_line
from . import kit_subproduct_wizards
11 changes: 11 additions & 0 deletions product_type_kit/wizards/kit_sub_product_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from odoo import fields, models


class KitSubProductLine(models.TransientModel):
_name = 'kit.sub.product.line'
_description = 'Kit Sub Product Line'

wizard_id = fields.Many2one('kit.sub.product.wizard', required=True, ondelete='cascade')
product_id = fields.Many2one('product.product', required=True)
quantity = fields.Float(string='Quantity', default=1.0)
price_unit = fields.Float(string='Price', default=0.0)
46 changes: 46 additions & 0 deletions product_type_kit/wizards/kit_subproduct_wizards.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# wizards/kit_sub_product_wizard.py

Choose a reason for hiding this comment

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

?

from odoo import api, fields, models


class KitSubProductWizard(models.TransientModel):
_name = 'kit.sub.product.wizard'
_description = 'Kit Sub Products Wizard'

order_line_id = fields.Many2one('sale.order.line', required=True)
sub_product_lines = fields.One2many('kit.sub.product.line', 'wizard_id')

@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
line = self.env['sale.order.line'].browse(self.env.context['default_order_line_id'])
previous_sub_lines = self.env['sale.order.line'].search([
('kit_parent_line_id', '=', line.id)
])
sub_lines = []
for sub_product in line.product_id.sub_product_ids:
prev_line = previous_sub_lines.filtered(lambda l: l.product_id == sub_product)
sub_lines.append((0, 0, {
'product_id': sub_product.id,
'quantity': prev_line.product_uom_qty if prev_line else 1.0,
'price_unit': prev_line.price_unit if prev_line else sub_product.lst_price,
}))
res['sub_product_lines'] = sub_lines
return res

def action_confirm(self):
SaleOrderLine = self.env['sale.order.line']
# Remove existing kit sub lines
existing_lines = SaleOrderLine.search([('kit_parent_line_id', '=', self.order_line_id.id)])
existing_lines.unlink()

for sub_line in self.sub_product_lines:
SaleOrderLine.create({
'order_id': self.order_line_id.order_id.id,
'product_id': sub_line.product_id.id,
'product_uom_qty': sub_line.quantity,
'price_unit': 0.0,
'kit_parent_line_id': self.order_line_id.id,
})

total_price = sum(l.quantity * l.price_unit for l in self.sub_product_lines)
self.order_line_id.price_unit = total_price