space to tab
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 6836793..6d4f850 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -9,770 +9,770 @@
 import erpnext
 import frappe
 from erpnext.controllers.item_variant import (ItemVariantExistsError,
-                                              copy_attributes_to_variant,
-                                              get_variant,
-                                              make_variant_item_code,
-                                              validate_item_variant_attributes)
+											  copy_attributes_to_variant,
+											  get_variant,
+											  make_variant_item_code,
+											  validate_item_variant_attributes)
 from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups,
-                                                         invalidate_cache_for)
+														 invalidate_cache_for)
 from frappe import _, msgprint
 from frappe.utils import (cint, cstr, flt, formatdate, get_timestamp, getdate,
-                          now_datetime, random_string, strip)
+						  now_datetime, random_string, strip)
 from frappe.utils.html_utils import clean_html
 from frappe.website.doctype.website_slideshow.website_slideshow import \
-    get_slideshow
+	get_slideshow
 from frappe.website.render import clear_cache
 from frappe.website.website_generator import WebsiteGenerator
 
 
 class DuplicateReorderRows(frappe.ValidationError):
-    pass
+	pass
 
 
 class StockExistsForTemplate(frappe.ValidationError):
-    pass
+	pass
 
 
 class Item(WebsiteGenerator):
-    website = frappe._dict(
-        page_title_field="item_name",
-        condition_field="show_in_website",
-        template="templates/generators/item.html",
-        no_cache=1
-    )
+	website = frappe._dict(
+		page_title_field="item_name",
+		condition_field="show_in_website",
+		template="templates/generators/item.html",
+		no_cache=1
+	)
 
-    def onload(self):
-        super(Item, self).onload()
+	def onload(self):
+		super(Item, self).onload()
 
-        self.set_onload('stock_exists', self.stock_ledger_created())
-        if self.is_fixed_asset:
-            asset = frappe.db.get_all("Asset", filters={"item_code": self.name, "docstatus": 1}, limit=1)
-            self.set_onload("asset_exists", True if asset else False)
+		self.set_onload('stock_exists', self.stock_ledger_created())
+		if self.is_fixed_asset:
+			asset = frappe.db.get_all("Asset", filters={"item_code": self.name, "docstatus": 1}, limit=1)
+			self.set_onload("asset_exists", True if asset else False)
 
-    def autoname(self):
-        if frappe.db.get_default("item_naming_by") == "Naming Series":
-            if self.variant_of:
-                if not self.item_code:
-                    template_item_name = frappe.db.get_value("Item", self.variant_of, "item_name")
-                    self.item_code = make_variant_item_code(self.variant_of, template_item_name, self)
-            else:
-                from frappe.model.naming import make_autoname
-                self.item_code = make_autoname(self.naming_series + '.#####')
-        elif not self.item_code:
-            msgprint(_("Item Code is mandatory because Item is not automatically numbered"), raise_exception=1)
+	def autoname(self):
+		if frappe.db.get_default("item_naming_by") == "Naming Series":
+			if self.variant_of:
+				if not self.item_code:
+					template_item_name = frappe.db.get_value("Item", self.variant_of, "item_name")
+					self.item_code = make_variant_item_code(self.variant_of, template_item_name, self)
+			else:
+				from frappe.model.naming import make_autoname
+				self.item_code = make_autoname(self.naming_series + '.#####')
+		elif not self.item_code:
+			msgprint(_("Item Code is mandatory because Item is not automatically numbered"), raise_exception=1)
 
-        self.item_code = strip(self.item_code)
-        self.name = self.item_code
+		self.item_code = strip(self.item_code)
+		self.name = self.item_code
 
-    def before_insert(self):
-        if not self.description:
-            self.description = self.item_name
+	def before_insert(self):
+		if not self.description:
+			self.description = self.item_name
 
-        if self.is_sales_item and not self.get('is_item_from_hub'):
-            self.publish_in_hub = 1
+		if self.is_sales_item and not self.get('is_item_from_hub'):
+			self.publish_in_hub = 1
 
-    def after_insert(self):
-        '''set opening stock and item price'''
-        if self.standard_rate:
-            self.add_price()
+	def after_insert(self):
+		'''set opening stock and item price'''
+		if self.standard_rate:
+			self.add_price()
 
-        if self.opening_stock:
-            self.set_opening_stock()
+		if self.opening_stock:
+			self.set_opening_stock()
 
-    def validate(self):
-        self.get_doc_before_save()
+	def validate(self):
+		self.get_doc_before_save()
 
-        super(Item, self).validate()
+		super(Item, self).validate()
 
-        if not self.item_name:
-            self.item_name = self.item_code
+		if not self.item_name:
+			self.item_name = self.item_code
 
-        if not self.description:
-            self.description = self.item_name
+		if not self.description:
+			self.description = self.item_name
 
-        self.validate_uom()
-        self.validate_description()
-        self.add_default_uom_in_conversion_factor_table()
-        self.validate_conversion_factor()
-        self.validate_item_type()
-        self.check_for_active_boms()
-        self.fill_customer_code()
-        self.check_item_tax()
-        self.validate_barcode()
-        self.validate_warehouse_for_reorder()
-        self.update_bom_item_desc()
-        self.synced_with_hub = 0
+		self.validate_uom()
+		self.validate_description()
+		self.add_default_uom_in_conversion_factor_table()
+		self.validate_conversion_factor()
+		self.validate_item_type()
+		self.check_for_active_boms()
+		self.fill_customer_code()
+		self.check_item_tax()
+		self.validate_barcode()
+		self.validate_warehouse_for_reorder()
+		self.update_bom_item_desc()
+		self.synced_with_hub = 0
 
-        self.validate_has_variants()
-        self.validate_stock_exists_for_template_item()
-        self.validate_attributes()
-        self.validate_variant_attributes()
-        self.validate_website_image()
-        self.make_thumbnail()
-        self.validate_fixed_asset()
-        self.validate_retain_sample()
+		self.validate_has_variants()
+		self.validate_stock_exists_for_template_item()
+		self.validate_attributes()
+		self.validate_variant_attributes()
+		self.validate_website_image()
+		self.make_thumbnail()
+		self.validate_fixed_asset()
+		self.validate_retain_sample()
 
