more test cases and bug fixes
diff --git a/erpnext/accounts/doctype/subscriptions/subscriptions.py b/erpnext/accounts/doctype/subscriptions/subscriptions.py
index e5c2cc5..9a130f3 100644
--- a/erpnext/accounts/doctype/subscriptions/subscriptions.py
+++ b/erpnext/accounts/doctype/subscriptions/subscriptions.py
@@ -13,9 +13,6 @@
 
 
 class Subscriptions(Document):
-	def before_save(self):
-		self.set_status()
-
 	def before_insert(self):
 		# update start just before the subscription doc is created
 		self.update_subscription_period()
@@ -29,6 +26,8 @@
 			self.current_invoice_start = self.trial_period_start
 		elif not date:
 			self.current_invoice_start = nowdate()
+		elif date:
+			self.current_invoice_start = date
 
 	def set_current_invoice_end(self):
 		if self.is_trialling():
@@ -81,15 +80,19 @@
 
 			return data
 
-	def set_status(self):
+	def set_status_grace_period(self):
+		if self.status == 'Past Due Date' and self.is_past_grace_period():
+			self.status = 'Canceled' if cint(SUBSCRIPTION_SETTINGS.cancel_after_grace) else 'Unpaid'
+
+	def set_subscription_status(self):
 		if self.is_trialling():
 			self.status = 'Trialling'
-		elif self.status == 'Past Due' and self.is_past_grace_period():
+		elif self.status == 'Past Due Date' and self.is_past_grace_period():
 			self.status = 'Canceled' if cint(SUBSCRIPTION_SETTINGS.cancel_after_grace) else 'Unpaid'
-		elif self.status == 'Past Due' and not self.has_outstanding_invoice():
+		elif self.status == 'Past Due Date' and not self.has_outstanding_invoice():
 			self.status = 'Active'
 		elif self.current_invoice_is_past_due():
-			self.status = 'Past Due'
+			self.status = 'Past Due Date'
 		elif self.is_new_subscription():
 			self.status = 'Active'
 			# todo: then generate new invoice
@@ -110,7 +113,7 @@
 		if self.current_invoice_is_past_due(current_invoice):
 			grace_period = cint(SUBSCRIPTION_SETTINGS.grace_period)
 
-			return nowdate() > add_days(current_invoice.due_date, grace_period)
+			return getdate(nowdate()) > add_days(current_invoice.due_date, grace_period)
 
 	def current_invoice_is_past_due(self, current_invoice=None):
 		if not current_invoice:
@@ -124,8 +127,11 @@
 	def get_current_invoice(self):
 		if len(self.invoices):
 			current = self.invoices[-1]
-			doc = frappe.get_doc('Sales Invoice', current.invoice)
-			return doc
+			if frappe.db.exists('Sales Invoice', current.invoice):
+				doc = frappe.get_doc('Sales Invoice', current.invoice)
+				return doc
+			else:
+				frappe.throw(_('Invoice {0} no longer exists'.format(invoice.invoice)))
 
 	def is_new_subscription(self):
 		return len(self.invoices) == 0
@@ -144,7 +150,7 @@
 
 	def after_insert(self):
 		# todo: deal with users who collect prepayments. Maybe a new Subscription Invoice doctype?
-		pass
+		self.set_subscription_status()
 
 	def generate_invoice(self):
 		invoice = self.create_invoice()
@@ -156,6 +162,8 @@
 
 	def create_invoice(self):
 		invoice = frappe.new_doc('Sales Invoice')
+		invoice.set_posting_time = 1
+		invoice.posting_date = self.current_invoice_start
 		invoice.customer = self.get_customer(self.subscriber)
 
 		# Subscription is better suited for service items. I won't update `update_stock`
