Browse Source

fix: employee status server-side validation (#26615)

develop
Rucha Mahabal 3 years ago
committed by GitHub
parent
commit
017ed3f5c1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      erpnext/hr/doctype/appraisal/appraisal.py
  2. 2
      erpnext/hr/doctype/attendance/attendance.py
  3. 3
      erpnext/hr/doctype/attendance_request/attendance_request.py
  4. 3
      erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
  5. 8
      erpnext/hr/doctype/employee/employee.py
  6. 30
      erpnext/hr/doctype/employee/test_employee.py
  7. 6
      erpnext/hr/doctype/employee_advance/employee_advance.py
  8. 4
      erpnext/hr/doctype/employee_checkin/employee_checkin.py
  9. 5
      erpnext/hr/doctype/employee_promotion/employee_promotion.py
  10. 2
      erpnext/hr/doctype/employee_referral/employee_referral.py
  11. 4
      erpnext/hr/doctype/employee_transfer/employee_transfer.py
  12. 3
      erpnext/hr/doctype/expense_claim/expense_claim.py
  13. 3
      erpnext/hr/doctype/leave_application/leave_application.py
  14. 3
      erpnext/hr/doctype/leave_encashment/leave_encashment.py
  15. 2
      erpnext/hr/doctype/shift_assignment/shift_assignment.py
  16. 3
      erpnext/hr/doctype/shift_request/shift_request.py
  17. 4
      erpnext/hr/doctype/travel_request/travel_request.py
  18. 11
      erpnext/hr/utils.py
  19. 2
      erpnext/payroll/doctype/additional_salary/additional_salary.py
  20. 3
      erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
  21. 3
      erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
  22. 2
      erpnext/payroll/doctype/employee_incentive/employee_incentive.py
  23. 3
      erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py
  24. 3
      erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py
  25. 5
      erpnext/payroll/doctype/retention_bonus/retention_bonus.py
  26. 2
      erpnext/payroll/doctype/salary_slip/salary_slip.py
  27. 3
      erpnext/projects/doctype/timesheet/timesheet.py

3
erpnext/hr/doctype/appraisal/appraisal.py

@ -9,7 +9,7 @@ from frappe.utils import flt, getdate
from frappe import _
from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document
from erpnext.hr.utils import set_employee_name
from erpnext.hr.utils import set_employee_name, validate_active_employee
class Appraisal(Document):
def validate(self):
@ -19,6 +19,7 @@ class Appraisal(Document):
if not self.goals:
frappe.throw(_("Goals cannot be empty"))
validate_active_employee(self.employee)
set_employee_name(self)
self.validate_dates()
self.validate_existing_appraisal()

2
erpnext/hr/doctype/attendance/attendance.py

@ -8,11 +8,13 @@ from frappe.utils import getdate, nowdate
from frappe import _
from frappe.model.document import Document
from frappe.utils import cstr, get_datetime, formatdate
from erpnext.hr.utils import validate_active_employee
class Attendance(Document):
def validate(self):
from erpnext.controllers.status_updater import validate_status
validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
validate_active_employee(self.employee)
self.validate_attendance_date()
self.validate_duplicate_record()
self.validate_employee_status()

3
erpnext/hr/doctype/attendance_request/attendance_request.py

@ -8,10 +8,11 @@ from frappe import _
from frappe.model.document import Document
from frappe.utils import date_diff, add_days, getdate
from erpnext.hr.doctype.employee.employee import is_holiday
from erpnext.hr.utils import validate_dates
from erpnext.hr.utils import validate_dates, validate_active_employee
class AttendanceRequest(Document):
def validate(self):
validate_active_employee(self.employee)
validate_dates(self, self.from_date, self.to_date)
if self.half_day:
if not getdate(self.from_date)<=getdate(self.half_day_date)<=getdate(self.to_date):

3
erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py

@ -7,12 +7,13 @@ import frappe
from frappe import _
from frappe.utils import date_diff, add_days, getdate, cint, format_date
from frappe.model.document import Document
from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, \
from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, validate_active_employee, \
get_holidays_for_employee, create_additional_leave_ledger_entry
class CompensatoryLeaveRequest(Document):
def validate(self):
validate_active_employee(self.employee)
validate_dates(self, self.work_from_date, self.work_end_date)
if self.half_day:
if not self.half_day_date:

8
erpnext/hr/doctype/employee/employee.py

@ -13,8 +13,10 @@ from frappe.model.document import Document
from erpnext.utilities.transaction_base import delete_events
from frappe.utils.nestedset import NestedSet
class EmployeeUserDisabledError(frappe.ValidationError): pass
class EmployeeLeftValidationError(frappe.ValidationError): pass
class EmployeeUserDisabledError(frappe.ValidationError):
pass
class InactiveEmployeeStatusError(frappe.ValidationError):
pass
class Employee(NestedSet):
nsm_parent_field = 'reports_to'
@ -196,7 +198,7 @@ class Employee(NestedSet):
message += "<br><br><ul><li>" + "</li><li>".join(link_to_employees)
message += "</li></ul><br>"
message += _("Please make sure the employees above report to another Active employee.")
throw(message, EmployeeLeftValidationError, _("Cannot Relieve Employee"))
throw(message, InactiveEmployeeStatusError, _("Cannot Relieve Employee"))
if not self.relieving_date:
throw(_("Please enter relieving date."))

30
erpnext/hr/doctype/employee/test_employee.py

@ -7,7 +7,7 @@ import frappe
import erpnext
import unittest
import frappe.utils
from erpnext.hr.doctype.employee.employee import EmployeeLeftValidationError
from erpnext.hr.doctype.employee.employee import InactiveEmployeeStatusError
test_records = frappe.get_test_records('Employee')
@ -45,10 +45,33 @@ class TestEmployee(unittest.TestCase):
employee2_doc.save()
employee1_doc.reload()
employee1_doc.status = 'Left'
self.assertRaises(EmployeeLeftValidationError, employee1_doc.save)
self.assertRaises(InactiveEmployeeStatusError, employee1_doc.save)
def test_employee_status_inactive(self):
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
employee = make_employee("test_employee_status@company.com")
employee_doc = frappe.get_doc("Employee", employee)
employee_doc.status = "Inactive"
employee_doc.save()
employee_doc.reload()
make_holiday_list()
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
frappe.db.sql("""delete from `tabSalary Structure` where name='Test Inactive Employee Salary Slip'""")
salary_structure = make_salary_structure("Test Inactive Employee Salary Slip", "Monthly",
employee=employee_doc.name, company=employee_doc.company)
salary_slip = make_salary_slip(salary_structure.name, employee=employee_doc.name)
self.assertRaises(InactiveEmployeeStatusError, salary_slip.save)
def tearDown(self):
frappe.db.rollback()
def make_employee(user, company=None, **kwargs):
""
if not frappe.db.get_value("User", user):
frappe.get_doc({
"doctype": "User",
@ -80,4 +103,5 @@ def make_employee(user, company=None, **kwargs):
employee.insert()
return employee.name
else:
frappe.db.set_value("Employee", {"employee_name":user}, "status", "Active")
return frappe.get_value("Employee", {"employee_name":user}, "name")

6
erpnext/hr/doctype/employee_advance/employee_advance.py

@ -8,6 +8,7 @@ from frappe import _
from frappe.model.document import Document
from frappe.utils import flt, nowdate
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
from erpnext.hr.utils import validate_active_employee
class EmployeeAdvanceOverPayment(frappe.ValidationError):
pass
@ -18,6 +19,7 @@ class EmployeeAdvance(Document):
'make_payment_via_journal_entry')
def validate(self):
validate_active_employee(self.employee)
self.set_status()
def on_cancel(self):
@ -183,9 +185,9 @@ def make_return_entry(employee, company, employee_advance_name, return_amount,
bank_cash_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment)
if not bank_cash_account:
frappe.throw(_("Please set a Default Cash Account in Company defaults"))
advance_account_currency = frappe.db.get_value('Account', advance_account, 'account_currency')
je = frappe.new_doc('Journal Entry')
je.posting_date = nowdate()
je.voucher_type = get_voucher_type(mode_of_payment)

4
erpnext/hr/doctype/employee_checkin/employee_checkin.py

@ -9,9 +9,11 @@ from frappe.model.document import Document
from frappe import _
from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift
from erpnext.hr.utils import validate_active_employee
class EmployeeCheckin(Document):
def validate(self):
validate_active_employee(self.employee)
self.validate_duplicate_log()
self.fetch_shift()
@ -122,7 +124,7 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki
def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type):
"""Given a set of logs in chronological order calculates the total working hours based on the parameters.
Zero is returned for all invalid cases.
:param logs: The List of 'Employee Checkin'.
:param check_in_out_type: One of: 'Alternating entries as IN and OUT during the same shift', 'Strictly based on Log Type in Employee Checkin'
:param working_hours_calc_type: One of: 'First Check-in and Last Check-out', 'Every Valid Check-in and Check-out'

5
erpnext/hr/doctype/employee_promotion/employee_promotion.py

@ -7,12 +7,11 @@ import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import getdate
from erpnext.hr.utils import update_employee
from erpnext.hr.utils import update_employee, validate_active_employee
class EmployeePromotion(Document):
def validate(self):
if frappe.get_value("Employee", self.employee, "status") != "Active":
frappe.throw(_("Cannot promote Employee with status Left or Inactive"))
validate_active_employee(self.employee)
def before_submit(self):
if getdate(self.promotion_date) > getdate():

2
erpnext/hr/doctype/employee_referral/employee_referral.py

@ -7,9 +7,11 @@ import frappe
from frappe import _
from frappe.utils import get_link_to_form
from frappe.model.document import Document
from erpnext.hr.utils import validate_active_employee
class EmployeeReferral(Document):
def validate(self):
validate_active_employee(self.referrer)
self.set_full_name()
self.set_referral_bonus_payment_status()

4
erpnext/hr/doctype/employee_transfer/employee_transfer.py

@ -10,10 +10,6 @@ from frappe.utils import getdate
from erpnext.hr.utils import update_employee
class EmployeeTransfer(Document):
def validate(self):
if frappe.get_value("Employee", self.employee, "status") != "Active":
frappe.throw(_("Cannot transfer Employee with status Left or Inactive"))
def before_submit(self):
if getdate(self.transfer_date) > getdate():
frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date"),

3
erpnext/hr/doctype/expense_claim/expense_claim.py

@ -6,7 +6,7 @@ import frappe, erpnext
from frappe import _
from frappe.utils import get_fullname, flt, cstr, get_link_to_form
from frappe.model.document import Document
from erpnext.hr.utils import set_employee_name, share_doc_with_approver
from erpnext.hr.utils import set_employee_name, share_doc_with_approver, validate_active_employee
from erpnext.accounts.party import get_party_account
from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
@ -23,6 +23,7 @@ class ExpenseClaim(AccountsController):
'make_payment_via_journal_entry')
def validate(self):
validate_active_employee(self.employee)
self.validate_advances()
self.validate_sanctioned_amount()
self.calculate_total_amount()

3
erpnext/hr/doctype/leave_application/leave_application.py

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, get_fullname, add_days, nowdate
from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver
from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver, validate_active_employee
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange
@ -22,6 +22,7 @@ class LeaveApplication(Document):
return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type)
def validate(self):
validate_active_employee(self.employee)
set_employee_name(self)
self.validate_dates()
self.validate_balance_leaves()

3
erpnext/hr/doctype/leave_encashment/leave_encashment.py

@ -7,7 +7,7 @@ import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import getdate, nowdate, flt
from erpnext.hr.utils import set_employee_name
from erpnext.hr.utils import set_employee_name, validate_active_employee
from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry
from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves
@ -15,6 +15,7 @@ from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leav
class LeaveEncashment(Document):
def validate(self):
set_employee_name(self)
validate_active_employee(self.employee)
self.get_leave_details_for_encashment()
self.validate_salary_structure()

2
erpnext/hr/doctype/shift_assignment/shift_assignment.py

@ -9,10 +9,12 @@ from frappe.model.document import Document
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, now_datetime, nowdate
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
from erpnext.hr.utils import validate_active_employee
from datetime import timedelta, datetime
class ShiftAssignment(Document):
def validate(self):
validate_active_employee(self.employee)
self.validate_overlapping_dates()
if self.end_date and self.end_date <= self.start_date:

3
erpnext/hr/doctype/shift_request/shift_request.py

@ -7,12 +7,13 @@ import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import formatdate, getdate
from erpnext.hr.utils import share_doc_with_approver
from erpnext.hr.utils import share_doc_with_approver, validate_active_employee
class OverlapError(frappe.ValidationError): pass
class ShiftRequest(Document):
def validate(self):
validate_active_employee(self.employee)
self.validate_dates()
self.validate_shift_request_overlap_dates()
self.validate_approver()

4
erpnext/hr/doctype/travel_request/travel_request.py

@ -5,6 +5,8 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from erpnext.hr.utils import validate_active_employee
class TravelRequest(Document):
pass
def validate(self):
validate_active_employee(self.employee)

11
erpnext/hr/utils.py

@ -3,13 +3,12 @@
import erpnext
import frappe
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee, InactiveEmployeeStatusError
from frappe import _
from frappe.desk.form import assign_to
from frappe.model.document import Document
from frappe.utils import (add_days, cstr, flt, format_datetime, formatdate,
get_datetime, getdate, nowdate, today, unique)
get_datetime, getdate, nowdate, today, unique, get_link_to_form)
class DuplicateDeclarationError(frappe.ValidationError): pass
@ -20,6 +19,7 @@ class EmployeeBoardingController(Document):
Assign to the concerned person and roles as per the onboarding/separation template
'''
def validate(self):
validate_active_employee(self.employee)
# remove the task if linked before submitting the form
if self.amended_from:
for activity in self.activities:
@ -522,3 +522,8 @@ def share_doc_with_approver(doc, user):
approver = approvers.get(doc.doctype)
if doc_before_save.get(approver) != doc.get(approver):
frappe.share.remove(doc.doctype, doc.name, doc_before_save.get(approver))
def validate_active_employee(employee):
if frappe.db.get_value("Employee", employee, "status") == "Inactive":
frappe.throw(_("Transactions cannot be created for an Inactive Employee {0}.").format(
get_link_to_form("Employee", employee)), InactiveEmployeeStatusError)

2
erpnext/payroll/doctype/additional_salary/additional_salary.py

@ -7,6 +7,7 @@ import frappe
from frappe.model.document import Document
from frappe import _, bold
from frappe.utils import getdate, date_diff, comma_and, formatdate
from erpnext.hr.utils import validate_active_employee
class AdditionalSalary(Document):
def on_submit(self):
@ -19,6 +20,7 @@ class AdditionalSalary(Document):
self.update_employee_referral(cancel=True)
def validate(self):
validate_active_employee(self.employee)
self.validate_dates()
self.validate_salary_structure()
self.validate_recurring_additional_salary_overlap()

3
erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py

@ -9,10 +9,11 @@ from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt
from frappe.model.document import Document
from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor
from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount
from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount, validate_active_employee
class EmployeeBenefitApplication(Document):
def validate(self):
validate_active_employee(self.employee)
self.validate_duplicate_on_payroll_period()
if not self.max_benefits:
self.max_benefits = get_max_benefits_remaining(self.employee, self.date, self.payroll_period)

3
erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py

@ -8,12 +8,13 @@ from frappe import _
from frappe.utils import flt
from frappe.model.document import Document
from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits
from erpnext.hr.utils import get_previous_claimed_amount
from erpnext.hr.utils import get_previous_claimed_amount, validate_active_employee
from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period
from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
class EmployeeBenefitClaim(Document):
def validate(self):
validate_active_employee(self.employee)
max_benefits = get_max_benefits(self.employee, self.claim_date)
if not max_benefits or max_benefits <= 0:
frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee))

2
erpnext/payroll/doctype/employee_incentive/employee_incentive.py

@ -6,9 +6,11 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
from erpnext.hr.utils import validate_active_employee
class EmployeeIncentive(Document):
def validate(self):
validate_active_employee(self.employee)
self.validate_salary_structure()
def validate_salary_structure(self):

3
erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py

@ -8,11 +8,12 @@ from frappe.model.document import Document
from frappe import _
from frappe.utils import flt
from frappe.model.mapper import get_mapped_doc
from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \
from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, validate_active_employee, \
calculate_annual_eligible_hra_exemption, validate_duplicate_exemption_for_payroll_period
class EmployeeTaxExemptionDeclaration(Document):
def validate(self):
validate_active_employee(self.employee)
validate_tax_declaration(self.declarations)
validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee)
self.set_total_declared_amount()

3
erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py

@ -7,11 +7,12 @@ import frappe
from frappe.model.document import Document
from frappe import _
from frappe.utils import flt
from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \
from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, validate_active_employee, \
calculate_hra_exemption_for_period, validate_duplicate_exemption_for_payroll_period
class EmployeeTaxExemptionProofSubmission(Document):
def validate(self):
validate_active_employee(self.employee)
validate_tax_declaration(self.tax_exemption_proofs)
self.set_total_actual_amount()
self.set_total_exemption_amount()

5
erpnext/payroll/doctype/retention_bonus/retention_bonus.py

@ -7,11 +7,10 @@ import frappe
from frappe.model.document import Document
from frappe import _
from frappe.utils import getdate
from erpnext.hr.utils import validate_active_employee
class RetentionBonus(Document):
def validate(self):
if frappe.get_value('Employee', self.employee, 'status') != 'Active':
frappe.throw(_('Cannot create Retention Bonus for Left or Inactive Employees'))
validate_active_employee(self.employee)
if getdate(self.bonus_payment_date) < getdate():
frappe.throw(_('Bonus Payment Date cannot be a past date'))

2
erpnext/payroll/doctype/salary_slip/salary_slip.py

@ -19,6 +19,7 @@ from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_appli
from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry
from erpnext.accounts.utils import get_fiscal_year
from erpnext.hr.utils import validate_active_employee
from six import iteritems
class SalarySlip(TransactionBase):
@ -39,6 +40,7 @@ class SalarySlip(TransactionBase):
def validate(self):
self.status = self.get_status()
validate_active_employee(self.employee)
self.validate_dates()
self.check_existing()
if not self.salary_slip_based_on_timesheet:

3
erpnext/projects/doctype/timesheet/timesheet.py

@ -15,12 +15,15 @@ from erpnext.manufacturing.doctype.workstation.workstation import (check_if_with
WorkstationHolidayError)
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
from erpnext.setup.utils import get_exchange_rate
from erpnext.hr.utils import validate_active_employee
class OverlapError(frappe.ValidationError): pass
class OverWorkLoggedError(frappe.ValidationError): pass
class Timesheet(Document):
def validate(self):
if self.employee:
validate_active_employee(self.employee)
self.set_employee_name()
self.set_status()
self.validate_dates()

Loading…
Cancel
Save