-        if not self.get("__islocal"):
-            self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
-            self.old_website_item_groups = frappe.db.sql_list("""select item_group
+		if not self.get("__islocal"):
+			self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
+			self.old_website_item_groups = frappe.db.sql_list("""select item_group
 				from `tabWebsite Item Group`
 				where parentfield='website_item_groups' and parenttype='Item' and parent=%s""", self.name)
 
-    def on_update(self):
-        invalidate_cache_for_item(self)
-        self.validate_name_with_item_group()
-        self.update_variants()
-        self.update_item_price()
-        self.update_template_item()
+	def on_update(self):
+		invalidate_cache_for_item(self)
+		self.validate_name_with_item_group()
+		self.update_variants()
+		self.update_item_price()
+		self.update_template_item()
 
-    def validate_description(self):
-        '''Clean HTML description if set'''
-        if cint(frappe.db.get_single_value('Stock Settings', 'clean_description_html')):
-            self.description = clean_html(self.description)
+	def validate_description(self):
+		'''Clean HTML description if set'''
+		if cint(frappe.db.get_single_value('Stock Settings', 'clean_description_html')):
+			self.description = clean_html(self.description)
 
-    def add_price(self, price_list=None):
-        '''Add a new price'''
-        if not price_list:
-            price_list = (frappe.db.get_single_value('Selling Settings', 'selling_price_list')
-                          or frappe.db.get_value('Price List', _('Standard Selling')))
-        if price_list:
-            item_price = frappe.get_doc({
-                "doctype": "Item Price",
-                "price_list": price_list,
-                "item_code": self.name,
-                "currency": erpnext.get_default_currency(),
-                "price_list_rate": self.standard_rate
-            })
-            item_price.insert()
+	def add_price(self, price_list=None):
+		'''Add a new price'''
+		if not price_list:
+			price_list = (frappe.db.get_single_value('Selling Settings', 'selling_price_list')
+						  or frappe.db.get_value('Price List', _('Standard Selling')))
+		if price_list:
+			item_price = frappe.get_doc({
+				"doctype": "Item Price",
+				"price_list": price_list,
+				"item_code": self.name,
+				"currency": erpnext.get_default_currency(),
+				"price_list_rate": self.standard_rate
+			})
+			item_price.insert()
 
-    def set_opening_stock(self):
-        '''set opening stock'''
-        if not self.is_stock_item or self.has_serial_no or self.has_batch_no:
-            return
+	def set_opening_stock(self):
+		'''set opening stock'''
+		if not self.is_stock_item or self.has_serial_no or self.has_batch_no:
+			return
 
-        if not self.valuation_rate and self.standard_rate:
-            self.valuation_rate = self.standard_rate
+		if not self.valuation_rate and self.standard_rate:
+			self.valuation_rate = self.standard_rate
 
-        if not self.valuation_rate:
-            frappe.throw(_("Valuation Rate is mandatory if Opening Stock entered"))
+		if not self.valuation_rate:
+			frappe.throw(_("Valuation Rate is mandatory if Opening Stock entered"))
 
-        from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+		from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 
-        # default warehouse, or Stores
-        default_warehouse = (self.default_warehouse
-                             or frappe.db.get_single_value('Stock Settings', 'default_warehouse')
-                             or frappe.db.get_value('Warehouse', {'warehouse_name': _('Stores')}))
+		# default warehouse, or Stores
+		default_warehouse = (self.default_warehouse
+							 or frappe.db.get_single_value('Stock Settings', 'default_warehouse')
+							 or frappe.db.get_value('Warehouse', {'warehouse_name': _('Stores')}))
 
-        if default_warehouse:
-            stock_entry = make_stock_entry(item_code=self.name, target=default_warehouse,
-                                           qty=self.opening_stock, rate=self.valuation_rate)
+		if default_warehouse:
+			stock_entry = make_stock_entry(item_code=self.name, target=default_warehouse,
+										   qty=self.opening_stock, rate=self.valuation_rate)
 
-            stock_entry.add_comment("Comment", _("Opening Stock"))
+			stock_entry.add_comment("Comment", _("Opening Stock"))
 
-    def make_route(self):
-        if not self.route:
-            return cstr(frappe.db.get_value('Item Group', self.item_group,
-                                            'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5))
+	def make_route(self):
+		if not self.route:
+			return cstr(frappe.db.get_value('Item Group', self.item_group,
+											'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5))
 
-    def validate_website_image(self):
-        """Validate if the website image is a public file"""
-        auto_set_website_image = False
-        if not self.website_image and self.image:
-            auto_set_website_image = True
-            self.website_image = self.image
+	def validate_website_image(self):
+		"""Validate if the website image is a public file"""
+		auto_set_website_image = False
+		if not self.website_image and self.image:
+			auto_set_website_image = True
+			self.website_image = self.image
 
-        if not self.website_image:
-            return
+		if not self.website_image:
+			return
 
-        # find if website image url exists as public
-        file_doc = frappe.get_all("File", filters={
-            "file_url": self.website_image
-        }, fields=["name", "is_private"], order_by="is_private asc", limit_page_length=1)
+		# find if website image url exists as public
+		file_doc = frappe.get_all("File", filters={
+			"file_url": self.website_image
+		}, fields=["name", "is_private"], order_by="is_private asc", limit_page_length=1)
 
-        if file_doc:
-            file_doc = file_doc[0]
+		if file_doc:
+			file_doc = file_doc[0]
 
-        if not file_doc:
-            if not auto_set_website_image:
-                frappe.msgprint(_("Website Image {0} attached to Item {1} cannot be found")
-                                .format(self.website_image, self.name))
+		if not file_doc:
+			if not auto_set_website_image:
+				frappe.msgprint(_("Website Image {0} attached to Item {1} cannot be found")
+								.format(self.website_image, self.name))
 
-            self.website_image = None
+			self.website_image = None
 
-        elif file_doc.is_private:
-            if not auto_set_website_image:
-                frappe.msgprint(_("Website Image should be a public file or website URL"))
+		elif file_doc.is_private:
+			if not auto_set_website_image:
+				frappe.msgprint(_("Website Image should be a public file or website URL"))
 
-            self.website_image = None
+			self.website_image = None
 
-    def make_thumbnail(self):
-        """Make a thumbnail of `website_image`"""
-        import requests.exceptions
+	def make_thumbnail(self):
+		"""Make a thumbnail of `website_image`"""
+		import requests.exceptions
 
-        if not self.is_new() and self.website_image != frappe.db.get_value(self.doctype, self.name, "website_image"):
-            self.thumbnail = None
+		if not self.is_new() and self.website_image != frappe.db.get_value(self.doctype, self.name, "website_image"):
+			self.thumbnail = None
 
-        if self.website_image and not self.thumbnail:
-            file_doc = None
+		if self.website_image and not self.thumbnail:
+			file_doc = None
 
-            try:
-                file_doc = frappe.get_doc("File", {
-                    "file_url": self.website_image,
-                    "attached_to_doctype": "Item",
-                    "attached_to_name": self.name
-                })
-            except frappe.DoesNotExistError:
-                pass
-                # cleanup
-                frappe.local.message_log.pop()
+			try:
+				file_doc = frappe.get_doc("File", {
+					"file_url": self.website_image,
+					"attached_to_doctype": "Item",
+					"attached_to_name": self.name
+				})
+			except frappe.DoesNotExistError:
+				pass
+				# cleanup
+				frappe.local.message_log.pop()
 
-            except requests.exceptions.HTTPError:
-                frappe.msgprint(_("Warning: Invalid attachment {0}").format(self.website_image))
-                self.website_image = None
+			except requests.exceptions.HTTPError:
+				frappe.msgprint(_("Warning: Invalid attachment {0}").format(self.website_image))
+				self.website_image = None
 
-            except requests.exceptions.SSLError:
-                frappe.msgprint(
-                    _("Warning: Invalid SSL certificate on attachment {0}").format(self.website_image))
-                self.website_image = None
+			except requests.exceptions.SSLError:
+				frappe.msgprint(
+					_("Warning: Invalid SSL certificate on attachment {0}").format(self.website_image))
+				self.website_image = None
 
-            # for CSV import
-            if self.website_image and not file_doc:
-                try:
-                    file_doc = frappe.get_doc({
-                        "doctype": "File",
-                        "file_url": self.website_image,
-                        "attached_to_doctype": "Item",
-                        "attached_to_name": self.name
-                    }).insert()
+			# for CSV import
+			if self.website_image and not file_doc:
+				try:
+					file_doc = frappe.get_doc({
+						"doctype": "File",
+						"file_url": self.website_image,
+						"attached_to_doctype": "Item",
+						"attached_to_name": self.name
+					}).insert()
 
-                except IOError:
-                    self.website_image = None
+				except IOError:
+					self.website_image = None
 
-            if file_doc:
-                if not file_doc.thumbnail_url:
-                    file_doc.make_thumbnail()
+			if file_doc:
+				if not file_doc.thumbnail_url:
+					file_doc.make_thumbnail()
 
-                self.thumbnail = file_doc.thumbnail_url
+				self.thumbnail = file_doc.thumbnail_url
 
-    def validate_fixed_asset(self):
-        if self.is_fixed_asset:
-            if self.is_stock_item:
-                frappe.throw(_("Fixed Asset Item must be a non-stock item."))
+	def validate_fixed_asset(self):
+		if self.is_fixed_asset:
+			if self.is_stock_item:
+				frappe.throw(_("Fixed Asset Item must be a non-stock item."))
 
-            if not self.asset_category:
-                frappe.throw(_("Asset Category is mandatory for Fixed Asset item"))
+			if not self.asset_category:
+				frappe.throw(_("Asset Category is mandatory for Fixed Asset item"))
 
-            if self.stock_ledger_created():
-                frappe.throw(_("Cannot be a fixed asset item as Stock Ledger is created."))
+			if self.stock_ledger_created():
+				frappe.throw(_("Cannot be a fixed asset item as Stock Ledger is created."))
 
-        if not self.is_fixed_asset:
-            asset = frappe.db.get_all("Asset", filters={"item_code": self.name, "docstatus": 1}, limit=1)
-            if asset:
-                frappe.throw(_('"Is Fixed Asset" cannot be unchecked, as Asset record exists against the item'))
+		if not self.is_fixed_asset:
+			asset = frappe.db.get_all("Asset", filters={"item_code": self.name, "docstatus": 1}, limit=1)
+			if asset:
+				frappe.throw(_('"Is Fixed Asset" cannot be unchecked, as Asset record exists against the item'))
 
-    def validate_retain_sample(self):
-        if self.retain_sample and not frappe.db.get_single_value('Stock Settings', 'sample_retention_warehouse'):
-            frappe.throw(_("Please select Sample Retention Warehouse in Stock Settings first"))
-        if self.retain_sample and not self.has_batch_no:
-            frappe.throw(_(" {0} Retain Sample is based on batch, please check Has Batch No to retain sample of item").format(
-                self.item_code))
+	def validate_retain_sample(self):
+		if self.retain_sample and not frappe.db.get_single_value('Stock Settings', 'sample_retention_warehouse'):
+			frappe.throw(_("Please select Sample Retention Warehouse in Stock Settings first"))
+		if self.retain_sample and not self.has_batch_no:
+			frappe.throw(_(" {0} Retain Sample is based on batch, please check Has Batch No to retain sample of item").format(
+				self.item_code))
 
-    def get_context(self, context):
-        context.show_search = True
-        context.search_link = '/product_search'
+	def get_context(self, context):
+		context.show_search = True
+		context.search_link = '/product_search'
 
-        context.parents = get_parent_item_groups(self.item_group)
+		context.parents = get_parent_item_groups(self.item_group)
 
-        self.set_variant_context(context)
-        self.set_attribute_context(context)
-        self.set_disabled_attributes(context)
+		self.set_variant_context(context)
+		self.set_attribute_context(context)
+		self.set_disabled_attributes(context)
 
-        return context
+		return context
 
-    def set_variant_context(self, context):
-        if self.has_variants:
-            context.no_cache = True
+	def set_variant_context(self, context):
+		if self.has_variants:
+			context.no_cache = True
 
-            # load variants
-            # also used in set_attribute_context
-            context.variants = frappe.get_all("Item",
-                                              filters={"variant_of": self.name, "show_variant_in_website": 1},
-                                              order_by="name asc")
+			# load variants
+			# also used in set_attribute_context
+			context.variants = frappe.get_all("Item",
+											  filters={"variant_of": self.name, "show_variant_in_website": 1},
+											  order_by="name asc")
 
-            variant = frappe.form_dict.variant
-            if not variant and context.variants:
-                # the case when the item is opened for the first time from its list
-                variant = context.variants[0]
+			variant = frappe.form_dict.variant
+			if not variant and context.variants:
+				# the case when the item is opened for the first time from its list
+				variant = context.variants[0]
 
-            if variant:
-                context.variant = frappe.get_doc("Item", variant)
+			if variant:
+				context.variant = frappe.get_doc("Item", variant)
 
-                for fieldname in ("website_image", "web_long_description", "description",
-                                  "website_specifications"):
-                    if context.variant.get(fieldname):
-                        value = context.variant.get(fieldname)
-                        if isinstance(value, list):
-                            value = [d.as_dict() for d in value]
+				for fieldname in ("website_image", "web_long_description", "description",
+								  "website_specifications"):
+					if context.variant.get(fieldname):
+						value = context.variant.get(fieldname)
+						if isinstance(value, list):
+							value = [d.as_dict() for d in value]
 
-                        context[fieldname] = value
+						context[fieldname] = value
 
-        if self.slideshow:
-            if context.variant and context.variant.slideshow:
-                context.update(get_slideshow(context.variant))
-            else:
-                context.update(get_slideshow(self))
+		if self.slideshow:
+			if context.variant and context.variant.slideshow:
+				context.update(get_slideshow(context.variant))
+			else:
+				context.update(get_slideshow(self))
 
-    def set_attribute_context(self, context):
-        if self.has_variants:
-            attribute_values_available = {}
-            context.attribute_values = {}
-            context.selected_attributes = {}
+	def set_attribute_context(self, context):
+		if self.has_variants:
+			attribute_values_available = {}
+			context.attribute_values = {}
+			context.selected_attributes = {}
 
-            # load attributes
-            for v in context.variants:
-                v.attributes = frappe.get_all("Item Variant Attribute",
-                                              fields=["attribute", "attribute_value"], filters={"parent": v.name})
+			# load attributes
+			for v in context.variants:
+				v.attributes = frappe.get_all("Item Variant Attribute",
+											  fields=["attribute", "attribute_value"], filters={"parent": v.name})
 
-                for attr in v.attributes:
-                    values = attribute_values_available.setdefault(attr.attribute, [])
-                    if attr.attribute_value not in values:
-                        values.append(attr.attribute_value)
+				for attr in v.attributes:
+					values = attribute_values_available.setdefault(attr.attribute, [])
+					if attr.attribute_value not in values:
+						values.append(attr.attribute_value)
 
-                    if v.name == context.variant.name:
-                        context.selected_attributes[attr.attribute] = attr.attribute_value
+					if v.name == context.variant.name:
+						context.selected_attributes[attr.attribute] = attr.attribute_value
 
-            # filter attributes, order based on attribute table
-            for attr in self.attributes:
-                values = context.attribute_values.setdefault(attr.attribute, [])
+			# filter attributes, order based on attribute table
+			for attr in self.attributes:
+				values = context.attribute_values.setdefault(attr.attribute, [])
 
-                if cint(frappe.db.get_value("Item Attribute", attr.attribute, "numeric_values")):
-                    for val in sorted(attribute_values_available.get(attr.attribute, []), key=flt):
-                        values.append(val)
+				if cint(frappe.db.get_value("Item Attribute", attr.attribute, "numeric_values")):
+					for val in sorted(attribute_values_available.get(attr.attribute, []), key=flt):
+						values.append(val)
 
-                else:
-                    # get list of values defined (for sequence)
-                    for attr_value in frappe.db.get_all("Item Attribute Value",
-                                                        fields=["attribute_value"], filters={"parent": attr.attribute}, order_by="idx asc"):
+				else:
+					# get list of values defined (for sequence)
+					for attr_value in frappe.db.get_all("Item Attribute Value",
+														fields=["attribute_value"], filters={"parent": attr.attribute}, order_by="idx asc"):
 
-                        if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []):
-                            values.append(attr_value.attribute_value)
+						if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []):
+							values.append(attr_value.attribute_value)
 
-            context.variant_info = json.dumps(context.variants)
+			context.variant_info = json.dumps(context.variants)
 
-    def set_disabled_attributes(self, context):
-        """Disable selection options of attribute combinations that do not result in a variant"""
-        if not self.attributes or not self.has_variants:
-            return
+	def set_disabled_attributes(self, context):
+		"""Disable selection options of attribute combinations that do not result in a variant"""
+		if not self.attributes or not self.has_variants:
+			return
 
-        context.disabled_attributes = {}
-        attributes = [attr.attribute for attr in self.attributes]
+		context.disabled_attributes = {}
+		attributes = [attr.attribute for attr in self.attributes]
 
-        def find_variant(combination):
-            for variant in context.variants:
-                if len(variant.attributes) < len(attributes):
-                    continue
+		def find_variant(combination):
+			for variant in context.variants:
+				if len(variant.attributes) < len(attributes):
+					continue
 
-                if "combination" not in variant:
-                    ref_combination = []
+				if "combination" not in variant:
+					ref_combination = []
 
-                    for attr in variant.attributes:
-                        idx = attributes.index(attr.attribute)
-                        ref_combination.insert(idx, attr.attribute_value)
+					for attr in variant.attributes:
+						idx = attributes.index(attr.attribute)
+						ref_combination.insert(idx, attr.attribute_value)
 
-                    variant["combination"] = ref_combination
+					variant["combination"] = ref_combination
 
-                if not (set(combination) - set(variant["combination"])):
-                    # check if the combination is a subset of a variant combination
-                    # eg. [Blue, 0.5] is a possible combination if exists [Blue, Large, 0.5]
-                    return True
+				if not (set(combination) - set(variant["combination"])):
+					# check if the combination is a subset of a variant combination
+					# eg. [Blue, 0.5] is a possible combination if exists [Blue, Large, 0.5]
+					return True
 
-        for i, attr in enumerate(self.attributes):
-            if i == 0:
-                continue
+		for i, attr in enumerate(self.attributes):
+			if i == 0:
+				continue
 
-            combination_source = []
+			combination_source = []
 
-            # loop through previous attributes
-            for prev_attr in self.attributes[:i]:
-                combination_source.append([context.selected_attributes.get(prev_attr.attribute)])
+			# loop through previous attributes
+			for prev_attr in self.attributes[:i]:
+				combination_source.append([context.selected_attributes.get(prev_attr.attribute)])
 
-            combination_source.append(context.attribute_values[attr.attribute])
+			combination_source.append(context.attribute_values[attr.attribute])
 
-            for combination in itertools.product(*combination_source):
-                if not find_variant(combination):
-                    context.disabled_attributes.setdefault(attr.attribute, []).append(combination[-1])
+			for combination in itertools.product(*combination_source):
+				if not find_variant(combination):
+					context.disabled_attributes.setdefault(attr.attribute, []).append(combination[-1])
 
-    def add_default_uom_in_conversion_factor_table(self):
-        uom_conv_list = [d.uom for d in self.get("uoms")]
-        if self.stock_uom not in uom_conv_list:
-            ch = self.append('uoms', {})
-            ch.uom = self.stock_uom
-            ch.conversion_factor = 1
+	def add_default_uom_in_conversion_factor_table(self):
+		uom_conv_list = [d.uom for d in self.get("uoms")]
+		if self.stock_uom not in uom_conv_list:
+			ch = self.append('uoms', {})
+			ch.uom = self.stock_uom
+			ch.conversion_factor = 1
 
-        to_remove = []
-        for d in self.get("uoms"):
-            if d.conversion_factor == 1 and d.uom != self.stock_uom:
-                to_remove.append(d)
+		to_remove = []
+		for d in self.get("uoms"):
+			if d.conversion_factor == 1 and d.uom != self.stock_uom:
+				to_remove.append(d)
 
-        [self.remove(d) for d in to_remove]
+		[self.remove(d) for d in to_remove]
 
-    def update_template_tables(self):
-        template = frappe.get_doc("Item", self.variant_of)
+	def update_template_tables(self):
+		template = frappe.get_doc("Item", self.variant_of)
 
-        # add item taxes from template
-        for d in template.get("taxes"):
-            self.append("taxes", {"tax_type": d.tax_type, "tax_rate": d.tax_rate})
+		# add item taxes from template
+		for d in template.get("taxes"):
+			self.append("taxes", {"tax_type": d.tax_type, "tax_rate": d.tax_rate})
 
-        # copy re-order table if empty
-        if not self.get("reorder_levels"):
-            for d in template.get("reorder_levels"):
-                n = {}
-                for k in ("warehouse", "warehouse_reorder_level",
-                          "warehouse_reorder_qty", "material_request_type"):
-                    n[k] = d.get(k)
-                self.append("reorder_levels", n)
+		# copy re-order table if empty
+		if not self.get("reorder_levels"):
+			for d in template.get("reorder_levels"):
+				n = {}
+				for k in ("warehouse", "warehouse_reorder_level",
+						  "warehouse_reorder_qty", "material_request_type"):
+					n[k] = d.get(k)
+				self.append("reorder_levels", n)
 
-    def validate_conversion_factor(self):
-        check_list = []
-        for d in self.get('uoms'):
-            if cstr(d.uom) in check_list:
-                frappe.throw(
-                    _("Unit of Measure {0} has been entered more than once in Conversion Factor Table").format(d.uom))
-            else:
-                check_list.append(cstr(d.uom))
+	def validate_conversion_factor(self):
+		check_list = []
+		for d in self.get('uoms'):
+			if cstr(d.uom) in check_list:
+				frappe.throw(
+					_("Unit of Measure {0} has been entered more than once in Conversion Factor Table").format(d.uom))
+			else:
+				check_list.append(cstr(d.uom))
 
-            if d.uom and cstr(d.uom) == cstr(self.stock_uom) and flt(d.conversion_factor) != 1:
-                frappe.throw(
-                    _("Conversion factor for default Unit of Measure must be 1 in row {0}").format(d.idx))
+			if d.uom and cstr(d.uom) == cstr(self.stock_uom) and flt(d.conversion_factor) != 1:
+				frappe.throw(
+					_("Conversion factor for default Unit of Measure must be 1 in row {0}").format(d.idx))
 
-    def validate_item_type(self):
-        if self.has_serial_no == 1 and self.is_stock_item == 0:
-            msgprint(_("'Has Serial No' can not be 'Yes' for non-stock item"), raise_exception=1)
+	def validate_item_type(self):
+		if self.has_serial_no == 1 and self.is_stock_item == 0:
+			msgprint(_("'Has Serial No' can not be 'Yes' for non-stock item"), raise_exception=1)
 
-        if self.has_serial_no == 0 and self.serial_no_series:
-            self.serial_no_series = None
+		if self.has_serial_no == 0 and self.serial_no_series:
+			self.serial_no_series = None
 
-    def check_for_active_boms(self):
-        if self.default_bom:
-            bom_item = frappe.db.get_value("BOM", self.default_bom, "item")
-            if bom_item not in (self.name, self.variant_of):
-                frappe.throw(
-                    _("Default BOM ({0}) must be active for this item or its template").format(bom_item))
+	def check_for_active_boms(self):
+		if self.default_bom:
+			bom_item = frappe.db.get_value("BOM", self.default_bom, "item")
+			if bom_item not in (self.name, self.variant_of):
+				frappe.throw(
+					_("Default BOM ({0}) must be active for this item or its template").format(bom_item))
 
-    def fill_customer_code(self):
-        """ Append all the customer codes and insert into "customer_code" field of item table """
-        cust_code = []
-        for d in self.get('customer_items'):
-            cust_code.append(d.ref_code)
-        self.customer_code = ','.join(cust_code)
+	def fill_customer_code(self):
+		""" Append all the customer codes and insert into "customer_code" field of item table """
+		cust_code = []
+		for d in self.get('customer_items'):
+			cust_code.append(d.ref_code)
+		self.customer_code = ','.join(cust_code)
 
-    def check_item_tax(self):
-        """Check whether Tax Rate is not entered twice for same Tax Type"""
-        check_list = []
-        for d in self.get('taxes'):
-            if d.tax_type:
-                account_type = frappe.db.get_value("Account", d.tax_type, "account_type")
+	def check_item_tax(self):
+		"""Check whether Tax Rate is not entered twice for same Tax Type"""
+		check_list = []
+		for d in self.get('taxes'):
+			if d.tax_type:
+				account_type = frappe.db.get_value("Account", d.tax_type, "account_type")
 
-                if account_type not in ['Tax', 'Chargeable', 'Income Account', 'Expense Account']:
-                    frappe.throw(
-                        _("Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable").format(d.idx))
-                else:
-                    if d.tax_type in check_list:
-                        frappe.throw(_("{0} entered twice in Item Tax").format(d.tax_type))
-                    else:
-                        check_list.append(d.tax_type)
+				if account_type not in ['Tax', 'Chargeable', 'Income Account', 'Expense Account']:
+					frappe.throw(
+						_("Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable").format(d.idx))
+				else:
+					if d.tax_type in check_list:
+						frappe.throw(_("{0} entered twice in Item Tax").format(d.tax_type))
+					else:
+						check_list.append(d.tax_type)
 
-    def validate_barcode(self):
-        from stdnum import ean
-        if len(self.barcodes) > 0:
-            for item_barcode in self.barcodes:
-                if item_barcode.barcode:
-                    duplicate = frappe.db.sql("""select parent from `tabItem Barcode` where barcode = %s and parent != %s""", (item_barcode.barcode, self.name))
-                    if duplicate:
-                        frappe.throw(_("Barcode {0} already used in Item {1}").format(
-                            item_barcode.barcode, duplicate[0][0]))
+	def validate_barcode(self):
+		from stdnum import ean
+		if len(self.barcodes) > 0:
+			for item_barcode in self.barcodes:
+				if item_barcode.barcode:
+					duplicate = frappe.db.sql("""select parent from `tabItem Barcode` where barcode = %s and parent != %s""", (item_barcode.barcode, self.name))
+					if duplicate:
+						frappe.throw(_("Barcode {0} already used in Item {1}").format(
+							item_barcode.barcode, duplicate[0][0]))
 
-                    if item_barcode.barcode_type:
-                        if not ean.is_valid(item_barcode.barcode):
-                            frappe.throw(_("Barcode {0} is not a valid {1} code").format(
-                                item_barcode.barcode, item_barcode.barcode_type))
+					if item_barcode.barcode_type:
+						if not ean.is_valid(item_barcode.barcode):
+							frappe.throw(_("Barcode {0} is not a valid {1} code").format(
+								item_barcode.barcode, item_barcode.barcode_type))
 
-    def validate_warehouse_for_reorder(self):
-        '''Validate Reorder level table for duplicate and conditional mandatory'''
-        warehouse = []
-        for d in self.get("reorder_levels"):
-            if not d.warehouse_group:
-                d.warehouse_group = d.warehouse
-            if d.get("warehouse") and d.get("warehouse") not in warehouse:
-                warehouse += [d.get("warehouse")]
-            else:
-                frappe.throw(_("Row {0}: An Reorder entry already exists for this warehouse {1}")
-                             .format(d.idx, d.warehouse), DuplicateReorderRows)
+	def validate_warehouse_for_reorder(self):
+		'''Validate Reorder level table for duplicate and conditional mandatory'''
+		warehouse = []
+		for d in self.get("reorder_levels"):
+			if not d.warehouse_group:
+				d.warehouse_group = d.warehouse
+			if d.get("warehouse") and d.get("warehouse") not in warehouse:
+				warehouse += [d.get("warehouse")]
+			else:
+				frappe.throw(_("Row {0}: An Reorder entry already exists for this warehouse {1}")
+							 .format(d.idx, d.warehouse), DuplicateReorderRows)
 
-            if d.warehouse_reorder_level and not d.warehouse_reorder_qty:
-                frappe.throw(_("Row #{0}: Please set reorder quantity").format(d.idx))
+			if d.warehouse_reorder_level and not d.warehouse_reorder_qty:
+				frappe.throw(_("Row #{0}: Please set reorder quantity").format(d.idx))
 
-    def stock_ledger_created(self):
-        if not hasattr(self, '_stock_ledger_created'):
-            self._stock_ledger_created = len(frappe.db.sql("""select name from `tabStock Ledger Entry`
+	def stock_ledger_created(self):
+		if not hasattr(self, '_stock_ledger_created'):
+			self._stock_ledger_created = len(frappe.db.sql("""select name from `tabStock Ledger Entry`
 				where item_code = %s limit 1""", self.name))
-        return self._stock_ledger_created
+		return self._stock_ledger_created
 
-    def validate_name_with_item_group(self):
-        # causes problem with tree build
-        if frappe.db.exists("Item Group", self.name):
-            frappe.throw(
-                _("An Item Group exists with same name, please change the item name or rename the item group"))
+	def validate_name_with_item_group(self):
+		# causes problem with tree build
+		if frappe.db.exists("Item Group", self.name):
+			frappe.throw(
+				_("An Item Group exists with same name, please change the item name or rename the item group"))
 
-    def update_item_price(self):
-        frappe.db.sql("""update `tabItem Price` set item_name=%s,
+	def update_item_price(self):
+		frappe.db.sql("""update `tabItem Price` set item_name=%s,
 			item_description=%s, modified=NOW() where item_code=%s""",
-                      (self.item_name, self.description, self.name))
+					  (self.item_name, self.description, self.name))
 
-    def on_trash(self):
-        super(Item, self).on_trash()
-        frappe.db.sql("""delete from tabBin where item_code=%s""", self.item_code)
-        frappe.db.sql("delete from `tabItem Price` where item_code=%s", self.name)
-        for variant_of in frappe.get_all("Item", filters={"variant_of": self.name}):
-            frappe.delete_doc("Item", variant_of.name)
+	def on_trash(self):
+		super(Item, self).on_trash()
+		frappe.db.sql("""delete from tabBin where item_code=%s""", self.item_code)
+		frappe.db.sql("delete from `tabItem Price` where item_code=%s", self.name)
+		for variant_of in frappe.get_all("Item", filters={"variant_of": self.name}):
+			frappe.delete_doc("Item", variant_of.name)
 
-    def before_rename(self, old_name, new_name, merge=False):
-        if self.item_name == old_name:
-            frappe.db.set_value("Item", old_name, "item_name", new_name)
+	def before_rename(self, old_name, new_name, merge=False):
+		if self.item_name == old_name:
+			frappe.db.set_value("Item", old_name, "item_name", new_name)
 
-        if merge:
-            # Validate properties before merging
-            if not frappe.db.exists("Item", new_name):
-                frappe.throw(_("Item {0} does not exist").format(new_name))
+		if merge:
+			# Validate properties before merging
+			if not frappe.db.exists("Item", new_name):
+				frappe.throw(_("Item {0} does not exist").format(new_name))
 
-            field_list = ["stock_uom", "is_stock_item", "has_serial_no", "has_batch_no"]
-            new_properties = [cstr(d) for d in frappe.db.get_value("Item", new_name, field_list)]
-            if new_properties != [cstr(self.get(fld)) for fld in field_list]:
-                frappe.throw(_("To merge, following properties must be same for both items")
-                             + ": \n" + ", ".join([self.meta.get_label(fld) for fld in field_list]))
+			field_list = ["stock_uom", "is_stock_item", "has_serial_no", "has_batch_no"]
+			new_properties = [cstr(d) for d in frappe.db.get_value("Item", new_name, field_list)]
+			if new_properties != [cstr(self.get(fld)) for fld in field_list]:
+				frappe.throw(_("To merge, following properties must be same for both items")
+							 + ": \n" + ", ".join([self.meta.get_label(fld) for fld in field_list]))
 
-    def after_rename(self, old_name, new_name, merge):
-        if self.route:
-            invalidate_cache_for_item(self)
-            clear_cache(self.route)
+	def after_rename(self, old_name, new_name, merge):
+		if self.route:
+			invalidate_cache_for_item(self)
+			clear_cache(self.route)
 
-        frappe.db.set_value("Item", new_name, "item_code", new_name)
+		frappe.db.set_value("Item", new_name, "item_code", new_name)
 
-        if merge:
-            self.set_last_purchase_rate(new_name)
-            self.recalculate_bin_qty(new_name)
+		if merge:
+			self.set_last_purchase_rate(new_name)
+			self.recalculate_bin_qty(new_name)
 
-        for dt in ("Sales Taxes and Charges", "Purchase Taxes and Charges"):
-            for d in frappe.db.sql("""select name, item_wise_tax_detail from `tab{0}`
+		for dt in ("Sales Taxes and Charges", "Purchase Taxes and Charges"):
+			for d in frappe.db.sql("""select name, item_wise_tax_detail from `tab{0}`
 					where ifnull(item_wise_tax_detail, '') != ''""".format(dt), as_dict=1):
 
-                item_wise_tax_detail = json.loads(d.item_wise_tax_detail)
-                if old_name in item_wise_tax_detail:
-                    item_wise_tax_detail[new_name] = item_wise_tax_detail[old_name]
-                    item_wise_tax_detail.pop(old_name)
+				item_wise_tax_detail = json.loads(d.item_wise_tax_detail)
+				if old_name in item_wise_tax_detail:
+					item_wise_tax_detail[new_name] = item_wise_tax_detail[old_name]
+					item_wise_tax_detail.pop(old_name)
 
-                    frappe.db.set_value(dt, d.name, "item_wise_tax_detail",
-                                        json.dumps(item_wise_tax_detail), update_modified=False)
+					frappe.db.set_value(dt, d.name, "item_wise_tax_detail",
+										json.dumps(item_wise_tax_detail), update_modified=False)
 
-    def set_last_purchase_rate(self, new_name):
-        last_purchase_rate = get_last_purchase_details(new_name).get("base_rate", 0)
-        frappe.db.set_value("Item", new_name, "last_purchase_rate", last_purchase_rate)
+	def set_last_purchase_rate(self, new_name):
+		last_purchase_rate = get_last_purchase_details(new_name).get("base_rate", 0)
+		frappe.db.set_value("Item", new_name, "last_purchase_rate", last_purchase_rate)
 
-    def recalculate_bin_qty(self, new_name):
-        from erpnext.stock.stock_balance import repost_stock
-        frappe.db.auto_commit_on_many_writes = 1
-        existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
-        frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
+	def recalculate_bin_qty(self, new_name):
+		from erpnext.stock.stock_balance import repost_stock
+		frappe.db.auto_commit_on_many_writes = 1
+		existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
+		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
-        repost_stock_for_warehouses = frappe.db.sql_list("""select distinct warehouse
+		repost_stock_for_warehouses = frappe.db.sql_list("""select distinct warehouse
 			from tabBin where item_code=%s""", new_name)
 
-        # Delete all existing bins to avoid duplicate bins for the same item and warehouse
-        frappe.db.sql("delete from `tabBin` where item_code=%s", new_name)
+		# Delete all existing bins to avoid duplicate bins for the same item and warehouse
+		frappe.db.sql("delete from `tabBin` where item_code=%s", new_name)
 
-        for warehouse in repost_stock_for_warehouses:
-            repost_stock(new_name, warehouse)
+		for warehouse in repost_stock_for_warehouses:
+			repost_stock(new_name, warehouse)
 
-        frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
-        frappe.db.auto_commit_on_many_writes = 0
+		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
+		frappe.db.auto_commit_on_many_writes = 0
 
-    def copy_specification_from_item_group(self):
-        self.set("website_specifications", [])
-        if self.item_group:
-            for label, desc in frappe.db.get_values("Item Website Specification",
-                                                    {"parent": self.item_group}, ["label", "description"]):
-                row = self.append("website_specifications")
-                row.label = label
-                row.description = desc
+	def copy_specification_from_item_group(self):
+		self.set("website_specifications", [])
+		if self.item_group:
+			for label, desc in frappe.db.get_values("Item Website Specification",
+													{"parent": self.item_group}, ["label", "description"]):
+				row = self.append("website_specifications")
+				row.label = label
+				row.description = desc
 
-    def update_bom_item_desc(self):
-        if self.is_new():
-            return
+	def update_bom_item_desc(self):
+		if self.is_new():
+			return
 
-        if self.db_get('description') != self.description:
-            frappe.db.sql("""
+		if self.db_get('description') != self.description:
+			frappe.db.sql("""
 				update `tabBOM`
 				set description = %s
 				where item = %s and docstatus < 2
 			""", (self.description, self.name))
 
-            frappe.db.sql("""
+			frappe.db.sql("""
 				update `tabBOM Item`
 				set description = %s
 				where item_code = %s and docstatus < 2
 			""", (self.description, self.name))
 
-            frappe.db.sql("""
+			frappe.db.sql("""
 				update `tabBOM Explosion Item`
 				set description = %s
 				where item_code = %s and docstatus < 2
 			""", (self.description, self.name))
 
-    def update_template_item(self):
-        """Set Show in Website for Template Item if True for its Variant"""
-        if self.variant_of and self.show_in_website:
-            self.show_variant_in_website = 1
-            self.show_in_website = 0
+	def update_template_item(self):
+		"""Set Show in Website for Template Item if True for its Variant"""
+		if self.variant_of and self.show_in_website:
+			self.show_variant_in_website = 1
+			self.show_in_website = 0
 
-        if self.show_variant_in_website:
-            # show template
-            template_item = frappe.get_doc("Item", self.variant_of)
+		if self.show_variant_in_website:
+			# show template
+			template_item = frappe.get_doc("Item", self.variant_of)
 
-            if not template_item.show_in_website:
-                template_item.show_in_website = 1
-                template_item.flags.dont_update_variants = True
-                template_item.flags.ignore_permissions = True
-                template_item.save()
+			if not template_item.show_in_website:
+				template_item.show_in_website = 1
+				template_item.flags.dont_update_variants = True
+				template_item.flags.ignore_permissions = True
+				template_item.save()
 
-    def update_variants(self):
-        if self.flags.dont_update_variants or \
-                frappe.db.get_single_value('Item Variant Settings', 'do_not_update_variants'):
-            return
-        if self.has_variants:
-            updated = []
-            variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name})
-            for d in variants:
-                variant = frappe.get_doc("Item", d)
-                copy_attributes_to_variant(self, variant)
-                variant.save()
-                updated.append(d.item_code)
-            if updated:
-                frappe.msgprint(_("Item Variants {0} updated").format(", ".join(updated)))
+	def update_variants(self):
+		if self.flags.dont_update_variants or \
+				frappe.db.get_single_value('Item Variant Settings', 'do_not_update_variants'):
+			return
+		if self.has_variants:
+			updated = []
+			variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name})
+			for d in variants:
+				variant = frappe.get_doc("Item", d)
+				copy_attributes_to_variant(self, variant)
+				variant.save()
+				updated.append(d.item_code)
+			if updated:
+				frappe.msgprint(_("Item Variants {0} updated").format(", ".join(updated)))
 