@@ -220,28 +228,36 @@
 		"""
 		if self.status == 'Active':
 			self.process_for_active()
-		elif self.status == 'Past Due':
+		elif self.status == 'Past Due Date':
 			self.process_for_past_due_date()
+		self.save()
 		# process_for_unpaid()
 
 	def process_for_active(self):
-		print 'processing for active'
 		if nowdate() > self.current_invoice_end and not self.has_outstanding_invoice():
-			print 'generating invoice'
 			self.generate_invoice()
-			print 'invoice generated'
 
 		if self.current_invoice_is_past_due():
-			print 'current invoice is past due'
-			self.status = 'Past Due'
+			self.status = 'Past Due Date'
 
 	def process_for_past_due_date(self):
-		if not self.has_outstanding_invoice():
-			self.status = 'Active'
-			self.update_subscription_period()
+		current_invoice = self.get_current_invoice()
+		if not current_invoice:
+			frappe.throw('Current invoice is missing')
 		else:
-			self.set_status()
+			if self.is_not_outstanding(current_invoice):
+				self.status = 'Active'
+				self.update_subscription_period()
+			else:
+				self.set_status_grace_period()
+
+	def is_not_outstanding(self, invoice):
+		return invoice.status == 'Paid'
 
 	def has_outstanding_invoice(self):
 		current_invoice = self.get_current_invoice()
-		return current_invoice.posting_date != self.current_invoice_start
+		if not current_invoice:
+			return False
+		else:
+			return not self.is_not_outstanding(current_invoice)
+		return True
diff --git a/erpnext/accounts/doctype/subscriptions/test_subscriptions.py b/erpnext/accounts/doctype/subscriptions/test_subscriptions.py
index abd2f3f..63177e4 100644
--- a/erpnext/accounts/doctype/subscriptions/test_subscriptions.py
+++ b/erpnext/accounts/doctype/subscriptions/test_subscriptions.py
@@ -5,7 +5,8 @@
 
 import frappe
 import unittest
-from frappe.utils.data import nowdate, add_days, get_last_day, cint, getdate, add_to_date
+from frappe.utils.data import nowdate, add_days, get_last_day, cint, getdate, add_to_date, get_datetime_str
+from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
 
 
 class TestSubscriptions(unittest.TestCase):
@@ -14,7 +15,7 @@
 			plan = frappe.new_doc('Subscription Plan')
 			plan.plan_name = '_Test Plan Name'
 			plan.item = '_Test Non Stock Item'
-			plan.cost = 999.99
+			plan.cost = 900
 			plan.billing_interval = 'Month'
 			plan.billing_interval_count = 1
 			plan.insert()
@@ -23,7 +24,7 @@
 			plan = frappe.new_doc('Subscription Plan')
 			plan.plan_name = '_Test Plan Name 2'
 			plan.item = '_Test Non Stock Item'
-			plan.cost = 1999.99
+			plan.cost = 1999
 			plan.billing_interval = 'Month'
 			plan.billing_interval_count = 1
 			plan.insert()
@@ -32,7 +33,7 @@
 			plan = frappe.new_doc('Subscription Plan')
 			plan.plan_name = '_Test Plan Name 3'
 			plan.item = '_Test Non Stock Item'
-			plan.cost = 1999.99
+			plan.cost = 1999
 			plan.billing_interval = 'Day'
 			plan.billing_interval_count = 14
 			plan.insert()
@@ -89,6 +90,7 @@
 		subscription.append('plans', {'plan': '_Test Plan Name'})
 
 		self.assertRaises(frappe.ValidationError, subscription.save)
+		subscription.delete()
 
 	def test_create_subscription_multi_with_different_billing_fails(self):
 		subscription = frappe.new_doc('Subscriptions')
@@ -99,22 +101,92 @@
 		subscription.append('plans', {'plan': '_Test Plan Name 3'})
 
 		self.assertRaises(frappe.ValidationError, subscription.save)
+		subscription.delete()
 
-	# def test_subscription_invoice_days_until_due(self):
-	# 	subscription = frappe.new_doc('Subscriptions')
-	# 	subscription.subscriber = '_Test Customer'
-	# 	subscription.append('plans', {'plan': '_Test Plan Name'})
-	# 	subscription.save()
+	def test_invoice_is_generated_at_end_of_billing_period(self):
+		subscription = frappe.new_doc('Subscriptions')
+		subscription.subscriber = '_Test Customer'
+		subscription.append('plans', {'plan': '_Test Plan Name'})
+		subscription.insert()
 
-	# 	generated_invoice_name = subscription.invoices[-1].invoice
-	# 	invoice = frappe.get_doc('Sales Invoice', generated_invoice_name)
+		self.assertEqual(subscription.status, 'Active')
 
-	# 	self.assertEqual(
-	# 		invoice.due_date, 
-	# 		add_days(subscription.current_invoice_end, cint(subscription.days_until_due))
-	# 	)
+		subscription.set_current_invoice_start('2018-01-01')
+		subscription.set_current_invoice_end()
 
-	# 	subscription.delete()
+		self.assertEqual(subscription.current_invoice_start, '2018-01-01')
+		self.assertEqual(subscription.current_invoice_end, '2018-01-31')
+		subscription.process()
+
+		self.assertEqual(len(subscription.invoices), 1)
+		self.assertEqual(subscription.current_invoice_start, nowdate())
+		self.assertEqual(subscription.status, 'Past Due Date')
+		subscription.delete()
+
+	def test_status_goes_back_to_active_after_invoice_is_paid(self):
+		subscription = frappe.new_doc('Subscriptions')
+		subscription.subscriber = '_Test Customer'
+		subscription.append('plans', {'plan': '_Test Plan Name'})
+		subscription.insert()
+		subscription.set_current_invoice_start('2018-01-01')
+		subscription.set_current_invoice_end()
+		subscription.process()	# generate first invoice
+		self.assertEqual(len(subscription.invoices), 1)
+		self.assertEqual(subscription.status, 'Past Due Date')
+
+		subscription.get_current_invoice()
+		current_invoice = subscription.get_current_invoice()
+
+		self.assertIsNotNone(current_invoice)
+
+		current_invoice.db_set('outstanding_amount', 0)
+		current_invoice.db_set('status', 'Paid')
+		subscription.process()
+
+		self.assertEqual(subscription.status, 'Active')
+		self.assertEqual(subscription.current_invoice_start, nowdate())
+		self.assertEqual(len(subscription.invoices), 1)
+
+		subscription.delete()
+
+	def test_subscription_cancel_after_grace_period(self):
+		settings = frappe.get_single('Subscription Settings')
+		default_grace_period_action = settings.cancel_after_grace
+		settings.cancel_after_grace = 1
+		settings.save()
+
+		subscription = frappe.new_doc('Subscriptions')
+		subscription.subscriber = '_Test Customer'
+		subscription.append('plans', {'plan': '_Test Plan Name'})
+		subscription.insert()
+		subscription.set_current_invoice_start('2018-01-01')
+		subscription.set_current_invoice_end()
+		subscription.process()	# generate first invoice
+
+		self.assertEqual(subscription.status, 'Past Due Date')
+
+		subscription.process()	
+		# This should change status to Canceled since grace period is 0
+		self.assertEqual(subscription.status, 'Canceled')
+
+		settings.cancel_after_grace = default_grace_period_action
+		settings.save()
+		subscription.delete()
+
+
+	def test_subscription_invoice_days_until_due(self):
+		subscription = frappe.new_doc('Subscriptions')
+		subscription.subscriber = '_Test Customer'
+		subscription.append('plans', {'plan': '_Test Plan Name'})
+		subscription.days_until_due = 10
+		subscription.insert()
+		subscription.set_current_invoice_start(get_datetime_str(add_to_date(nowdate(), months=-1)))
+		subscription.set_current_invoice_end()
+		subscription.process()	# generate first invoice
+		self.assertEqual(len(subscription.invoices), 1)
+		self.assertEqual(subscription.status, 'Active')
+
+		subscription.delete()
 
 	def test_subscription_creation_with_multiple_plans(self):
 		pass