#51 refactor: traer categorias anidadas en un solo request de productos

Issue #51: discriminacion y administracion de categorias de productos.

- product.read ahora solicita template.categories.name; se elimina la
  llamada separada model.product.category.read

- ProductTrytonService y CategoryTrytonService construyen el mapa
  {id_tryton: nombre} desde 'template.categories.' de los productos leídos
- import_from_external pasa de 3 llamadas RPC a 2
This commit is contained in:
2026-10-10 17:52:21 -05:00
parent 128ba215dd
commit 6e14dd9f36
5 changed files with 112 additions and 111 deletions

View File

@@ -53,15 +53,14 @@ class CategoryTrytonService:
return self._empty_report()
tryton_products = self._get_product_details(product_ids, context)
category_ids = set()
for tryton_product in tryton_products:
category_ids.update(self._extract_category_ids(tryton_product))
if not category_ids:
category_names = self._extract_category_names(tryton_products)
if not category_names:
return self._empty_report()
tryton_categories = self._get_category_details(
sorted(category_ids), context
)
tryton_categories = [
{"id": category_id, "name": category_names[category_id]}
for category_id in sorted(category_names)
]
checked = [
{"id": c.get("id"), "name": c.get("name")}
@@ -121,21 +120,27 @@ class CategoryTrytonService:
return self.client.call(method, params) or []
def _get_product_details(self, product_ids, context):
tryton_fields = ["id", "name", "template.categories"]
tryton_fields = [
"id",
"name",
"template.categories",
"template.categories.name",
]
method = "model.product.product.read"
params = (product_ids, tryton_fields, context)
return self.client.call(method, params)
@staticmethod
def _extract_category_ids(tryton_product):
"""Extrae los IDs de categorías de la respuesta RPC de Tryton"""
template = tryton_product.get("template.") or {}
return template.get("categories") or []
def _get_category_details(self, category_ids, context):
method = "model.product.category.read"
params = (category_ids, ["id", "name"], context)
return self.client.call(method, params)
def _extract_category_names(tryton_products):
"""Extrae el mapa {id_tryton: nombre} de las categorías anidadas en
la respuesta de product.read (clave 'template.categories.name',
agrupada bajo 'template.' como 'categories.')"""
names = {}
for tryton_product in tryton_products:
template = tryton_product.get("template.") or {}
for category in template.get("categories.") or []:
names[category.get("id")] = category.get("name")
return names
def _empty_report(self):
return {

View File

@@ -107,19 +107,16 @@ class ProductTrytonService:
reporte {"created": [...], "updated": [...]}
"""
empty_report = {"created": [], "updated": []}
category_ids = {
category_id
for tryton_product in tryton_products
for category_id in self._extract_category_ids(tryton_product)
}
if not category_ids:
category_names = self._extract_category_names(tryton_products)
if not category_names:
return {}, empty_report
categories = {}
report = {"created": [], "updated": []}
tryton_categories = self._get_category_details(
sorted(category_ids), context
)
tryton_categories = [
{"id": category_id, "name": category_names[category_id]}
for category_id in sorted(category_names)
]
for tryton_category in tryton_categories:
try:
category, status, changes = upsert_category(tryton_category)
@@ -149,11 +146,17 @@ class ProductTrytonService:
template = tryton_product.get("template.") or {}
return template.get("categories") or []
def _get_category_details(self, category_ids, context):
"""Obtiene los nombres de categorías desde Tryton"""
method = "model.product.category.read"
params = (category_ids, ["id", "name"], context)
return self.client.call(method, params)
@staticmethod
def _extract_category_names(tryton_products):
"""Extrae el mapa {id_tryton: nombre} de las categorías anidadas en
la respuesta de product.read (clave 'template.categories.name',
agrupada bajo 'template.' como 'categories.')"""
names = {}
for tryton_product in tryton_products:
template = tryton_product.get("template.") or {}
for category in template.get("categories.") or []:
names[category.get("id")] = category.get("name")
return names
def _sync_product_categories(self, product, tryton_product, categories):
"""Vincula al producto las categorías importadas desde Tryton"""
@@ -271,6 +274,7 @@ class ProductTrytonService:
"default_uom.rec_name",
"list_price",
"template.categories",
"template.categories.name",
]
method = "model.product.product.read"
params = (product_ids, tryton_fields, context)