-    def validate_has_variants(self):
-        if not self.has_variants and frappe.db.get_value("Item", self.name, "has_variants"):
-            if frappe.db.exists("Item", {"variant_of": self.name}):
-                frappe.throw(_("Item has variants."))
+	def validate_has_variants(self):
+		if not self.has_variants and frappe.db.get_value("Item", self.name, "has_variants"):
+			if frappe.db.exists("Item", {"variant_of": self.name}):
+				frappe.throw(_("Item has variants."))
 
-    def validate_stock_exists_for_template_item(self):
-        if self.stock_ledger_created() and self._doc_before_save:
-            if (self._doc_before_save.has_variants != self.has_variants
-                    or self._doc_before_save.variant_of != self.variant_of):
-                frappe.throw(_("Cannot change Variant properties after stock transction. You will have to make a new Item to do this.").format(self.name),
-                             StockExistsForTemplate)
+	def validate_stock_exists_for_template_item(self):
+		if self.stock_ledger_created() and self._doc_before_save:
+			if (self._doc_before_save.has_variants != self.has_variants
+					or self._doc_before_save.variant_of != self.variant_of):
+				frappe.throw(_("Cannot change Variant properties after stock transction. You will have to make a new Item to do this.").format(self.name),
+							 StockExistsForTemplate)
 
