fix: don't cancel job card if manufacturing entry has made
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index d15d81e..ec28eb7 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -17,6 +17,7 @@
 
 class OperationMismatchError(frappe.ValidationError): pass
 class OperationSequenceError(frappe.ValidationError): pass
+class JobCardCancelError(frappe.ValidationError): pass
 
 class JobCard(Document):
 	def validate(self):
@@ -217,33 +218,49 @@
 		field = "operation_id"
 		data = self.get_current_operation_data()
 		if data and len(data) > 0:
-			for_quantity = data[0].completed_qty
-			time_in_mins = data[0].time_in_mins
+			for_quantity = flt(data[0].completed_qty)
+			time_in_mins = flt(data[0].time_in_mins)
 
-		if self.get(field):
-			time_data = frappe.db.sql("""
+		wo = frappe.get_doc('Work Order', self.work_order)
+		if self.operation_id:
+			self.validate_produced_quantity(for_quantity, wo)
+			self.update_work_order_data(for_quantity, time_in_mins, wo)
+
+	def validate_produced_quantity(self, for_quantity, wo):
+		if self.docstatus < 2: return
+
+		if wo.produced_qty > for_quantity:
+			first_part_msg = (_("The {0} {1} is used to calculate the valuation cost for the finished good {2}.")
+				.format(frappe.bold(_("Job Card")), frappe.bold(self.name), frappe.bold(self.production_item)))
+
+			second_part_msg = (_("Kindly cancel the Manufacturing Entries first against the work order {0}.")
+				.format(frappe.bold(get_link_to_form("Work Order", self.work_order))))
+
+			frappe.throw(_("{0} {1}").format(first_part_msg, second_part_msg),
+				JobCardCancelError, title = _("Error"))
+
+	def update_work_order_data(self, for_quantity, time_in_mins, wo):
+		time_data = frappe.db.sql("""
 				SELECT
 					min(from_time) as start_time, max(to_time) as end_time
 				FROM `tabJob Card` jc, `tabJob Card Time Log` jctl
 				WHERE
 					jctl.parent = jc.name and jc.work_order = %s
-					and jc.{0} = %s and jc.docstatus = 1
-			""".format(field), (self.work_order, self.get(field)), as_dict=1)
+					and jc.operation_id = %s and jc.docstatus = 1
+			""", (self.work_order, self.operation_id), as_dict=1)
 
-			wo = frappe.get_doc('Work Order', self.work_order)
+		for data in wo.operations:
+			if data.get("name") == self.operation_id:
+				data.completed_qty = for_quantity
+				data.actual_operation_time = time_in_mins
+				data.actual_start_time = time_data[0].start_time if time_data else None
+				data.actual_end_time = time_data[0].end_time if time_data else None
 
-			for data in wo.operations:
-				if data.get("name") == self.get(field):
-					data.completed_qty = for_quantity
-					data.actual_operation_time = time_in_mins
-					data.actual_start_time = time_data[0].start_time if time_data else None
-					data.actual_end_time = time_data[0].end_time if time_data else None
-
-			wo.flags.ignore_validate_update_after_submit = True
-			wo.update_operation_status()
-			wo.calculate_operating_cost()
-			wo.set_actual_dates()
-			wo.save()
+		wo.flags.ignore_validate_update_after_submit = True
+		wo.update_operation_status()
+		wo.calculate_operating_cost()
+		wo.set_actual_dates()
+		wo.save()
 
 	def get_current_operation_data(self):
 		return frappe.get_all('Job Card',
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index ce9699e..a77bd15 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -5,7 +5,7 @@
 from __future__ import unicode_literals
 import unittest
 import frappe
-from frappe.utils import flt, time_diff_in_hours, now, add_months, cint, today
+from frappe.utils import flt, now, add_months, cint, today, add_to_date
 from erpnext.manufacturing.doctype.work_order.work_order import (make_stock_entry,
 	ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError, CapacityError)
 from erpnext.stock.doctype.stock_entry import test_stock_entry
@@ -14,6 +14,7 @@
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+from erpnext.manufacturing.doctype.job_card.job_card import JobCardCancelError
 
 class TestWorkOrder(unittest.TestCase):
 	def setUp(self):
@@ -369,21 +370,49 @@
 		self.assertEqual(ste.total_additional_costs, 1000)
 
 	def test_job_card(self):
+		stock_entries = []
 		data = frappe.get_cached_value('BOM',
 			{'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
 
-		if data:
-			frappe.db.set_value("Manufacturing Settings",
-				None, "disable_capacity_planning", 0)
+		bom, bom_item = data
 
-			bom, bom_item = data
+		bom_doc = frappe.get_doc('BOM', bom)
+		work_order = make_wo_order_test_record(item=bom_item, qty=1,
+			bom_no=bom, source_warehouse="_Test Warehouse - _TC")
 
-			bom_doc = frappe.get_doc('BOM', bom)
-			work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom)
-			self.assertTrue(work_order.planned_end_date)
+		for row in work_order.required_items:
+			stock_entry_doc = test_stock_entry.make_stock_entry(item_code=row.item_code,
+				target="_Test Warehouse - _TC", qty=row.required_qty, basic_rate=100)
+			stock_entries.append(stock_entry_doc)
 
-			job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name})
-			self.assertEqual(len(job_cards), len(bom_doc.operations))
+		ste = frappe.get_doc(make_stock_entry(work_order.name, "Material Transfer for Manufacture", 1))
+		ste.submit()
+		stock_entries.append(ste)
+
+		job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name})
+		self.assertEqual(len(job_cards), len(bom_doc.operations))
+
+		for i, job_card in enumerate(job_cards):
+			doc = frappe.get_doc("Job Card", job_card)
+			doc.append("time_logs", {
+				"from_time": now(),
+				"hours": i,
+				"to_time": add_to_date(now(), i),
+				"completed_qty": doc.for_quantity
+			})
+			doc.submit()
+
+		ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 1))
+		ste1.submit()
+		stock_entries.append(ste1)
+
+		for job_card in job_cards:
+			doc = frappe.get_doc("Job Card", job_card)
+			self.assertRaises(JobCardCancelError, doc.cancel)
+
+		stock_entries.reverse()
+		for stock_entry in stock_entries:
+			stock_entry.cancel()
 
 	def test_capcity_planning(self):
 		frappe.db.set_value("Manufacturing Settings", None, {
@@ -509,7 +538,6 @@
 		ste1.submit()
 		ste_cancel_list.append(ste1)
 
-		print(wo_order.name)
 		ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Material Consumption for Manufacture", 2))
 		self.assertEquals(ste3.fg_completed_qty, 2)