categorias-productos #51 #52
@@ -214,8 +214,50 @@ class ProductTrytonService:
|
||||
changes.append(
|
||||
f"Unidad: {product.measuring_unit} → {unit_name}"
|
||||
)
|
||||
category_changes = self._describe_category_changes(
|
||||
product, tryton_product
|
||||
)
|
||||
if category_changes:
|
||||
changes.append(category_changes)
|
||||
return ", ".join(changes)
|
||||
|
||||
def _describe_category_changes(self, product, tryton_product):
|
||||
"""Describe los cambios de categorías entre el producto local y Tryton"""
|
||||
tryton_ids = set(
|
||||
str(category_id)
|
||||
for category_id in self._extract_category_ids(tryton_product)
|
||||
)
|
||||
local = {
|
||||
external_id: name
|
||||
for external_id, name in product.categories.values_list(
|
||||
"external_id", "name"
|
||||
)
|
||||
if external_id
|
||||
}
|
||||
local_ids = set(local)
|
||||
|
||||
added = sorted(tryton_ids - local_ids)
|
||||
removed = sorted(local_ids - tryton_ids)
|
||||
if not added and not removed:
|
||||
return ""
|
||||
|
||||
parts = []
|
||||
if added:
|
||||
names = {}
|
||||
for external_id in added:
|
||||
category = (
|
||||
ProductCategory.objects.filter(
|
||||
external_id=external_id
|
||||
)
|
||||
.values_list("name", flat=True)
|
||||
.first()
|
||||
)
|
||||
names[external_id] = category or external_id
|
||||
parts.append(", ".join(f"+{names[e]}" for e in added))
|
||||
if removed:
|
||||
parts.append(", ".join(f"-{local[e]}" for e in removed))
|
||||
return f"Categoría: {', '.join(parts)}"
|
||||
|
||||
@staticmethod
|
||||
def _format_price(value):
|
||||
"""Formatea un precio sin ceros decimales innecesarios"""
|
||||
@@ -269,6 +311,17 @@ class ProductTrytonService:
|
||||
unit = tryton_product.get("default_uom.")
|
||||
if not product.measuring_unit == unit.get("rec_name"):
|
||||
return True
|
||||
tryton_category_ids = set(
|
||||
str(category_id)
|
||||
for category_id in self._extract_category_ids(tryton_product)
|
||||
)
|
||||
local_category_ids = set(
|
||||
product.categories.exclude(
|
||||
external_id__isnull=True
|
||||
).values_list("external_id", flat=True)
|
||||
)
|
||||
if tryton_category_ids != local_category_ids:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _create_product(self, tryton_product):
|
||||
|
||||
@@ -661,7 +661,7 @@ class TestProductsFromTryton(TestCase, LoginMixin):
|
||||
|
||||
@patch("sabatron_tryton_rpc_client.client.Client.call")
|
||||
@patch("sabatron_tryton_rpc_client.client.Client.connect")
|
||||
def test_import_assigns_categories_to_untouched_products(
|
||||
def test_import_assigns_categories_marks_product_as_updated(
|
||||
self, mock_connect, mock_call
|
||||
):
|
||||
mock_connect.return_value = None
|
||||
@@ -724,12 +724,20 @@ class TestProductsFromTryton(TestCase, LoginMixin):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
content = json.loads(response.content.decode("utf-8"))
|
||||
self.assertEqual(content["untouched_products"], [])
|
||||
self.assertEqual(
|
||||
content["untouched_products"], [{"id": 2, "name": "Papa"}]
|
||||
content["updated_products"],
|
||||
[
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Papa",
|
||||
"detail": "Categoría: +Tubérculos",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
untouched_product = Product.objects.get(id=2)
|
||||
categories = untouched_product.categories.all()
|
||||
updated_product = Product.objects.get(id=2)
|
||||
categories = updated_product.categories.all()
|
||||
self.assertEqual(len(categories), 1)
|
||||
self.assertEqual(categories[0].name, "Tubérculos")
|
||||
self.assertEqual(categories[0].external_id, "9")
|
||||
@@ -744,3 +752,104 @@ class TestProductsFromTryton(TestCase, LoginMixin):
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
@patch("sabatron_tryton_rpc_client.client.Client.call")
|
||||
@patch("sabatron_tryton_rpc_client.client.Client.connect")
|
||||
def test_category_change_marks_product_as_updated(
|
||||
self, mock_connect, mock_call
|
||||
):
|
||||
mock_connect.return_value = None
|
||||
local_abarrotes = ProductCategory.objects.create(
|
||||
name="Abarrotes", external_id="5"
|
||||
)
|
||||
self.product2.categories.add(local_abarrotes)
|
||||
|
||||
def fake_call(*args, **kwargs):
|
||||
product_search = "model.product.product.search"
|
||||
search_args = [
|
||||
[["salable", "=", True]],
|
||||
0,
|
||||
1000,
|
||||
[["rec_name", "ASC"], ["id", None]],
|
||||
{"company": 1},
|
||||
]
|
||||
if args == (product_search, search_args):
|
||||
return [192]
|
||||
|
||||
product_read = "model.product.product.read"
|
||||
product_args = (
|
||||
[192],
|
||||
[
|
||||
"id",
|
||||
"name",
|
||||
"default_uom.id",
|
||||
"default_uom.rec_name",
|
||||
"list_price",
|
||||
"template.categories",
|
||||
],
|
||||
{"company": 1},
|
||||
)
|
||||
if args == (product_read, product_args):
|
||||
return [
|
||||
{
|
||||
"id": 192,
|
||||
"list_price": Decimal("4500"),
|
||||
"name": "Papa",
|
||||
"default_uom.": {"id": 2, "rec_name": "Kilogram"},
|
||||
"template.": {"id": 999, "categories": [8]},
|
||||
},
|
||||
]
|
||||
|
||||
category_read = "model.product.category.read"
|
||||
category_args = (
|
||||
[8],
|
||||
["id", "name"],
|
||||
{"company": 1},
|
||||
)
|
||||
if args == (category_read, category_args):
|
||||
return [
|
||||
{"id": 8, "name": "Bebidas"},
|
||||
]
|
||||
|
||||
raise Exception(
|
||||
f"Sorry, args non expected on this test: {args}"
|
||||
)
|
||||
|
||||
mock_call.side_effect = fake_call
|
||||
|
||||
url = "/don_confiao/api/importar_productos_de_tryton"
|
||||
response = self.client.post(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
content = json.loads(response.content.decode("utf-8"))
|
||||
self.assertEqual(content["untouched_products"], [])
|
||||
self.assertEqual(content["failed_products"], [])
|
||||
self.assertEqual(
|
||||
content["updated_products"],
|
||||
[
|
||||
{
|
||||
"id": self.product2.pk,
|
||||
"name": "Papa",
|
||||
"detail": "Categoría: +Bebidas, -Abarrotes",
|
||||
}
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
content["created_categories"],
|
||||
[
|
||||
{
|
||||
"id": ProductCategory.objects.get(
|
||||
external_id="8"
|
||||
).pk,
|
||||
"name": "Bebidas",
|
||||
"external_id": "8",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
refreshed_product = Product.objects.get(pk=self.product2.pk)
|
||||
categories = list(refreshed_product.categories.all())
|
||||
self.assertEqual(
|
||||
[(c.external_id, c.name) for c in categories],
|
||||
[("8", "Bebidas")],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user