-            if self.has_variants or self.variant_of:
-                if not self.is_child_table_same('attributes'):
-                    frappe.throw(
-                        _('Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item'))
+			if self.has_variants or self.variant_of:
+				if not self.is_child_table_same('attributes'):
+					frappe.throw(
+						_('Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item'))
 
-    def validate_uom(self):
-        if not self.get("__islocal"):
-            check_stock_uom_with_bin(self.name, self.stock_uom)
-        if self.has_variants:
-            for d in frappe.db.get_all("Item", filters={"variant_of": self.name}):
-                check_stock_uom_with_bin(d.name, self.stock_uom)
-        if self.variant_of:
-            template_uom = frappe.db.get_value("Item", self.variant_of, "stock_uom")
-            if template_uom != self.stock_uom:
-                frappe.throw(_("Default Unit of Measure for Variant '{0}' must be same as in Template '{1}'")
-                             .format(self.stock_uom, template_uom))
+	def validate_uom(self):
+		if not self.get("__islocal"):
+			check_stock_uom_with_bin(self.name, self.stock_uom)
+		if self.has_variants:
+			for d in frappe.db.get_all("Item", filters={"variant_of": self.name}):
+				check_stock_uom_with_bin(d.name, self.stock_uom)
+		if self.variant_of:
+			template_uom = frappe.db.get_value("Item", self.variant_of, "stock_uom")
+			if template_uom != self.stock_uom:
+				frappe.throw(_("Default Unit of Measure for Variant '{0}' must be same as in Template '{1}'")
+							 .format(self.stock_uom, template_uom))
 
