Skip to content

18.0 product kit vkas #695

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 2 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
1 change: 1 addition & 0 deletions product_kit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
23 changes: 23 additions & 0 deletions product_kit/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
'name': 'Product Kit',
'sequence': 1,
'category': 'Tutorials/product_kit',
'version': '1.0',
'application': True,
'installable': True,
'license': 'LGPL-3',
'depends': [
'base',
'stock',
'sale_management',
'account'
],
'data': [
'views/product_template_views.xml',
'views/sale_order_views.xml',
'views/kit_products_wizard_views.xml',
'security/ir.model.access.csv',
'views/sale_order_report.xml',
'views/account_move_views.xml',
]
}
6 changes: 6 additions & 0 deletions product_kit/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from . import product_template
from . import sale_order_line
from . import kit_products_wizard_line
from . import kit_products_wizard
from . import sale_order
from . import account_move
14 changes: 14 additions & 0 deletions product_kit/models/account_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from odoo import models, fields


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

sale_id = fields.Many2one('sale.order', string="Sale Order", compute='_compute_sale_id', store=True, readonly=True)
print_kit = fields.Boolean(related="sale_id.print_kit", readonly=True)
from_wizard = fields.Boolean(related="line_ids.sale_line_ids.from_wizard", store=True, readonly=True)

def _compute_sale_id(self):
for move in self:
sale_orders = move.line_ids.mapped("sale_line_ids.order_id")
move.sale_id = sale_orders and sale_orders[0] or False
99 changes: 99 additions & 0 deletions product_kit/models/kit_products_wizard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from odoo import models, fields, api


class KitProductsWizard(models.TransientModel):
_name = "kit.products.wizard"
_description = "Kit Products Wizard"

sale_order_id = fields.Many2one("sale.order", string="Sale Order", readonly=True)
product_template_id = fields.Many2one("product.template", string="Product", readonly=True)
kit_product_ids = fields.One2many("kit.products.wizard.line", "wizard_id", string="Sub Products")

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

sale_order_id = self.env.context.get("default_sale_order_id")
product_template_id = self.env.context.get("default_product_template_id")

if sale_order_id:
res["sale_order_id"] = sale_order_id

if product_template_id:
res["product_template_id"] = product_template_id
product_template = self.env["product.template"].browse(product_template_id)

existing_lines = self.env["sale.order.line"].search([
("order_id", "=", sale_order_id),
("product_id", "in", product_template.kit_product_ids.ids)
])

kit_product_vals = []
if existing_lines:
for line in existing_lines:
kit_product_vals.append((0, 0, {
"product_id": line.product_id.id,
"product_qty": line.product_uom_qty,
"price_unit": line.product_id.list_price,
}))
else:
for kit_product in product_template.kit_product_ids:
kit_product_vals.append((0, 0, {
"product_id": kit_product.id,
"product_qty": 1,
"price_unit": kit_product.list_price,
}))

res["kit_product_ids"] = kit_product_vals

return res

def action_generate_order_lines(self):
"""Updates or creates sale order lines for each kit product in the wizard"""
self.ensure_one()

main_product_line = self.env["sale.order.line"].search([
("order_id", "=", self.sale_order_id.id),
("product_id", "in", self.product_template_id.product_variant_ids.ids),
], limit=1)

original_price = self.product_template_id.list_price
total_price = original_price

for kit_product in self.kit_product_ids:
existing_line = self.env["sale.order.line"].search([
("order_id", "=", self.sale_order_id.id),
("product_id", "=", kit_product.product_id.id),
("from_wizard", "=", True),
], limit=1)

sub_total = kit_product.product_qty * kit_product.product_id.list_price
total_price += sub_total

if existing_line:
existing_line.write({
"product_uom_qty": kit_product.product_qty,
"price_unit": 0.00, # Ensure sub-products remain zero-priced
})
else:
self.env["sale.order.line"].create({
"name": kit_product.product_id.name,
"order_id": self.sale_order_id.id,
"product_id": kit_product.product_id.id,
"product_uom_qty": kit_product.product_qty,
"price_unit": 0.00,
"from_wizard": True,
})

if main_product_line:
main_product_line.price_unit = total_price * main_product_line.product_uom_qty
else:
self.env["sale.order.line"].create({
"order_id": self.sale_order_id.id,
"product_id": self.product_template_id.product_variant_id.id,
"product_uom_qty": 1,
"price_unit": total_price,
"name": self.product_template_id.name,
})

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


class KitProductsWizardLine(models.TransientModel):
_name = "kit.products.wizard.line"
_description = "Kit Products Wizard Line"

wizard_id = fields.Many2one("kit.products.wizard", required=True)
product_id = fields.Many2one("product.product", string="Product")
product_qty = fields.Float(string="Quantity")
price_unit = fields.Float(string="Price")
11 changes: 11 additions & 0 deletions product_kit/models/product_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from odoo import models, fields


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

