Skip to content
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

[14.0][REF] sale_product_pack: improve coverage and make it easy to overload #159

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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 sale_product_pack/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from . import models
from . import product_pack_line
from . import sale_order_line
from . import sale_order
32 changes: 32 additions & 0 deletions sale_product_pack/models/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright 2023 Foodles (http://www.foodles.co).
# @author Pierre Verkest <pierreverkest84@gmail.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from collections import defaultdict

from odoo import models


class BaseModel(models.AbstractModel):
_inherit = "base"

def group_recordset_by(self, key):
"""Return a collection of pairs ``(key, recordset)`` from ``self``. The
``key`` is a function computing a key value for each element. This
function is similar to ``itertools.groupby``, but aggregates all
elements under the same key, not only consecutive elements.

it's also similar to ``òdoo.tools.misc.groupby`` but return a recordset
of sale.order.line instead list

this let write some code likes this::

my_recordset.filtered(
lambda record: record.to_use
).group_recordset_by(
lambda record: record.group_key
)
"""
groups = defaultdict(self.env[self._name].browse)
for elem in self:
groups[key(elem)] |= elem
return groups.items()
20 changes: 0 additions & 20 deletions sale_product_pack/models/sale_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,3 @@ def check_pack_line_unlink(self):
" delete the pack itself"
)
)

def write(self, vals):
if "order_line" in vals:
to_delete_ids = [e[1] for e in vals["order_line"] if e[0] == 2]
subpacks_to_delete_ids = (
self.env["sale.order.line"]
.search(
[("id", "child_of", to_delete_ids), ("id", "not in", to_delete_ids)]
)
.ids
)
if subpacks_to_delete_ids:
for cmd in vals["order_line"]:
if cmd[1] in subpacks_to_delete_ids:
if cmd[0] != 2:
cmd[0] = 2
subpacks_to_delete_ids.remove(cmd[1])
for to_delete_id in subpacks_to_delete_ids:
vals["order_line"].append([2, to_delete_id, False])
return super().write(vals)
29 changes: 28 additions & 1 deletion sale_product_pack/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright 2019 Tecnativa - Ernesto Tejeda
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.fields import first
Expand Down Expand Up @@ -83,9 +84,23 @@ def create(self, vals):
record.expand_pack_line()
return record

@api.model
def _pack_fields_trigger_expand_pack_line_on_write(self):
"""A set of fields that will trigger expand pack line
To propagate information over sale order line add your
field in this list and overload the following method:
`ProductPack.get_sale_order_line_vals`
Be aware if pack line are "modifiable" user input can
be overwrite once save if one of this field as been
changed on the pack line...
"""
return {"product_id", "product_uom_qty"}

def write(self, vals):
res = super().write(vals)
if "product_id" in vals or "product_uom_qty" in vals:
if self._pack_fields_trigger_expand_pack_line_on_write() & set(vals.keys()):
for record in self:
record.expand_pack_line(write=True)
return res
Expand Down Expand Up @@ -121,3 +136,15 @@ def action_open_parent_pack_product_view(self):
"view_mode": "tree,form",
"domain": domain,
}

def unlink(self):
for order, lines in self.group_recordset_by(lambda sol: sol.order_id):
pack_component_to_delete = self.env["sale.order.line"].search(
petrus-v marked this conversation as resolved.
Show resolved Hide resolved
[
("id", "child_of", lines.ids),
("id", "not in", lines.ids),
("order_id", "=", order.id),
]
)
pack_component_to_delete.unlink()
return super().unlink()
191 changes: 190 additions & 1 deletion sale_product_pack/tests/test_sale_product_pack.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Copyright 2019 Tecnativa - Ernesto Tejeda
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo.tests import SavepointCase
from odoo.exceptions import UserError
from odoo.tests import Form, SavepointCase


class TestSaleProductPack(SavepointCase):
Expand Down Expand Up @@ -155,6 +156,7 @@ def qty_in_order():
}
)
total_qty_init = qty_in_order()

# change qty of main sol
main_sol.product_uom_qty = 2 * main_sol.product_uom_qty
total_qty_updated = qty_in_order()
Expand All @@ -169,6 +171,105 @@ def qty_in_order():
total_qty_confirmed = qty_in_order()
self.assertAlmostEqual(total_qty_updated * 2, total_qty_confirmed)

def test_update_qty_do_not_expand(self):
product_cp = self.env.ref("product_pack.product_pack_cpu_detailed_components")
main_sol = self.env["sale.order.line"].create(
{
"order_id": self.sale_order.id,
"name": product_cp.name,
"product_id": product_cp.id,
"product_uom_qty": 1,
}
)
main_sol.with_context(update_prices=True).product_uom_qty = 2
self.assertTrue(
all(
self.sale_order.order_line.filtered(
lambda sol: sol.pack_parent_line_id == main_sol
).mapped(lambda sol: sol.product_uom_qty == 1)
),
)

def test_update_pack_qty_with_new_component(self):
product_cp = self.env.ref("product_pack.product_pack_cpu_detailed_components")
main_sol = self.env["sale.order.line"].create(
{
"order_id": self.sale_order.id,
"name": product_cp.name,
"product_id": product_cp.id,
"product_uom_qty": 1,
}
)

