From 7f1b2de74d989cbaa49d05eb1e1548271bd24092 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 26 Apr 2021 22:23:48 +0530 Subject: [PATCH] feat: Employee Referral (#24997) * feat: Employee referal design and status sync * feat: create job Applicant and linked Document * feat: Added list view indicator * chore: formatted file * feat: Addedd Employee Referral form Dashboard * feat: pay compensation via additional salary * test: Employee Rreferral * Update erpnext/hr/doctype/employee_advance/employee_advance.js Co-authored-by: Rucha Mahabal * fix: changes requested * fix: changes requested * fix: sider * fix: translation * fix: test * feat: added to Dashboard * fix: changes * chore: clean-up Employee Referral form - fix labels - fix full name field - fix failing test - set title for Employee Referral form - set fields in List View and Standard Filter * feat: option to add a resume link - copy resume and resume link from Employee Referral to Job Applicant * fix: multiple fixes - confirm before rejecting employee referral - set primary actions for custom buttons - Validations for additional salary against employee referrals with status as accepted only - fix list view indicators - code formatting style * feat: Add field to track Referral Bonus Payment Status - fix visibility of additional salary button Co-authored-by: Rucha Mahabal --- .../employee_advance/employee_advance.js | 53 ++-- .../employee_advance_dashboard.py | 8 +- .../hr/doctype/employee_referral/__init__.py | 0 .../employee_referral/employee_referral.js | 68 ++++ .../employee_referral/employee_referral.json | 294 ++++++++++++++++++ .../employee_referral/employee_referral.py | 71 +++++ .../employee_referral_dashboard.py | 15 + .../employee_referral_list.js | 14 + .../test_employee_referral.py | 60 ++++ .../doctype/job_applicant/job_applicant.json | 10 +- .../hr/doctype/job_applicant/job_applicant.py | 11 + erpnext/hr/workspace/hr/hr.json | 11 +- .../additional_salary/additional_salary.py | 33 +- 13 files changed, 613 insertions(+), 35 deletions(-) create mode 100644 erpnext/hr/doctype/employee_referral/__init__.py create mode 100644 erpnext/hr/doctype/employee_referral/employee_referral.js create mode 100644 erpnext/hr/doctype/employee_referral/employee_referral.json create mode 100644 erpnext/hr/doctype/employee_referral/employee_referral.py create mode 100644 erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py create mode 100644 erpnext/hr/doctype/employee_referral/employee_referral_list.js create mode 100644 erpnext/hr/doctype/employee_referral/test_employee_referral.py diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js index 5037ceb489..fa4b06aad3 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.js +++ b/erpnext/hr/doctype/employee_advance/employee_advance.js @@ -34,7 +34,7 @@ frappe.ui.form.on('Employee Advance', { }; }); - frm.set_query('salary_component', function(doc) { + frm.set_query('salary_component', function() { return { filters: { "type": "Deduction" @@ -44,48 +44,49 @@ frappe.ui.form.on('Employee Advance', { }, refresh: function(frm) { - if (frm.doc.docstatus===1 - && (flt(frm.doc.paid_amount) < flt(frm.doc.advance_amount)) - && frappe.model.can_create("Payment Entry")) { + if (frm.doc.docstatus === 1 && + (flt(frm.doc.paid_amount) < flt(frm.doc.advance_amount)) && + frappe.model.can_create("Payment Entry")) { frm.add_custom_button(__('Payment'), - function() { frm.events.make_payment_entry(frm); }, __('Create')); - } - else if ( - frm.doc.docstatus === 1 - && flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount) - && frappe.model.can_create("Expense Claim") + function () { + frm.events.make_payment_entry(frm); + }, __('Create')); + } else if ( + frm.doc.docstatus === 1 && + flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount) && + frappe.model.can_create("Expense Claim") ) { frm.add_custom_button( __("Expense Claim"), - function() { + function () { frm.events.make_expense_claim(frm); }, __('Create') ); } - if (frm.doc.docstatus === 1 - && (flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) && flt(frm.doc.paid_amount) != flt(frm.doc.return_amount))) { + if (frm.doc.docstatus === 1 && + (flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) && flt(frm.doc.paid_amount) != flt(frm.doc.return_amount))) { - if (frm.doc.repay_unclaimed_amount_from_salary == 0 && frappe.model.can_create("Journal Entry")){ - frm.add_custom_button(__("Return"), function() { + if (frm.doc.repay_unclaimed_amount_from_salary == 0 && frappe.model.can_create("Journal Entry")) { + frm.add_custom_button(__("Return"), function() { frm.trigger('make_return_entry'); }, __('Create')); - }else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")){ - frm.add_custom_button(__("Deduction from salary"), function() { + } else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")) { + frm.add_custom_button(__("Deduction from salary"), function() { frm.events.make_deduction_via_additional_salary(frm); }, __('Create')); } } }, - make_deduction_via_additional_salary: function(frm){ + make_deduction_via_additional_salary: function(frm) { frappe.call({ method: "erpnext.hr.doctype.employee_advance.employee_advance.create_return_through_additional_salary", args: { doc: frm.doc }, - callback: function (r){ + callback: function(r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); } @@ -94,7 +95,7 @@ frappe.ui.form.on('Employee Advance', { make_payment_entry: function(frm) { var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry"; - if(frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) { + if (frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) { method = "erpnext.hr.doctype.employee_advance.employee_advance.make_bank_entry"; } return frappe.call({ @@ -148,11 +149,11 @@ frappe.ui.form.on('Employee Advance', { }); }, - employee: function (frm) { + employee: function(frm) { if (frm.doc.employee) { frappe.run_serially([ - () => frm.trigger('get_employee_currency'), - () => frm.trigger('get_pending_amount') + () => frm.trigger('get_employee_currency'), + () => frm.trigger('get_pending_amount') ]); } }, @@ -199,7 +200,7 @@ frappe.ui.form.on('Employee Advance', { } else { frm.set_value("exchange_rate", 1.0); frm.set_df_property('exchange_rate', 'hidden', 1); - frm.set_df_property("exchange_rate", "description", "" ); + frm.set_df_property("exchange_rate", "description", ""); } frm.refresh_fields(); } @@ -215,8 +216,8 @@ frappe.ui.form.on('Employee Advance', { callback: function(r) { frm.set_value("exchange_rate", flt(r.message)); frm.set_df_property('exchange_rate', 'hidden', 0); - frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency - + " = [?] " + company_currency); + frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + + " = [?] " + company_currency); } }); } diff --git a/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py b/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py index c3b4a3a889..2f493e2d4e 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py @@ -4,10 +4,10 @@ from frappe import _ def get_data(): return { 'fieldname': 'employee_advance', - 'non_standard_fieldnames': { - 'Payment Entry': 'reference_name', - 'Journal Entry': 'reference_name' - }, + 'non_standard_fieldnames': { + 'Payment Entry': 'reference_name', + 'Journal Entry': 'reference_name' + }, 'transactions': [ { 'items': ['Expense Claim'] diff --git a/erpnext/hr/doctype/employee_referral/__init__.py b/erpnext/hr/doctype/employee_referral/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.js b/erpnext/hr/doctype/employee_referral/employee_referral.js new file mode 100644 index 0000000000..9c99bbbefa --- /dev/null +++ b/erpnext/hr/doctype/employee_referral/employee_referral.js @@ -0,0 +1,68 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Employee Referral", { + refresh: function(frm) { + if (frm.doc.docstatus === 1 && frm.doc.status === "Pending") { + frm.add_custom_button(__("Reject Employee Referral"), function() { + frappe.confirm( + __("Are you sure you want to reject the Employee Referral?"), + function() { + frm.doc.status = "Rejected"; + frm.dirty(); + frm.save_or_update(); + }, + function() { + window.close(); + } + ); + }); + + frm.add_custom_button(__("Create Job Applicant"), function() { + frm.events.create_job_applicant(frm); + }).addClass("btn-primary"); + } + + // To check whether Payment is done or not + if (frm.doc.docstatus === 1 && frm.doc.status === "Accepted") { + frappe.db.get_list("Additional Salary", { + filters: { + ref_docname: cur_frm.doc.name, + docstatus: 1 + }, + fields: ["count(name) as additional_salary_count"] + }).then((data) => { + + let additional_salary_count = data[0].additional_salary_count; + + if (frm.doc.is_applicable_for_referral_bonus && !additional_salary_count) { + frm.add_custom_button(__("Create Additional Salary"), function() { + frm.events.create_additional_salary(frm); + }).addClass("btn-primary"); + } + }); + } + + + + }, + create_job_applicant: function(frm) { + frappe.model.open_mapped_doc({ + method: "erpnext.hr.doctype.employee_referral.employee_referral.create_job_applicant", + frm: frm + }); + }, + + create_additional_salary: function(frm) { + frappe.call({ + method: "erpnext.hr.doctype.employee_referral.employee_referral.create_additional_salary", + args: { + doc: frm.doc + }, + callback: function (r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); + }, +}); diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.json b/erpnext/hr/doctype/employee_referral/employee_referral.json new file mode 100644 index 0000000000..bfd404b435 --- /dev/null +++ b/erpnext/hr/doctype/employee_referral/employee_referral.json @@ -0,0 +1,294 @@ +{ + "actions": [], + "autoname": "format:HR-REF-{####}", + "creation": "2021-03-23 14:54:45.047051", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "first_name", + "last_name", + "full_name", + "email", + "contact_no", + "resume", + "resume_link", + "column_break_6", + "date", + "status", + "for_designation", + "current_employer", + "current_job_title", + "referrer_details_section", + "referrer", + "referrer_name", + "column_break_14", + "is_applicable_for_referral_bonus", + "referral_payment_status", + "department", + "additional_information_section", + "qualification_reason", + "work_references", + "amended_from" + ], + "fields": [ + { + "fieldname": "first_name", + "fieldtype": "Data", + "label": "First Name ", + "reqd": 1 + }, + { + "fieldname": "last_name", + "fieldtype": "Data", + "label": "Last Name", + "reqd": 1 + }, + { + "fieldname": "full_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Full Name", + "read_only": 1 + }, + { + "fieldname": "contact_no", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "Contact No.", + "options": "Phone" + }, + { + "fieldname": "current_employer", + "fieldtype": "Data", + "label": "Current Employer " + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "date", + "fieldtype": "Date", + "in_standard_filter": 1, + "label": "Date", + "reqd": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "options": "Pending\nIn Process\nAccepted\nRejected", + "permlevel": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "current_job_title", + "fieldtype": "Data", + "label": "Current Job Title" + }, + { + "fieldname": "resume", + "fieldtype": "Attach", + "label": "Resume" + }, + { + "fieldname": "referrer_details_section", + "fieldtype": "Section Break", + "label": "Referrer Details" + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "additional_information_section", + "fieldtype": "Section Break", + "label": "Additional Information " + }, + { + "fieldname": "work_references", + "fieldtype": "Text Editor", + "label": "Work References" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Referral", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fieldname": "for_designation", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "For Designation ", + "options": "Designation", + "reqd": 1 + }, + { + "fieldname": "email", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Email", + "options": "Email", + "reqd": 1, + "unique": 1 + }, + { + "default": "1", + "fieldname": "is_applicable_for_referral_bonus", + "fieldtype": "Check", + "label": "Is Applicable for Referral Bonus" + }, + { + "fieldname": "qualification_reason", + "fieldtype": "Text Editor", + "label": "Why is this Candidate Qualified for this Position?" + }, + { + "fieldname": "referrer", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Referrer", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "referrer.employee_name", + "fieldname": "referrer_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Referrer Name", + "read_only": 1 + }, + { + "fieldname": "resume_link", + "fieldtype": "Data", + "label": "Resume Link" + }, + { + "fieldname": "referral_payment_status", + "fieldtype": "Select", + "label": "Referral Bonus Payment Status", + "options": "\nUnpaid\nPaid", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2021-04-26 21:21:38.094086", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Referral", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "amend": 1, + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "full_name" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.py b/erpnext/hr/doctype/employee_referral/employee_referral.py new file mode 100644 index 0000000000..45d68729ce --- /dev/null +++ b/erpnext/hr/doctype/employee_referral/employee_referral.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import get_link_to_form +from frappe.model.document import Document + +class EmployeeReferral(Document): + def validate(self): + self.set_full_name() + self.set_referral_bonus_payment_status() + + def set_full_name(self): + self.full_name = " ".join(filter(None, [self.first_name, self.last_name])) + + def set_referral_bonus_payment_status(self): + if not self.is_applicable_for_referral_bonus: + self.referral_payment_status = "" + else: + if not self.referral_payment_status: + self.referral_payment_status = "Unpaid" + + +@frappe.whitelist() +def create_job_applicant(source_name, target_doc=None): + emp_ref = frappe.get_doc("Employee Referral", source_name) + #just for Api call if some set status apart from default Status + status = emp_ref.status + if emp_ref.status in ["Pending", "In process"]: + status = "Open" + + job_applicant = frappe.new_doc("Job Applicant") + job_applicant.employee_referral = emp_ref.name + job_applicant.status = status + job_applicant.applicant_name = emp_ref.full_name + job_applicant.email_id = emp_ref.email + job_applicant.phone_number = emp_ref.contact_no + job_applicant.resume_attachment = emp_ref.resume + job_applicant.resume_link = emp_ref.resume_link + job_applicant.save() + + frappe.msgprint(_("Job Applicant {0} created successfully.").format( + get_link_to_form("Job Applicant", job_applicant.name)), + title=_("Success"), indicator="green") + + emp_ref.db_set("status", "In Process") + + return job_applicant + + +@frappe.whitelist() +def create_additional_salary(doc): + import json + from six import string_types + + if isinstance(doc, string_types): + doc = frappe._dict(json.loads(doc)) + + if not frappe.db.exists("Additional Salary", {"ref_docname": doc.name}): + additional_salary = frappe.new_doc("Additional Salary") + additional_salary.employee = doc.referrer + additional_salary.company = frappe.db.get_value("Employee", doc.referrer, "company") + additional_salary.overwrite_salary_structure_amount = 0 + additional_salary.ref_doctype = doc.doctype + additional_salary.ref_docname = doc.name + + return additional_salary + diff --git a/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py new file mode 100644 index 0000000000..afa2a1ff1f --- /dev/null +++ b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py @@ -0,0 +1,15 @@ +from __future__ import unicode_literals + +def get_data(): + return { + 'fieldname': 'employee_referral', + 'non_standard_fieldnames': { + 'Additional Salary': 'ref_docname' + }, + 'transactions': [ + { + 'items': ['Job Applicant', 'Additional Salary'] + }, + + ] + } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_referral/employee_referral_list.js b/erpnext/hr/doctype/employee_referral/employee_referral_list.js new file mode 100644 index 0000000000..7533ab635f --- /dev/null +++ b/erpnext/hr/doctype/employee_referral/employee_referral_list.js @@ -0,0 +1,14 @@ +frappe.listview_settings['Employee Referral'] = { + add_fields: ["status"], + get_indicator: function (doc) { + if (doc.status == "Pending") { + return [__(doc.status), "grey", "status,=," + doc.status]; + } else if (doc.status == "In Process") { + return [__(doc.status), "orange", "status,=," + doc.status]; + } else if (doc.status == "Accepted") { + return [__(doc.status), "green", "status,=," + doc.status]; + } else if (doc.status == "Rejected") { + return [__(doc.status), "red", "status,=," + doc.status]; + } + }, +}; \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_referral/test_employee_referral.py b/erpnext/hr/doctype/employee_referral/test_employee_referral.py new file mode 100644 index 0000000000..a674f39026 --- /dev/null +++ b/erpnext/hr/doctype/employee_referral/test_employee_referral.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +from frappe.utils import today +from erpnext.hr.doctype.designation.test_designation import create_designation +from erpnext.hr.doctype.employee_referral.employee_referral import create_job_applicant, create_additional_salary +from erpnext.hr.doctype.employee.test_employee import make_employee +import unittest + +class TestEmployeeReferral(unittest.TestCase): + def test_workflow_and_status_sync(self): + emp_ref = create_employee_referral() + + #Check Initial status + self.assertTrue(emp_ref.status, "Pending") + + job_applicant = create_job_applicant(emp_ref.name) + + + #Check status sync + emp_ref.reload() + self.assertTrue(emp_ref.status, "In Process") + + job_applicant.reload() + job_applicant.status = "Rejected" + job_applicant.save() + + emp_ref.reload() + self.assertTrue(emp_ref.status, "Rejected") + + job_applicant.reload() + job_applicant.status = "Accepted" + job_applicant.save() + + emp_ref.reload() + self.assertTrue(emp_ref.status, "Accepted") + + + # Check for Referral reference in additional salary + + add_sal = create_additional_salary(emp_ref) + self.assertTrue(add_sal.ref_docname, emp_ref.name) + + +def create_employee_referral(): + emp_ref = frappe.new_doc("Employee Referral") + emp_ref.first_name = "Mahesh" + emp_ref.last_name = "Singh" + emp_ref.email = "a@b.c" + emp_ref.date = today() + emp_ref.for_designation = create_designation().name + emp_ref.referrer = make_employee("testassetmovemp@example.com", company="_Test Company") + emp_ref.is_applicable_for_employee_referral_compensation = 1 + emp_ref.save() + emp_ref.submit() + + return emp_ref \ No newline at end of file diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.json b/erpnext/hr/doctype/job_applicant/job_applicant.json index 1360fd1890..bcea5f50d9 100644 --- a/erpnext/hr/doctype/job_applicant/job_applicant.json +++ b/erpnext/hr/doctype/job_applicant/job_applicant.json @@ -18,6 +18,7 @@ "job_title", "source", "source_name", + "employee_referral", "applicant_rating", "section_break_6", "notes", @@ -152,13 +153,20 @@ "fieldtype": "Link", "label": "Currency", "options": "Currency" + }, + { + "fieldname": "employee_referral", + "fieldtype": "Link", + "label": "Employee Referral", + "options": "Employee Referral", + "read_only": 1 } ], "icon": "fa fa-user", "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2020-09-18 12:39:02.557563", + "modified": "2021-03-24 15:51:11.117517", "modified_by": "Administrator", "module": "HR", "name": "Job Applicant", diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.py b/erpnext/hr/doctype/job_applicant/job_applicant.py index a6aef04919..0594ba395b 100644 --- a/erpnext/hr/doctype/job_applicant/job_applicant.py +++ b/erpnext/hr/doctype/job_applicant/job_applicant.py @@ -28,10 +28,21 @@ class JobApplicant(Document): if self.email_id: validate_email_address(self.email_id, True) + if self.employee_referral: + self.set_status_for_employee_referral() + if not self.applicant_name and self.email_id: guess = self.email_id.split('@')[0] self.applicant_name = ' '.join([p.capitalize() for p in guess.split('.')]) + def set_status_for_employee_referral(self): + emp_ref = frappe.get_doc("Employee Referral", self.employee_referral) + if self.status in ["Open", "Replied", "Hold"]: + emp_ref.db_set("status", "In Process") + elif self.status in ["Accepted", "Rejected"]: + emp_ref.db_set("status", self.status) + + def check_email_id_is_unique(self): if self.email_id: names = frappe.db.sql_list("""select name from `tabJob Applicant` diff --git a/erpnext/hr/workspace/hr/hr.json b/erpnext/hr/workspace/hr/hr.json index f4b56a0e17..c5201c22c9 100644 --- a/erpnext/hr/workspace/hr/hr.json +++ b/erpnext/hr/workspace/hr/hr.json @@ -520,6 +520,15 @@ "onboard": 1, "type": "Link" }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Referral", + "link_to": "Employee Referral", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, { "dependencies": "", "hidden": 0, @@ -814,7 +823,7 @@ "type": "Link" } ], - "modified": "2021-03-24 17:35:21.483297", + "modified": "2021-04-26 13:36:15.413819", "modified_by": "Administrator", "module": "HR", "name": "HR", diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py index 13b6c05e22..ebeddf97f9 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py @@ -13,12 +13,19 @@ class AdditionalSalary(Document): if self.ref_doctype == "Employee Advance" and self.ref_docname: frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", self.amount) + self.update_employee_referral() + + def on_cancel(self): + self.update_employee_referral(cancel=True) + def validate(self): self.validate_dates() self.validate_salary_structure() self.validate_recurring_additional_salary_overlap() + self.validate_employee_referral() + if self.amount < 0: - frappe.throw(_("Amount should not be less than zero.")) + frappe.throw(_("Amount should not be less than zero")) def validate_salary_structure(self): if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}): @@ -70,6 +77,27 @@ class AdditionalSalary(Document): if self.payroll_date and getdate(self.payroll_date) > getdate(relieving_date): frappe.throw(_("Payroll date can not be greater than employee's relieving date.")) + def validate_employee_referral(self): + if self.ref_doctype == "Employee Referral": + referral_details = frappe.db.get_value("Employee Referral", self.ref_docname, + ["is_applicable_for_referral_bonus", "status"], as_dict=1) + + if not referral_details.is_applicable_for_referral_bonus: + frappe.throw(_("Employee Referral {0} is not applicable for referral bonus.").format( + self.ref_docname)) + + if self.type == "Deduction": + frappe.throw(_("Earning Salary Component is required for Employee Referral Bonus.")) + + if referral_details.status != "Accepted": + frappe.throw(_("Additional Salary for referral bonus can only be created against Employee Referral with status {0}").format( + frappe.bold("Accepted"))) + + def update_employee_referral(self, cancel=False): + if self.ref_doctype == "Employee Referral": + status = "Unpaid" if cancel else "Paid" + frappe.db.set_value("Employee Referral", self.ref_docname, "referral_payment_status", status) + def get_amount(self, sal_start_date, sal_end_date): start_date = getdate(sal_start_date) end_date = getdate(sal_end_date) @@ -110,8 +138,7 @@ def get_additional_salaries(employee, start_date, end_date, component_type): for d in additional_salary_list: if d.overwrite: if d.component in components_to_overwrite: - frappe.throw(_("Multiple Additional Salaries with overwrite " - "property exist for Salary Component {0} between {1} and {2}.").format( + frappe.throw(_("Multiple Additional Salaries with overwrite property exist for Salary Component {0} between {1} and {2}.").format( frappe.bold(d.component), start_date, end_date), title=_("Error")) components_to_overwrite.append(d.component)