Skip to content
Open
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 sale_kit_management/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizard
16 changes: 16 additions & 0 deletions sale_kit_management/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
'name': 'Sale Kit Management',
'version': "1.0",
'category': "Sales/Sales",
'description': "Creates a Kit Option in Products and Sales Order",
'depends': ['sale_management', 'stock'],
'data': [
'security/ir.model.access.csv',
'wizard/sale_order_kit_views.xml',
'views/product_views.xml',
'views/sale_order_views.xml',
],
'installable': True,
'author': "times",
'license': "LGPL-3",
}
4 changes: 4 additions & 0 deletions sale_kit_management/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import product_template
from . import product_subproduct_line
from . import sale_order_line
from . import sale_order_kit_config_line
49 changes: 49 additions & 0 deletions sale_kit_management/models/product_subproduct_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from odoo import api, fields, models


class ProductSubproductLine(models.Model):
_name = 'product.subproduct.line'
_description = "Subproducts"

product_tmpl_id = fields.Many2one('product.template', ondelete='cascade')
product_id = fields.Many2one('product.product', string="Product", required=True)
product_uom_qty = fields.Float(related='product_id.qty_available', string="Quantity on Hand")
kit_unit_qty = fields.Float(string="Kit Unit Qty", default=1.0)
price_unit = fields.Float(string="Unit Price", compute='_compute_price_unit', store=True, readonly=False)
amount = fields.Float(string="Amount", compute='_compute_amount', store=True)

@api.depends('kit_unit_qty', 'price_unit')
def _compute_amount(self):
for line in self:
line.amount = line.kit_unit_qty * line.price_unit

@api.depends('product_id')
def _compute_price_unit(self):
for line in self:
if line.product_id:
line.price_unit = line.product_id.lst_price
else:
line.price_unit = 0.0

@api.model_create_multi
def create(self, vals_list):
lines = super().create(vals_list)
lines.mapped('product_tmpl_id')._recompute_kit_list_price()
return lines

def write(self, vals):
res = super().write(vals)
if 'price_unit' in vals or 'kit_unit_qty' in vals or 'product_id' in vals:
self.mapped('product_tmpl_id')._recompute_kit_list_price()
return res

@api.ondelete(at_uninstall=False)
def _ondelete_recompute_kit_price(self):
for tmpl in self.mapped('product_tmpl_id'):
if tmpl.is_kit:
remaining = tmpl.subproduct_ids - self
if remaining:
tmpl.list_price = sum(
sub.price_unit * sub.kit_unit_qty
for sub in remaining
)
24 changes: 24 additions & 0 deletions sale_kit_management/models/product_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from odoo import api, fields, models


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

is_kit = fields.Boolean()
subproduct_ids = fields.One2many('product.subproduct.line', 'product_tmpl_id')

def _recompute_kit_list_price(self):
for tmpl in self:
if tmpl.is_kit and tmpl.subproduct_ids:
tmpl.list_price = sum(
sub.price_unit * sub.kit_unit_qty
for sub in tmpl.subproduct_ids
)

@api.onchange('subproduct_ids')
def _onchange_subproduct_ids(self):
if self.is_kit and self.subproduct_ids:
self.list_price = sum(
sub.price_unit * sub.kit_unit_qty
for sub in self.subproduct_ids
)
11 changes: 11 additions & 0 deletions sale_kit_management/models/sale_order_kit_config_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from odoo import fields, models


class SaleOrderKitConfigLine(models.Model):
_name = 'sale.order.kit.config.line'
_description = "Kit Configuration Line"

sale_order_line_id = fields.Many2one('sale.order.line', string="Kit Parent Line", ondelete='cascade', required=True, index=True)
product_id = fields.Many2one('product.product', string="Product", required=True)
kit_unit_qty = fields.Float(string="Kit Unit Qty", default=1.0)
price_unit = fields.Float(string="Unit Price")
94 changes: 94 additions & 0 deletions sale_kit_management/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from odoo import _, api, fields, models
from odoo.exceptions import UserError


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

_KIT_SUBPRODUCT_SEQ_BASE = 200

is_kit = fields.Boolean(related='product_id.product_tmpl_id.is_kit')
is_kit_subproduct = fields.Boolean(string="Is Kit Subproduct", default=False)
kit_unit_qty = fields.Float(string="Kit Unit Qty")
kit_parent_line_id = fields.Many2one('sale.order.line', string="Kit Parent Line", ondelete='cascade', index=True)
has_kit_subproducts = fields.Boolean(compute='_compute_has_kit_subproducts')
kit_config_line_ids = fields.One2many('sale.order.kit.config.line', 'sale_order_line_id', string="Kit Configuration")

@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if vals.get('is_kit_subproduct') or vals.get('kit_parent_line_id'):
continue
seq = vals.get('sequence', 0)
if seq >= self._KIT_SUBPRODUCT_SEQ_BASE:
vals['sequence'] = self._KIT_SUBPRODUCT_SEQ_BASE - 1
return super().create(vals_list)

@api.depends('order_id.order_line.kit_parent_line_id')
def _compute_has_kit_subproducts(self):
for line in self:
if line.is_kit and line.id:
line.has_kit_subproducts = any(
l.kit_parent_line_id.id == line.id
for l in line.order_id.order_line
if l.is_kit_subproduct
)
else:
line.has_kit_subproducts = False

def _compute_kit_price(self):
for line in self:
if line.is_kit and line.kit_config_line_ids:
line.price_unit = sum(
cl.price_unit * cl.product_uom_qty
for cl in line.kit_config_line_ids
)

