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/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",