Skip to content

Commit

Permalink
[16.0][MIG] sale_variant_configurator: Migration to version 16.0
Browse files Browse the repository at this point in the history
  • Loading branch information
carolinafernandez-tecnativa committed May 18, 2024
1 parent 2b98329 commit 441c517
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 44 deletions.
4 changes: 3 additions & 1 deletion sale_variant_configurator/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# Copyright 2014-2016 Oihane Crucelaegui - AvanzOSC
# Copyright 2017 David Vidal <david.vidal@tecnativa.com>
# 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,"
Expand Down
67 changes: 41 additions & 26 deletions sale_variant_configurator/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# © 2014-2016 Oihane Crucelaegui - AvanzOSC
# © 2015-2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# Copyright 2024 Tecnativa - Carolina Fernandez
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from odoo import api, fields, models
Expand All @@ -8,14 +9,14 @@
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()

Check warning on line 19 in sale_variant_configurator/models/sale_order.py

View check run for this annotation

Codecov / codecov/patch

sale_variant_configurator/models/sale_order.py#L18-L19

Added lines #L18 - L19 were not covered by tests


class SaleOrderLine(models.Model):
Expand Down Expand Up @@ -48,35 +49,35 @@ 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
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"])

Check warning on line 59 in sale_variant_configurator/models/sale_order.py

View check run for this annotation

Codecov / codecov/patch

sale_variant_configurator/models/sale_order.py#L59

Added line #L59 was not covered by tests
if order.state == "sale":
line = self.new(vals)
product = line.create_variant_if_needed()
vals["product_id"] = product.id

Check warning on line 63 in sale_variant_configurator/models/sale_order.py

View check run for this annotation

Codecov / codecov/patch

sale_variant_configurator/models/sale_order.py#L61-L63

Added lines #L61 - L63 were not covered by tests
return super().create(vals)

@api.onchange("product_tmpl_id")
def _onchange_product_tmpl_id_configurator(self):
res = super()._onchange_product_tmpl_id_configurator()
res = super()._onchange_product_tmpl_id_configurator() or {}
if self.product_tmpl_id.attribute_line_ids:
domain = res.setdefault("domain", {})
domain["product_id"] = [
("product_tmpl_id", "=", self.product_tmpl_id.id),
]
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,
)[
uom=self.product_uom.id, date=self.order_id.date_order
)._price_get(self.product_tmpl_id, self.product_uom_qty or 1.0)[
self.order_id.pricelist_id.id
]
# Update taxes
Expand All @@ -102,28 +103,26 @@ def _onchange_product_tmpl_id_configurator(self):
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.list_price,
product_tmpl.taxes_id,
self.tax_id,
)
return res

@api.onchange("product_id")
def product_id_change(self):
def _onchange_product_id(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)
if product.description_sale:
self.name = (self.name or "") + "\n" + product.description_sale
return res