def write(self, vals):
result = super().write(vals)
if 'product_uom_qty' in vals:
for line in self.filtered(lambda l: l.is_kit and l.id):
child_lines = self.env['sale.order.line'].search([
('kit_parent_line_id', '=', line.id),
('is_kit_subproduct', '=', True),
])
for child in child_lines:
if child.kit_unit_qty:
child.product_uom_qty = line.product_uom_qty * child.kit_unit_qty
child.price_unit = 0
return result

@api.ondelete(at_uninstall=False)
def _ondelete_handle_kit(self):
for line in self.filtered(lambda l: l.is_kit_subproduct):
if line.kit_parent_line_id in self:
continue
section_in_batch = any(
l.display_type == 'line_section'
and l.kit_parent_line_id.id == line.kit_parent_line_id.id
for l in self
)
if section_in_batch:
continue
raise UserError(_(
"You cannot delete subproduct lines directly. "
"Delete the parent kit product or delete the entire subproducts section."
))

child_lines = self.env['sale.order.line'].search([
('kit_parent_line_id', 'in', self.ids),
('id', 'not in', self.ids),
])
if child_lines:
child_lines.unlink()

def action_open_kit_configurator(self):
self.ensure_one()
return {
'name': _("Kit"),
'type': 'ir.actions.act_window',
'res_model': 'sale.order.kit',
'view_mode': 'form',
'target': 'new',
'context': {'default_sale_order_line_id': self.id},
}
6 changes: 6 additions & 0 deletions sale_kit_management/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_subproduct_line,access_product_subproduct_line,model_product_subproduct_line,sales_team.group_sale_salesman,1,1,1,1
access_sale_order_kit,access_sale_order_kit,model_sale_order_kit,sales_team.group_sale_salesman,1,1,1,1
access_sale_order_kit_line,access_sale_order_kit_line,model_sale_order_kit_line,sales_team.group_sale_salesman,1,1,1,1
access_sale_order_kit_config_line,access_sale_order_kit_config_line,model_sale_order_kit_config_line,sales_team.group_sale_salesman,1,1,1,1

33 changes: 33 additions & 0 deletions sale_kit_management/views/product_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="product_form_kit_view" 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_form_view" />
<field name="arch" type="xml">

<xpath expr="//span[@name='sale_option']" position="after">
<span name="kit_option" class="d-inline-flex">
<field name="is_kit" string="Is Kit" />
<label string="Kit" for="is_kit" />
</span>
</xpath>

<xpath expr="//notebook/page[@name='general_information']" position="after">
<page name="subproducts" string="Subproducts" invisible="not is_kit">
<field name="subproduct_ids">
<list editable="bottom">
<field name="product_id" string="Product" />
<field name="product_uom_qty" string="On Hand Qty" />
<field name="kit_unit_qty" string="Kit Unit Qty"
decoration-info="product_id" />
<field name="price_unit" string="Unit Price" />
<field name="amount" string="Total Amount" />
</list>
</field>
</page>
</xpath>

</field>
</record>
</odoo>
68 changes: 68 additions & 0 deletions sale_kit_management/views/sale_order_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="sale_order_form_kit" model="ir.ui.view">
<field name="name">sale.order.form.kit</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form" />
<field name="arch" type="xml">

<xpath
expr="//notebook/page[@name='order_lines']/field[@name='order_line']/list/field[@name='product_id' and @optional='hide']"
position="after">
<button name="action_open_kit_configurator"
type="object"
icon="fa-cubes"
invisible="not is_kit" width="25px" />
</xpath>

<xpath
expr="//notebook/page[@name='order_lines']/field[@name='order_line']/list/field[@name='product_id']"
position="before">
<field name="is_kit_subproduct" column_invisible="1" />
<field name="has_kit_subproducts" column_invisible="1" />
</xpath>

<xpath expr="//notebook/page[@name='order_lines']/field[@name='order_line']/list"
position="attributes">
<attribute name="decoration-muted">is_kit_subproduct</attribute>
</xpath>

<xpath
expr="//notebook/page[@name='order_lines']/field[@name='order_line']/list/field[@name='product_id' and @optional='hide']"
position="attributes">
<attribute name="readonly">is_kit_subproduct or not product_updatable</attribute>
</xpath>
<xpath
expr="//notebook/page[@name='order_lines']/field[@name='order_line']/list/field[@name='product_template_id']"
position="attributes">
<attribute name="readonly">is_kit_subproduct or (id and not product_updatable)</attribute>
</xpath>
<xpath
expr="//notebook/page[@name='order_lines']/field[@name='order_line']/list/field[@name='product_uom_qty']"
position="attributes">
<attribute name="readonly">is_kit_subproduct</attribute>
</xpath>
<xpath
expr="//notebook/page[@name='order_lines']/field[@name='order_line']/list/field[@name='product_uom_id']"
position="attributes">
<attribute name="readonly">is_kit_subproduct</attribute>
</xpath>
<xpath
expr="//notebook/page[@name='order_lines']/field[@name='order_line']/list/field[@name='price_unit']"
position="attributes">
<attribute name="readonly">is_kit_subproduct</attribute>
</xpath>
<xpath
expr="//notebook/page[@name='order_lines']/field[@name='order_line']/list/field[@name='tax_ids']"
position="attributes">
<attribute name="readonly">is_kit_subproduct</attribute>
</xpath>
<xpath
expr="//notebook/page[@name='order_lines']/field[@name='order_line']/list/field[@name='discount']"
position="attributes">
<attribute name="readonly">is_kit_subproduct</attribute>
</xpath>

</field>
</record>
</odoo>
1 change: 1 addition & 0 deletions sale_kit_management/wizard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import sale_order_kit
Loading