Merge branch 'develop' into docs-templates
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.js b/erpnext/accounts/doctype/share_transfer/share_transfer.js
index 446ae68..364ca6f 100644
--- a/erpnext/accounts/doctype/share_transfer/share_transfer.js
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.js
@@ -16,7 +16,7 @@
 				};
 			};
 		});
-		if (frm.doc.docstatus == 1) {
+		if (frm.doc.docstatus == 1 && frm.doc.equity_or_liability_account && frm.doc.asset_account) {
 			frm.add_custom_button(__('Create Journal Entry'), function () {
 				erpnext.share_transfer.make_jv(frm);
 			});
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.json b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.json
index ae2aa54..b40243c 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.json
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.json
@@ -1,13 +1,13 @@
 {
  "add_total_row": 0,
  "creation": "2019-09-23 16:35:02.836134",
- "disable_prepared_report": 0,
+ "disable_prepared_report": 1,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Report",
  "idx": 0,
  "is_standard": "Yes",
- "modified": "2019-09-23 16:35:02.836134",
+ "modified": "2019-10-22 13:00:31.539726",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Fixed Asset Register",
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
index 2c9e48a..f395499 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
@@ -4,6 +4,7 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
+from frappe.utils import cstr
 
 def execute(filters=None):
 	filters = frappe._dict(filters or {})
@@ -149,12 +150,12 @@
 		FROM `tabAsset Finance Book`
 		WHERE
 			parentfield='finance_books'
-			AND finance_book=%s''', (finance_book)))
+			AND ifnull(finance_book, '')=%s''', cstr(finance_book)))
 
 def get_purchase_receipt_supplier_map():
 	return frappe._dict(frappe.db.sql(''' Select
 		pr.name, pr.supplier
-		FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri 
+		FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri
 		WHERE
 			pri.parent = pr.name
 			AND pri.is_fixed_asset=1
@@ -164,7 +165,7 @@
 def get_purchase_invoice_supplier_map():
 	return frappe._dict(frappe.db.sql(''' Select
 		pi.name, pi.supplier
-		FROM `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pii 
+		FROM `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pii
 		WHERE
 			pii.parent = pi.name
 			AND pii.is_fixed_asset=1
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index ff0b65b..4506db6 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -589,6 +589,23 @@
 		frappe.db.set_value("Accounts Settings", "Accounts Settings",
 			"unlink_advance_payment_on_cancelation_of_order", 0)
 
+	def test_schedule_date(self):
+		po = create_purchase_order(do_not_submit=True)
+		po.schedule_date = None
+		po.append("items", {
+			"item_code": "_Test Item",
+			"qty": 1,
+			"rate": 100,
+			"schedule_date": add_days(nowdate(), 5)
+		})
+		po.save()
+		self.assertEqual(po.schedule_date, add_days(nowdate(), 1))
+
+		po.items[0].schedule_date = add_days(nowdate(), 2)
+		po.save()
+		self.assertEqual(po.schedule_date, add_days(nowdate(), 2))
+
+
 def make_pr_against_po(po, received_qty=0):
 	pr = make_purchase_receipt(po)
 	pr.get("items")[0].qty = received_qty or 5
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 9d37df0..0dde898 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -695,8 +695,10 @@
 	def validate_schedule_date(self):
 		if not self.get("items"):
 			return
-		if not self.schedule_date:
-			self.schedule_date = min([d.schedule_date for d in self.get("items")])
+
+		earliest_schedule_date = min([d.schedule_date for d in self.get("items")])
+		if earliest_schedule_date:
+			self.schedule_date = earliest_schedule_date
 
 		if self.schedule_date:
 			for d in self.get('items'):
diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py
index bd98037..3be08a2 100644
--- a/erpnext/erpnext_integrations/connectors/shopify_connection.py
+++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py
@@ -29,7 +29,8 @@
 			validate_item(order, shopify_settings)
 			create_order(order, shopify_settings)
 		except Exception as e:
-			make_shopify_log(status="Error", message=e.message, exception=False)
+			make_shopify_log(status="Error", exception=e)
+
 		else:
 			make_shopify_log(status="Success")
 
@@ -42,9 +43,9 @@
 		sales_order = get_sales_order(cstr(order['id']))
 		if sales_order:
 			create_sales_invoice(order, shopify_settings, sales_order)
-		make_shopify_log(status="Success")
-	except Exception:
-		make_shopify_log(status="Error", exception=True)
+			make_shopify_log(status="Success")
+	except Exception as e:
+		make_shopify_log(status="Error", exception=e, rollback=True)
 
 def prepare_delivery_note(order, request_id=None):
 	frappe.set_user('Administrator')
@@ -56,8 +57,8 @@
 		if sales_order:
 			create_delivery_note(order, shopify_settings, sales_order)
 		make_shopify_log(status="Success")
-	except Exception:
-		make_shopify_log(status="Error", exception=True)
+	except Exception as e:
+		make_shopify_log(status="Error", exception=e, rollback=True)
 
 def get_sales_order(shopify_order_id):
 	sales_order = frappe.db.get_value("Sales Order", filters={"shopify_order_id": shopify_order_id})
@@ -97,7 +98,7 @@
 			message = 'Following items are exists in order but relevant record not found in Product master'
 			message += "\n" + ", ".join(product_not_exists)
 
-			make_shopify_log(status="Error", message=message, exception=True)
+			make_shopify_log(status="Error", exception=e, rollback=True)
 
 			return ''
 
diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py
index 0c821e0..7d3f572 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py
@@ -12,23 +12,38 @@
 	pass
 
 
-def make_shopify_log(status="Queued", message=None, exception=False):
+def make_shopify_log(status="Queued", exception=None, rollback=False):
 	# if name not provided by log calling method then fetch existing queued state log
+	make_new = False
+
 	if not frappe.flags.request_id:
-		return
+		make_new = True
 
-	log = frappe.get_doc("Shopify Log", frappe.flags.request_id)
-
-	if exception:
+	if rollback:
 		frappe.db.rollback()
-		log = frappe.get_doc({"doctype":"Shopify Log"}).insert(ignore_permissions=True)
 
-	log.message = message if message else ''
+	if make_new:
+		log = frappe.get_doc({"doctype":"Shopify Log"}).insert(ignore_permissions=True)
+	else:
+		log = log = frappe.get_doc("Shopify Log", frappe.flags.request_id)
+
+	log.message = get_message(exception)
 	log.traceback = frappe.get_traceback()
 	log.status = status
 	log.save(ignore_permissions=True)
 	frappe.db.commit()
 
+def get_message(exception):
+	message = None
+
+	if hasattr(exception, 'message'):
+		message = exception.message
+	elif hasattr(exception, '__str__'):
+		message = e.__str__()
+	else:
+		message = "Something went wrong while syncing"
+	return message
+
 def dump_request_data(data, event="create/order"):
 	event_mapper = {
 		"orders/create": get_webhook_address(connector_name='shopify_connection', method="sync_sales_order", exclude_uri=True),
@@ -43,11 +58,11 @@
 	}).insert(ignore_permissions=True)
 
 	frappe.db.commit()
-	frappe.enqueue(method=event_mapper[event], queue='short', timeout=300, is_async=True, 
+	frappe.enqueue(method=event_mapper[event], queue='short', timeout=300, is_async=True,
 		**{"order": data, "request_id": log.name})
 
 @frappe.whitelist()
 def resync(method, name, request_data):
 	frappe.db.set_value("Shopify Log", name, "status", "Queued", update_modified=False)
-	frappe.enqueue(method=method, queue='short', timeout=300, is_async=True, 
+	frappe.enqueue(method=method, queue='short', timeout=300, is_async=True,
 		**{"order": json.loads(request_data), "request_id": name})
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
index e2f6d49..a4332b1 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
@@ -30,13 +30,9 @@
 		# url = get_shopify_url('admin/webhooks.json', self)
 		created_webhooks = [d.method for d in self.webhooks]
 		url = get_shopify_url('admin/api/2019-04/webhooks.json', self)
-		print('url', url)
 		for method in webhooks:
-			print('method', method)
 			session = get_request_session()
-			print('session', session)
 			try:
-				print(get_header(self))
 				d = session.post(url, data=json.dumps({
 					"webhook": {
 						"topic": method,
@@ -44,7 +40,6 @@
 						"format": "json"
 						}
 					}), headers=get_header(self))
-				print('d', d.json())
 				d.raise_for_status()
 				self.update_webhook_table(method, d.json())
 			except Exception as e:
@@ -67,7 +62,6 @@
 			self.remove(d)
 
 	def update_webhook_table(self, method, res):
-		print('update')
 		self.append("webhooks", {
 			"webhook_id": res['webhook']['id'],
 			"method": method
@@ -75,7 +69,6 @@
 
 def get_shopify_url(path, settings):
 	if settings.app_type == "Private":
-		print(settings.api_key, settings.get_password('password'), settings.shopify_url, path)
 		return 'https://{}:{}@{}/{}'.format(settings.api_key, settings.get_password('password'), settings.shopify_url, path)
 	else:
 		return 'https://{}/{}'.format(settings.shopify_url, path)
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index eb01b8c..225ae29 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -35,7 +35,8 @@
 			# name can be BOM/ITEM/001, BOM/ITEM/001-1, BOM-ITEM-001, BOM-ITEM-001-1
 
 			# split by item
-			names = [name.split(self.item)[-1][1:] for name in names]
+			names = [name.split(self.item, 1) for name in names]
+			names = [d[-1][1:] for d in filter(lambda x: len(x) > 1 and x[-1], names)]
 
 			# split by (-) if cancelled
 			names = [cint(name.split('-')[-1]) for name in names]
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 78e7b4a..ccc48e1 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -682,10 +682,10 @@
    "label": "Additional Discount and Coupon Code"
   },
   {
-    "fieldname": "coupon_code",
-    "fieldtype": "Link",
-    "label": "Coupon Code",
-    "options": "Coupon Code"
+   "fieldname": "coupon_code",
+   "fieldtype": "Link",
+   "label": "Coupon Code",
+   "options": "Coupon Code"
   },
   {
    "default": "Grand Total",
@@ -1185,6 +1185,7 @@
    "default": "0",
    "fieldname": "skip_delivery_note",
    "fieldtype": "Check",
+   "hidden": 1,
    "label": "Skip Delivery Note",
    "print_hide": 1
   }
@@ -1192,7 +1193,7 @@
  "icon": "fa fa-file-text",
  "idx": 105,
  "is_submittable": 1,
- "modified": "2019-10-14 08:46:07.540565",
+ "modified": "2019-10-22 14:26:42.767189",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order",
@@ -1269,4 +1270,4 @@
  "title_field": "title",
  "track_changes": 1,
  "track_seen": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 758fb37..7fa7d3b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -329,8 +329,8 @@
 			if total_completed_qty > flt(completed_qty):
 				job_card = frappe.db.get_value('Job Card', {'operation_id': d.name}, 'name')
 				if not job_card:
-					frappe.throw(_("Work Order {0}: job card not found for the operation {1}")
-						.format(self.work_order, job_card))
+					frappe.throw(_("Work Order {0}: Job Card not found for the operation {1}")
+						.format(self.work_order, d.operation))
 
 				work_order_link = frappe.utils.get_link_to_form('Work Order', self.work_order)
 				job_card_link = frappe.utils.get_link_to_form('Job Card', job_card)
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index e5ae70c..68b8b50 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -39,6 +39,9 @@
 
 	data = []
 	conversion_factors = {}
+
+	_func = lambda x: x[1]
+
 	for (company, item, warehouse) in sorted(iwb_map):
 		if item_map.get(item):
 			qty_dict = iwb_map[(company, item, warehouse)]
@@ -70,7 +73,9 @@
 					'latest_age': 0
 				}
 				if fifo_queue:
-					fifo_queue = sorted(fifo_queue, key=lambda fifo_data: fifo_data[1])
+					fifo_queue = sorted(filter(_func, fifo_queue), key=_func)
+					if not fifo_queue: continue
+
 					stock_ageing_data['average_age'] = get_average_age(fifo_queue, to_date)
 					stock_ageing_data['earliest_age'] = date_diff(to_date, fifo_queue[0][1])
 					stock_ageing_data['latest_age'] = date_diff(to_date, fifo_queue[-1][1])
diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.py b/erpnext/stock/report/total_stock_summary/total_stock_summary.py
index 41e2f86..ed52393 100644
--- a/erpnext/stock/report/total_stock_summary/total_stock_summary.py
+++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.py
@@ -30,7 +30,7 @@
 
 	if filters.get("group_by") == "Warehouse":
 		if filters.get("company"):
-			conditions += " AND warehouse.company = '%s'" % frappe.db.escape(filters.get("company"), percent=False)
+			conditions += " AND warehouse.company = %s" % frappe.db.escape(filters.get("company"), percent=False)
 
 		conditions += " GROUP BY ledger.warehouse, item.item_code"
 		columns += "'' as company, ledger.warehouse"
diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html
index e857ce1..fe53f34 100644
--- a/erpnext/templates/includes/cart/cart_address.html
+++ b/erpnext/templates/includes/cart/cart_address.html
@@ -104,7 +104,7 @@
 				{
 					label: __('Country'),
 					fieldname: 'country',
-					fieldtype: 'Data',
+					fieldtype: 'Link',
 					reqd: 1
 				},
 			],