is_kit = fields.Boolean(string="Is Kit")
kit_product_ids = fields.Many2many(
'product.product',
string="Sub Products"
)
7 changes: 7 additions & 0 deletions product_kit/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_kit = fields.Boolean(string="Print in report", default=True)
36 changes: 36 additions & 0 deletions product_kit/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from odoo import models, fields


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

is_kit = fields.Boolean(related="product_template_id.is_kit", store=True)
from_wizard = fields.Boolean(string="Created from Wizard", default=False)

def action_open_kit_wizard(self):
"""Opens the wizard with Sale Order ID in context"""
return {
"name": "Kit Products",
"type": "ir.actions.act_window",
"res_model": "kit.products.wizard",
"view_mode": "form",
"target": "new",
"context": {
"default_sale_order_id": self.order_id.id,
"default_product_template_id": self.product_template_id.id,
}
}

def unlink(self):
"""Delete only the related wizard-generated sale order lines when a main product line is deleted."""
main_product_lines = self.filtered(lambda line: not line.from_wizard)

for main_line in main_product_lines:
kit_lines = self.search([
("order_id", "=", main_line.order_id.id),
("from_wizard", "=", True),
("product_id", "in", main_line.product_id.kit_product_ids.ids),
])
kit_lines.unlink()

return super().unlink()
4 changes: 4 additions & 0 deletions product_kit/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_kit.access_kit_products_wizard_line,access_kit_products_wizard_line,product_kit.model_kit_products_wizard_line,base.group_user,1,1,1,1
product_kit.access_kit_products_wizard,access_kit_products_wizard,product_kit.model_kit_products_wizard,base.group_user,1,1,1,1
8 changes: 8 additions & 0 deletions product_kit/views/account_move_views.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_invoice_document" inherit_id="account.report_invoice_document">
<xpath expr="//tbody/t[@t-foreach='lines']" position="attributes">
<attribute name="t-if">line.move_id.sale_id.print_kit or not line.sale_line_ids.from_wizard</attribute>
</xpath>
</template>
</odoo>
37 changes: 37 additions & 0 deletions product_kit/views/kit_products_wizard_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="action_kit_products_wizard" model="ir.actions.act_window">
<field name="name">Kit Products</field>
<field name="res_model">kit.products.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>


<record id="view_kit_products_wizard" model="ir.ui.view">
<field name="name">view_kit_products_wizard</field>
<field name="model">kit.products.wizard</field>
<field name="arch" type="xml">
<form string="Kit Products">
<sheet>
<group>
<field name="product_template_id"/>
</group>
<group>
<field name="kit_product_ids" nolabel="1">
<list editable="bottom">
<field name="product_id"/>
<field name="product_qty"/>
<field name="price_unit"/>
</list>
</field>
</group>
<footer>
<button name="action_generate_order_lines" type="object" string="Submit" class="btn-primary"/>
<button string="Close" class="oe_secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
</odoo>
21 changes: 21 additions & 0 deletions product_kit/views/product_template_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_template_property_form2" model="ir.ui.view">
<field name="name">product.template.stock.property.form.inherit2</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='group_general']" position="inside">
<label for="is_kit" class="oe_inline"/>
<div class="o_row w-100">
<field name="is_kit"/>
</div>

<label for="kit_product_ids" class="oe_inline" invisible = "not is_kit"/>
<div class="o_row w-100" >
<field name="kit_product_ids" widget="many2many_tags" invisible = "not is_kit"/>
</div>
</xpath>
</field>
</record>
</odoo>
15 changes: 15 additions & 0 deletions product_kit/views/sale_order_report.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_saleorder_document_inherit" inherit_id="sale.sale_order_portal_content">
<xpath expr="//table[@id='sales_order_table']//tbody[@class='sale_tbody']//t[@t-foreach='lines_to_report']" position="attributes">
<attribute name="t-if">
(sale_order.print_kit and line.from_wizard) or (not line.from_wizard)
</attribute>
</xpath>
</template>
<template id="report_saleorder_document" inherit_id="sale.report_saleorder_document">
<xpath expr="//tbody/t[@t-foreach='lines_to_report']" position="attributes">
<attribute name="t-if">doc.print_kit or not line.from_wizard</attribute>
</xpath>
</template>
</odoo>
26 changes: 26 additions & 0 deletions product_kit/views/sale_order_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_sale_order_line_form_inherit" model="ir.ui.view">
<field name="name">view_sale_order_line_form_inherit</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='payment_term_id']" position="after">
<field name="print_kit"/>
</xpath>
<xpath expr="//field[@name='order_line']/list//field[@name='product_template_id']" position="after">
<button name="action_open_kit_wizard" type="object" string="+" class="oe_highlight"
invisible = "not is_kit or state in ['sale']"/>
</xpath>
<xpath expr="//field[@name='order_line']/list//field[@name='product_template_id']" position="attributes">
<attribute name="readonly">from_wizard</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/list//field[@name='product_uom_qty']" position="attributes">
<attribute name="readonly">from_wizard</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/list//field[@name='price_unit']" position="attributes">
<attribute name="readonly">from_wizard</attribute>
</xpath>
</field>
</record>
</odoo>