fix: fetch account balance info
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html b/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html
new file mode 100644
index 0000000..2c4d4bb
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html
@@ -0,0 +1,28 @@
+
+{% if not jQuery.isEmptyObject(data) %}
+<h5 style="margin-top: 20px;"> {{ __("Balance Details") }} </h5>
+<table class="table table-bordered small">
+	<thead>
+		<tr>
+			<th style="width: 20%">{{ __("Account Type") }}</th>
+			<th style="width: 20%" class="text-right">{{ __("Current Balance") }}</th>
+			<th style="width: 20%" class="text-right">{{ __("Available Balance") }}</th>
+			<th style="width: 20%" class="text-right">{{ __("Reserved Balance") }}</th>
+			<th style="width: 20%" class="text-right">{{ __("Uncleared Balance") }}</th>
+		</tr>
+	</thead>
+	<tbody>
+		{% for(const [key, value] of Object.entries(data)) { %}
+			<tr>
+				<td> {%= key %} </td>
+				<td class="text-right"> {%= value["current_balance"] %} </td>
+				<td class="text-right"> {%= value["available_balance"] %} </td>
+				<td class="text-right"> {%= value["reserved_balance"] %} </td>
+				<td class="text-right"> {%= value["uncleared_balance"] %} </td>
+			</tr>
+		{% } %}
+	</tbody>
+</table>
+{% else %}
+<p style="margin-top: 30px;"> Account Balance Information Not Available. </p>
+{% endif %}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js
index 48e0c0b..239a0bc 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js
@@ -2,6 +2,27 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Mpesa Settings', {
-	// refresh: function(frm) {
-	// }
+	onload_post_render: function(frm) {
+		frm.events.setup_account_balance_html(frm);
+	},
+
+	get_account_balance: function(frm) {
+		if (!frm.initiator_name && !frm.security_credentials) return;
+		frappe.call({
+			method: "get_account_balance_info",
+			doc: frm.doc
+		});
+	},
+
+	setup_account_balance_html: function(frm) {
+		console.log(frm.doc.account_balance)
+		$("div").remove(".form-dashboard-section.custom");
+		frm.dashboard.add_section(
+			frappe.render_template('account_balance', {
+				data: JSON.parse(frm.doc.account_balance)
+			})
+		);
+		frm.dashboard.show();
+	}
+
 });
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
index 9c0bef1..fc7b310 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
@@ -9,10 +9,14 @@
   "payment_gateway_name",
   "consumer_key",
   "consumer_secret",
-  "column_break_4",
+  "initiator_name",
   "till_number",
+  "sandbox",
+  "column_break_4",
   "online_passkey",
-  "sandbox"
+  "security_credential",
+  "get_account_balance",
+  "account_balance"
  ],
  "fields": [
   {
@@ -58,11 +62,32 @@
    "fieldtype": "Password",
    "label": " Online PassKey",
    "reqd": 1
+  },
+  {
+   "fieldname": "initiator_name",
+   "fieldtype": "Data",
+   "label": "Initiator Name"
+  },
+  {
+   "fieldname": "security_credential",
+   "fieldtype": "Small Text",
+   "label": "Security Credential"
+  },
+  {
+   "fieldname": "account_balance",
+   "fieldtype": "Long Text",
+   "hidden": 1,
+   "label": "Account Balance",
+   "read_only": 1
+  },
+  {
+   "fieldname": "get_account_balance",
+   "fieldtype": "Button",
+   "label": "Get Account Balance"
   }
  ],
- "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-09-10 09:07:28.557461",
+ "modified": "2020-09-25 20:21:38.215494",
  "modified_by": "Administrator",
  "module": "ERPNext Integrations",
  "name": "Mpesa Settings",
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
index c92c1b2..3af0baa 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
@@ -4,17 +4,14 @@
 
 
 from __future__ import unicode_literals
-import json
-import requests
-from six.moves.urllib.parse import urlencode
+from json import loads, dumps
 
 import frappe
 from frappe.model.document import Document
 from frappe import _
-from frappe.utils import get_url, call_hook_method, cint, flt, cstr
+from frappe.utils import call_hook_method
 from frappe.integrations.utils import create_request_log, create_payment_gateway
 from frappe.utils import get_request_site_address
-from frappe.utils.password import get_decrypted_password
 from frappe.utils import get_request_site_address
 from erpnext.erpnext_integrations.utils import create_mode_of_payment
 from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_connector import MpesaConnector
@@ -35,16 +32,29 @@
 
 	def request_for_payment(self, **kwargs):
 		response = frappe._dict(generate_stk_push(**kwargs))
+		self.handle_api_response("CheckoutRequestID", kwargs, response)
+
+	def get_account_balance_info(self):
+		payload = dict(
+			reference_doctype="Mpesa Settings",
+			reference_docname=self.name,
+			doc_details=vars(self)
+		)
+		response = frappe._dict(get_account_balance(payload))
+		self.handle_api_response("ConversationID", payload, response)
+
+	def handle_api_response(self, global_id, request_dict, response):
 		# check error response
