forked from shopinvader/odoo-shopinvader
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
shopinvader: refactor and improve binding wiz
* delegate to backend * collect records in a job * create one job per template This way, we get: * no frozen instance if tons of records to be created * if a product fails it doesn't make other fail
- Loading branch information
Showing
9 changed files
with
148 additions
and
69 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
# @author Simone Orsi <[email protected]> | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
||
from collections import defaultdict | ||
from contextlib import contextmanager | ||
|
||
from odoo import _, api, fields, models, tools | ||
|
@@ -408,6 +409,95 @@ def bind_all_category(self, domain=None): | |
# TODO: we should exclude levels from `category_binding_level` as well | ||
self._bind_all_content("product.category", "shopinvader.category", domain) | ||
|
||
def bind_selected_products(self, products, langs=None, run_immediately=False): | ||
"""Bind given product variants. | ||
:param products: product.product recordset | ||
:param langs: res.lang recordset. If none, all langs from backend | ||
:param run_immediately: do not use jobs | ||
""" | ||
for backend in self: | ||
langs = langs or backend.lang_ids | ||
grouped_by_template = defaultdict(self.env["product.product"].browse) | ||
for rec in products: | ||
grouped_by_template[rec.product_tmpl_id] |= rec | ||
method = backend.with_delay().bind_single_product | ||
if run_immediately: | ||
method = backend.bind_single_product | ||
for tmpl, variants in grouped_by_template.items(): | ||
method(langs, tmpl, variants) | ||
|
||
def bind_single_product(self, langs, product_tmpl, variants): | ||
"""Bind given product variants for given template and languages. | ||
:param langs: res.lang recordset | ||
:param product_tmpl: product.template browse record | ||
:param variants: product.product recordset | ||
:param run_immediately: do not use jobs | ||
""" | ||
self.ensure_one() | ||
shopinvader_products = self._get_or_create_shopinvader_products( | ||
langs, product_tmpl | ||
) | ||
for shopinvader_product in shopinvader_products: | ||
self._get_or_create_shopinvader_variants(shopinvader_product, variants) | ||
self.auto_bind_categories() | ||
|
||
def _get_or_create_shopinvader_products(self, langs, product_tmpl): | ||
"""Get template bindings for given languages or create if missing. | ||
:param langs: res.lang recordset | ||
:param product_tmpl: product.template browse record | ||
""" | ||
binding_model = self.env["shopinvader.product"].with_context(active_test=False) | ||
bound_templates = binding_model.search( | ||
[ | ||
("record_id", "=", product_tmpl.id), | ||
("backend_id", "=", self.id), | ||
("lang_id", "in", langs.ids), | ||
] | ||
) | ||
for lang in langs: | ||
shopinvader_product = bound_templates.filtered(lambda x: x.lang_id == lang) | ||
if not shopinvader_product: | ||
# fmt: off | ||
data = { | ||
"record_id": product_tmpl.id, | ||
"backend_id": self.id, | ||
"lang_id": lang.id, | ||
} | ||
# fmt: on | ||
bound_templates |= binding_model.create(data) | ||
elif not shopinvader_product.active: | ||
shopinvader_product.write({"active": True}) | ||
return bound_templates | ||
|
||
def _get_or_create_shopinvader_variants(self, shopinvader_product, variants): | ||
"""Get variant bindings, create if missing. | ||
:param langs: res.lang recordset | ||
:param product_tmpl: product.template browse record | ||
""" | ||
binding_model = self.env["shopinvader.variant"] | ||
bound_variants = shopinvader_product.shopinvader_variant_ids | ||
for variant in variants: | ||
shopinvader_variant = bound_variants.filtered( | ||
lambda p: p.record_id == variant | ||
) | ||
if not shopinvader_variant: | ||
# fmt: off | ||
data = { | ||
"record_id": variant.id, | ||
"backend_id": self.id, | ||
"shopinvader_product_id": | ||
shopinvader_product.id, | ||
} | ||
# fmt: on | ||
bound_variants |= binding_model.create(data) | ||
elif not shopinvader_variant.active: | ||
shopinvader_variant.write({"active": True}) | ||
return bound_variants | ||
|
||
def _send_notification(self, notification, record): | ||
self.ensure_one() | ||
record.ensure_one() | ||
|
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
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,8 +1,8 @@ | ||
# Copyright 2017 ACSONE SA/NV | ||
# Copyright 2021 Camptocamp (http://www.camptocamp.com). | ||
# @author Simone Orsi <[email protected]> | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
||
from collections import defaultdict | ||
|
||
from odoo import api, fields, models | ||
|
||
|
||
|
@@ -27,6 +27,7 @@ class ShopinvaderVariantBindingWizard(models.TransientModel): | |
help="List of langs for which a binding must exists. If not " | ||
"specified, the list of langs defined on the backend is used.", | ||
) | ||
run_immediately = fields.Boolean(help="Do not schedule jobs.") | ||
|
||
@api.model | ||
def default_get(self, fields_list): | ||
|
@@ -38,77 +39,26 @@ def default_get(self, fields_list): | |
res["lang_ids"] = [(6, None, backend.lang_ids.ids)] | ||
return res | ||
|
||
def _get_langs_to_bind(self): | ||
self.ensure_one() | ||
return self.lang_ids or self.backend_id.lang_ids | ||
|
||
def _get_bound_templates(self): | ||
""" | ||
return a dict of bound shopinvader.product by product template id | ||
:return: | ||
""" | ||
self.ensure_one() | ||
binding = self.env["shopinvader.product"] | ||
product_template_ids = self.mapped("product_ids.product_tmpl_id") | ||
bound_templates = binding.with_context(active_test=False).search( | ||
[ | ||
("record_id", "in", product_template_ids.ids), | ||
("backend_id", "=", self.backend_id.id), | ||
("lang_id", "in", self._get_langs_to_bind().ids), | ||
] | ||
) | ||
ret = defaultdict(dict) | ||
for bt in bound_templates: | ||
ret[bt.record_id][bt.lang_id] = bt | ||
for product in self.mapped("product_ids.product_tmpl_id"): | ||
product_tmpl_id = product | ||
bind_records = ret.get(product_tmpl_id) | ||
for lang_id in self._get_langs_to_bind(): | ||
bind_record = bind_records and bind_records.get(lang_id) | ||
if not bind_record: | ||
data = { | ||
"record_id": product.id, | ||
"backend_id": self.backend_id.id, | ||
"lang_id": lang_id.id, | ||
} | ||
ret[product_tmpl_id][lang_id] = binding.create(data) | ||
elif not bind_record.active: | ||
bind_record.write({"active": True}) | ||
return ret | ||
|
||
def bind_products(self): | ||
for wizard in self: | ||
bound_templates = wizard._get_bound_templates() | ||
binding = self.env["shopinvader.variant"] | ||
for product in wizard.product_ids: | ||
bound_products = bound_templates[product.product_tmpl_id] | ||
for lang_id in wizard._get_langs_to_bind(): | ||
for shopinvader_product in bound_products[lang_id]: | ||
bound_variants = shopinvader_product.shopinvader_variant_ids | ||
bind_record = bound_variants.filtered( | ||
lambda p: p.record_id == product | ||
) | ||
if not bind_record: | ||
# fmt: off | ||
data = { | ||
"record_id": product.id, | ||
"backend_id": wizard.backend_id.id, | ||
"shopinvader_product_id": | ||
shopinvader_product.id, | ||
} | ||
# fmt: on | ||
binding.create(data) | ||
elif not bind_record.active: | ||
bind_record.write({"active": True}) | ||
wizard.backend_id.auto_bind_categories() | ||
backend = wizard.backend_id | ||
method = backend.with_delay().bind_selected_products | ||
if wizard.run_immediately: | ||
method = backend.bind_selected_products | ||
method( | ||
wizard.product_ids, | ||
langs=wizard.lang_ids, | ||
run_immediately=wizard.run_immediately, | ||
) | ||
|
||
@api.model | ||
def bind_langs(self, backend, lang_ids): | ||
""" | ||
Ensure that a shopinvader.variant exists for each lang_id. If not, | ||
create a new binding for the missing lang. This method is usefull | ||
"""Ensure that a shopinvader.variant exists for each lang_id. | ||
If not, create a new binding for the missing lang. This method is useful | ||
to ensure that when a lang is added to a backend, all the binding | ||
for this lang are created for the existing bound products | ||
for this lang are created for the existing bound products. | ||
:param backend: backend record | ||
:param lang_ids: list of lang ids we must ensure that a binding exists | ||
:return: | ||
|
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