Loyalty Program (#12631)

* First Cut for the Loyalty Program

* finished the collection part

* redmeption for the loyalty point

	update the loyalty point entry in the FIFO style
	make the accounting entry knocking the debtors account against the expense account selected in the loyalty program
	update the outstanding balance in the client side

* completed for the desk viewe

* wrap up for the desk and shopping cart

* pos

* fix and test the travis
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/__init__.py b/erpnext/accounts/doctype/loyalty_point_entry/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_point_entry/__init__.py
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.js b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.js
new file mode 100644
index 0000000..d7dc7f3
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Loyalty Point Entry', {
+	refresh: function(frm) {
+
+	}
+});
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json
new file mode 100644
index 0000000..be95f00
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json
@@ -0,0 +1,419 @@
+{
+ "allow_copy": 0, 
+ "allow_guest_to_view": 0, 
+ "allow_import": 0, 
+ "allow_rename": 0, 
+ "autoname": "LPE.####", 
+ "beta": 0, 
+ "creation": "2018-01-23 05:40:18.117583", 
+ "custom": 0, 
+ "docstatus": 0, 
+ "doctype": "DocType", 
+ "document_type": "", 
+ "editable_grid": 1, 
+ "engine": "InnoDB", 
+ "fields": [
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_program", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Program", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Loyalty Program", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_program_tier", 
+   "fieldtype": "Data", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Program Tier", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "customer", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Customer", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Customer", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "sales_invoice", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Sales Invoice", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Sales Invoice", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "redeem_against", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Redeem Against", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Loyalty Point Entry", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_points", 
+   "fieldtype": "Int", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Points", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "purchase_amount", 
+   "fieldtype": "Currency", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Purchase Amount", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "expiry_date", 
+   "fieldtype": "Date", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Expiry Date", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "posting_date", 
+   "fieldtype": "Date", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Posting Date", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "company", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Company", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Company", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }
+ ], 
+ "has_web_view": 0, 
+ "hide_heading": 0, 
+ "hide_toolbar": 0, 
+ "idx": 0, 
+ "image_view": 0, 
+ "in_create": 0, 
+ "is_submittable": 0, 
+ "issingle": 0, 
+ "istable": 0, 
+ "max_attachments": 0, 
+ "modified": "2018-03-26 08:52:09.468010", 
+ "modified_by": "Administrator", 
+ "module": "Accounts", 
+ "name": "Loyalty Point Entry", 
+ "name_case": "", 
+ "owner": "Administrator", 
+ "permissions": [
+  {
+   "amend": 0, 
+   "apply_user_permissions": 0, 
+   "cancel": 0, 
+   "create": 0, 
+   "delete": 0, 
+   "email": 1, 
+   "export": 1, 
+   "if_owner": 0, 
+   "import": 0, 
+   "permlevel": 0, 
+   "print": 1, 
+   "read": 1, 
+   "report": 1, 
+   "role": "Auditor", 
+   "set_user_permissions": 0, 
+   "share": 0, 
+   "submit": 0, 
+   "write": 0
+  }, 
+  {
+   "amend": 0, 
+   "apply_user_permissions": 0, 
+   "cancel": 0, 
+   "create": 0, 
+   "delete": 0, 
+   "email": 1, 
+   "export": 1, 
+   "if_owner": 0, 
+   "import": 0, 
+   "permlevel": 0, 
+   "print": 1, 
+   "read": 1, 
+   "report": 1, 
+   "role": "Accounts Manager", 
+   "set_user_permissions": 0, 
+   "share": 0, 
+   "submit": 0, 
+   "write": 0
+  }, 
+  {
+   "amend": 0, 
+   "apply_user_permissions": 0, 
+   "cancel": 0, 
+   "create": 0, 
+   "delete": 0, 
+   "email": 1, 
+   "export": 1, 
+   "if_owner": 0, 
+   "import": 0, 
+   "permlevel": 0, 
+   "print": 1, 
+   "read": 1, 
+   "report": 1, 
+   "role": "Accounts User", 
+   "set_user_permissions": 0, 
+   "share": 0, 
+   "submit": 0, 
+   "write": 0
+  }
+ ], 
+ "quick_entry": 1, 
+ "read_only": 0, 
+ "read_only_onload": 0, 
+ "show_name_in_global_search": 0, 
+ "sort_field": "modified", 
+ "sort_order": "DESC", 
+ "title_field": "customer", 
+ "track_changes": 1, 
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
new file mode 100644
index 0000000..37fce0b
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from frappe.utils import today
+
+exclude_from_linked_with = True
+
+class LoyaltyPointEntry(Document):
+	pass
+
+
+def get_loyalty_point_entries(customer, loyalty_program, expiry_date=None, company=None):
+	if not expiry_date:
+		date = today()
+	args_list = [customer, loyalty_program, expiry_date]
+	condition = ''
+	if company:
+		condition = " and company=%s "
+		args_list.append(company)
+	loyalty_point_details = frappe.db.sql('''select name, loyalty_points, expiry_date, loyalty_program_tier
+		from `tabLoyalty Point Entry` where customer=%s and loyalty_program=%s and expiry_date>=%s and loyalty_points>0
+		{condition} order by expiry_date'''.format(condition=condition), tuple(args_list), as_dict=1)
+	return loyalty_point_details
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.js b/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.js
new file mode 100644
index 0000000..a916b67
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Loyalty Point Entry", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Loyalty Point Entry
+		() => frappe.tests.make('Loyalty Point Entry', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.py b/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.py
new file mode 100644
index 0000000..b6e2d57
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestLoyaltyPointEntry(unittest.TestCase):
+	pass
diff --git a/erpnext/accounts/doctype/loyalty_point_entry_redemption/__init__.py b/erpnext/accounts/doctype/loyalty_point_entry_redemption/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_point_entry_redemption/__init__.py
diff --git a/erpnext/accounts/doctype/loyalty_point_entry_redemption/loyalty_point_entry_redemption.json b/erpnext/accounts/doctype/loyalty_point_entry_redemption/loyalty_point_entry_redemption.json
new file mode 100644
index 0000000..8a8dfbb
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_point_entry_redemption/loyalty_point_entry_redemption.json
@@ -0,0 +1,134 @@
+{
+ "allow_copy": 0, 
+ "allow_guest_to_view": 0, 
+ "allow_import": 0, 
+ "allow_rename": 0, 
+ "beta": 0, 
+ "creation": "2018-03-26 01:32:10.108450", 
+ "custom": 0, 
+ "docstatus": 0, 
+ "doctype": "DocType", 
+ "document_type": "", 
+ "editable_grid": 1, 
+ "engine": "InnoDB", 
+ "fields": [
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "sales_invoice", 
+   "fieldtype": "Data", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Sales Invoice", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "redemption_date", 
+   "fieldtype": "Date", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Redemption Date", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "redeemed_points", 
+   "fieldtype": "Int", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Redeemed Points", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }
+ ], 
+ "has_web_view": 0, 
+ "hide_heading": 0, 
+ "hide_toolbar": 0, 
+ "idx": 0, 
+ "image_view": 0, 
+ "in_create": 0, 
+ "is_submittable": 0, 
+ "issingle": 0, 
+ "istable": 1, 
+ "max_attachments": 0, 
+ "modified": "2018-03-26 03:12:59.173071", 
+ "modified_by": "Administrator", 
+ "module": "Accounts", 
+ "name": "Loyalty Point Entry Redemption", 
+ "name_case": "", 
+ "owner": "Administrator", 
+ "permissions": [], 
+ "quick_entry": 1, 
+ "read_only": 0, 
+ "read_only_onload": 0, 
+ "show_name_in_global_search": 0, 
+ "sort_field": "modified", 
+ "sort_order": "DESC", 
+ "track_changes": 1, 
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/loyalty_point_entry_redemption/loyalty_point_entry_redemption.py b/erpnext/accounts/doctype/loyalty_point_entry_redemption/loyalty_point_entry_redemption.py
new file mode 100644
index 0000000..e4382b6
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_point_entry_redemption/loyalty_point_entry_redemption.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class LoyaltyPointEntryRedemption(Document):
+	pass
diff --git a/erpnext/accounts/doctype/loyalty_program/__init__.py b/erpnext/accounts/doctype/loyalty_program/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_program/__init__.py
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.js b/erpnext/accounts/doctype/loyalty_program/loyalty_program.js
new file mode 100644
index 0000000..524a671
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.js
@@ -0,0 +1,65 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Loyalty Program', {
+	setup: function(frm) {
+		var help_content =
+			`<table class="table table-bordered" style="background-color: #f9f9f9;">
+				<tr><td>
+					<h4>
+						<i class="fa fa-hand-right"></i>
+						${__('Notes')}
+					</h4>
+					<ul>
+						<li>
+							${__("Loyalty Points will be calculated from the spent done (via the Sales Invoice), based on collection factor mentioned.")}
+						</li>
+						<li>
+							${__("There can be multiple tiered collection factor based on the total spent. But the conversion factor for redemption will always be same for all the tier.")}
+						</li>
+						<li>
+							${__("In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent")}
+						</li>
+						<li>
+							${__("If unlimited expiry for the Loyalty Points, keep the Expiry Duration empty or 0.")}
+						</li>
+						<li>
+							${__("If Auto Opt In is checked, then the customers will be automatically linked with the concerned Loyalty Program (on save)")}
+						</li>
+						<li>
+							${__("One customer can be part of only single Loyalty Program.")}
+						</li>
+					</ul>
+				</td></tr>
+			</table>`;
+		set_field_options("loyalty_program_help", help_content);
+	},
+
+	onload: function(frm) {
+		frm.set_query("expense_account", function(doc) {
+			return {
+				filters: {
+					"root_type": "Expense",
+					'is_group': 0,
+					'company': doc.company
+				}
+			};
+		});
+
+		frm.set_query("cost_center", function() {
+			return {
+				filters: {
+					company: frm.doc.company
+				}
+			};
+		});
+
+		frm.set_value("company", frappe.defaults.get_user_default("Company"));
+	},
+
+	refresh: function(frm) {
+		if (frm.doc.loyalty_program_type === "Single Tier Program" && frm.doc.collection_rules.length > 1) {
+			frappe.throw(__("Please select the Multiple Tier Program type for more than one collection rules."));
+		}
+	}
+});
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.json b/erpnext/accounts/doctype/loyalty_program/loyalty_program.json
new file mode 100644
index 0000000..4536a7a
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.json
@@ -0,0 +1,687 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:loyalty_program_name",
+ "beta": 0,
+ "creation": "2018-01-23 06:23:05.731431",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "disabled",
+   "fieldtype": "Check",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Disabled",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "auto_opt_in",
+   "fieldtype": "Check",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Auto Opt In (For all customers)",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "section_break_2",
+   "fieldtype": "Section Break",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "loyalty_program_name",
+   "fieldtype": "Data",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 1,
+   "in_standard_filter": 0,
+   "label": "Loyalty Program Name",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 1,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "loyalty_program_type",
+   "fieldtype": "Select",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Loyalty Program Type",
+   "length": 0,
+   "no_copy": 0,
+   "options": "Single Tier Program\nMultiple Tier Program",
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "column_break_7",
+   "fieldtype": "Column Break",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "customer_group",
+   "fieldtype": "Link",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Customer Group",
+   "length": 0,
+   "no_copy": 0,
+   "options": "Customer Group",
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "customer_territory",
+   "fieldtype": "Link",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Customer Territory",
+   "length": 0,
+   "no_copy": 0,
+   "options": "Territory",
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "rules",
+   "fieldtype": "Section Break",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Collection Tier",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "collection_rules",
+   "fieldtype": "Table",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Collection Rules",
+   "length": 0,
+   "no_copy": 0,
+   "options": "Loyalty Program Collection",
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 1,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "redemption",
+   "fieldtype": "Section Break",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Redemption",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "description": "1 Loyalty Points = How much base currency?",
+   "fieldname": "conversion_factor",
+   "fieldtype": "Float",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Conversion Factor",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "expiry_duration",
+   "fieldtype": "Int",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Expiry Duration (in days)",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "column_break_10",
+   "fieldtype": "Column Break",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "expense_account",
+   "fieldtype": "Link",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Expense Account",
+   "length": 0,
+   "no_copy": 0,
+   "options": "Account",
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Cost Center",
+   "length": 0,
+   "no_copy": 0,
+   "options": "Cost Center",
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Company",
+   "length": 0,
+   "no_copy": 0,
+   "options": "Company",
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "help_section",
+   "fieldtype": "Section Break",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Help Section",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "loyalty_program_help",
+   "fieldtype": "HTML",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Loyalty Program Help",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2018-03-21 10:20:25.468206",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Loyalty Program",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "amend": 0,
+   "apply_user_permissions": 0,
+   "cancel": 0,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "if_owner": 0,
+   "import": 0,
+   "permlevel": 0,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "set_user_permissions": 0,
+   "share": 1,
+   "submit": 0,
+   "write": 1
+  }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
new file mode 100644
index 0000000..593e426
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import today, flt
+
+class LoyaltyProgram(Document):
+	pass
+
+
+def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=None):
+	if not expiry_date:
+		expiry_date = today()
+	args_list = [customer, loyalty_program, expiry_date]
+	condition = ''
+	if company:
+		condition = " and company=%s "
+		args_list.append(company)
+	loyalty_point_details = frappe.db.sql('''select sum(loyalty_points) as loyalty_points,
+		sum(purchase_amount) as total_spent from `tabLoyalty Point Entry`
+		where customer=%s and loyalty_program=%s and (expiry_date>=%s) {condition}
+		group by customer'''.format(condition=condition), tuple(args_list), as_dict=1)
+	if loyalty_point_details:
+		return loyalty_point_details[0]
+	else:
+		return {"loyalty_points": 0, "total_spent": 0}
+
+@frappe.whitelist()
+def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None, company=None, silent=False):
+	lp_details = frappe._dict()
+	customer_loyalty_program = frappe.db.get_value("Customer", customer, "loyalty_program")
+
+	if not (customer_loyalty_program or silent):
+		frappe.throw(_("Customer isn't enrolled in any Loyalty Program"))
+	elif silent and not customer_loyalty_program:
+		return frappe._dict({"loyalty_program": None})
+
+	if loyalty_program and loyalty_program != customer_loyalty_program:
+		frappe.throw(_("Customer isn't enrolled in this Loyalty Program"))
+
+	if not loyalty_program:
+		loyalty_program = customer_loyalty_program
+	if not company:
+		company = frappe.db.get_default("company") or frappe.get_all("Company")[0].name
+
+	lp_details.update(get_loyalty_details(customer, loyalty_program, expiry_date, company))
+
+	lp_details.update({"loyalty_program": loyalty_program})
+	loyalty_program = frappe.get_doc("Loyalty Program", lp_details.loyalty_program)
+
+	lp_details.expiry_duration = loyalty_program.expiry_duration
+	lp_details.conversion_factor = loyalty_program.conversion_factor
+	lp_details.expense_account = loyalty_program.expense_account
+	lp_details.cost_center = loyalty_program.cost_center
+	lp_details.company = loyalty_program.company
+
+	tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules], key=lambda rule:rule.min_spent, reverse=True)
+	for i, d in enumerate(tier_spent_level):
+		if i==0 or lp_details.total_spent < d.min_spent:
+			lp_details.tier_name = d.tier_name
+			lp_details.collection_factor = d.collection_factor
+		else:
+			break
+	return lp_details
+
+@frappe.whitelist()
+def get_redeemption_factor(loyalty_program=None, customer=None):
+	customer_loyalty_program = None
+	if not loyalty_program:
+		customer_loyalty_program = frappe.db.get_value("Customer", customer, "loyalty_program")
+		loyalty_program = customer_loyalty_program
+	if loyalty_program:
+		return frappe.db.get_value("Loyalty Program", loyalty_program, "conversion_factor")
+	else:
+		frappe.throw(_("Customer isn't enrolled in any Loyalty Program"))
+
+
+def validate_loyalty_points(ref_doc, points_to_redeem):
+	loyalty_program = None
+	posting_date = None
+
+	if ref_doc.doctype == "Sales Invoice":
+		posting_date = ref_doc.posting_date
+	else:
+		posting_date = today()
+
+	if hasattr(ref_doc, "loyalty_program") and ref_doc.loyalty_program:
+		loyalty_program = ref_doc.loyalty_program
+	else:
+		loyalty_program = frappe.db.get_value("Customer", ref_doc.customer, ["loyalty_program"])
+
+	if loyalty_program and frappe.db.get_value("Loyalty Program", loyalty_program, ["company"]) !=\
+		ref_doc.company:
+		frappe.throw(_("The Loyalty Program isn't valid for the selected company"))
+
+	if loyalty_program and points_to_redeem:
+		loyalty_program_details = get_loyalty_program_details(ref_doc.customer, loyalty_program,
+			posting_date, ref_doc.company)
+
+		if points_to_redeem > loyalty_program_details.loyalty_points:
+			frappe.throw(_("You don't have enought Loyalty Points to redeem"))
+
+		loyalty_amount = flt(points_to_redeem * loyalty_program_details.conversion_factor)
+
+		if loyalty_amount > ref_doc.grand_total:
+			frappe.throw(_("You can't redeem Loyalty Points having more value than the Grand Total."))
+
+		if not ref_doc.loyalty_amount and ref_doc.loyalty_amount != loyalty_amount:
+			ref_doc.loyalty_amount = loyalty_amount
+
+		if ref_doc.doctype == "Sales Invoice":
+			ref_doc.loyalty_program = loyalty_program
+			if not ref_doc.loyalty_redemption_account:
+				ref_doc.loyalty_redemption_account = loyalty_program_details.expense_account
+
+			if not ref_doc.loyalty_redemption_cost_center:
+				ref_doc.loyalty_redemption_cost_center = loyalty_program_details.cost_center
+
+		elif ref_doc.doctype == "Sales Order":
+			return loyalty_amount
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.js b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.js
new file mode 100644
index 0000000..9321c14
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Loyalty Program", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Loyalty Program
+		() => frappe.tests.make('Loyalty Program', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
new file mode 100644
index 0000000..a5cf765
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestLoyaltyProgram(unittest.TestCase):
+	pass
diff --git a/erpnext/accounts/doctype/loyalty_program_collection/__init__.py b/erpnext/accounts/doctype/loyalty_program_collection/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_program_collection/__init__.py
diff --git a/erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.json b/erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.json
new file mode 100644
index 0000000..6ea5216
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.json
@@ -0,0 +1,161 @@
+{
+ "allow_copy": 0, 
+ "allow_guest_to_view": 0, 
+ "allow_import": 0, 
+ "allow_rename": 0, 
+ "beta": 0, 
+ "creation": "2018-01-23 06:56:37.163859", 
+ "custom": 0, 
+ "docstatus": 0, 
+ "doctype": "DocType", 
+ "document_type": "", 
+ "editable_grid": 0, 
+ "engine": "InnoDB", 
+ "fields": [
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "tier_name", 
+   "fieldtype": "Data", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Tier Name", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 1, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "min_spent", 
+   "fieldtype": "Currency", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Minimum Total Spent", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "column_break_3", 
+   "fieldtype": "Column Break", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "description": "For how much spent = 1 Loyalty Point", 
+   "fieldname": "collection_factor", 
+   "fieldtype": "Currency", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Collection Factor (=1 LP)", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 1, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
+  }
+ ], 
+ "has_web_view": 0, 
+ "hide_heading": 0, 
+ "hide_toolbar": 0, 
+ "idx": 0, 
+ "image_view": 0, 
+ "in_create": 0, 
+ "is_submittable": 0, 
+ "issingle": 0, 
+ "istable": 1, 
+ "max_attachments": 0, 
+ "modified": "2018-01-23 07:19:10.316392", 
+ "modified_by": "Administrator", 
+ "module": "Accounts", 
+ "name": "Loyalty Program Collection", 
+ "name_case": "", 
+ "owner": "Administrator", 
+ "permissions": [], 
+ "quick_entry": 1, 
+ "read_only": 0, 
+ "read_only_onload": 0, 
+ "show_name_in_global_search": 0, 
+ "sort_field": "modified", 
+ "sort_order": "DESC", 
+ "track_changes": 1, 
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.py b/erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.py
new file mode 100644
index 0000000..42cc38c
--- /dev/null
+++ b/erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class LoyaltyProgramCollection(Document):
+	pass
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index fd48faa..a633cc3 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -240,14 +240,23 @@
 	"""Make payment request"""
 
 	args = frappe._dict(args)
+
 	ref_doc = frappe.get_doc(args.dt, args.dn)
 	grand_total = get_amount(ref_doc, args.dt)
+	if args.loyalty_points and args.dt == "Sales Order":
+		from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points
+		loyalty_amount = validate_loyalty_points(ref_doc, int(args.loyalty_points))
+		frappe.db.set_value("Sales Order", args.dn, "loyalty_points", int(args.loyalty_points), update_modified=False)
+		frappe.db.set_value("Sales Order", args.dn, "loyalty_amount", loyalty_amount, update_modified=False)
+		grand_total = grand_total - loyalty_amount
+
 	gateway_account = get_gateway_details(args) or frappe._dict()
 
 	existing_payment_request = frappe.db.get_value("Payment Request",
 		{"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ["!=", 2]})
 
 	if existing_payment_request:
+		frappe.db.set_value("Payment Request", existing_payment_request, "grand_total", grand_total, update_modified=False)
 		pr = frappe.get_doc("Payment Request", existing_payment_request)
 
 	else:
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 8f57352..f2b9ba2 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -17,6 +17,8 @@
 	onload: function() {
 		var me = this;
 		this._super();
+		console.log("class erpnext.accounts.SalesInvoiceController, onload this->", this);
+		
 
 		if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
 			// show debit_to in print format
@@ -36,6 +38,7 @@
 	},
 
 	refresh: function(doc, dt, dn) {
+		console.log("triggered the SalesInvoiceController");
 		this._super();
 		if(cur_frm.msgbox && cur_frm.msgbox.$wrapper.is(":visible")) {
 			// hide new msgbox
@@ -228,7 +231,7 @@
 				price_list: this.frm.doc.selling_price_list,
 			}, function() {
 				me.apply_pricing_rule();
-			})
+			});
 	},
 
 	make_inter_company_invoice: function() {
@@ -355,7 +358,7 @@
 				});
 			}
 		}
