# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt from __future__ import print_function, unicode_literals import frappe from frappe.exceptions import SQLError def execute(): """ Structure History: 1. Item and Item Variant 2. Item, Variant Attribute, Manage Variants and Manage Variant Items 3. Item, Item Variant Attribute, Item Attribute and Item Attribute Type (latest) """ rename_and_reload_doctypes() 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("Item 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 SQLError: print("`tabItem Variant` not found") def rename_and_reload_doctypes(): if "tabVariant Attribute" in frappe.db.get_tables(): frappe.rename_doc("DocType", "Variant Attribute", "Item Variant Attribute") frappe.reload_doctype("Item") frappe.reload_doc("Stock", "DocType", "Item Variant Attribute") frappe.reload_doc("Stock", "DocType", "Item Attribute Value") frappe.reload_doc("Stock", "DocType", "Item Attribute") def migrate_manage_variants(): item_attribute = {} for d in frappe.db.sql("""select DISTINCT va.attribute, i.variant_of from `tabItem Variant Attribute` va, `tabItem` i where va.parent = i.name and ifnull(i.variant_of, '')!=''""", 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() # 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 Variant` where parent=%s""", item.name) if not item_attributes and not all_variants: item = frappe.get_doc("Item", item.name) item.has_variants = 0 item.save() continue attribute_value_options = {} for attribute, value in item_attributes: attribute_value_options.setdefault(attribute, []).append(value) 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 save_attributes_in_template(item, attribute_value_options) frappe.delete_doc("DocType", "Item Variant") def save_attributes_in_template(item, attribute_value_options): # store attribute in Item 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()