[fix] rename old filenames that start with FileData and fix missing Item images
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index b2d8068..e4eefce 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -188,3 +188,4 @@
 erpnext.patches.v5_4.notify_system_managers_regarding_wrong_tax_calculation
 erpnext.patches.v5_4.fix_invoice_outstanding
 execute:frappe.db.sql("update `tabStock Ledger Entry` set stock_queue = '[]' where voucher_type = 'Stock Reconciliation' and ifnull(qty_after_transaction, 0) = 0")
+erpnext.patches.v5_4.fix_missing_item_images
diff --git a/erpnext/patches/v5_4/fix_missing_item_images.py b/erpnext/patches/v5_4/fix_missing_item_images.py
new file mode 100644
index 0000000..d782cd1
--- /dev/null
+++ b/erpnext/patches/v5_4/fix_missing_item_images.py
@@ -0,0 +1,116 @@
+from __future__ import unicode_literals
+import frappe
+import os
+from frappe.utils import get_files_path
+from frappe.utils.file_manager import get_content_hash
+
+def execute():
+	files_path = get_files_path()
+
+	# get files that don't have attached_to_name but exist
+	unlinked_files = get_unlinked_files(files_path)
+	if not unlinked_files:
+		return
+
+	fixed_files = fix_files_for_item(files_path, unlinked_files)
+
+	# fix remaining files
+	for key, file_data in unlinked_files.items():
+		if key not in fixed_files:
+			rename_and_set_content_hash(files_path, unlinked_files, key)
+			frappe.db.commit()
+
+def fix_files_for_item(files_path, unlinked_files):
+	fixed_files = []
+
+	# make a list of files/something and /files/something to check in child table's image column
+	file_urls = [key for key in unlinked_files.keys()] + ["/" + key for key in unlinked_files.keys()]
+	file_item_code = get_file_item_code(file_urls)
+
+	for (file_url, item_code), children in file_item_code.items():
+		new_file_url = "/files/{0}".format(unlinked_files[file_url]["file_name"])
+
+		for row in children:
+			# print file_url, new_file_url, item_code, row.doctype, row.name
+
+			# replace image in these rows with the new file url
+			frappe.db.set_value(row.doctype, row.name, "image", new_file_url, update_modified=False)
+
+		# set it as attachment of this item code
+		file_data = frappe.get_doc("File Data", unlinked_files[file_url]["file"])
+		file_data.attached_to_doctype = "Item"
+		file_data.attached_to_name = item_code
+		file_data.save()
+
+		# set it as image in Item
+		if not frappe.db.get_value("Item", item_code, "image"):
+			frappe.db.set_value("Item", item_code, "image", new_file_url, update_modified=False)
+
+		rename_and_set_content_hash(files_path, unlinked_files, file_url)
+
+		fixed_files.append(file_url)
+
+		# commit
+		frappe.db.commit()
+
+	return fixed_files
+
+def rename_and_set_content_hash(files_path, unlinked_files, file_url):
+	# rename this file
+	old_filename = os.path.join(files_path, unlinked_files[file_url]["file"])
+	new_filename = os.path.join(files_path, unlinked_files[file_url]["file_name"])
+
+	if not os.path.exists(new_filename):
+		os.rename(old_filename, new_filename)
+
+	# set content hash if missing
+	file_data_name = unlinked_files[file_url]["file"]
+	if not frappe.db.get_value("File Data", file_data_name, "content_hash"):
+		with open(new_filename, "r") as f:
+			content_hash = get_content_hash(f.read())
+			frappe.db.set_value("File Data", file_data_name, "content_hash", content_hash)
+
+def get_unlinked_files(files_path):
+	# find files that have the same name as a File Data doc
+	# and the file_name mentioned in that File Data doc doesn't exist
+	# and it isn't already attached to a doc
+	unlinked_files = {}
+	files = os.listdir(files_path)
+	for file in files:
+		if not frappe.db.exists("File Data", {"file_name": file}):
+			file_data = frappe.db.get_value("File Data", {"name": file},
+				["file_name", "attached_to_doctype", "attached_to_name"], as_dict=True)
+
+			if (file_data
+				and file_data.file_name
+				and file_data.file_name not in files
+				and not file_data.attached_to_doctype
+				and not file_data.attached_to_name):
+
+				file_data["file"] = file
+				unlinked_files["files/{0}".format(file)] = file_data
+
+	return unlinked_files
+
+def get_file_item_code(file_urls):
+	# get a map of file_url, item_code and list of documents where file_url will need to be changed in image field
+	file_item_code = {}
+
+	doctypes = frappe.db.sql_list("""select name from `tabDocType` dt
+		where istable=1
+			and exists (select name from `tabDocField` df where df.parent=dt.name and df.fieldname='item_code')
+			and exists (select name from `tabDocField` df where df.parent=dt.name and df.fieldname='image')""")
+
+	for doctype in doctypes:
+		result = frappe.db.sql("""select name, image, item_code, '{0}' as doctype from `tab{0}`
+				where image in ({1})""".format(doctype, ", ".join(["%s"]*len(file_urls))),
+				file_urls, as_dict=True)
+
+		for r in result:
+			key = (r.image, r.item_code)
+			if key not in file_item_code:
+				file_item_code[key] = []
+
+			file_item_code[key].append(r)
+
+	return file_item_code