diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index e6639833e9a..c93e5f9d3a5 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -212,18 +212,16 @@ class PaymentEntry(AccountsController): self.validate_journal_entry() if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim", "Fees"): - if self.party_type in ("Customer", "Student"): + if self.party_type == "Customer": ref_party_account = ref_doc.debit_to + elif self.party_type == "Student": + ref_party_account = ref_doc.receivable_account elif self.party_type=="Supplier": ref_party_account = ref_doc.credit_to elif self.party_type=="Employee": ref_party_account = ref_doc.payable_account - if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"): - ref_party_account = ref_doc.debit_to \ - if self.party_type in ("Customer", "Student") else ref_doc.credit_to - - if ref_party_account != self.party_account: + if ref_party_account != self.party_account: frappe.throw(_("{0} {1} is associated with {2}, but Party Account is {3}") .format(d.reference_doctype, d.reference_name, ref_party_account, self.party_account)) @@ -695,10 +693,12 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= party_type = "Student" # party account - if dt in ("Sales Invoice", "Fees"): + if dt == "Sales Invoice": party_account = doc.debit_to elif dt == "Purchase Invoice": party_account = doc.credit_to + elif dt == "Fees": + party_account = doc.receivable_account else: party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company) diff --git a/erpnext/schools/doctype/fee_component/fee_component.json b/erpnext/schools/doctype/fee_component/fee_component.json index da659fa511f..ccf1f659db6 100644 --- a/erpnext/schools/doctype/fee_component/fee_component.json +++ b/erpnext/schools/doctype/fee_component/fee_component.json @@ -45,6 +45,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -107,36 +137,6 @@ "set_only_once": 0, "unique": 0, "width": "300px" - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 } ], "has_web_view": 0, @@ -150,7 +150,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-07-18 15:03:18.945746", + "modified": "2017-09-11 16:48:07.810959", "modified_by": "Administrator", "module": "Schools", "name": "Fee Component", diff --git a/erpnext/schools/doctype/fee_schedule/fee_schedule.js b/erpnext/schools/doctype/fee_schedule/fee_schedule.js index 757355da449..5eba62de85d 100644 --- a/erpnext/schools/doctype/fee_schedule/fee_schedule.js +++ b/erpnext/schools/doctype/fee_schedule/fee_schedule.js @@ -3,10 +3,39 @@ frappe.ui.form.on('Fee Schedule', { setup: function(frm) { - frm.add_fetch("fee_structure", "default_receivable_account", "debit_to"); - frm.add_fetch("fee_structure", "default_income_account", "against_income_account"); + frm.add_fetch("fee_structure", "receivable_account", "receivable_account"); + frm.add_fetch("fee_structure", "income_account", "income_account"); frm.add_fetch("fee_structure", "cost_center", "cost_center"); + frappe.realtime.on("fee_schedule_progress", function(data) { + if (data.progress && data.progress === 0) { + frappe.msgprint(__("Fee records will be created in the background. In case of any error the error message will be updated in the Schedule.")); + } + if (data.progress) { + frm.reload_doc(); + frm.dashboard.add_progress("Fee Creation Status", data.progress); + } + }); + }, + onload: function(frm) { + frm.set_query("receivable_account", function(doc) { + return { + filters: { + 'account_type': 'Receivable', + 'is_group': 0, + 'company': doc.company + } + }; + }); + frm.set_query("income_account", function(doc) { + return { + filters: { + 'account_type': 'Income Account', + 'is_group': 0, + 'company': doc.company + } + }; + }); frm.set_query("student_group", "student_groups", function() { return { "program": frm.doc.program, @@ -24,7 +53,8 @@ frappe.ui.form.on('Fee Schedule', { frm.dashboard.add_indicator(__('Total Outstanding: {0}', [format_currency(info.total_unpaid, info.currency)]), info.total_unpaid ? 'orange' : 'green'); } - if (!frm.doc.fee_creation_status || frm.doc.fee_creation_status == "Failed") { + + if (!frm.doc.__islocal && !frm.doc.fee_creation_status || frm.doc.fee_creation_status == "Failed") { frm.add_custom_button(__('Create Fees'), function() { frappe.call({ method: "create_fees", diff --git a/erpnext/schools/doctype/fee_schedule/fee_schedule.json b/erpnext/schools/doctype/fee_schedule/fee_schedule.json index 7215603adc1..8dd51f792c0 100644 --- a/erpnext/schools/doctype/fee_schedule/fee_schedule.json +++ b/erpnext/schools/doctype/fee_schedule/fee_schedule.json @@ -136,6 +136,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "send_email", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Send Email", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -749,7 +779,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "debit_to", + "fieldname": "receivable_account", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -758,7 +788,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Academic Receivable Account", + "label": "Receivable Account", "length": 0, "no_copy": 0, "options": "Account", @@ -780,7 +810,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "against_income_account", + "fieldname": "income_account", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -999,7 +1029,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-09-07 12:36:02.678355", + "modified": "2017-09-11 16:57:27.427777", "modified_by": "Administrator", "module": "Schools", "name": "Fee Schedule", diff --git a/erpnext/schools/doctype/fee_schedule/fee_schedule.py b/erpnext/schools/doctype/fee_schedule/fee_schedule.py index 822a62dc8e8..1b2299ce3bc 100644 --- a/erpnext/schools/doctype/fee_schedule/fee_schedule.py +++ b/erpnext/schools/doctype/fee_schedule/fee_schedule.py @@ -42,14 +42,15 @@ class FeeSchedule(Document): def create_fees(self): if not self.fee_creation_status or self.fee_creation_status == "Failed": self.fee_creation_status = "In Process" + frappe.publish_realtime("fee_schedule_progress", {"progress": 0, "reload": True}, user=frappe.session.user) enqueue(generate_fee, queue='default', timeout=6000, event='generate_fee', fee_schedule=self.name) - frappe.msgprint(_("Fee records will be created in the background. In case of any error, the error message will be updated in the Schedule, check after refresh in 5 minutes.")) - def generate_fee(fee_schedule): doc = frappe.get_doc("Fee Schedule", fee_schedule) error = False + total_records = sum([int(d.total_students) for d in doc.student_groups]) + created_records = 0 for d in doc.student_groups: try: students = frappe.db.sql(""" select sg.program, sg.batch, sgs.student, sgs.student_name @@ -74,6 +75,9 @@ def generate_fee(fee_schedule): doc.send_payment_request = 1 doc.save() doc.submit() + created_records += 1 + frappe.publish_realtime("fee_schedule_progress", {"progress": created_records}, user=frappe.session.user) + except Exception as e: error = True err_msg = frappe.local.message_log and "\n\n".join(frappe.local.message_log) or cstr(e) diff --git a/erpnext/schools/doctype/fee_structure/fee_structure.js b/erpnext/schools/doctype/fee_structure/fee_structure.js index 78b85887a87..300bdc869f3 100644 --- a/erpnext/schools/doctype/fee_structure/fee_structure.js +++ b/erpnext/schools/doctype/fee_structure/fee_structure.js @@ -1,13 +1,15 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -cur_frm.add_fetch("company", "default_receivable_account", "debit_to"); -cur_frm.add_fetch("company", "default_income_account", "against_income_account"); -cur_frm.add_fetch("company", "cost_center", "cost_center"); - frappe.ui.form.on('Fee Structure', { + setup: function(frm) { + frm.add_fetch("company", "default_receivable_account", "receivable_account"); + frm.add_fetch("company", "default_income_account", "income_account"); + frm.add_fetch("company", "cost_center", "cost_center"); + }, + onload: function(frm) { - frm.set_query("debit_to", function(doc) { + frm.set_query("receivable_account", function(doc) { return { filters: { 'account_type': 'Receivable', @@ -16,6 +18,15 @@ frappe.ui.form.on('Fee Structure', { } }; }); + frm.set_query("income_account", function(doc) { + return { + filters: { + 'account_type': 'Income Account', + 'is_group': 0, + 'company': doc.company + } + }; + }); }, refresh: function(frm) { diff --git a/erpnext/schools/doctype/fee_structure/fee_structure.json b/erpnext/schools/doctype/fee_structure/fee_structure.json index c3df7bb67a7..d93a667bd30 100644 --- a/erpnext/schools/doctype/fee_structure/fee_structure.json +++ b/erpnext/schools/doctype/fee_structure/fee_structure.json @@ -202,37 +202,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "disabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -418,7 +387,7 @@ "collapsible": 0, "columns": 0, "default": "", - "fieldname": "debit_to", + "fieldname": "receivable_account", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -427,7 +396,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Academic Receivable Account", + "label": "Receivable Account", "length": 0, "no_copy": 0, "options": "Account", @@ -449,7 +418,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "against_income_account", + "fieldname": "income_account", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -608,7 +577,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-09-07 12:04:14.807690", + "modified": "2017-09-11 15:18:27.975666", "modified_by": "Administrator", "module": "Schools", "name": "Fee Structure", diff --git a/erpnext/schools/doctype/fees/fees.js b/erpnext/schools/doctype/fees/fees.js index 5bb130bf71a..45d30d45838 100644 --- a/erpnext/schools/doctype/fees/fees.js +++ b/erpnext/schools/doctype/fees/fees.js @@ -6,8 +6,8 @@ frappe.ui.form.on("Fees", { setup: function(frm) { frm.add_fetch("student", "title", "student_name"); frm.add_fetch("student", "student_email_id", "student_email"); - frm.add_fetch("fee_structure", "debit_to", "debit_to"); - frm.add_fetch("fee_structure", "against_income_account", "against_income_account"); + frm.add_fetch("fee_structure", "receivable_account", "receivable_account"); + frm.add_fetch("fee_structure", "income_account", "income_account"); frm.add_fetch("fee_structure", "cost_center", "cost_center"); }, @@ -19,7 +19,6 @@ frappe.ui.form.on("Fees", { } }; }); - frm.set_query("fee_structure",function(){ return{ "filters":{ @@ -27,9 +26,7 @@ frappe.ui.form.on("Fees", { } }; }); - - // debit account for booking the fee - frm.set_query("debit_to", function(doc) { + frm.set_query("receivable_account", function(doc) { return { filters: { 'account_type': 'Receivable', @@ -38,7 +35,15 @@ frappe.ui.form.on("Fees", { } }; }); - + frm.set_query("income_account", function(doc) { + return { + filters: { + 'account_type': 'Income Account', + 'is_group': 0, + 'company': doc.company + } + }; + }); if (!frm.doc.posting_date) { frm.doc.posting_date = frappe.datetime.get_today(); } @@ -98,7 +103,7 @@ frappe.ui.form.on("Fees", { }, make_payment_request: function(frm) { - if (!frm.doc.contact_email) { + if (!frm.doc.student_email) { frappe.msgprint(__("Please set the Email ID for the Student to send the Payment Request")); } else { frappe.call({ @@ -106,7 +111,7 @@ frappe.ui.form.on("Fees", { args: { "dt": frm.doc.doctype, "dn": frm.doc.name, - "recipient_id": frm.doc.contact_email + "recipient_id": frm.doc.student_email }, callback: function(r) { if(!r.exc){ @@ -136,23 +141,6 @@ frappe.ui.form.on("Fees", { frm.refresh(); }, - program: function(frm) { - if (frm.doc.program && frm.doc.academic_term) { - frappe.call({ - method: "erpnext.schools.api.get_fee_structure", - args: { - "program": frm.doc.program, - "academic_term": frm.doc.academic_term - }, - callback: function(r) { - if(r.message) { - frm.set_value("fee_structure" ,r.message); - } - } - }); - } - }, - academic_term: function() { frappe.ui.form.trigger("Fees", "program"); }, diff --git a/erpnext/schools/doctype/fees/fees.json b/erpnext/schools/doctype/fees/fees.json index b01b5d0637b..8286048ce27 100644 --- a/erpnext/schools/doctype/fees/fees.json +++ b/erpnext/schools/doctype/fees/fees.json @@ -1146,7 +1146,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "debit_to", + "fieldname": "receivable_account", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -1155,7 +1155,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Academic Receivable Account", + "label": "Receivable Account", "length": 0, "no_copy": 0, "options": "Account", @@ -1177,7 +1177,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "against_income_account", + "fieldname": "income_account", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -1274,7 +1274,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-08-23 14:12:58.772381", + "modified": "2017-09-11 16:04:35.725204", "modified_by": "Administrator", "module": "Schools", "name": "Fees", diff --git a/erpnext/schools/doctype/fees/fees.py b/erpnext/schools/doctype/fees/fees.py index ac5564e300b..2ce115b4661 100644 --- a/erpnext/schools/doctype/fees/fees.py +++ b/erpnext/schools/doctype/fees/fees.py @@ -11,6 +11,7 @@ from erpnext.accounts.doctype.payment_request.payment_request import make_paymen from frappe.utils.csvutils import getlink from erpnext.controllers.accounts_controller import AccountsController from erpnext.accounts.general_ledger import delete_gl_entries +from erpnext.schools.api import get_student_guardians class Fees(AccountsController): @@ -32,15 +33,27 @@ class Fees(AccountsController): self.company = frappe.defaults.get_defaults().company if not self.currency: self.currency = frappe.defaults.get_defaults().currency - if not (self.debit_to and self.against_income_account and self.cost_center): + if not (self.receivable_account and self.income_account and self.cost_center): accounts_details = frappe.get_all("Company", fields=["default_receivable_account", "default_income_account", "cost_center"], filters={"name": self.company})[0] - if not self.debit_to: - self.debit_to = accounts_details.default_receivable_account - if not self.against_income_account: - self.against_income_account = accounts_details.default_income_account + if not self.receivable_account: + self.receivable_account = accounts_details.default_receivable_account + if not self.income_account: + self.income_account = accounts_details.default_income_account if not self.cost_center: self.cost_center = accounts_details.cost_center + if not self.student_email: + self.student_email = self.get_student_emails() + + def get_student_emails(self): + guardians = get_student_guardians(self.student) + email_list = [] + for guardian in guardians: + email = frappe.db.get_value("Guardian", guardian.guardian, "email_address") + if email: + email_list.append(email) + return ", ".join(email_list) + def calculate_total(self): """Calculates total amount.""" @@ -55,7 +68,7 @@ class Fees(AccountsController): self.make_gl_entries() if self.send_payment_request and self.student_email: - pr = make_payment_request(dt="Fees", dn=self.name, recipient_id=self.contact_email, + pr = make_payment_request(dt="Fees", dn=self.name, recipient_id=self.student_email, submit_doc=True, use_dummy_message=True) frappe.msgprint(_("Payment request {0} created").format(getlink("Payment Request", pr.name))) @@ -68,17 +81,17 @@ class Fees(AccountsController): if not self.grand_total: return student_gl_entries = self.get_gl_dict({ - "account": self.debit_to, + "account": self.receivable_account, "party_type": "Student", "party": self.student, - "against": self.against_income_account, + "against": self.income_account, "debit": self.grand_total, "debit_in_account_currency": self.grand_total, "against_voucher": self.name, "against_voucher_type": self.doctype }) fee_gl_entry = self.get_gl_dict({ - "account": self.against_income_account, + "account": self.income_account, "against": self.student, "credit": self.grand_total, "credit_in_account_currency": self.grand_total,