Merge pull request #3549 from neilLasrado/item-varients

Validation changed for Item Template cannot have Stock
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 1280cc0..ce6ac7a 100755
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -50,6 +50,23 @@
    "width": "300px"
   }, 
   {
+   "fieldname": "image", 
+   "fieldtype": "Attach", 
+   "hidden": 1, 
+   "label": "Image", 
+   "permlevel": 0, 
+   "precision": ""
+  }, 
+  {
+   "fieldname": "image_view", 
+   "fieldtype": "Image", 
+   "label": "Image View", 
+   "options": "image", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1
+  }, 
+  {
    "fieldname": "quantity_and_rate", 
    "fieldtype": "Section Break", 
    "label": "Quantity and Rate", 
@@ -452,7 +469,7 @@
  ], 
  "idx": 1, 
  "istable": 1, 
- "modified": "2015-06-02 14:18:56.294949", 
+ "modified": "2015-07-02 03:00:44.496683", 
  "modified_by": "Administrator", 
  "module": "Accounts", 
  "name": "Purchase Invoice Item", 
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index 4ace40a..6dcd591 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -69,6 +69,23 @@
    "width": "200px"
   }, 
   {
+   "fieldname": "image", 
+   "fieldtype": "Attach", 
+   "hidden": 1, 
+   "label": "Image", 
+   "permlevel": 0, 
+   "precision": ""
+  }, 
+  {
+   "fieldname": "image_view", 
+   "fieldtype": "Image", 
+   "label": "Image View", 
+   "options": "image", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1
+  }, 
+  {
    "fieldname": "quantity_and_rate", 
    "fieldtype": "Section Break", 
    "label": "Quantity and Rate", 
@@ -505,7 +522,7 @@
  ], 
  "idx": 1, 
  "istable": 1, 
- "modified": "2015-06-02 14:18:45.176726", 
+ "modified": "2015-07-02 02:59:08.413213", 
  "modified_by": "Administrator", 
  "module": "Accounts", 
  "name": "Sales Invoice Item", 
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 716cc3d..9b68810 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -175,16 +175,15 @@
 
 		else:
 			if row.update_stock or row.dn_detail:
+				parenttype, parent, item_row = row.parenttype, row.parent, row.item_row
 				if row.dn_detail:
-					row.parenttype = "Delivery Note"
-					row.parent = row.delivery_note
-					row.item_row = row.dn_detail
+					parenttype, parent, item_row = "Delivery Note", row.delivery_note, row.dn_detail
 
 				my_sle = self.sle.get((item_code, row.warehouse))
 				for i, sle in enumerate(my_sle):
 					# find the stock valution rate from stock ledger entry