-		else this.frm.trigger("refresh")
+		else this.frm.trigger("refresh");
 	},
 
 	amount: function(){
@@ -364,13 +367,21 @@
 
 	change_amount: function(){
 		if(this.frm.doc.paid_amount > this.frm.doc.grand_total){
-			this.calculate_write_off_amount()
+			this.calculate_write_off_amount();
 		}else {
-			this.frm.set_value("change_amount", 0.0)
-			this.frm.set_value("base_change_amount", 0.0)
+			this.frm.set_value("change_amount", 0.0);
+			this.frm.set_value("base_change_amount", 0.0);
 		}
 
 		this.frm.refresh_fields();
+	},
+
+	loyalty_amount: function(){
+		console.log("triggered the loyalty amount");
+		this.calculate_outstanding_amount();
+		this.frm.refresh_field("outstanding_amount");
+		this.frm.refresh_field("paid_amount");
+		this.frm.refresh_field("base_paid_amount");
 	}
 });
 
@@ -521,7 +532,12 @@
 });
 
 frappe.ui.form.on('Sales Invoice', {
+	refresh: function(frm) {
+		frm.add_fetch('customer', 'loyalty_program', 'loyalty_program');
+	},
+
 	setup: function(frm){
+		
 		frm.custom_make_buttons = {
 			'Delivery Note': 'Delivery',
 			'Sales Invoice': 'Sales Return',
@@ -612,7 +628,71 @@
 				refresh_field(['timesheets'])
 			}
 		})
+	},
+
+	onload: function(frm) {
+		frm.redemption_conversion_factor = null;
+	},
+
+	redeem_loyalty_points: function(frm) {
+		frm.events.get_loyalty_details(frm);
+	},
+
+	loyalty_points: function(frm) {
+		if (frm.redemption_conversion_factor) {
+			frm.events.set_loyalty_points(frm);
+		} else {
+			frappe.call({
+				method: "erpnext.accounts.doctype.loyalty_program.loyalty_program.get_redeemption_factor",
+				args: {
+					"loyalty_program": frm.doc.loyalty_program
+				},
+				callback: function(r) {
+					if (r) {
+						frm.redemption_conversion_factor = r.message;
+						frm.events.set_loyalty_points(frm);
+					}
+				}
+			});
+		}
+	},
+
+	get_loyalty_details: function(frm) {
+		if (frm.doc.customer && frm.doc.redeem_loyalty_points) {
+			frappe.call({
+				method: "erpnext.accounts.doctype.loyalty_program.loyalty_program.get_loyalty_program_details",
+				args: {
+					"customer": frm.doc.customer,
+					"till_date": frm.doc.posting_date,
+					"company": frm.doc.company
+				},
+				callback: function(r) {
+					if (r) {
+						frm.set_value("loyalty_program", r.message.loyalty_program);
+						frm.set_value("loyalty_redemption_account", r.message.expense_account);
+						frm.set_value("loyalty_redemption_cost_center", r.message.cost_center);
+						frm.redemption_conversion_factor = r.message.conversion_factor;
+						// let max_loyalty_points = parseInt((frm.doc.grand_total-frm.doc.total_advance)/r.message.conversion_factor);
+						// let redeemable_points = max_loyalty_points > r.message.loyalty_points ? r.message.loyalty_points : max_loyalty_points;
+						// frm.set_value("loyalty_points", redeemable_points);
+					}
+				}
+			});
+		}
+	},
+
+	set_loyalty_points: function(frm) {
+		if (frm.redemption_conversion_factor) {
+			let loyalty_amount = flt(frm.redemption_conversion_factor*flt(frm.doc.loyalty_points), precision("loyalty_amount"));
+			var remaining_amount = flt(frm.doc.grand_total - frm.doc.total_advance)
+			if (frm.doc.grand_total && (remaining_amount < loyalty_amount)) {
+				let redeemable_amount = parseInt(remaining_amount/frm.redemption_conversion_factor);
+				frappe.throw(__("You can only redeem max {0} points in this order.",[redeemable_amount]));
+			}
+			frm.set_value("loyalty_amount", loyalty_amount);
+		}
 	}
