Initial commit: Odoo 18.0-20251222 extra-addons
This commit is contained in:
1
sale_product_set/wizard/__init__.py
Executable file
1
sale_product_set/wizard/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import sale_product_set_wizard
|
||||
115
sale_product_set/wizard/sale_product_set_wizard.py
Executable file
115
sale_product_set/wizard/sale_product_set_wizard.py
Executable file
@@ -0,0 +1,115 @@
|
||||
# Copyright 2015 Anybox S.A.S
|
||||
# Copyright 2016-2020 Camptocamp SA
|
||||
# @author Simone Orsi <simahawk@gmail.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import api, exceptions, fields, models
|
||||
|
||||
|
||||
class SaleProductSetWizard(models.TransientModel):
|
||||
_inherit = "product.set.wizard"
|
||||
_name = "sale.product.set.wizard"
|
||||
_description = "Wizard model to add product set into a quotation"
|
||||
|
||||
order_id = fields.Many2one(
|
||||
"sale.order",
|
||||
"Sale Order",
|
||||
required=True,
|
||||
default=lambda self: self.env.context.get("active_id")
|
||||
if self.env.context.get("active_model") == "sale.order"
|
||||
else None,
|
||||
ondelete="cascade",
|
||||
)
|
||||
partner_id = fields.Many2one(related="order_id.partner_id", ondelete="cascade")
|
||||
skip_existing_products = fields.Boolean(
|
||||
default=False,
|
||||
help="Enable this to not add new lines "
|
||||
"for products already included in SO lines.",
|
||||
)
|
||||
|
||||
@api.depends_context("product_set_add__set_line_ids")
|
||||
def _compute_product_set_line_ids(self):
|
||||
line_ids = self.env.context.get("product_set_add__set_line_ids", [])
|
||||
lines_from_ctx = self.env["product.set.line"].browse(line_ids)
|
||||
for rec in self:
|
||||
lines = lines_from_ctx.filtered(
|
||||
lambda x, rec=rec: x.product_set_id == rec.product_set_id
|
||||
)
|
||||
if lines:
|
||||
# Use the ones from ctx but make sure they belong to the same set.
|
||||
rec.product_set_line_ids = lines
|
||||
else:
|
||||
# Fallback to all lines from current set
|
||||
return super()._compute_product_set_line_ids()
|
||||
|
||||
def _check_partner(self):
|
||||
"""Validate order partner against product set's partner if any."""
|
||||
if not self.product_set_id.partner_id or self.env.context.get(
|
||||
"product_set_add_skip_validation"
|
||||
):
|
||||
return
|
||||
allowed_partners = self._allowed_order_partners()
|
||||
if self.order_id.partner_id not in allowed_partners:
|
||||
raise exceptions.ValidationError(
|
||||
self.env._(
|
||||
"You can use a sale order assigned "
|
||||
"only to following partner(s): {}"
|
||||
).format(", ".join(allowed_partners.mapped("name")))
|
||||
)
|
||||
return super()._check_partner()
|
||||
|
||||
def _allowed_order_partners(self):
|
||||
"""Product sets' partners allowed for current sale order."""
|
||||
partner_ids = self.env.context.get("allowed_order_partner_ids")
|
||||
if partner_ids:
|
||||
return self.env["res.partner"].browse(partner_ids)
|
||||
return self.product_set_id.partner_id
|
||||
|
||||
def add_set(self):
|
||||
"""Add product set, multiplied by quantity in sale order line"""
|
||||
res = super().add_set()
|
||||
if not self.order_id:
|
||||
return res
|
||||
order_lines = self._prepare_order_lines()
|
||||
if order_lines:
|
||||
self.order_id.write({"order_line": order_lines})
|
||||
return order_lines
|
||||
|
||||
def _prepare_order_lines(self):
|
||||
max_sequence = self._get_max_sequence()
|
||||
order_lines = []
|
||||
for seq, set_line in enumerate(self._get_lines(), start=1):
|
||||
values = self.prepare_sale_order_line_data(set_line)
|
||||
# When we play with sequence widget on a set of product,
|
||||
# it's possible to have a negative sequence.
|
||||
# In this case, the line is not added at the correct place.
|
||||
# So we have to force it with the order of the line.
|
||||
values.update({"sequence": max_sequence + seq})
|
||||
order_lines.append((0, 0, values))
|
||||
return order_lines
|
||||
|
||||
def _get_max_sequence(self):
|
||||
max_sequence = 0
|
||||
if self.order_id.order_line:
|
||||
max_sequence = max(line.sequence for line in self.order_id.order_line)
|
||||
return max_sequence
|
||||
|
||||
def _get_lines(self):
|
||||
if not self.order_id:
|
||||
yield from super()._get_lines()
|
||||
so_product_ids = self.order_id.order_line.mapped("product_id").ids
|
||||
for set_line in self.product_set_line_ids:
|
||||
if self.skip_existing_products and set_line.product_id.id in so_product_ids:
|
||||
continue
|
||||
yield set_line
|
||||
|
||||
def prepare_sale_order_line_data(self, set_line, max_sequence=0):
|
||||
self.ensure_one()
|
||||
line_values = set_line.prepare_sale_order_line_values(
|
||||
self.order_id, self.quantity, max_sequence=max_sequence
|
||||
)
|
||||
if set_line.display_type:
|
||||
line_values.update(
|
||||
{"name": set_line.name, "display_type": set_line.display_type}
|
||||
)
|
||||
|
||||
return line_values
|
||||
88
sale_product_set/wizard/sale_product_set_wizard_view.xml
Executable file
88
sale_product_set/wizard/sale_product_set_wizard_view.xml
Executable file
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="product_set_add_form_view" model="ir.ui.view">
|
||||
<field name="name">sale.product.set.wizard.form.view</field>
|
||||
<field name="model">sale.product.set.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Add set in sale order line">
|
||||
<group name="main" colspan="4">
|
||||
<field
|
||||
name="order_id"
|
||||
invisible="context.get('default_order_id')"
|
||||
domain="[('state', 'in', ('draft','sent'))]"
|
||||
/>
|
||||
<field name="partner_id" invisible="1" />
|
||||
<field
|
||||
name="product_set_id"
|
||||
domain="['|',('partner_id', '=', False),('partner_id', '=', partner_id)]"
|
||||
/>
|
||||
<field name="quantity" />
|
||||
<field name="skip_existing_products" />
|
||||
</group>
|
||||
<group name="lines" colspan="4">
|
||||
<field
|
||||
name="product_set_line_ids"
|
||||
nolabel="1"
|
||||
colspan="4"
|
||||
widget="section_and_note_one2many"
|
||||
domain="[('product_set_id', '=', product_set_id)]"
|
||||
>
|
||||
<list>
|
||||
<field
|
||||
name="product_id"
|
||||
options="{'no_open': True, 'no_create': True}"
|
||||
required="not display_type"
|
||||
/>
|
||||
<field name="display_type" column_invisible="True" />
|
||||
<field name="name" widget="section_and_note_text" />
|
||||
<control>
|
||||
<create name="add_line_control" string="Add a line" />
|
||||
<create
|
||||
name="add_section_control"
|
||||
string="Add a section"
|
||||
context="{'default_display_type': 'line_section'}"
|
||||
/>
|
||||
<create
|
||||
name="add_note_control"
|
||||
string="Add a note"
|
||||
context="{'default_display_type': 'line_note'}"
|
||||
/>
|
||||
</control>
|
||||
</list>
|
||||
</field>
|
||||
</group>
|
||||
<footer>
|
||||
<button
|
||||
name="add_set"
|
||||
string="Add set"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
/>
|
||||
<button special="cancel" string="Cancel" class="oe_link" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record
|
||||
id="act_open_wizard_product_set_add_from_order"
|
||||
model="ir.actions.act_window"
|
||||
>
|
||||
<field name="name">Add set in sale order</field>
|
||||
<field name="res_model">sale.product.set.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="product_set_add_form_view" />
|
||||
<field name="domain">[]</field>
|
||||
<field name="context">{'default_order_id': order_id}</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
<record id="act_open_wizard_product_set_add_from_set" model="ir.actions.act_window">
|
||||
<field name="name">Add set to sale order</field>
|
||||
<field name="res_model">sale.product.set.wizard</field>
|
||||
<field name="binding_model_id" ref="product_set.model_product_set" />
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="product_set_add_form_view" />
|
||||
<field name="domain">[]</field>
|
||||
<field name="context">{'default_product_set_id': product_set_id}</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user