-
-
Notifications
You must be signed in to change notification settings - Fork 268
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[MIG] sale_variant_configurator: Migration to version 16.0
TT46599
- Loading branch information
1 parent
2b98329
commit 511e6a6
Showing
6 changed files
with
85 additions
and
195 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,14 @@ | ||
# Copyright 2014-2016 Oihane Crucelaegui - AvanzOSC | ||
# Copyright 2017 David Vidal <[email protected]> | ||
# Copyright 2015-2021 Tecnativa - Pedro M. Baeza | ||
# Copyright 2024 Tecnativa - Carolina Fernandez | ||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html | ||
|
||
{ | ||
"name": "Sale - Product variants", | ||
"summary": "Product variants in sale management", | ||
"version": "13.0.1.0.1", | ||
"version": "16.0.1.0.0", | ||
"development_status": "Production/Stable", | ||
"license": "AGPL-3", | ||
"depends": ["sale", "product_variant_configurator"], | ||
"author": "OdooMRP team," | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
# © 2014-2016 Oihane Crucelaegui - AvanzOSC | ||
# © 2015-2016 Pedro M. Baeza <[email protected]> | ||
# Copyright 2024 Tecnativa - Carolina Fernandez | ||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html | ||
|
||
from odoo import api, fields, models | ||
|
@@ -8,19 +9,20 @@ | |
class SaleOrder(models.Model): | ||
_inherit = "sale.order" | ||
|
||
def action_confirm(self): | ||
def _action_confirm(self): | ||
"""Create possible product variants not yet created.""" | ||
lines_without_product = self.mapped("order_line").filtered( | ||
lambda x: not x.product_id and x.product_tmpl_id | ||
) | ||
for line in lines_without_product: | ||
line.create_variant_if_needed() | ||
return super().action_confirm() | ||
return super()._action_confirm() | ||
|
||
|
||
class SaleOrderLine(models.Model): | ||
_inherit = ["sale.order.line", "product.configurator"] | ||
_name = "sale.order.line" | ||
_partner_id_field = "order_partner_id" | ||
|
||
product_tmpl_id = fields.Many2one( | ||
store=True, | ||
|
@@ -48,127 +50,30 @@ class SaleOrderLine(models.Model): | |
), | ||
] | ||
|
||
@api.model | ||
def create(self, vals): | ||
@api.model_create_multi | ||
def create(self, vals_list): | ||
"""Create product if not exist when the sales order is already | ||
confirmed and a line is added. | ||
""" | ||
if vals.get("order_id") and not vals.get("product_id"): | ||
order = self.env["sale.order"].browse(vals["order_id"]) | ||
if order.state == "sale": | ||
line = self.new(vals) | ||
product = line.create_variant_if_needed() | ||
vals["product_id"] = product.id | ||
return super().create(vals) | ||
for vals in vals_list: | ||
if vals.get("order_id") and not vals.get("product_id"): | ||
order = self.env["sale.order"].browse(vals["order_id"]) | ||
if order.state == "sale": | ||
line = self.new(vals) | ||
product = line.create_variant_if_needed() | ||
vals["product_id"] = product.id | ||
return super().create(vals_list) | ||
|
||
@api.onchange("product_tmpl_id") | ||
def _onchange_product_tmpl_id_configurator(self): | ||
res = super()._onchange_product_tmpl_id_configurator() | ||
if self.product_tmpl_id.attribute_line_ids: | ||
domain = res.setdefault("domain", {}) | ||
domain["product_uom"] = [ | ||
("category_id", "=", self.product_tmpl_id.uom_id.category_id.id), | ||
] | ||
self.product_uom = self.product_tmpl_id.uom_id | ||
self.price_unit = self.order_id.pricelist_id.with_context( | ||
{"uom": self.product_uom.id, "date": self.order_id.date_order} | ||
).template_price_get( | ||
self.product_tmpl_id.id, | ||
self.product_uom_qty or 1.0, | ||
self.order_id.partner_id.id, | ||
)[ | ||
self.order_id.pricelist_id.id | ||
] | ||
# Update taxes | ||
fpos = ( | ||
self.order_id.fiscal_position_id | ||
or self.order_id.partner_id.property_account_position_id | ||
) | ||
# If company_id is set, always filter taxes by the company | ||
taxes = self.product_tmpl_id.taxes_id.filtered( | ||
lambda r: not self.company_id or r.company_id == self.company_id | ||
) | ||
self.tax_id = fpos.map_tax(taxes) if fpos else taxes | ||
product_tmpl = self.product_tmpl_id.with_context( | ||
lang=self.order_id.partner_id.lang, | ||
partner=self.order_id.partner_id, | ||
quantity=self.product_uom_qty, | ||
date=self.order_id.date_order, | ||
pricelist=self.order_id.pricelist_id.id, | ||
uom=self.product_uom.id, | ||
@api.model | ||
def _get_product_description(self, template, product, product_attributes): | ||
res = super()._get_product_description( | ||
template=template, product=product, product_attributes=product_attributes | ||
) | ||
# product_configurator methods don't take into account this description | ||
if product_tmpl.description_sale: | ||
self.name = (self.name or "") + "\n" + product_tmpl.description_sale | ||
if self.order_id.pricelist_id and self.order_id.partner_id: | ||
self.price_unit = self.env["account.tax"]._fix_tax_included_price( | ||
product_tmpl.price, | ||
product_tmpl.taxes_id, | ||
self.tax_id, | ||
) | ||
return res | ||
|
||
@api.onchange("product_id") | ||
def product_id_change(self): | ||
"""Call again the configurator onchange after this main onchange | ||
for making sure the SO line description is correct. | ||
It also puts the proper lang in context for getting the product and | ||
attributes in the customer language. | ||
""" | ||
obj = self.with_context(lang=self.order_id.partner_id.lang) | ||
res = super(SaleOrderLine, obj).product_id_change() | ||
obj._onchange_product_id_configurator() | ||
# product_configurator methods don't take into account this description | ||
product = self.product_id.with_context(lang=self.order_id.partner_id.lang) | ||
product = self.product_id.with_context(lang=self.order_partner_id.lang) | ||
if product.description_sale: | ||
self.name = (self.name or "") + "\n" + product.description_sale | ||
res = (res or "") + "\n" + product.description_sale | ||
return res | ||
|
||
def _update_price_configurator(self): | ||
"""If there are enough data (template, pricelist & partner), check new | ||
price and update line if different. | ||
""" | ||
self.ensure_one() | ||
if ( | ||
not self.product_tmpl_id | ||
or not self.order_id.pricelist_id | ||
or not self.order_id.partner_id | ||
): | ||
return | ||
product_tmpl = self.product_tmpl_id.with_context( | ||
lang=self.order_id.partner_id.lang, | ||
partner=self.order_id.partner_id, | ||
quantity=self.product_uom_qty, | ||
date_order=self.order_id.date_order, | ||
pricelist=self.order_id.pricelist_id.id, | ||
uom=self.product_uom.id, | ||
fiscal_position=self.env.context.get("fiscal_position"), | ||
) | ||
price = self.env["account.tax"]._fix_tax_included_price( | ||
self.price_extra + self._get_display_price(product_tmpl), | ||
product_tmpl.taxes_id, | ||
self.tax_id, | ||
) | ||
if self.price_unit != price: | ||
self.price_unit = price | ||
|
||
@api.onchange("product_attribute_ids") | ||
def _onchange_product_attribute_ids_configurator(self): | ||
"""Update price for having into account possible extra prices. | ||
It also puts the proper lang in context for getting the product and | ||
attributes in the customer language. | ||
""" | ||
obj = self.with_context(lang=self.order_id.partner_id.lang) | ||
res = super(SaleOrderLine, obj)._onchange_product_attribute_ids_configurator() | ||
self._update_price_configurator() | ||
return res | ||
|
||
@api.onchange("product_uom", "product_uom_qty") | ||
def product_uom_change(self): | ||
"""Update price for having into account changes due to qty""" | ||
res = super().product_uom_change() | ||
if not self.product_id: | ||
self._update_price_configurator() | ||
return res | ||
@api.depends("product_attribute_ids") | ||
def _compute_price_unit(self): | ||
return super()._compute_price_unit() |
Oops, something went wrong.