-		if hasattr(response, "requestId"):
+		if getattr(response, "requestId"):
 			req_name = getattr(response, "requestId")
 			error = response
 		else:
 			# global checkout id used as request name
-			req_name = getattr(response, "CheckoutRequestID")
+			req_name = getattr(response, global_id)
 			error = None
 
-		create_request_log(kwargs, "Host", "Mpesa", req_name, error)
+		create_request_log(request_dict, "Host", "Mpesa", req_name, error)
+
 		if error:
 			frappe.throw(_(getattr(response, "errorMessage")), title=_("Transaction Error"))
 
@@ -76,24 +86,84 @@
 	""" Verify the transaction result received via callback """
 	transaction_response = frappe._dict(kwargs["Body"]["stkCallback"])
 
-	checkout_id = getattr(transaction_response, "CheckoutRequestID")
+	checkout_id = getattr(transaction_response, "CheckoutRequestID", "")
 	request = frappe.get_doc("Integration Request", checkout_id)
-	transaction_data = frappe._dict(json.loads(request.data))
+	transaction_data = frappe._dict(loads(request.data))
 
 	if transaction_response['ResultCode'] == 0:
 		if transaction_data.reference_doctype and transaction_data.reference_docname:
 			try:
 				frappe.get_doc(transaction_data.reference_doctype,
 					transaction_data.reference_docname).run_method("on_payment_authorized", 'Completed')
-				request.db_set('output', transaction_response)
-				request.db_set('status', 'Completed')
+				request.process_response('error', transaction_response)
 			except Exception:
-				request.db_set('error', transaction_response)
-				request.db_set('status', 'Failed')
+				request.process_response('error', transaction_response)
 				frappe.log_error(frappe.get_traceback())
 
 	else:
-		request.db_set('error', transaction_response)
-		request.db_set('status', 'Failed')
+		request.process_response('error', transaction_response)
 
-	frappe.publish_realtime('process_phone_payment', after_commit=True, user=request.owner, message=transaction_response)
\ No newline at end of file
+	frappe.publish_realtime('process_phone_payment', after_commit=True, doctype=transaction_data.reference_doctype,
+		docname=transaction_data.reference_docname, user=request.owner, message=transaction_response)
+
+def get_account_balance(request_payload):
+	""" Call account balance API to send the request to the Mpesa Servers """
+	try:
+		mpesa_settings = frappe.get_doc("Mpesa Settings", request_payload.get("reference_docname"))
+		env = "production" if not mpesa_settings.sandbox else "sandbox"
+		connector = MpesaConnector(env=env,
+			app_key=mpesa_settings.consumer_key,
+			app_secret=mpesa_settings.get_password("consumer_secret"))
+
+		# callback_url = get_request_site_address(True) + "/api/method/erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings.process_balance_info"
+		callback_url = "https://b014ca8e7957.ngrok.io/api/method/erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings.process_balance_info"
+
+		response = connector.get_balance(mpesa_settings.initiator_name, mpesa_settings.security_credential, mpesa_settings.till_number, 4, mpesa_settings.name, callback_url, callback_url)
+		return response
+	except Exception:
+		frappe.log_error(title=_("Account Balance Processing Error"))
+		frappe.throw(title=_("Error"), message=_("Please check your configuration and try again"))
+
+@frappe.whitelist(allow_guest=True)
+def process_balance_info(**kwargs):
+
+	account_balance_response = frappe._dict(kwargs["Result"])
+
+	conversation_id = getattr(account_balance_response, "ConversationID", "")
+	request = frappe.get_doc("Integration Request", conversation_id)
+
+	if request.status == "Completed":
+		return
+
+	transaction_data = frappe._dict(loads(request.data))
+	frappe.logger().debug(account_balance_response)
+
+	if account_balance_response["ResultCode"] == 0:
+		try:
+			result_params = account_balance_response["ResultParameters"]["ResultParameter"]
+			for param in result_params:
+				if param["Key"] == "AccountBalance":
+					balance_info = param["Value"]
+					balance_info = convert_to_json(balance_info)
+
+			ref_doc = frappe.get_doc(transaction_data.reference_doctype, transaction_data.reference_docname)
+			ref_doc.db_set("account_balance", balance_info)
+
+			request.process_response('output', account_balance_response)
+		except:
+			request.process_response('error', account_balance_response)
+			frappe.log_error(title=_("Mpesa Account Balance Processing Error"), message=account_balance_response)
+	else:
+		request.process_response('error', account_balance_response)
+
+def convert_to_json(balance_info):
+	balance_dict = frappe._dict()
+	for account_info in balance_info.split("&"):
+		account_info = account_info.split('|')
+		balance_dict[account_info[0]] = dict(
+			current_balance=account_info[2],
+			available_balance=account_info[3],
+			reserved_balance=account_info[4],
+			uncleared_balance=account_info[5]
+		)
+	return dumps(balance_dict)
\ No newline at end of file