diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 7f077cfd4dd..fb2a7ee95f8 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -837,7 +837,17 @@ class Item(Document): frappe.throw(_("Item {0} is not a template item.").format(frappe.bold(self.variant_of))) if based_on == "Item Attribute": + previous_doc = self.get_doc_before_save() + saved_attributes = ( + {(row.attribute, row.attribute_value) for row in previous_doc.attributes} + if previous_doc + else set() + ) + for d in self.attributes: + if (d.attribute, d.attribute_value) in saved_attributes: + continue + if not frappe.db.exists( "Item Variant Attribute", {"attribute": d.attribute, "parent": self.variant_of} ): diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 073c8c8be93..deda65c911c 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -360,6 +360,45 @@ class TestItem(FrappeTestCase): self.assertRaises(InvalidItemAttributeValueError, attribute.save) frappe.db.rollback() + def test_disabled_attribute_blocks_only_attribute_changes(self): + frappe.delete_doc_if_exists("Item", "_Test Disabled Attribute Template-L", force=1) + frappe.delete_doc_if_exists("Item", "_Test Disabled Attribute Template", force=1) + frappe.delete_doc_if_exists("Item Attribute", "_Test Disabled Size", force=1) + + attribute = frappe.get_doc( + { + "doctype": "Item Attribute", + "attribute_name": "_Test Disabled Size", + "item_attribute_values": [ + {"attribute_value": "Large", "abbr": "L"}, + {"attribute_value": "Small", "abbr": "S"}, + ], + } + ).insert() + + template = make_item( + "_Test Disabled Attribute Template", + { + "has_variants": 1, + "variant_based_on": "Item Attribute", + "attributes": [{"attribute": attribute.name}], + }, + ) + + variant = create_variant(template.name, {attribute.name: "Large"}) + variant.save() + + attribute.disabled = 1 + attribute.save() + + variant.reload() + variant.description = "Edited after the attribute was disabled" + variant.save() + + variant.reload() + variant.attributes[0].attribute_value = "Small" + self.assertRaises(frappe.ValidationError, variant.save) + def test_rename_attribute_value_updates_variants(self): frappe.delete_doc_if_exists("Item", "_Test Variant Item-L", force=1)