Skip to content
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_product_type/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizard
17 changes: 17 additions & 0 deletions new_product_type/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "New Product Type",
"version": "1.0",
'author': "habar",
"depends": ["sale", "product", 'account'],
"data": [
'security/ir.model.access.csv',
"report/sale_order_portal_report.xml",
"report/sale_order_report.xml",
"views/product_kit_wizard_views.xml",
"views/product_views.xml",
"report/invoice_report.xml",
"views/sale_order_line_views.xml",
],
"installable": True,
'license': 'LGPL-3',
}
4 changes: 4 additions & 0 deletions new_product_type/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import product_template
from . import sale_order_line
from . import sale_order
from . import account_move
15 changes: 15 additions & 0 deletions new_product_type/models/account_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from odoo import models


class AccountMoveLine(models.Model):
_inherit = "account.move.line"

def show_in_report(self):
self.ensure_one()

sale_line = self.sale_line_ids[:1]

return (
not sale_line.is_kit_product
or sale_line.order_id.print_in_report
)
15 changes: 15 additions & 0 deletions new_product_type/models/product_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from odoo import api, fields, models
from odoo.exceptions import ValidationError


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

is_kit = fields.Boolean()
sub_product = fields.Many2many("product.product")

@api.constrains("sub_product")
def _check_no_self_product_reference(self):
for record in self:
if record.product_variant_id in record.sub_product:
raise ValidationError("A product cannot be added as a sub-product in its own kit.")
9 changes: 9 additions & 0 deletions new_product_type/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from odoo import fields, models


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

print_in_report = fields.Boolean(
string="Print Kit Products"
)
39 changes: 39 additions & 0 deletions new_product_type/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 UserError


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

is_kit = fields.Boolean(
related="product_id.product_tmpl_id.is_kit",
store=True
)
is_kit_product = fields.Boolean()
kit_parent_line_id = fields.Many2one("sale.order.line", ondelete="cascade")
extra_price = fields.Float(default=0.0)

@api.ondelete(at_uninstall=False)
def _check_kit_product_restriction(self):
for line in self:
if line.is_kit_product:
raise UserError(_("You cannot delete a kit sub product directly."))

def show_in_report(self):
self.ensure_one()
return (
not self.is_kit_product
or self.order_id.print_in_report
)

def action_open_kit_wizard(self):
return {
"type": "ir.actions.act_window",
"name": "Configure Kit",
"res_model": "product.kit.wizard",
"view_mode": "form",
"target": "new",
"context": {
"active_id": self.id
},
}
14 changes: 14 additions & 0 deletions new_product_type/report/invoice_report.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<template id="report_invoice_document_inherit_print_option"
inherit_id="account.report_invoice_document">
<xpath expr="//t[@t-set='lines_to_report']"
position="attributes">
<attribute name="t-value">
o._get_move_lines_to_report().filtered(
lambda l: l.show_in_report()
)
</attribute>
</xpath>
</template>
</odoo>
14 changes: 14 additions & 0 deletions new_product_type/report/sale_order_portal_report.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<template id="sale_order_portal_content_inherit_print_option"
inherit_id="sale.sale_order_portal_content">
<xpath expr="//t[@t-set='lines_to_report']"
position="attributes">
<attribute name="t-value">
sale_order._get_order_lines_to_report().filtered(
lambda l: l.show_in_report()
)
</attribute>
</xpath>
</template>
</odoo>
14 changes: 14 additions & 0 deletions new_product_type/report/sale_order_report.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<template id="report_saleorder_document_inherit_print_option"
inherit_id="sale.report_saleorder_document">
<xpath expr="//t[@t-set='lines_to_report']"
position="attributes">
<attribute name="t-value">
doc._get_order_lines_to_report().filtered(
lambda l: l.show_in_report()
)
</attribute>
</xpath>
</template>
</odoo>
3 changes: 3 additions & 0 deletions new_product_type/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_create,perm_write,perm_unlink
access_product_kit_wizard,access_product_kit_wizard,model_product_kit_wizard,base.group_user,1,1,1,1
access_product_kit_wizard_line,access_product_kit_wizard_line,model_product_kit_wizard_line,base.group_user,1,1,1,1
29 changes: 29 additions & 0 deletions new_product_type/views/product_kit_wizard_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="product_kit_wizard_form" model="ir.ui.view">
<field name="name">product.kit.wizard.form</field>
<field name="model">product.kit.wizard</field>
<field name="arch" type="xml">
<form string="Configure Kit">
<sheet>
<group>
<field name="main_product_id" readonly="1" class="fw-bold"/>
</group>
<separator string="Sub Products"/>
<field name="line_ids" nolabel="1">
<list editable="bottom" create="false" class="table table-sm">
<field name="product_id" readonly="1" optional="show" force_save="1"/>
<field name="quantity" widget="float" sum="Total quantity"/>
<field name="price" widget="float"/>
<field name="price_subtotal" string="Total Price" sum="Grand Total"/>
</list>
</field>
</sheet>
<footer>
<button name="action_confirm" type="object" string="Confirm" class="btn btn-primary"/>
<button string="Cancel" special="cancel" class="btn-secondary"/>
</footer>
</form>
</field>
</record>
</odoo>
17 changes: 17 additions & 0 deletions new_product_type/views/product_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="product_template_view_form" model="ir.ui.view">
<field name="name">product.template.view.form</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='invoice_policy']" position="after">
<field name="is_kit"/>
</xpath>
<xpath expr="//field[@name='is_kit']" position="after">
<field name="sub_product" widget="many2many_tags"
options="{'no_create': True}" invisible="not is_kit"/>
</xpath>
</field>
</record>
</odoo>
38 changes: 38 additions & 0 deletions new_product_type/views/sale_order_line_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="sale_order_line_form_view" model="ir.ui.view">
<field name="name">sale.order.line.form.view</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" position="attributes">
<attribute name="decoration-warning">is_kit_product</attribute>
</xpath>
<xpath expr="//field[@name='product_template_id']" position="after">
<button name="action_open_kit_wizard"
type="object"
string="Configure Kit"
class="btn-primary"
invisible="not is_kit or state!='draft'"/>
</xpath>
<xpath expr="//field[@name='order_line']/list/field[@name='product_id']" position="attributes">
<attribute name="readonly">is_kit_product</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/list/field[@name='product_template_id']" position="attributes">
<attribute name="readonly">is_kit_product</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/list/field[@name='price_unit']" position="attributes">
<attribute name="readonly">is_kit_product</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/list/field[@name='product_uom_qty']" position="attributes">
<attribute name="readonly">is_kit_product</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/list/field[@name='tax_ids']" position="attributes">
<attribute name="readonly">is_kit_product</attribute>
</xpath>
<xpath expr="//field[@name='payment_term_id']" position="after">
<field name="print_in_report"/>
</xpath>
</field>
</record>
</odoo>
2 changes: 2 additions & 0 deletions new_product_type/wizard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import product_kit_wizard
from . import product_kit_wizard_line
103 changes: 103 additions & 0 deletions new_product_type/wizard/product_kit_wizard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from odoo import api, fields, models


