From 6c0e695d02f6d5e72732410f7188f04102b0631f Mon Sep 17 00:00:00 2001 From: Mohammed Irfan Date: Mon, 27 Nov 2023 15:55:41 +0530 Subject: [PATCH] feat: Advance Refund --- .../healthcare/doctype/patient/patient.js | 105 +++++++++++++++ .../healthcare/doctype/patient/patient.py | 124 +++++++++++++++++- 2 files changed, 228 insertions(+), 1 deletion(-) diff --git a/healthcare/healthcare/doctype/patient/patient.js b/healthcare/healthcare/doctype/patient/patient.js index ab78fca03c..8459459f9c 100644 --- a/healthcare/healthcare/doctype/patient/patient.js +++ b/healthcare/healthcare/doctype/patient/patient.js @@ -48,6 +48,9 @@ frappe.ui.form.on('Patient', { }, __('Create')); frm.toggle_enable(['customer'], 0); } + frm.add_custom_button(__('Advance Refund'), function() { + get_advance_refund(frm); + }, __('Create')); frappe.contacts.render_address_and_contact(frm); erpnext.utils.set_party_dashboard_indicators(frm); } else { @@ -145,3 +148,105 @@ let invoice_registration = function (frm) { } }); }; + + +let get_advance_refund = function (frm) { + + const fields = [ + { + label: 'Company', + fieldname: 'company', + fieldtype: 'Link', + options: 'Company', + default: frappe.defaults.get_user_default('Company') + }, + { + label: 'Advance Amount', + fieldname: 'advance_amount', + fieldtype: 'Currency', + read_only: true, + }, + { + label: 'Mode of Payment', + fieldname: 'mode_of_payment', + fieldtype: 'Link', + options: 'Mode of Payment', + reqd: 1 + }, + { + label: 'Refund Amount', + fieldname: 'refund_amount', + fieldtype: 'Currency', + } + ]; + + let d = new frappe.ui.Dialog ({ + title: "Advance Refund", + fields: fields, + primary_action_label: "Refund", + primary_action: function(values) { + if (values.refund_amount <= values.advance_amount) { + frappe.call({ + doc: frm.doc, + method: 'advance_refund', + args: { + company: values.company, + party : frm.doc.customer, + mode_of_payment : values.mode_of_payment, + refund_amount : values.refund_amount + }, + callback: function(response) { + if (response.message) { + frappe.msgprint(__("Advance refund successful")) + } else { + frappe.msgprint(__("Advance refund failed")) + } + d.hide(); + } + }) + } else { + frappe.throw(__("Refund amount cannot be greater than Advance amount")) + } + } + }); + + get_advance_amount(frm, d); + + d.fields_dict['company'].df.onchange = () => { + get_advance_amount(frm, d) + } + +} + +let get_advance_amount = function(frm, d) { + let advanceAmount = 0; + + frappe.db.get_list('Payment Entry', { + filters: { + 'company' : d.get_value('company'), + 'party': frm.doc.customer, + 'docstatus' : ['!=', 2], + 'unallocated_amount': ['>', 0] + }, + fields: ['unallocated_amount'] + }).then((res) => { + res.forEach((value) => { + advanceAmount += value.unallocated_amount; + }); + + d.set_values({ + 'advance_amount': advanceAmount + }); + + if (advanceAmount == 0) { + frappe.msgprint(__("There is no Advance amount to Refund")) + } else { + d.show(); + } + + }) + .catch((error) => { + console.error(error); + frappe.msgprint(__('Error fetching data from Payment Entry')); + }); +} \ No newline at end of file diff --git a/healthcare/healthcare/doctype/patient/patient.py b/healthcare/healthcare/doctype/patient/patient.py index d5507dfcd0..a9d023ba6e 100644 --- a/healthcare/healthcare/doctype/patient/patient.py +++ b/healthcare/healthcare/doctype/patient/patient.py @@ -11,11 +11,17 @@ from frappe.contacts.doctype.contact.contact import get_default_contact from frappe.model.document import Document from frappe.model.naming import set_name_by_naming_series -from frappe.utils import cint, cstr, getdate +from frappe.utils import cint, cstr, flt, getdate from frappe.utils.nestedset import get_root_of +import erpnext from erpnext import get_default_currency +from erpnext.accounts.doctype.journal_entry.journal_entry import ( + get_account_balance_and_party_type, + get_party_account_and_balance, +) from erpnext.accounts.party import get_dashboard_info +from erpnext.accounts.utils import reconcile_against_document from erpnext.selling.doctype.customer.customer import make_address from healthcare.healthcare.doctype.healthcare_settings.healthcare_settings import ( @@ -293,6 +299,122 @@ def update_patient_based_on_existing_customer(self): ) self.notify_update() + @frappe.whitelist() + def advance_refund(self, company, party, mode_of_payment, refund_amount): + new_je = self.create_journal_entry(company, party, mode_of_payment, refund_amount) + + filters = { + "company": company, + "party": party, + "docstatus": ["!=", 2], + "unallocated_amount": [">", 0], + } + + payment_entries = frappe.get_all("Payment Entry", filters=filters, pluck="name") + for pe in payment_entries: + get_pe = frappe.get_doc("Payment Entry", pe) + + entry_list = [] + + payment_details = self.get_payment_details(company, party, new_je, get_pe, refund_amount) + + entry_list.append(payment_details) + + reconcile_against_document(entry_list, skip_ref_details_update_for_pe=False) + + return {"message": "success"} + + def create_journal_entry(self, company, party, mode_of_payment, refund_amount): + + paid_to = frappe.get_cached_value( + "Mode of Payment Account", {"parent": mode_of_payment, "company": company}, "default_account" + ) + + paid_from = frappe.get_cached_value("Company", company, "default_receivable_account") + + party_type = "Customer" + + journal_entry = frappe.new_doc("Journal Entry") + + journal_entry.voucher_type = "Journal Entry" + journal_entry.posting_date = frappe.utils.nowdate() + + account_info = get_party_account_and_balance(company, party_type, party, cost_center=None) + + journal_entry.append( + "accounts", + { + "account": paid_from, + "party_type": party_type, + "party": party, + "debit_in_account_currency": refund_amount, + "credit_in_account_currency": 0, + "balance": account_info["balance"], + "party_balance": account_info["party_balance"], + }, + ) + + account_info_2 = get_account_balance_and_party_type( + account=paid_to, date=journal_entry.posting_date, company=company + ) + + journal_entry.append( + "accounts", + { + "account": paid_to, + "party_type": "", + "party": "", + "debit_in_account_currency": 0, + "credit_in_account_currency": refund_amount, + "balance": account_info_2["balance"], + }, + ) + + journal_entry.save() + journal_entry.submit() + + return journal_entry + + def get_payment_details(self, company, party, new_je, get_pe, refund_amount): + + defaultAccount = frappe.get_cached_value("Company", company, "default_receivable_account") + + dr_or_cr = ( + "credit_in_account_currency" + if erpnext.get_party_account_type(get_pe.party_type) == "Receivable" + else "debit_in_account_currency" + ) + + if get_pe.unallocated_amount > refund_amount: + allocated_amount = refund_amount + else: + allocated_amount = get_pe.unallocated_amount + + difference_account = frappe.get_cached_value("Company", company, "exchange_gain_loss_account") + + return frappe._dict( + { + "voucher_type": "Payment Entry", + "voucher_no": get_pe.name, + "voucher_detail_no": None, + "against_voucher_type": "Journal Entry", + "against_voucher": new_je.name, + "account": defaultAccount, + "exchange_rate": 0, + "party_type": "Customer", + "party": party, + "is_advance": None, + "dr_or_cr": dr_or_cr, + "unreconciled_amount": flt(get_pe.unallocated_amount), + "unadjusted_amount": flt(get_pe.unallocated_amount), + "allocated_amount": flt(allocated_amount), + "difference_amount": 0.0, + "difference_account": difference_account, + "difference_posting_date": frappe.utils.nowdate(), + "cost_center": None, + } + ) + def create_customer(doc): customer = frappe.get_doc(