-    def validate_attributes(self):
-        if (self.has_variants or self.variant_of) and self.variant_based_on == 'Item Attribute':
-            attributes = []
-            if not self.attributes:
-                frappe.throw(_("Attribute table is mandatory"))
-            for d in self.attributes:
-                if d.attribute in attributes:
-                    frappe.throw(
-                        _("Attribute {0} selected multiple times in Attributes Table".format(d.attribute)))
-                else:
-                    attributes.append(d.attribute)
+	def validate_attributes(self):
+		if (self.has_variants or self.variant_of) and self.variant_based_on == 'Item Attribute':
+			attributes = []
+			if not self.attributes:
+				frappe.throw(_("Attribute table is mandatory"))
+			for d in self.attributes:
+				if d.attribute in attributes:
+					frappe.throw(
+						_("Attribute {0} selected multiple times in Attributes Table".format(d.attribute)))
+				else:
+					attributes.append(d.attribute)
 
-    def validate_variant_attributes(self):
-        if self.variant_of and self.variant_based_on == 'Item Attribute':
-            args = {}
-            for d in self.attributes:
-                if not d.attribute_value:
-                    frappe.throw(_("Please specify Attribute Value for attribute {0}").format(d.attribute))
-                args[d.attribute] = d.attribute_value
+	def validate_variant_attributes(self):
+		if self.variant_of and self.variant_based_on == 'Item Attribute':
+			args = {}
+			for d in self.attributes:
+				if not d.attribute_value:
+					frappe.throw(_("Please specify Attribute Value for attribute {0}").format(d.attribute))
+				args[d.attribute] = d.attribute_value
 
