[fix] [patch] Item Template Attributes - migration from v4 and v5
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index b15ed40..2955740 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -190,4 +190,4 @@
 erpnext.patches.v5_4.stock_entry_additional_costs
 erpnext.patches.v5_4.cleanup_journal_entry #2015-08-14
 execute:frappe.db.sql("update `tabProduction Order` pro set description = (select description from tabItem where name=pro.production_item) where ifnull(description, '') = ''")
-erpnext.patches.v5_4.item_template
+erpnext.patches.v5_7.item_template_attribtues
diff --git a/erpnext/patches/v5_4/item_template.py b/erpnext/patches/v5_4/item_template.py
deleted file mode 100644
index 455aae6..0000000
--- a/erpnext/patches/v5_4/item_template.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	item_attribute = {}
-	for d in  frappe.db.sql("""select DISTINCT va.attribute, i.variant_of from `tabVariant Attribute` va, `tabItem` i \
-		where va.parent = i.name""", as_dict=1):
-		item_attribute.setdefault(d.variant_of, []).append({"attribute": d.attribute})
-	
-	for item, attributes in item_attribute.items():
-		template = frappe.get_doc("Item", item)
-		template.set('attributes', attributes)
-		template.save()
-		
-	frappe.delete_doc("DocType", "Manage Variants")
-	frappe.delete_doc("DocType", "Manage Variants Item")
\ No newline at end of file
diff --git a/erpnext/patches/v5_7/__init__.py b/erpnext/patches/v5_7/__init__.py
new file mode 100644
index 0000000..baffc48
--- /dev/null
+++ b/erpnext/patches/v5_7/__init__.py
@@ -0,0 +1 @@
+from __future__ import unicode_literals
diff --git a/erpnext/patches/v5_7/item_template_attributes.py b/erpnext/patches/v5_7/item_template_attributes.py
new file mode 100644
index 0000000..800d938
--- /dev/null
+++ b/erpnext/patches/v5_7/item_template_attributes.py
@@ -0,0 +1,112 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+import MySQLdb
+
+def execute():
+	"""
+		Structure History:
+			1. Item and Item Attribute
+			2. Item, Variant Attribute, Manage Variants and Manage Variant Items
+			3. Item, Variant Attribute (latest)
+	"""
+	frappe.db.reload_doctype("Item")
+	frappe.db.reload_doctype("Variant Attribute")
+
+	variant_templates = frappe.get_all("Item", filters={"has_variants": 1}, limit_page_length=1)
+	if not variant_templates:
+		# database does not have items that have variants
+		# so no point in running the patch
+		return
+
+	variant_attributes = frappe.get_all("Variant Attribute", fields=["*"], limit_page_length=1)
+
+	if variant_attributes:
+		# manage variant patch is already applied
+		migrate_manage_variants()
+
+	else:
+		# old structure based on "Item Variant" table
+		try:
+			migrate_item_variants()
+
+		except MySQLdb.ProgrammingError:
+			print "`tabItem Variant` not found"
+
+def migrate_manage_variants():
+	item_attribute = {}
+	for d in  frappe.db.sql("""select DISTINCT va.attribute, i.variant_of from `tabVariant Attribute` va, `tabItem` i \
+		where va.parent = i.name""", as_dict=1):
+		item_attribute.setdefault(d.variant_of, []).append({"attribute": d.attribute})
+
+	for item, attributes in item_attribute.items():
+		template = frappe.get_doc("Item", item)
+		template.set('attributes', attributes)
+		template.save()
+
+	frappe.delete_doc("DocType", "Manage Variants")
+	frappe.delete_doc("DocType", "Manage Variants Item")
+
+# patch old style
+def migrate_item_variants():
+	for item in frappe.get_all("Item", filters={"has_variants": 1}):
+		all_variants = frappe.get_all("Item", filters={"variant_of": item.name}, fields=["name", "description"])
+		item_attributes = frappe.db.sql("""select distinct item_attribute, item_attribute_value
+			from `tabItem Attribute` where parent=%s""", item.name)
+
+		attribute_value_options = {}
+		for attribute, value in item_attributes:
+			attribute_value_options.setdefault(attribute, []).append(value)
+
+		save_attributes_in_template(item, attribute_value_options)
+
+		possible_combinations = get_possible_combinations(attribute_value_options)
+
+		for variant in all_variants:
+			for combination in possible_combinations:
+				match = True
+				for attribute, value in combination.items():
+					if "{0}: {1}".format(attribute, value) not in variant.description:
+						match = False
+						break
+
+				if match:
+					# found the right variant
+					save_attributes_in_variant(variant, combination)
+					break
+
+	frappe.delete_doc("DocType", "Item Attribute")
+
+def save_attributes_in_template(item, attribute_value_options):
+	# store attribute in Variant Attribute table for template
+	template = frappe.get_doc("Item", item)
+	template.set("attributes", [{"attribute": attribute} for attribute in attribute_value_options.keys()])
+	template.save()
+
+def get_possible_combinations(attribute_value_options):
+	possible_combinations = []
+
+	for attribute, values in attribute_value_options.items():
+		if not possible_combinations:
+			for v in values:
+				possible_combinations.append({attribute: v})
+
+		else:
+			for v in values:
+				for combination in possible_combinations:
+					combination[attribute] = v
+
+	return possible_combinations
+
+def save_attributes_in_variant(variant, combination):
+	# add data into attributes table
+	variant_item = frappe.get_doc("Item", variant.name)
+	variant_item.set("attributes", [])
+	for attribute, value in combination.items():
+		variant_item.append("attributes", {
+			"attribute": attribute,
+			"attribute_value": value
+		})
+	variant_item.save()