class ProductKitWizard(models.TransientModel):
_name = "product.kit.wizard"
_description = "Kit Sub Product Wizard"

sale_line_id = fields.Many2one("sale.order.line")
main_product_id = fields.Many2one(
"product.product",
string="Product"
)
line_ids = fields.One2many(
"product.kit.wizard.line",
"wizard_id",
string="Sub Products"
)

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

sale_line = self.env["sale.order.line"].browse(
self.env.context.get("active_id")
)

if not sale_line.product_id:
return res

order = sale_line.order_id
product = sale_line.product_id

lines = []

for sub_product in product.product_tmpl_id.sub_product:

existing_line = order.order_line.filtered(
lambda l:
l.product_id == sub_product
and l.kit_parent_line_id == sale_line
)[:1]

lines.append(
(0, 0, {
"product_id": sub_product.id,
"quantity": (
existing_line.product_uom_qty
if existing_line else 1.0
),
"price": (
existing_line.extra_price
if existing_line else sub_product.lst_price
),
})
)

res.update({
"sale_line_id": sale_line.id,
"main_product_id": product.id,
"line_ids": lines,
})

return res

def action_confirm(self):
self.ensure_one()
order = self.sale_line_id.order_id
parent_line = self.sale_line_id
total_price = parent_line.product_id.lst_price

has_extra_price = 'extra_price' in self.env['sale.order.line']._fields

for line in self.line_ids:
existing_line = order.order_line.filtered(
lambda l: l.product_id == line.product_id and l.kit_parent_line_id == parent_line
)[:1]

vals = {
"name": line.product_id.display_name,
"product_uom_qty": line.quantity,
"price_unit": 0.0,
"sequence": parent_line.sequence + 1,
}

if has_extra_price:
vals["extra_price"] = line.price

if existing_line:
existing_line.write(vals)
else:
vals.update({
"order_id": order.id,
"product_id": line.product_id.id,
"is_kit_product": True,
"kit_parent_line_id": parent_line.id,
})
self.env["sale.order.line"].create(vals)

total_price += line.quantity * line.price

parent_line.write({"price_unit": total_price})

return {"type": "ir.actions.act_window_close"}
24 changes: 24 additions & 0 deletions new_product_type/wizard/product_kit_wizard_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from odoo import api, fields, models


class ProductKitWizardLine(models.TransientModel):
_name = "product.kit.wizard.line"
_description = "Kit Wizard Line"

wizard_id = fields.Many2one("product.kit.wizard")
product_id = fields.Many2one(
"product.product",
readonly=True
)
quantity = fields.Float(default=1.0)
price = fields.Float()
price_subtotal = fields.Float(
string="Total Price",
compute="_compute_price_subtotal",
store=True
)

@api.depends("quantity", "price")
def _compute_price_subtotal(self):
for line in self:
line.price_subtotal = line.quantity * line.price