mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-29 22:58:25 +00:00
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
This commit is contained in:
committed by
Nabin Hait
parent
5d134e0465
commit
da486eeebd
@@ -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 %}
|
||||
40
erpnext/templates/pages/order.js
Normal file
40
erpnext/templates/pages/order.js
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -32,6 +32,13 @@ def get_context(context):
|
||||
|
||||
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",
|
||||
|
||||
Reference in New Issue
Block a user