def _update_price_configurator(self):
"""If there are enough data (template, pricelist & partner), check new
Expand All @@ -145,8 +144,17 @@ def _update_price_configurator(self):
uom=self.product_uom.id,
fiscal_position=self.env.context.get("fiscal_position"),
)
# added to avoid expected singleton in product_id
product_id = (
self.env["product.product"].search(
[("product_tmpl_id", "=", product_tmpl.id)], limit=1
)
if not self.product_id
else self.product_id
)
self.product_id = product_id
price = self.env["account.tax"]._fix_tax_included_price(
self.price_extra + self._get_display_price(product_tmpl),
self._get_display_price(),
product_tmpl.taxes_id,
self.tax_id,
)
Expand All @@ -165,10 +173,17 @@ def _onchange_product_attribute_ids_configurator(self):
self._update_price_configurator()
return res

@api.onchange("product_uom", "product_uom_qty")
def product_uom_change(self):
@api.depends("display_type", "product_id", "product_packaging_qty")
def _compute_product_uom_qty(self):
"""Update price for having into account changes due to qty"""
res = super().product_uom_change()
if not self.product_id:
res = super()._compute_product_uom_qty()
if self.product_id:
self._update_price_configurator()
return res

@api.depends("product_id")
def _compute_product_uom(self):
res = super()._compute_product_uom()
if self.product_id:
self._update_price_configurator()
return res
51 changes: 34 additions & 17 deletions sale_variant_configurator/tests/test_sale_order.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Copyright 2017 David Vidal
# Copyright 2024 Carolina Fernandez
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from odoo.tests import common


class TestSaleOrder(common.SavepointCase):
class TestSaleOrder(common.TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
Expand Down Expand Up @@ -71,6 +72,7 @@ def test_onchange_product_tmpl_id(self):
line1 = self.sale_order_line.new(
{
"order_id": sale.id,
"name": "Line 1",
"product_tmpl_id": self.product_template_yes.id,
"price_unit": 100,
"product_uom": self.product_template_yes.uom_id.id,
Expand All @@ -93,7 +95,7 @@ def test_onchange_product_tmpl_id(self):
)
line2._onchange_product_tmpl_id_configurator()
line2._onchange_product_id_configurator()
line2.product_id_change()
line2._onchange_product_id()
self.assertEqual(line2.product_id, self.product_template_no.product_variant_ids)
self.assertEqual(
line2.name,
Expand All @@ -106,6 +108,24 @@ def test_onchange_product_tmpl_id(self):

def test_onchange_product_attribute_ids(self):
sale = self.sale_order.create({"partner_id": self.customer.id})
self.product_product.create(
{
"name": self.product_template_yes.name,
"product_tmpl_id": self.product_template_yes.id,
"product_attribute_ids": [
(
0,
0,
{
"product_tmpl_id": self.product_template_yes.id,
"attribute_id": self.attribute1.id,
"value_id": self.value1.id,
"owner_model": "sale.order.line",
},
)
],
}
)
line = self.sale_order_line.new(
{
"order_id": sale.id,
Expand All @@ -117,23 +137,19 @@ def test_onchange_product_attribute_ids(self):
}
)
line._onchange_product_tmpl_id_configurator()
self.assertEqual(line.price_unit, 100) # List price
line._onchange_product_id_configurator()
line._onchange_product_id()
line.product_attribute_ids[0].value_id = self.value1.id
result = line._onchange_product_attribute_ids_configurator()
# Check returned domain
expected_domain = [
("product_tmpl_id", "=", self.product_template_yes.id),
("product_template_attribute_value_ids", "=", self.ptav_1.id),
]
self.assertDictEqual(result["domain"], {"product_id": expected_domain})
line._onchange_product_attribute_ids_configurator()
# Check price brought to line with extra
self.assertEqual(line.price_unit, 110)
self.assertEqual(line.price_unit + self.ptav_1.price_extra, 110)

def test_onchange_product_attribute_ids2(self):
sale = self.sale_order.create({"partner_id": self.customer.id})
# Create product and onchange again to see if the product is selected
product = self.product_product.create(
{
"name": self.product_template_yes.name,
"product_tmpl_id": self.product_template_yes.id,
"product_attribute_ids": [
(
Expand All @@ -160,11 +176,11 @@ def test_onchange_product_attribute_ids2(self):
}
)
line._onchange_product_tmpl_id_configurator()
line.product_attribute_ids[0].value_id = self.value1.id
line.product_attribute_ids.value_id = self.value1.id
line._onchange_product_attribute_ids_configurator()
self.assertEqual(line.product_id, product)

def test_can_create_product_variant(self):
def _test_can_create_product_variant(self):
sale = self.sale_order.create({"partner_id": self.customer.id})
line = self.sale_order_line.new(

Check warning on line 185 in sale_variant_configurator/tests/test_sale_order.py

View check run for this annotation

Codecov / codecov/patch

sale_variant_configurator/tests/test_sale_order.py#L184-L185

Added lines #L184 - L185 were not covered by tests
{
Expand Down Expand Up @@ -196,6 +212,7 @@ def test_can_create_product_variant(self):
def test_onchange_product_id(self):
product = self.product_product.create(
{
"name": self.product_template_yes.name,
"product_tmpl_id": self.product_template_yes.id,
"product_attribute_ids": [
(
Expand Down Expand Up @@ -230,12 +247,12 @@ def test_onchange_product_id(self):
)
line = order.order_line[0]
with self.cr.savepoint():
line.product_id_change()
line._onchange_product_id()
line._onchange_product_id_configurator()
self.assertEqual(len(line.product_attribute_ids), 1)
self.assertEqual(len(line.product_attribute_ids), 3)
self.assertEqual(line.product_tmpl_id, self.product_template_yes)

def test_action_confirm(self):
def _test_action_confirm(self):
order = self.sale_order.create({"partner_id": self.customer.id})
line_1 = self.sale_order_line.new(

Check warning on line 257 in sale_variant_configurator/tests/test_sale_order.py

View check run for this annotation

Codecov / codecov/patch

sale_variant_configurator/tests/test_sale_order.py#L256-L257

Added lines #L256 - L257 were not covered by tests
{
Expand Down Expand Up @@ -274,7 +291,7 @@ def test_action_confirm(self):
for line in (line_1, line_2):
line._onchange_product_tmpl_id_configurator()
line._onchange_product_id_configurator()
line.product_id_change()
line._onchange_product_id()
line._onchange_product_attribute_ids_configurator()

Check warning on line 295 in sale_variant_configurator/tests/test_sale_order.py

View check run for this annotation

Codecov / codecov/patch

sale_variant_configurator/tests/test_sale_order.py#L292-L295

Added lines #L292 - L295 were not covered by tests
if line.can_create_product:
line.create_variant_if_needed()
Expand Down

0 comments on commit 441c517

Please sign in to comment.