feat: validate if removed item attributes exist in variants (#22911)
* feat: validate if removed item attributes exist in variants * Update erpnext/stock/doctype/item/item.py Co-authored-by: Marica <maricadsouza221197@gmail.com> * Update erpnext/stock/doctype/item/item.py Co-authored-by: Marica <maricadsouza221197@gmail.com> * refactor: smaller loop Co-authored-by: marination <maricadsouza221197@gmail.com> * feat: don't run validation for new entries Co-authored-by: Marica <maricadsouza221197@gmail.com> * fix: use tuple as is Co-authored-by: Marica <maricadsouza221197@gmail.com> * refactor: error description Co-authored-by: Marica <maricadsouza221197@gmail.com> * refactor: remove unused variable Co-authored-by: Marica <maricadsouza221197@gmail.com>
This commit is contained in:
parent
cf4268e21a
commit
7556517a4f
@ -111,6 +111,7 @@ class Item(WebsiteGenerator):
|
||||
self.synced_with_hub = 0
|
||||
|
||||
self.validate_has_variants()
|
||||
self.validate_attributes_in_variants()
|
||||
self.validate_stock_exists_for_template_item()
|
||||
self.validate_attributes()
|
||||
self.validate_variant_attributes()
|
||||
@ -806,6 +807,77 @@ class Item(WebsiteGenerator):
|
||||
if frappe.db.exists("Item", {"variant_of": self.name}):
|
||||
frappe.throw(_("Item has variants."))
|
||||
|
||||
def validate_attributes_in_variants(self):
|
||||
if not self.has_variants or self.get("__islocal"):
|
||||
return
|
||||
|
||||
old_doc = self.get_doc_before_save()
|
||||
old_doc_attributes = set([attr.attribute for attr in old_doc.attributes])
|
||||
own_attributes = [attr.attribute for attr in self.attributes]
|
||||
|
||||
# Check if old attributes were removed from the list
|
||||
# Is old_attrs is a subset of new ones
|
||||
# that means we need not check any changes
|
||||
if old_doc_attributes.issubset(set(own_attributes)):
|
||||
return
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
# get all item variants
|
||||
items = [item["name"] for item in frappe.get_all("Item", {"variant_of": self.name})]
|
||||
|
||||
# get all deleted attributes
|
||||
deleted_attribute = list(old_doc_attributes.difference(set(own_attributes)))
|
||||
|
||||
# fetch all attributes of these items
|
||||
item_attributes = frappe.get_all(
|
||||
"Item Variant Attribute",
|
||||
filters={
|
||||
"parent": ["in", items],
|
||||
"attribute": ["in", deleted_attribute]
|
||||
},
|
||||
fields=["attribute", "parent"]
|
||||
)
|
||||
not_included = defaultdict(list)
|
||||
|
||||
for attr in item_attributes:
|
||||
if attr["attribute"] not in own_attributes:
|
||||
not_included[attr["parent"]].append(attr["attribute"])
|
||||
|
||||
if not len(not_included):
|
||||
return
|
||||
|
||||
def body(docnames):
|
||||
docnames.sort()
|
||||
return "<br>".join(docnames)
|
||||
|
||||
def table_row(title, body):
|
||||
return """<tr>
|
||||
<td>{0}</td>
|
||||
<td>{1}</td>
|
||||
</tr>""".format(title, body)
|
||||
|
||||
rows = ''
|
||||
for docname, attr_list in not_included.items():
|
||||
link = "<a href='#Form/Item/{0}'>{0}</a>".format(frappe.bold(_(docname)))
|
||||
rows += table_row(link, body(attr_list))
|
||||
|
||||
error_description = _('The following deleted attributes exist in Variants but not in the Template. You can either delete the Variants or keep the attribute(s) in template.')
|
||||
|
||||
message = """
|
||||
<div>{0}</div><br>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<td>{1}</td>
|
||||
<td>{2}</td>
|
||||
</thead>
|
||||
{3}
|
||||
</table>
|
||||
""".format(error_description, _('Variant Items'), _('Attributes'), rows)
|
||||
|
||||
frappe.throw(message, title=_("Variant Attribute Error"), is_minimizable=True, wide=True)
|
||||
|
||||
|
||||
def validate_stock_exists_for_template_item(self):
|
||||
if self.stock_ledger_created() and self._doc_before_save:
|
||||
if (cint(self._doc_before_save.has_variants) != cint(self.has_variants)
|
||||
|
Loading…
x
Reference in New Issue
Block a user