-            variant = get_variant(self.variant_of, args, self.name)
-            if variant:
-                frappe.throw(_("Item variant {0} exists with same attributes")
-                             .format(variant), ItemVariantExistsError)
+			variant = get_variant(self.variant_of, args, self.name)
+			if variant:
+				frappe.throw(_("Item variant {0} exists with same attributes")
+							 .format(variant), ItemVariantExistsError)
 
-            validate_item_variant_attributes(self, args)
+			validate_item_variant_attributes(self, args)
 
 
 def get_timeline_data(doctype, name):
-    '''returns timeline data based on stock ledger entry'''
-    out = {}
-    items = dict(frappe.db.sql('''select posting_date, count(*)
+	'''returns timeline data based on stock ledger entry'''
+	out = {}
+	items = dict(frappe.db.sql('''select posting_date, count(*)
 		from `tabStock Ledger Entry` where item_code=%s
 			and posting_date > date_sub(curdate(), interval 1 year)
 			group by posting_date''', name))
 
-    for date, count in items.iteritems():
-        timestamp = get_timestamp(date)
-        out.update({timestamp: count})
+	for date, count in items.iteritems():
+		timestamp = get_timestamp(date)
+		out.update({timestamp: count})
 
-    return out
+	return out
 
 
 def validate_end_of_life(item_code, end_of_life=None, disabled=None, verbose=1):
-    if (not end_of_life) or (disabled is None):
-        end_of_life, disabled = frappe.db.get_value("Item", item_code, ["end_of_life", "disabled"])
+	if (not end_of_life) or (disabled is None):
+		end_of_life, disabled = frappe.db.get_value("Item", item_code, ["end_of_life", "disabled"])
 
-    if end_of_life and end_of_life != "0000-00-00" and getdate(end_of_life) <= now_datetime().date():
-        msg = _("Item {0} has reached its end of life on {1}").format(item_code, formatdate(end_of_life))
-        _msgprint(msg, verbose)
+	if end_of_life and end_of_life != "0000-00-00" and getdate(end_of_life) <= now_datetime().date():
+		msg = _("Item {0} has reached its end of life on {1}").format(item_code, formatdate(end_of_life))
+		_msgprint(msg, verbose)
 
-    if disabled:
-        _msgprint(_("Item {0} is disabled").format(item_code), verbose)
+	if disabled:
+		_msgprint(_("Item {0} is disabled").format(item_code), verbose)
 
 
 def validate_is_stock_item(item_code, is_stock_item=None, verbose=1):
-    if not is_stock_item:
-        is_stock_item = frappe.db.get_value("Item", item_code, "is_stock_item")
+	if not is_stock_item:
+		is_stock_item = frappe.db.get_value("Item", item_code, "is_stock_item")
 
-    if is_stock_item != 1:
-        msg = _("Item {0} is not a stock Item").format(item_code)
+	if is_stock_item != 1:
+		msg = _("Item {0} is not a stock Item").format(item_code)
 
-        _msgprint(msg, verbose)
+		_msgprint(msg, verbose)
 
 
 def validate_cancelled_item(item_code, docstatus=None, verbose=1):
-    if docstatus is None:
-        docstatus = frappe.db.get_value("Item", item_code, "docstatus")
+	if docstatus is None:
+		docstatus = frappe.db.get_value("Item", item_code, "docstatus")
 
-    if docstatus == 2:
-        msg = _("Item {0} is cancelled").format(item_code)
-        _msgprint(msg, verbose)
+	if docstatus == 2:
+		msg = _("Item {0} is cancelled").format(item_code)
+		_msgprint(msg, verbose)
 
 
 def _msgprint(msg, verbose):
-    if verbose:
-        msgprint(msg, raise_exception=True)
-    else:
-        raise frappe.ValidationError(msg)
+	if verbose:
+		msgprint(msg, raise_exception=True)
+	else:
+		raise frappe.ValidationError(msg)
 
 
 def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
-    """returns last purchase details in stock uom"""
-    # get last purchase order item details
-    last_purchase_order = frappe.db.sql("""\
+	"""returns last purchase details in stock uom"""
+	# get last purchase order item details
+	last_purchase_order = frappe.db.sql("""\
 		select po.name, po.transaction_date, po.conversion_rate,
 			po_item.conversion_factor, po_item.base_price_list_rate,
 			po_item.discount_percentage, po_item.base_rate
@@ -782,8 +782,8 @@
 		order by po.transaction_date desc, po.name desc
 		limit 1""", (item_code, cstr(doc_name)), as_dict=1)
 
-    # get last purchase receipt item details
-    last_purchase_receipt = frappe.db.sql("""\
+	# get last purchase receipt item details
+	last_purchase_receipt = frappe.db.sql("""\
 		select pr.name, pr.posting_date, pr.posting_time, pr.conversion_rate,
 			pr_item.conversion_factor, pr_item.base_price_list_rate, pr_item.discount_percentage,
 			pr_item.base_rate
@@ -793,79 +793,79 @@
 		order by pr.posting_date desc, pr.posting_time desc, pr.name desc
 		limit 1""", (item_code, cstr(doc_name)), as_dict=1)
 
-    purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date
-                                  or "1900-01-01")
-    purchase_receipt_date = getdate(last_purchase_receipt and
-                                    last_purchase_receipt[0].posting_date or "1900-01-01")
+	purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date
+								  or "1900-01-01")
+	purchase_receipt_date = getdate(last_purchase_receipt and
+									last_purchase_receipt[0].posting_date or "1900-01-01")
 
-    if (purchase_order_date > purchase_receipt_date) or \
-            (last_purchase_order and not last_purchase_receipt):
-        # use purchase order
-        last_purchase = last_purchase_order[0]
-        purchase_date = purchase_order_date
+	if (purchase_order_date > purchase_receipt_date) or \
+			(last_purchase_order and not last_purchase_receipt):
+		# use purchase order
+		last_purchase = last_purchase_order[0]
+		purchase_date = purchase_order_date
 
-    elif (purchase_receipt_date > purchase_order_date) or \
-            (last_purchase_receipt and not last_purchase_order):
-        # use purchase receipt
-        last_purchase = last_purchase_receipt[0]
-        purchase_date = purchase_receipt_date
+	elif (purchase_receipt_date > purchase_order_date) or \
+			(last_purchase_receipt and not last_purchase_order):
+		# use purchase receipt
+		last_purchase = last_purchase_receipt[0]
+		purchase_date = purchase_receipt_date
 
-    else:
-        return frappe._dict()
+	else:
+		return frappe._dict()
 
-    conversion_factor = flt(last_purchase.conversion_factor)
-    out = frappe._dict({
-        "base_price_list_rate": flt(last_purchase.base_price_list_rate) / conversion_factor,
-        "base_rate": flt(last_purchase.base_rate) / conversion_factor,
-        "discount_percentage": flt(last_purchase.discount_percentage),
-        "purchase_date": purchase_date
-    })
+	conversion_factor = flt(last_purchase.conversion_factor)
+	out = frappe._dict({
+		"base_price_list_rate": flt(last_purchase.base_price_list_rate) / conversion_factor,
+		"base_rate": flt(last_purchase.base_rate) / conversion_factor,
+		"discount_percentage": flt(last_purchase.discount_percentage),
+		"purchase_date": purchase_date
+	})
 
-    conversion_rate = flt(conversion_rate) or 1.0
-    out.update({
-        "price_list_rate": out.base_price_list_rate / conversion_rate,
-        "rate": out.base_rate / conversion_rate,
-        "base_rate": out.base_rate
-    })
+	conversion_rate = flt(conversion_rate) or 1.0
+	out.update({
+		"price_list_rate": out.base_price_list_rate / conversion_rate,
+		"rate": out.base_rate / conversion_rate,
+		"base_rate": out.base_rate
+	})
 
-    return out
+	return out
 
 
 def invalidate_cache_for_item(doc):
-    invalidate_cache_for(doc, doc.item_group)
+	invalidate_cache_for(doc, doc.item_group)
 
-    website_item_groups = list(set((doc.get("old_website_item_groups") or [])
-                                   + [d.item_group for d in doc.get({"doctype": "Website Item Group"}) if d.item_group]))
+	website_item_groups = list(set((doc.get("old_website_item_groups") or [])
+								   + [d.item_group for d in doc.get({"doctype": "Website Item Group"}) if d.item_group]))
 
-    for item_group in website_item_groups:
-        invalidate_cache_for(doc, item_group)
+	for item_group in website_item_groups:
+		invalidate_cache_for(doc, item_group)
 
-    if doc.get("old_item_group") and doc.get("old_item_group") != doc.item_group:
-        invalidate_cache_for(doc, doc.old_item_group)
+	if doc.get("old_item_group") and doc.get("old_item_group") != doc.item_group:
+		invalidate_cache_for(doc, doc.old_item_group)
 
 
 def check_stock_uom_with_bin(item, stock_uom):
-    if stock_uom == frappe.db.get_value("Item", item, "stock_uom"):
-        return
+	if stock_uom == frappe.db.get_value("Item", item, "stock_uom"):
+		return
 
-    matched = True
-    ref_uom = frappe.db.get_value("Stock Ledger Entry",
-                                  {"item_code": item}, "stock_uom")
+	matched = True
+	ref_uom = frappe.db.get_value("Stock Ledger Entry",
+								  {"item_code": item}, "stock_uom")
 
-    if ref_uom:
-        if cstr(ref_uom) != cstr(stock_uom):
-            matched = False
-    else:
-        bin_list = frappe.db.sql("select * from tabBin where item_code=%s", item, as_dict=1)
-        for bin in bin_list:
-            if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0
-                    or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom):
-                matched = False
-                break
+	if ref_uom:
+		if cstr(ref_uom) != cstr(stock_uom):
+			matched = False
+	else:
+		bin_list = frappe.db.sql("select * from tabBin where item_code=%s", item, as_dict=1)
+		for bin in bin_list:
+			if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0
+					or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom):
+				matched = False
+				break
 
-        if matched and bin_list:
-            frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item))
+		if matched and bin_list:
+			frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item))
 
-    if not matched:
-        frappe.throw(
-            _("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.").format(item))
+	if not matched:
+		frappe.throw(
+			_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.").format(item))