diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index b7d80ae6089..11ab02021be 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -950,12 +950,12 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre @frappe.whitelist() -def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None, mode_of_payment=None): +def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None): doc = frappe.get_doc(dt, dn) if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0: frappe.throw(_("Can only make payment against unbilled {0}").format(dt)) - if dt in ("Sales Invoice", "Sales Order", "Dunning", "POS Invoice"): + if dt in ("Sales Invoice", "Sales Order", "Dunning"): party_type = "Customer" elif dt in ("Purchase Invoice", "Purchase Order"): party_type = "Supplier" @@ -965,7 +965,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= party_type = "Student" # party account - if dt in ["Sales Invoice", "POS Invoice"]: + if dt == "Sales Invoice": party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to elif dt == "Purchase Invoice": party_account = doc.credit_to @@ -984,7 +984,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account) # payment type - if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees", "Dunning", "POS Invoice") and doc.outstanding_amount > 0)) \ + if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)) \ or (dt=="Purchase Invoice" and doc.outstanding_amount < 0): payment_type = "Receive" else: @@ -994,7 +994,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= grand_total = outstanding_amount = 0 if party_amount: grand_total = outstanding_amount = party_amount - elif dt in ("Sales Invoice", "Purchase Invoice", "POS Invoice"): + elif dt in ("Sales Invoice", "Purchase Invoice"): if party_account_currency == doc.company_currency: grand_total = doc.base_rounded_total or doc.base_grand_total else: @@ -1021,11 +1021,11 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= outstanding_amount = grand_total - flt(doc.advance_paid) # bank or cash - bank = get_default_bank_cash_account(doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment", mode_of_payment), + bank = get_default_bank_cash_account(doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account) if not bank: - bank = get_default_bank_cash_account(doc.company, "Cash", mode_of_payment=doc.get("mode_of_payment", mode_of_payment), + bank = get_default_bank_cash_account(doc.company, "Cash", mode_of_payment=doc.get("mode_of_payment"), account=bank_account) paid_amount = received_amount = 0 @@ -1050,7 +1050,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= pe.company = doc.company pe.cost_center = doc.get("cost_center") pe.posting_date = nowdate() - pe.mode_of_payment = doc.get("mode_of_payment", mode_of_payment) + pe.mode_of_payment = doc.get("mode_of_payment") pe.party_type = party_type pe.party = doc.get(scrub(party_type)) pe.contact_person = doc.get("contact_person") diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js index e1e43140c01..5c7218608d2 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.js +++ b/erpnext/accounts/doctype/payment_request/payment_request.js @@ -25,7 +25,7 @@ frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){ }) frappe.ui.form.on("Payment Request", "refresh", function(frm) { - if(frm.doc.payment_request_type == 'Inward' && + if(frm.doc.payment_request_type == 'Inward' && frm.doc.payment_channel === "Phone" && !in_list(["Initiated", "Paid"], frm.doc.status) && !frm.doc.__islocal && frm.doc.docstatus==1){ frm.add_custom_button(__('Resend Payment Email'), function(){ frappe.call({ diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index d01f2984458..ebe8cb1330e 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -86,6 +86,7 @@ class PaymentRequest(Document): payment_record = dict( reference_doctype="Payment Request", reference_docname=self.name, + payment_reference=self.reference_name, grand_total=self.grand_total, sender=self.email_to, currency=self.currency, @@ -154,10 +155,14 @@ class PaymentRequest(Document): }) def set_as_paid(self): - payment_entry = self.create_payment_entry() - self.make_invoice() + if self.payment_channel == "Phone": + self.db_set("status", "Paid") - return payment_entry + else: + payment_entry = self.create_payment_entry() + self.make_invoice() + + return payment_entry def create_payment_entry(self, submit=True): """create entry""" @@ -269,7 +274,7 @@ class PaymentRequest(Document): # if shopping cart enabled and in session if (shopping_cart_settings.enabled and hasattr(frappe.local, "session") - and frappe.local.session.user != "Guest") and self.payment_channel != "Phone": + and frappe.local.session.user != "Guest") and self.payment_channel != "Phone": success_url = shopping_cart_settings.payment_success_url if success_url: @@ -332,6 +337,7 @@ def make_payment_request(**args): "payment_request_type": args.get("payment_request_type"), "currency": ref_doc.currency, "grand_total": grand_total, + "mode_of_payment": args.mode_of_payment, "email_to": args.recipient_id or ref_doc.owner, "subject": _("Payment Request for {0}").format(args.dn), "message": gateway_account.get("message") or get_dummy_message(ref_doc), diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 73cf1188e23..1a304ca3a88 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -58,6 +58,7 @@ class POSInvoice(SalesInvoice): against_psi_doc.make_loyalty_point_entry() if self.redeem_loyalty_points and self.loyalty_points: self.apply_loyalty_points() + self.check_phone_payments() self.set_status(update=True) def on_cancel(self): @@ -70,6 +71,18 @@ class POSInvoice(SalesInvoice): against_psi_doc.delete_loyalty_point_entry() against_psi_doc.make_loyalty_point_entry() + def check_phone_payments(self): + for pay in self.payments: + if pay.type == "Phone" and pay.amount >= 0: + paid_amt = frappe.db.get_value("Payment Request", + filters=dict( + reference_doctype="POS Invoice", reference_name=self.name, + mode_of_payment=pay.mode_of_payment, status="Paid"), + fieldname="grand_total") + + if pay.amount != paid_amt: + return frappe.throw(_("Payment related to {0} is not completed").format(pay.mode_of_payment)) + def validate_stock_availablility(self): allow_negative_stock = frappe.db.get_value('Stock Settings', None, 'allow_negative_stock') @@ -333,6 +346,7 @@ class POSInvoice(SalesInvoice): "payment_request_type": "Inward", "party_type": "Customer", "party": self.customer, + "mode_of_payment": pay.mode_of_payment, "recipient_id": self.contact_mobile, "submit_doc": True } diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py index 8fc05c65b1e..6c36c16b619 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py @@ -76,7 +76,7 @@ def generate_stk_push(**kwargs): response = connector.stk_push(business_shortcode=mpesa_settings.till_number, passcode=mpesa_settings.get_password("online_passkey"), amount=args.grand_total, - callback_url=callback_url, reference_code=args.payment_request_name, + callback_url=callback_url, reference_code=mpesa_settings.till_number, phone_number=args.sender, description="POS Payment") return response @@ -100,14 +100,15 @@ def verify_transaction(**kwargs): transaction_data = frappe._dict(loads(request.data)) if transaction_response['ResultCode'] == 0: - if transaction_data.reference_doctype and transaction_data.reference_docname: + if request.reference_doctype and request.reference_docname: try: - doc = frappe.get_doc(transaction_data.reference_doctype, - transaction_data.reference_docname).run_method("on_payment_authorized", 'Completed') + doc = frappe.get_doc(request.reference_doctype, + request.reference_docname) + doc.run_method("on_payment_authorized", 'Completed') item_response = transaction_response["CallbackMetadata"]["Item"] mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name") - frappe.db.set_value("POS Invoice", doc.reference_docname, "mpesa_receipt_number", mpesa_receipt) + frappe.db.set_value("POS Invoice", doc.reference_name, "mpesa_receipt_number", mpesa_receipt) request.process_response('output', transaction_response) except Exception: request.process_response('error', transaction_response) @@ -116,8 +117,8 @@ def verify_transaction(**kwargs): else: request.process_response('error', transaction_response) - 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) + frappe.publish_realtime('process_phone_payment', doctype="POS Invoice", + docname=transaction_data.payment_reference, user=request.owner, message=transaction_response) def get_account_balance(request_payload): """Call account balance API to send the request to the Mpesa Servers.""" diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 915564c0290..b1f7de00db9 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -9,8 +9,8 @@ erpnext.PointOfSale.Payment = class { } init_component() { - this.prepare_dom(); - this.initialize_numpad(); + this.prepare_dom(); + this.initialize_numpad(); this.bind_events(); this.attach_shortcuts(); @@ -18,32 +18,32 @@ erpnext.PointOfSale.Payment = class { prepare_dom() { this.wrapper.append( - `
+ `
PAYMENT METHOD
-
-
-
-
-
-
-
-
-
-
- Complete Order +
+
+
+
+
+
+
+
+
+
+ Complete Order
-
-
-
-
` - ) - this.$component = this.wrapper.find('.payment-section'); + + + +
` + ) + this.$component = this.wrapper.find('.payment-section'); this.$payment_modes = this.$component.find('.payment-modes'); this.$totals_remarks = this.$component.find('.totals-remarks'); this.$totals = this.$component.find('.totals'); @@ -174,16 +174,16 @@ erpnext.PointOfSale.Payment = class { } }) - frappe.realtime.on("process_phone_payments", function(data) { + frappe.realtime.on("process_phone_payment", function(data) { + console.log('within') frappe.dom.unfreeze(); let message = data["ResultDesc"]; let title = __("Payment Failed"); - const frm = me.events.get_frm(); if (data["ResultCode"] == 0) { title = __("Payment Received"); - $('[data-fieldname=request_for_payment]').text("Paid") - cur_pos.submit() + $('.btn.btn-xs.btn-default[data-fieldname=request_for_payment]').html(`Payment Received`) + me.events.submit_invoice(); } frappe.msgprint({ @@ -527,5 +527,5 @@ erpnext.PointOfSale.Payment = class { toggle_component(show) { show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none'); - } + } } \ No newline at end of file