-					if sle.voucher_type == row.parenttype and row.parent == sle.voucher_no and \
-						sle.voucher_detail_no == row.item_row:
+					if sle.voucher_type == parenttype and parent == sle.voucher_no and \
+						sle.voucher_detail_no == item_row:
 							previous_stock_value = len(my_sle) > i+1 and \
 								flt(my_sle[i+1].stock_value) or 0.0
 							return  previous_stock_value - flt(sle.stock_value)
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index 65ecfd8..f0eebb1 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -61,7 +61,7 @@
 	var d = locals[cdt][cdn];
 	if (d.item_code) {
 		return frappe.call({
-			doc: cur_frm.doc,
+			doc: doc,
 			method: "get_bom_material_detail",
 			args: {
 				'item_code': d.item_code,
@@ -234,5 +234,3 @@
 cur_frm.cscript.image = function() {
 	refresh_field("image_view");
 }
-
-
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 80965ec..725e020 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -169,3 +169,4 @@
 erpnext.patches.v5_0.index_on_account_and_gl_entry
 execute:frappe.db.sql("""delete from `tabProject Task`""")
 erpnext.patches.v5_0.item_variants
+erpnext.patches.v5_0.update_item_desc_in_invoice
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/update_item_desc_in_invoice.py b/erpnext/patches/v5_0/update_item_desc_in_invoice.py
new file mode 100644
index 0000000..b7a071c
--- /dev/null
+++ b/erpnext/patches/v5_0/update_item_desc_in_invoice.py
@@ -0,0 +1,50 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+from frappe.website.utils import find_first_image
+from frappe.utils import cstr
+import re
+
+def execute():
+	item_details = frappe._dict()
+	for d in frappe.db.sql("select name, description, image from `tabItem`", as_dict=1):
+		description = cstr(d.description).strip()
+		item_details.setdefault(d.name, frappe._dict({
+			"description": description,
+			"image": d.image
+		}))
+
+
+	dt_list= ["Sales Invoice Item","Purchase Invoice Item"]
+	for dt in dt_list:
+		frappe.reload_doctype(dt)
+		records = frappe.db.sql("""select name, item_code, description from `tab{0}`
+			where description is not null """.format(dt), as_dict=1)
+
+		count = 1
+		for d in records:
+			if d.item_code and item_details.get(d.item_code) \
+					and cstr(d.description) == item_details.get(d.item_code).description:
+				desc = item_details.get(d.item_code).description
+				image = item_details.get(d.item_code).image
+			else:
+				desc, image = extract_image_and_description(cstr(d.description))
+				
+				if not image:
+					image = item_details.get(d.item_code).image
+
+			frappe.db.sql("""update `tab{0}` set description = %s, image = %s
+				where name = %s """.format(dt), (desc, image, d.name))
+
+			count += 1
+			if count % 500 == 0:
+				frappe.db.commit()
+
+
+def extract_image_and_description(data):
+	image_url = find_first_image(data)
+	desc = data
+	for tag in ("img", "table", "tr", "td"):
+		desc =  re.sub("\</*{0}[^>]*\>".format(tag), "", desc)
+	return desc, image_url
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 0ba368d..6ebafdb 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -46,6 +46,8 @@
 		"""sync tasks and remove table"""
 		if self.flags.dont_sync_tasks: return
 
+
+		task_added_or_deleted = False
 		task_names = []
 		for t in self.tasks:
 			if t.task_id:
@@ -53,6 +55,7 @@
 			else:
 				task = frappe.new_doc("Task")
 				task.project = self.name
+				task_added_or_deleted = True
 
 			task.update({
 				"subject": t.title,
@@ -70,15 +73,22 @@
 		# delete
 		for t in frappe.get_all("Task", ["name"], {"project": self.name, "name": ("not in", task_names)}):
 			frappe.delete_doc("Task", t.name)
+			task_added_or_deleted = True
+			
+		if task_added_or_deleted:
+			self.update_project()
+
+	def update_project(self):
+		self.update_percent_complete()
+		self.update_costing()
 
 	def update_percent_complete(self):
-		total = frappe.db.sql("""select count(*) from tabTask where project=%s""",
-			self.name)[0][0]
+		total = frappe.db.sql("""select count(*) from tabTask where project=%s""", self.name)[0][0]
 		if total:
 			completed = frappe.db.sql("""select count(*) from tabTask where
 				project=%s and status in ('Closed', 'Cancelled')""", self.name)[0][0]
-			frappe.db.set_value("Project", self.name, "percent_complete",
-			 	int(float(completed) / total * 100))
+				
+			self.percent_complete = flt(completed) / total * 100
 
 	def update_costing(self):
 		total_cost = frappe.db.sql("""select sum(total_costing_amount) as costing_amount,
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index f5541cc..7229203 100644
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -43,15 +43,8 @@
 	def on_update(self):
 		self.check_recursion()
 		self.reschedule_dependent_tasks()
-		self.update_percentage()
 		self.update_project()
 
-	def update_percentage(self):
-		"""update percent complete in project"""
-		if self.project and not self.flags.from_project:
-			project = frappe.get_doc("Project", self.project)
-			project.run_method("update_percent_complete")
-
 	def update_total_expense_claim(self):
 		self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim`
 			where project = %s and task = %s and approval_status = "Approved" and docstatus=1""",(self.project, self.name))
@@ -70,10 +63,10 @@
 		self.act_end_date= tl.end_date
 
 	def update_project(self):
-		if self.project and frappe.db.exists("Project", self.project):
+		if self.project and not self.flags.from_project:
 			project = frappe.get_doc("Project", self.project)
 			project.flags.dont_sync_tasks = True
-			project.update_costing()
+			project.update_project()
 			project.save()
 
 	def check_recursion(self):
diff --git a/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py
index 2398950..0ecc9c0 100644
--- a/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py
@@ -4,7 +4,7 @@
 from __future__ import unicode_literals
 import frappe
 
-from frappe.utils import add_days, getdate, cint
+from frappe.utils import add_days, getdate, cint, cstr
 
 from frappe import throw, _
 from erpnext.utilities.transaction_base import TransactionBase, delete_events
@@ -73,7 +73,7 @@
 						"owner": email_map[d.sales_person] or self.owner,
 						"subject": description,
 						"description": description,
-						"starts_on": key["scheduled_date"] + " 10:00:00",
+						"starts_on": cstr(key["scheduled_date"]) + " 10:00:00",
 						"event_type": "Private",
 						"ref_type": self.doctype,
 						"ref_name": self.name