patch to add due date to GL Entries for Sales/Purchase invoice
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index d781ec7..f7a6abb 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -432,4 +432,5 @@
 erpnext.patches.v8_6.update_timesheet_company_from_PO
 erpnext.patches.v8_6.set_write_permission_for_quotation_for_sales_manager
 erpnext.patches.v8_5.remove_project_type_property_setter
-erpnext.patches.v8_7.add_more_gst_fields
\ No newline at end of file
+erpnext.patches.v8_7.add_more_gst_fields
+erpnext.patches.v8_8.update_gl_due_date_for_pi_and_si
\ No newline at end of file
diff --git a/erpnext/patches/v8_8/update_gl_due_date_for_pi_and_si.py b/erpnext/patches/v8_8/update_gl_due_date_for_pi_and_si.py
new file mode 100644
index 0000000..48da3ea
--- /dev/null
+++ b/erpnext/patches/v8_8/update_gl_due_date_for_pi_and_si.py
@@ -0,0 +1,88 @@
+from __future__ import unicode_literals
+import frappe
+
+"""
+This will update existing GL Entries by saving its linked Purchase/Sales Invoice's
+due date as the due date for the GL Entry
+"""
+
+
+def execute():
+	pi_kwargs = dict(
+		voucher_type='Purchase Invoice', doctype='GL Entry', fields=['voucher_no'],
+		limit_start=0, limit_page_length=5, filters={
+			"ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
+			'voucher_type': 'Purchase Invoice', 'debit': ('!=', '0')
+		}
+	)
+	si_kwargs = dict(
+		voucher_type='Sales Invoice', doctype='GL Entry', fields=['voucher_no'],
+		limit_start=0, limit_page_length=5, filters={
+			"ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
+			'voucher_type': 'Sales Invoice', 'credit': ('!=', '0')
+		}
+	)
+	kwargs = [pi_kwargs, si_kwargs]
+
+	for kwarg in kwargs:
+		for batch in get_result_in_batches(**kwarg):
+			conditions, names = build_conditions(batch, kwarg.get('voucher_type'))
+			if conditions and names:
+				start = 'UPDATE `tabGL Entry` SET `due_date` = CASE '
+				cond = ' '.join(conditions)
+				frappe.db.sql(
+					start + cond + ' ELSE `due_date` END WHERE `voucher_no` IN %s',
+					values=(names,)
+				)
+
+
+def get_result_in_batches(**kwargs):
+	"""A simple generator to yield slices of GL Entry records"""
+	while True:
+		batch = get_gle_batch(**kwargs)
+		if batch:
+			yield batch
+		else:
+			return
+
+
+def get_gle_batch(**kwargs):
+	"""Returns a slice of records in GL Entry"""
+	doctype = kwargs.get('doctype')
+	fields = kwargs.get('fields')
+	limit_start = kwargs.get('limit_start')
+	limit_page_length = kwargs.get('limit_page_length')
+	filters = kwargs.get('filters')
+
+	results = frappe.get_list(
+		doctype, fields=fields, limit_start=limit_start, limit_page_length=limit_page_length,
+		filters=filters
+	)
+
+	return results
+
+
+def build_conditions(query_results, voucher_type):
+	"""
+	builds the string to be used is sql CASE statement. Returns the a tuple of
+	the string for the CASE statement and a tuple of applicable voucher names
+	"""
+	conditions = []
+	invoice_names = []
+
+	# first extract the voucher names into two separate lists so it can be easy to query the db
+	for result in query_results:
+		voucher_no = result.get('voucher_no')
+		if voucher_no:
+			invoice_names.append("%s" % (voucher_no,))
+
+	# get invoice details
+	invoice_details = frappe.get_list(
+		voucher_type, fields=['name', 'due_date'], filters={'name': ('in', invoice_names)}
+	)
+
+	if invoice_details:
+		for d in invoice_details:
+			conditions.append('WHEN `voucher_no`="{number}" THEN "{date}"'.format(number=d.name, date=d.due_date))
+
+	return conditions, invoice_names