+
 })
 
 frappe.ui.form.on('Sales Invoice Timesheet', {
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 89bfc8f..663042f 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -388,6 +388,38 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fieldname": "redeem_loyalty_points", 
+   "fieldtype": "Check", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Redeem Loyalty Points", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
    "fieldname": "column_break1", 
    "fieldtype": "Column Break", 
    "hidden": 0, 
@@ -2428,6 +2460,234 @@
    "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
+   "collapsible": 0, 
+   "collapsible_depends_on": "", 
+   "columns": 0, 
+   "depends_on": "redeem_loyalty_points", 
+   "fieldname": "loyalty_points_redemption", 
+   "fieldtype": "Section Break", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Points Redemption", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_points", 
+   "fieldtype": "Int", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Points", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_amount", 
+   "fieldtype": "Currency", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Amount", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 1, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "column_break_77", 
+   "fieldtype": "Column Break", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_program", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Program", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Loyalty Program", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 1, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_redemption_account", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Redemption Account", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Account", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_redemption_cost_center", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Redemption Cost Center", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Cost Center", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
    "collapsible": 1, 
    "collapsible_depends_on": "discount_amount", 
    "columns": 0, 
@@ -3426,7 +3686,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
-   "depends_on": "is_pos", 
+   "depends_on": "eval: doc.is_pos || doc.redeem_loyalty_points", 
    "fieldname": "paid_amount", 
    "fieldtype": "Currency", 
    "hidden": 0, 
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 4c617b4..7f11f35 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -4,7 +4,7 @@
 from __future__ import unicode_literals
 import frappe, erpnext
 import frappe.defaults
-from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days
+from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr
 from frappe import _, msgprint, throw
 from erpnext.accounts.party import get_party_account, get_due_date
 from erpnext.controllers.stock_controller import update_gl_entries_after
@@ -21,6 +21,8 @@
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delivery_note_serial_no
 from erpnext.setup.doctype.company.company import update_company_current_month_sales
 from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
+from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
+	get_loyalty_program_details, get_loyalty_details, validate_loyalty_points
 
 from six import iteritems
 
@@ -106,6 +108,9 @@
 		self.set_status()
 		if self.is_pos and not self.is_return:
 			self.verify_payment_amount_is_positive()
+		if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points:
+			validate_loyalty_points(self, self.loyalty_points)
+
 
 	def before_save(self):
 		set_account_for_mode_of_payment(self)
@@ -152,6 +157,15 @@
 			self.update_project()
 		update_linked_invoice(self.doctype, self.name, self.inter_company_invoice_reference)
 
+		# create the loyalty point ledger entry if the customer is enrolled in any loyalty program 
+		if not self.is_return and self.loyalty_program:
+			self.make_loyalty_point_entry()
+		elif self.is_return and self.return_against and self.loyalty_program:
+			against_si_doc = frappe.get_doc("Sales Invoice", self.return_against)
+			against_si_doc.delete_loyalty_point_entry()
+		if self.redeem_loyalty_points and self.loyalty_points:
+			self.apply_loyalty_points()
+
 	def validate_pos_paid_amount(self):
 		if len(self.payments) == 0 and self.is_pos:
 			frappe.throw(_("At least one mode of payment is required for POS invoice."))
@@ -191,6 +205,11 @@
 		if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') == "Each Transaction":
 			update_company_current_month_sales(self.company)
 			self.update_project()
+		if not self.is_return and self.loyalty_program:
+			self.delete_loyalty_point_entry()
+		elif self.is_return and self.return_against and self.loyalty_program:
+			against_si_doc = frappe.get_doc("Sales Invoice", self.return_against)
+			against_si_doc.make_loyalty_point_entry()
 
 		unlink_inter_company_invoice(self.doctype, self.name, self.inter_company_invoice_reference)
 
@@ -607,7 +626,8 @@
 			from erpnext.accounts.general_ledger import make_gl_entries
 
 			# if POS and amount is written off, updating outstanding amt after posting all gl entries
-			update_outstanding = "No" if (cint(self.is_pos) or self.write_off_account) else "Yes"
+			update_outstanding = "No" if (cint(self.is_pos) or self.write_off_account or
+				cint(self.redeem_loyalty_points)) else "Yes"
 
 			make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
 				update_outstanding=update_outstanding, merge_entries=False)
@@ -640,6 +660,7 @@
 		# merge gl entries before adding pos entries
 		gl_entries = merge_similar_entries(gl_entries)
 
+		self.make_loyalty_point_redemption_gle(gl_entries)
 		self.make_pos_gl_entries(gl_entries)
 		self.make_gle_for_change_amount(gl_entries)
 
@@ -716,6 +737,29 @@
 			erpnext.is_perpetual_inventory_enabled(self.company):
 			gl_entries += super(SalesInvoice, self).get_gl_entries()
 
+	def make_loyalty_point_redemption_gle(self, gl_entries):
+		if cint(self.redeem_loyalty_points):
+			gl_entries.append(
+				self.get_gl_dict({
+					"account": self.debit_to,
+					"party_type": "Customer",
+					"party": self.customer,
+					"against": "Expense account - " + cstr(self.loyalty_redemption_account) + " for the Loyalty Program",
+					"credit": self.loyalty_amount,
+					"against_voucher": self.return_against if cint(self.is_return) else self.name,
+					"against_voucher_type": self.doctype
+				})
+			)
+			gl_entries.append(
+				self.get_gl_dict({
+					"account": self.loyalty_redemption_account,
+					"cost_center": self.loyalty_redemption_cost_center,
+					"against": self.customer,
+					"debit": self.loyalty_amount,
+					"remark": "Loyalty Points redeemed by the customer"
+				})
+			)
+
 	def make_pos_gl_entries(self, gl_entries):
 		if cint(self.is_pos):
 			for payment_mode in self.payments:
@@ -920,6 +964,71 @@
 			if entry.amount < 0:
 				frappe.throw(_("Row #{0} (Payment Table): Amount must be positive").format(entry.idx))
 
+	# collection of the loyalty points, create the ledger entry for that.
+	def make_loyalty_point_entry(self):
+		loyalty_program_details = get_loyalty_program_details(self.customer, company=self.company)
+		if loyalty_program_details:
+			points_earned = int(self.grand_total/loyalty_program_details.collection_factor)
+			doc = frappe.get_doc({
+				"doctype": "Loyalty Point Entry",
+				"company": self.company,
+				"loyalty_program": loyalty_program_details.loyalty_program,
+				"loyalty_program_tier": loyalty_program_details.tier_name,
+				"customer": self.customer,
+				"sales_invoice": self.name,
+				"loyalty_points": points_earned,
+				"purchase_amount": self.grand_total,
+				"expiry_date": add_days(self.posting_date, loyalty_program_details.expiry_duration),
+				"posting_date": self.posting_date
+			})
+			doc.flags.ignore_permissions = 1
+			doc.save()
+			# frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", loyalty_program_details.tier_name)
+
+	# valdite the redemption and then delete the loyalty points earned on cancel of the invoice
+	def delete_loyalty_point_entry(self):
+		lp_entry = frappe.db.sql("select name from `tabLoyalty Point Entry` where sales_invoice=%s",
+			(self.name), as_dict=1)[0]
+		against_lp_entry = frappe.db.sql('''select name, sales_invoice from `tabLoyalty Point Entry`
+			where redeem_against=%s''', (lp_entry.name), as_dict=1)
+		if against_lp_entry:
+			invoice_list = ", ".join([d.sales_invoice for d in against_lp_entry])
+			frappe.throw(_('''Sales Invoice can't be cancelled since the Loyalty Points earned has been redeemed. 
+				First cancel the Sales Invoice No {0}''').format(invoice_list))
+		else:
+			frappe.db.sql('''delete from `tabLoyalty Point Entry` where sales_invoice=%s''', (self.name))
+
+	# redeem the loyalty points.
+	def apply_loyalty_points(self):
+		from erpnext.accounts.doctype.loyalty_point_entry.loyalty_point_entry \
+			import get_loyalty_point_entries
+		loyalty_point_entries = get_loyalty_point_entries(self.customer, self.loyalty_program, self.posting_date, self.company)
+
+		points_to_redeem = self.loyalty_amount
+		for lp_entry in loyalty_point_entries:
+			if lp_entry.loyalty_points > points_to_redeem:
+				redeemed_points = points_to_redeem
+			else:
+				redeemed_points = lp_entry.loyalty_points
+			doc = frappe.get_doc({
+				"doctype": "Loyalty Point Entry",
+				"company": self.company,
+				"loyalty_program": self.loyalty_program,
+				"loyalty_program_tier": lp_entry.loyalty_program_tier,
+				"customer": self.customer,
+				"sales_invoice": self.name,
+				"redeem_against": lp_entry.name,
+				"loyalty_points": -(redeemed_points),
+				"purchase_amount": self.grand_total,
+				"expiry_date": lp_entry.expiry_date,
+				"posting_date": self.posting_date
+			})
+			doc.flags.ignore_permissions = 1
+			doc.save()
+			points_to_redeem -= redeemed_points
+			if points_to_redeem < 1: # since points_to_redeem is integer
+				break
+
 	def book_income_for_deferred_revenue(self):
 		# book the income on the last day, but it will be trigger on the 1st of month at 12:00 AM
 		# start_date: 1st of the last month or the start date
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 6f9db34..bf1c139 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -462,7 +462,7 @@
 		if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
 			grand_total = self.doc.rounded_total or self.doc.grand_total
 			if self.doc.party_account_currency == self.doc.currency:
-				total_amount_to_pay = flt(grand_total  - self.doc.total_advance
+				total_amount_to_pay = flt(grand_total - self.doc.total_advance
 					- flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
 			else:
 				total_amount_to_pay = flt(flt(grand_total *
@@ -481,11 +481,11 @@
 			paid_amount = self.doc.paid_amount \
 				if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
 
-
 			self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
 				self.doc.precision("outstanding_amount"))
 
 	def calculate_paid_amount(self):
+
 		paid_amount = base_paid_amount = 0.0
 
 		if self.doc.is_pos:
@@ -497,6 +497,10 @@
 		elif not self.doc.is_return:
 			self.doc.set('payments', [])
 
+		if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
+			base_paid_amount += self.doc.loyalty_amount
+			paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
+
 		self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
 		self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
 
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index c24945e..303d21f 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -568,9 +568,8 @@
 
 	calculate_outstanding_amount: function(update_paid_amount) {
 		// NOTE:
-		// paid_amount and write_off_amount is only for POS Invoice
+		// paid_amount and write_off_amount is only for POS/Loyalty Point Redemption Invoice
 		// total_advance is only for non POS Invoice
-
 		if(this.frm.doc.doctype == "Sales Invoice" && this.frm.doc.is_return){
 			this.calculate_paid_amount();
 		}
@@ -602,16 +601,19 @@
 			}
 
 			if(this.frm.doc.doctype == "Sales Invoice") {
-				this.set_default_payment(total_amount_to_pay, update_paid_amount);
+				let total_amount_for_payment = (this.frm.doc.redeem_loyalty_points && this.frm.doc.loyalty_amount)
+					? flt(total_amount_to_pay - this.frm.doc.loyalty_amount, precision("base_grand_total"))
+					: total_amount_to_pay;
+				this.set_default_payment(total_amount_for_payment, update_paid_amount);
 				this.calculate_paid_amount();
 			}
 			this.calculate_change_amount();
 
 			var paid_amount = (this.frm.doc.party_account_currency == this.frm.doc.currency) ?
 				this.frm.doc.paid_amount : this.frm.doc.base_paid_amount;
-
 			this.frm.doc.outstanding_amount =  flt(total_amount_to_pay - flt(paid_amount) +
 				flt(this.frm.doc.change_amount * this.frm.doc.conversion_rate), precision("outstanding_amount"));
+			console.log("set the outstanding amount");
 		}
 	},
 
@@ -644,9 +646,14 @@
 		} else if(!this.frm.doc.is_return){
 			this.frm.doc.payments = [];
 		}
+		if (this.frm.doc.redeem_loyalty_points && this.frm.doc.loyalty_amount) {
+			base_paid_amount += this.frm.doc.loyalty_amount;
+			paid_amount += flt(this.frm.doc.loyalty_amount / me.frm.doc.conversion_rate, precision("paid_amount"));
+		}
 
 		this.frm.doc.paid_amount = flt(paid_amount, precision("paid_amount"));
 		this.frm.doc.base_paid_amount = flt(base_paid_amount, precision("base_paid_amount"));
+		console.log("paid amount set as -> ", paid_amount, base_paid_amount);
 	},
 
 	calculate_change_amount: function(){
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index 88e66ca..5499ab0 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -103,6 +103,11 @@
 			// indicator
 			erpnext.utils.set_party_dashboard_indicators(frm);
 
+			//
+			if (frm.doc.__onload.dashboard_info.loyalty_point) {
+				frm.dashboard.add_indicator(__('Loyalty Point: {0}', [frm.doc.__onload.dashboard_info.loyalty_point]), 'blue');
+			}
+
 		} else {
 			frappe.contacts.clear_address_and_contact(frm);
 		}
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 3fdf6cf..5215854 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -1470,6 +1470,97 @@
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 1, 
+   "columns": 0, 
+   "fieldname": "column_break_38", 
+   "fieldtype": "Section Break", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Points", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_program", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Program", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Loyalty Program", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_program_tier", 
+   "fieldtype": "Read Only", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Program Tier", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 1, 
    "collapsible_depends_on": "default_sales_partner", 
    "columns": 0, 
    "fieldname": "sales_team_section_break", 