self.assertEqual(
sum(
self.sale_order.order_line.filtered(
lambda sol: sol.pack_parent_line_id == main_sol
).mapped("product_uom_qty")
),
3,
"Expected 3 lines with quantity 1 while setup this test",
)

product_cp.pack_line_ids |= self.env["product.pack.line"].create(
{
"parent_product_id": product_cp.id,
"product_id": self.env.ref("product.product_product_12").id,
"quantity": 2,
}
)

main_sol.product_uom_qty = 2
self.assertEqual(
sum(
self.sale_order.order_line.filtered(
lambda sol: sol.pack_parent_line_id == main_sol
).mapped("product_uom_qty")
),
10,
"Expected 3 lines with quantity 2 and new component line with quantity 4",
)

def test_update_pack_qty_with_new_component_do_not_expand(self):
product_cp = self.env.ref("product_pack.product_pack_cpu_detailed_components")
main_sol = self.env["sale.order.line"].create(
{
"order_id": self.sale_order.id,
"name": product_cp.name,
"product_id": product_cp.id,
"product_uom_qty": 1,
}
)

self.assertEqual(
sum(
self.sale_order.order_line.filtered(
lambda sol: sol.pack_parent_line_id == main_sol
).mapped("product_uom_qty")
),
3,
"Expected 3 lines with quantity 1 while setup this test",
)

product_cp.pack_line_ids |= self.env["product.pack.line"].create(
{
"parent_product_id": product_cp.id,
"product_id": self.env.ref("product.product_product_12").id,
"quantity": 2,
}
)

main_sol.with_context(update_prices=True).product_uom_qty = 2
self.assertEqual(
sum(
self.sale_order.order_line.filtered(
lambda sol: sol.pack_parent_line_id == main_sol
).mapped("product_uom_qty")
),
3,
"Expected 3 lines with quantity 2 and no new component line",
)

def test_do_not_expand(self):
product_cp = self.env.ref("product_pack.product_pack_cpu_detailed_components")
pack_line = self.env["sale.order.line"].create(
Expand Down Expand Up @@ -221,3 +322,91 @@ def test_create_several_lines(self):
self.assertEqual(sequence_tp, self.sale_order.order_line[5].sequence)
self.assertEqual(sequence_tp, self.sale_order.order_line[6].sequence)
self.assertEqual(sequence_tp, self.sale_order.order_line[7].sequence)

def test_copy_sale_order_with_detailed_product_pack(self):
product_cp = self.env.ref("product_pack.product_pack_cpu_detailed_components")
self.env["sale.order.line"].create(
{
"order_id": self.sale_order.id,
"name": product_cp.name,
"product_id": product_cp.id,
"product_uom_qty": 1,
}
)
copied_order = self.sale_order.copy()
copied_order_component_lines_pack_line = copied_order.order_line.filtered(
lambda line: line.product_id.pack_ok
)
copied_order_component_lines = copied_order.order_line.filtered(
lambda line: line.pack_parent_line_id
)
self.assertEqual(
copied_order_component_lines.pack_parent_line_id,
copied_order_component_lines_pack_line,
)

def test_check_pack_line_unlink(self):
product_cp = self.env.ref("product_pack.product_pack_cpu_detailed_components")
self.env["sale.order.line"].create(
{
"order_id": self.sale_order.id,
"name": product_cp.name,
"product_id": product_cp.id,
"product_uom_qty": 1,
}
)
with Form(self.sale_order) as so_form:
with self.assertRaisesRegex(
UserError,
"You cannot delete this line because is part of a pack in this "
"sale order. In order to delete this line you need to delete the "
"pack itself",
):
so_form.order_line.remove(len(self.sale_order.order_line) - 1)

def test_unlink_pack_form_proxy(self):
product_cp = self.env.ref("product_pack.product_pack_cpu_detailed_components")
self.env["sale.order.line"].create(
{
"order_id": self.sale_order.id,
"name": product_cp.name,
"product_id": product_cp.id,
"product_uom_qty": 1,
}
)
with Form(self.sale_order) as so_form:
so_form.order_line.remove(0)
so_form.save()
self.assertEqual(len(self.sale_order.order_line), 0)

def test_unlink_pack_record_unlink(self):
product_cp = self.env.ref("product_pack.product_pack_cpu_detailed_components")
self.env["sale.order.line"].create(
{
"order_id": self.sale_order.id,
"name": product_cp.name,
"product_id": product_cp.id,
"product_uom_qty": 1,
}
)
pack_line = self.sale_order.order_line.filtered(
lambda line: line.product_id.pack_ok
)
pack_line.unlink()
self.assertEqual(len(self.sale_order.order_line), 0)

def test_unlink_pack_old_style_like_ui(self):
product_cp = self.env.ref("product_pack.product_pack_cpu_detailed_components")
self.env["sale.order.line"].create(
{
"order_id": self.sale_order.id,
"name": product_cp.name,
"product_id": product_cp.id,
"product_uom_qty": 1,
}
)
pack_line = self.sale_order.order_line.filtered(
lambda line: line.product_id.pack_ok
)
self.sale_order.write({"order_line": [(2, pack_line.id)]})
self.assertEqual(len(self.sale_order.order_line), 0)
Loading