@@ -1677,7 +1768,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-06-26 13:12:30.677834", 
+ "modified": "2018-06-27 12:12:30.677834", 
  "modified_by": "Administrator", 
  "module": "Selling", 
  "name": "Customer", 
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index b5961a9..1e63e59 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -24,6 +24,9 @@
 
 	def load_dashboard_info(self):
 		info = get_dashboard_info(self.doctype, self.name)
+		loyalty_point_details = self.get_loyalty_points()
+		if loyalty_point_details and loyalty_point_details.get("loyalty_points"):
+			info["loyalty_point"] = loyalty_point_details.loyalty_points
 		self.set_onload('dashboard_info', info)
 
 	def autoname(self):
@@ -33,6 +36,11 @@
 		else:
 			set_name_by_naming_series(self)
 
+	def get_loyalty_points(self):
+		if self.loyalty_program:
+			from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_details
+			return get_loyalty_details(self.name, self.loyalty_program)
+
 	def get_customer_name(self):
 		if frappe.db.get_value("Customer", self.customer_name):
 			count = frappe.db.sql("""select ifnull(MAX(CAST(SUBSTRING_INDEX(name, ' ', -1) AS UNSIGNED)), 0) from tabCustomer
@@ -51,6 +59,7 @@
 		self.flags.old_lead = self.lead_name
 		validate_party_accounts(self)
 		self.validate_credit_limit_on_change()
+		self.set_loyalty_program()
 		self.check_customer_group_change()
 
 	def check_customer_group_change(self):
@@ -178,6 +187,17 @@
 		if frappe.defaults.get_global_default('cust_master_name') == 'Customer Name':
 			frappe.db.set(self, "customer_name", newdn)
 
+	def set_loyalty_program(self):
+		if not self.loyalty_program:
+			loyalty_programs = frappe.get_all("Loyalty Program", fields=["name", "customer_group",
+				"customer_territory"], filters={"auto_opt_in": 1, "disabled": 0})
+			from frappe.desk.treeview import get_children
+			for loyalty_program in loyalty_programs:
+				customer_groups = get_children("Customer Group", loyalty_program.customer_group, )
+				if self.customer_group in customer_groups and\
+					self.territory in get_children("Territory", loyalty_program.customer_territory):
+					self.loyalty_program = loyalty_program.name
+
 
 def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None):
 	if frappe.db.get_default("cust_master_name") == "Customer Name":
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index c34f26b..9223adb 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -1912,6 +1912,99 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_points_redemption", 
+   "fieldtype": "Section Break", 
+   "hidden": 1, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Points Redemption", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_points", 
+   "fieldtype": "Int", 
+   "hidden": 1, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Points", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 1, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "loyalty_amount", 
+   "fieldtype": "Currency", 
+   "hidden": 1, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Loyalty Amount", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 1, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
    "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 6fbe587..ee6b1c7 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -558,6 +558,10 @@
 		if target.company_address:
 			target.update(get_fetch_values("Sales Invoice", 'company_address', target.company_address))
 
+		# set the redeem loyalty points if provided via shopping cart
+		if source.loyalty_points and source.order_type == "Shopping Cart":
+			target.redeem_loyalty_points = 1
+
 	def update_item(source, target, source_parent):
 		target.amount = flt(source.amount) - flt(source.billed_amt)
 		target.base_amount = target.amount * flt(source_parent.conversion_rate)
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index 7893b00..294b2ba 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -95,7 +95,9 @@
 			frm: this.frm,
 			wrapper: this.wrapper.find('.cart-container'),
 			events: {
-				on_customer_change: (customer) => this.frm.set_value('customer', customer),
+				on_customer_change: (customer) => {
+					this.frm.set_value('customer', customer);
+				},
 				on_field_change: (item_code, field, value, batch_no) => {
 					this.update_item_in_cart(item_code, field, value, batch_no);
 				},
@@ -119,6 +121,46 @@
 				},
 				get_item_details: (item_code) => {
 					return this.items.get(item_code);
+				},
+				get_loyalty_details: () => {
+					var me = this;
+					if (this.frm.doc.customer) {
+						frappe.call({
+							method: "erpnext.accounts.doctype.loyalty_program.loyalty_program.get_loyalty_program_details",
+							args: {
+								"customer": me.frm.doc.customer,
+								"till_date": me.frm.doc.posting_date,
+								"company": me.frm.doc.company,
+								"silent": true
+							},
+							callback: function(r) {
+								if (r.message.loyalty_program && r.message.loyalty_points) {
+									me.cart.events.set_loyalty_details(r.message, true);
+								}
+								if (!r.message.loyalty_program) {
+									var loyalty_details = {
+										loyalty_points: 0,
+										loyalty_program: '',
+										expense_account: '',
+										cost_center: ''
+									}
+									me.cart.events.set_loyalty_details(loyalty_details, false);
+								}
+							}
+						});
+					}
+				},
+				set_loyalty_details: (details, view_status) => {
+					if (view_status) {
+						this.cart.available_loyalty_points.$wrapper.removeClass("hide");
+					} else {
+						this.cart.available_loyalty_points.$wrapper.addClass("hide");
+					}
+					this.cart.available_loyalty_points.set_value(details.loyalty_points);
+					this.cart.available_loyalty_points.refresh_input();
+					this.frm.set_value("loyalty_program", details.loyalty_program);
+					this.frm.set_value("loyalty_redemption_account", details.expense_account);
+					this.frm.set_value("loyalty_redemption_cost_center", details.cost_center);
 				}
 			}
 		});
@@ -563,6 +605,7 @@
 	make() {
 		this.make_dom();
 		this.make_customer_field();
+		this.make_loyalty_points();
 		this.make_numpad();
 	}
 
@@ -598,16 +641,27 @@
 						</div>
 					</div>
 				</div>
-				<div class="number-pad-container">
+				<div class="row">
+					<div class="number-pad-container col-sm-6"></div>
+					<div class="col-sm-6 loyalty-program-section">
+						<div class="loyalty-program-field"> </div>
+					</div>
 				</div>
 			</div>
 		`);
+
+
 		this.$cart_items = this.wrapper.find('.cart-items');
 		this.$empty_state = this.wrapper.find('.cart-items .empty-state');
 		this.$taxes_and_totals = this.wrapper.find('.taxes-and-totals');
 		this.$discount_amount = this.wrapper.find('.discount-amount');
 		this.$grand_total = this.wrapper.find('.grand-total');
 		this.$qty_total = this.wrapper.find('.quantity-total');
+		// this.$loyalty_button = this.wrapper.find('.loyalty-button');
+
+		// this.$loyalty_button.on('click', () => {
+		// 	this.loyalty_button.show();
+		// })
 
 		this.toggle_taxes_and_totals(false);
 		this.$grand_total.on('click', () => {
@@ -765,6 +819,7 @@
 				},
 				onchange: () => {
 					this.events.on_customer_change(this.customer_field.get_value());
+					this.events.get_loyalty_details();
 				}
 			},
 			parent: this.wrapper.find('.customer-field'),
@@ -774,6 +829,21 @@
 		this.customer_field.set_value(this.frm.doc.customer);
 	}
 
+
+	make_loyalty_points() {
+		this.available_loyalty_points = frappe.ui.form.make_control({
+			df: {
+				fieldtype: 'Int',
+				label: 'Available Loyalty Points',
+				read_only: 1,
+				fieldname: 'available_loyalty_points'
+			},
+			parent: this.wrapper.find('.loyalty-program-field')
+		});
+		this.available_loyalty_points.set_value(this.frm.doc.loyalty_points);
+	}
+
+
 	disable_numpad_control() {
 		let disabled_btns = [];
 		if(!this.frm.allow_edit_rate) {
@@ -1458,7 +1528,8 @@
 		this.set_flag();
 		this.dialog = new frappe.ui.Dialog({
 			fields: this.get_fields(),
-			width: 800
+			width: 800,
+			invoice_frm: this.frm
 		});
 
 		this.set_title();
@@ -1543,6 +1614,44 @@
 			},
 			{
 				fieldtype: 'Section Break',
+				depends_on: 'eval: this.invoice_frm.doc.loyalty_program'
+			},
+			{
+				fieldtype: 'Check',
+				label: 'Redeem Loyalty Points',
+				fieldname: 'redeem_loyalty_points',
+				onchange: () => {
+					me.update_cur_frm_value("redeem_loyalty_points", () => {
+						frappe.flags.redeem_loyalty_points = false;
+						me.update_loyalty_points();
+					});
+				}
+			},
+			{
+				fieldtype: 'Column Break',
+			},
+			{
+				fieldtype: 'Int',
+				fieldname: "loyalty_points",
+				label: __("Loyalty Points"),
+				depends_on: "redeem_loyalty_points",
+				onchange: () => {
+					me.update_cur_frm_value("loyalty_points", () => {
+						frappe.flags.loyalty_points = false;
+						me.update_loyalty_points();
+					});
+				}
+			},
+			{
+				fieldtype: 'Currency',
+				label: __("Loyalty Amount"),
+				fieldname: "loyalty_amount",
+				options: me.frm.doc.currency,
+				read_only: 1,
+				depends_on: "redeem_loyalty_points"
+			},
+			{
+				fieldtype: 'Section Break',
 			},
 			{
 				fieldtype: 'Currency',
@@ -1603,6 +1712,9 @@
 	set_flag() {
 		frappe.flags.write_off_amount = true;
 		frappe.flags.change_amount = true;
+		frappe.flags.loyalty_points = true;
+		frappe.flags.redeem_loyalty_points = true;
+		frappe.flags.payment_method = true;
 	}
 
 	update_cur_frm_value(fieldname, callback) {
@@ -1619,15 +1731,15 @@
 
 	update_payment_value(fieldname, value) {
 		var me = this;
-		$.each(this.frm.doc.payments, function(i, data) {
-			if (__(data.mode_of_payment) == __(fieldname)) {
-				frappe.model.set_value('Sales Invoice Payment', data.name, 'amount', value)
-					.then(() => {
-						me.update_change_amount();
-						me.update_write_off_amount();
-					});
-			}
-		});
+			$.each(this.frm.doc.payments, function(i, data) {
+				if (__(data.mode_of_payment) == __(fieldname)) {
+					frappe.model.set_value('Sales Invoice Payment', data.name, 'amount', value)
+						.then(() => {
+							me.update_change_amount();
+							me.update_write_off_amount();
+						});
+				}
+			});
 	}
 
 	update_change_amount() {
@@ -1643,4 +1755,22 @@
 		this.dialog.set_value("paid_amount", this.frm.doc.paid_amount);
 		this.dialog.set_value("outstanding_amount", this.frm.doc.outstanding_amount);
 	}
+
+	update_payment_amount() {
+		var me = this;
+		$.each(this.frm.doc.payments, function(i, data) {
+			console.log("setting the ", data.mode_of_payment, " for the value", data.amount);
+			me.dialog.set_value(data.mode_of_payment, data.amount);
+		});
+	}
+
+	update_loyalty_points() {
+		if (this.dialog.get_value("redeem_loyalty_points")) {
+			this.dialog.set_value("loyalty_points", this.frm.doc.loyalty_points);
+			this.dialog.set_value("loyalty_amount", this.frm.doc.loyalty_amount);
+			this.update_payment_amount();
+			this.show_paid_amount();
+		}
+	}
+
 }
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 3683695..cd02e30 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -570,6 +570,7 @@
 		self.assertEqual(dn.name, dt.delivery_stops[0].delivery_note)
 
 def create_delivery_note(**args):
+	print (frappe.session.user)
 	dn = frappe.new_doc("Delivery Note")
 	args = frappe._dict(args)
 	dn.posting_date = args.posting_date or nowdate()
diff --git a/erpnext/templates/pages/order.html b/erpnext/templates/pages/order.html
index 3a6d225..712eefd 100644
--- a/erpnext/templates/pages/order.html
+++ b/erpnext/templates/pages/order.html
@@ -18,26 +18,26 @@
 {% block page_content %}
 
 <div class="row transaction-subheading">
-    <div class="col-xs-6">
-        <span class="indicator {{ doc.indicator_color or ("blue" if doc.docstatus==1 else "darkgrey") }}">
-            {{ _(doc.indicator_title) or _(doc.status) or _("Submitted") }}
-        </span>
+	<div class="col-xs-6">
+		<span class="indicator {{ doc.indicator_color or ("blue" if doc.docstatus==1 else "darkgrey") }}">
+			{{ _(doc.indicator_title) or _(doc.status) or _("Submitted") }}
+		</span>
 	</div>
-    <div class="col-xs-6 text-muted text-right small">
-        {{ frappe.utils.formatdate(doc.transaction_date, 'medium') }}
+	<div class="col-xs-6 text-muted text-right small">
+		{{ frappe.utils.formatdate(doc.transaction_date, 'medium') }}
 		{% if doc.valid_till %}
 		<p>
-        {{ _("Valid Till") }}: {{ frappe.utils.formatdate(doc.valid_till, 'medium') }}
+		{{ _("Valid Till") }}: {{ frappe.utils.formatdate(doc.valid_till, 'medium') }}
 		</p>
 		{% endif %}
-    </div>
+	</div>
 </div>
 
 <p class='small' style='padding-top: 15px;'>
 {% if doc.doctype == 'Supplier Quotation' %}
-    <b>{{ doc.supplier_name}}</b>
+	<b>{{ doc.supplier_name}}</b>
 {% else %}
-    <b>{{ doc.customer_name}}</b>
+	<b>{{ doc.customer_name}}</b>
 {% endif %}
 {% if doc.contact_display %}
 	<br>
@@ -51,64 +51,94 @@
 
 <div class="order-container">
 
-    <!-- items -->
-    <div class="order-item-table">
-        <div class="row order-items order-item-header text-muted">
-            <div class="col-sm-6 col-xs-6 h6 text-uppercase">
-                {{ _("Item") }}
-            </div>
-            <div class="col-sm-3 col-xs-3 text-right h6 text-uppercase">
-                {{ _("Quantity") }}
-            </div>
-            <div class="col-sm-3 col-xs-3 text-right h6 text-uppercase">
-                {{ _("Amount") }}
-            </div>
-        </div>
-        {% for d in doc.items %}
-        <div class="row order-items">
-            <div class="col-sm-6 col-xs-6">
-                {{ item_name_and_description(d) }}
-            </div>
-            <div class="col-sm-3 col-xs-3 text-right">
-                {{ d.qty }}
-                {% if d.delivered_qty is defined and d.delivered_qty != None %}
-                <p class="text-muted small">{{
-                    _("Delivered: {0}").format(d.delivered_qty) }}</p>
-                {% endif %}
-            </div>
-            <div class="col-sm-3 col-xs-3 text-right">
-                {{ d.get_formatted("amount")	 }}
-                <p class="text-muted small">{{
-                    _("@ {0}").format(d.get_formatted("rate")) }}</p>
-            </div>
-        </div>
-        {% endfor %}
-    </div>
+	<!-- items -->
+	<div class="order-item-table">
+		<div class="row order-items order-item-header text-muted">
+			<div class="col-sm-6 col-xs-6 h6 text-uppercase">
+				{{ _("Item") }}
+			</div>
+			<div class="col-sm-3 col-xs-3 text-right h6 text-uppercase">
+				{{ _("Quantity") }}
+			</div>
+			<div class="col-sm-3 col-xs-3 text-right h6 text-uppercase">
+				{{ _("Amount") }}
+			</div>
+		</div>
+		{% for d in doc.items %}
+		<div class="row order-items">
+			<div class="col-sm-6 col-xs-6">
+				{{ item_name_and_description(d) }}
+			</div>
+			<div class="col-sm-3 col-xs-3 text-right">
+				{{ d.qty }}
+				{% if d.delivered_qty is defined and d.delivered_qty != None %}
+				<p class="text-muted small">{{
+					_("Delivered: {0}").format(d.delivered_qty) }}</p>
+				{% endif %}
+			</div>
+			<div class="col-sm-3 col-xs-3 text-right">
+				{{ d.get_formatted("amount")	 }}
+				<p class="text-muted small">{{
+					_("@ {0}").format(d.get_formatted("rate")) }}</p>
+			</div>
+		</div>
+		{% endfor %}
+	</div>
 
-    <!-- taxes -->
-    <div class="order-taxes row">
-        <div class="col-sm-6"><!-- empty --></div>
-        <div class="col-sm-6 text-right">
-            {% include "erpnext/templates/includes/order/order_taxes.html" %}
-        </div>
-    </div>
+	<!-- taxes -->
+	<div class="order-taxes row">
+		<div class="col-sm-6"><!-- empty --></div>
+		<div class="col-sm-6 text-right">
+			{% include "erpnext/templates/includes/order/order_taxes.html" %}
+		</div>
+	</div>
 </div>
 
-<div class="cart-taxes row small">
-    <div class="col-sm-6"><!-- empty --></div>
-    <div class="col-sm-6">
-		{% if enabled_checkout %}
-	        {% if (doc.doctype=="Sales Order" and doc.per_billed <= 0)
-				or (doc.doctype=="Sales Invoice" and doc.outstanding_amount > 0) %}
-			<div class="page-header-actions-block" data-html-block="header-actions">
-				<p>
-				    <a href="/api/method/erpnext.accounts.doctype.payment_request.payment_request.make_payment_request?dn={{ doc.name }}&dt={{ doc.doctype }}&submit_doc=1&order_type=Shopping Cart"
-				        class="btn btn-primary btn-sm">{{ _("Pay") }} {{ doc.get_formatted("grand_total") }} </a>
-				</p>
+{% if enabled_checkout and ((doc.doctype=="Sales Order" and doc.per_billed <= 0)
+	or (doc.doctype=="Sales Invoice" and doc.outstanding_amount > 0)) %}
+
+<div class="panel panel-default">
+	<div class="panel-heading">
+		<div class="row">
+			<div class="form-column col-sm-6 address-title">
+				<strong>Payment</strong>
 			</div>
-			{% endif %}
-		{% endif %}
+		</div>
 	</div>
+	<div class="panel-collapse">
+		<div class="panel-body text-muted small">
+			<div class="row">
+				<div class="form-column col-sm-6">
+					{% if available_loyalty_points %}
+					<div class="form-group">
+						<div class="h6">Enter Loyalty Points</div>
+						<div class="control-input-wrapper">
+							<div class="control-input">
+								<input class="form-control" type="number" min="0" max="{{ available_loyalty_points }}" id="loyalty-point-to-redeem">
+							</div>
+							<p class="help-box small text-muted hidden-xs"> Available Points: {{ available_loyalty_points }} </p>
+						</div>
+					</div>
+					{% endif %}
+				</div>
+
+				<div class="form-column col-sm-6">
+					<div id="loyalty-points-status" style="text-align: right"></div>
+					<div class="page-header-actions-block" data-html-block="header-actions">
+						<p>
+							<a href="/api/method/erpnext.accounts.doctype.payment_request.payment_request.make_payment_request?dn={{ doc.name }}&dt={{ doc.doctype }}&submit_doc=1&order_type=Shopping Cart"
+								class="btn btn-primary btn-sm" id="pay-for-order">{{ _("Pay") }} {{ doc.get_formatted("grand_total") }} </a>
+						</p>
+					</div>
+				</div>
+
+			</div>
+
+		</div>
+	</div>
+</div>
+{% endif %}
+
 
 {% if attachments %}
 <div class="order-item-table">
@@ -131,7 +161,20 @@
 </div>
 {% if doc.terms %}
 <div class="terms-and-condition text-muted small">
-    <hr><p>{{ doc.terms }}</p>
+	<hr><p>{{ doc.terms }}</p>
 </div>
 {% endif %}
 {% endblock %}
+
+{% block script %}
+	<script> {% include "templates/pages/order.js" %} </script>
+	<script>
+		window.doc_info = {
+			customer: '{{doc.customer}}',
+			doctype: '{{ doc.doctype }}',
+			doctype_name: '{{ doc.name }}',
+			grand_total: '{{ doc.grand_total }}',
+			currency: '{{ doc.currency }}'
+		}
+	</script>
+{% endblock %}
\ No newline at end of file
diff --git a/erpnext/templates/pages/order.js b/erpnext/templates/pages/order.js
new file mode 100644
index 0000000..21c3a14
--- /dev/null
+++ b/erpnext/templates/pages/order.js
@@ -0,0 +1,40 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ready(function(){
+
+	var loyalty_points_input = document.getElementById("loyalty-point-to-redeem");
+	var loyalty_points_status = document.getElementById("loyalty-points-status");
+	loyalty_points_input.onblur = apply_loyalty_points;
+
+	function apply_loyalty_points() {
+		var loyalty_points = parseInt(loyalty_points_input.value);
+		if (loyalty_points) {
+			frappe.call({
+				method: "erpnext.accounts.doctype.loyalty_program.loyalty_program.get_redeemption_factor",
+				args: {
+					"customer": doc_info.customer
+				},
+				callback: function(r) {
+					if (r) {
+						var message = ""
+						let loyalty_amount = flt(r.message*loyalty_points);
+						if (doc_info.grand_total && doc_info.grand_total < loyalty_amount) {
+							let redeemable_amount = parseInt(doc_info.grand_total/r.message);
+							message = "You can only redeem max " + redeemable_amount + " points in this order.";
+							frappe.msgprint(__(message));
+						} else {
+							message = loyalty_points + " Loyalty Points of amount "+ loyalty_amount + " is applied."
+							frappe.msgprint(__(message));
+							var remaining_amount = flt(doc_info.grand_total) - flt(loyalty_amount);
+							var payment_button = document.getElementById("pay-for-order");
+							payment_button.innerHTML = __("Pay Remaining");
+							payment_button.href = "/api/method/erpnext.accounts.doctype.payment_request.payment_request.make_payment_request?dn="+doc_info.doctype_name+"&dt="+doc_info.doctype+"&loyalty_points="+loyalty_points+"&submit_doc=1&order_type=Shopping Cart";
+						}
+						loyalty_points_status.innerHTML = message;
+					}
+				}
+			});
+		}
+	}
+})
\ No newline at end of file
diff --git a/erpnext/templates/pages/order.py b/erpnext/templates/pages/order.py
index 2874047..70bd702 100644
--- a/erpnext/templates/pages/order.py
+++ b/erpnext/templates/pages/order.py
@@ -32,6 +32,13 @@
 
 	if not frappe.has_website_permission(context.doc):
 		frappe.throw(_("Not Permitted"), frappe.PermissionError)
+	
+	# check for the loyalty program of the customer
+	customer_loyalty_program = frappe.db.get_value("Customer", context.doc.customer, "loyalty_program")	
+	if customer_loyalty_program:
+		from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details
+		loyalty_program_details = get_loyalty_program_details(context.doc.customer, customer_loyalty_program)
+		context.available_loyalty_points = int(loyalty_program_details.get("loyalty_points"))
 
 def get_attachments(dt, dn):
         return frappe.get_all("File",