Skip to content

Commit

Permalink
fix: Add Salary Slip creation from Timesheet
Browse files Browse the repository at this point in the history
- added `doctype_js` hook and script to show salary slip button in Timesheet

- Added `PayrollTimesheet` class to override Timesheet for Payslip status

- `make_salary_slip` renamed to `make_salary_slip_from_timesheet` and moved to `salary_slip.py`

- test salary slip creation from Timesheet
  • Loading branch information
ruchamahabal committed Jun 16, 2022
1 parent 881c332 commit 5956f2d
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 31 deletions.
22 changes: 9 additions & 13 deletions hrms/hooks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from . import __version__ as app_version

app_name = "hrms"
app_title = "HRMS"
app_publisher = "Frappe Technologies Pvt. Ltd."
Expand Down Expand Up @@ -30,7 +28,7 @@
# page_js = {"page" : "public/js/file.js"}

# include js in doctype views
# doctype_js = {"doctype" : "public/js/doctype.js"}
doctype_js = {"Timesheet": "public/js/timesheet.js"}
# doctype_list_js = {"doctype" : "public/js/doctype_list.js"}
# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"}
# doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"}
Expand All @@ -43,7 +41,7 @@

# website user home page (by Role)
# role_home_page = {
# "Role": "home_page"
# "Role": "home_page"
# }

# Generators
Expand Down Expand Up @@ -94,15 +92,15 @@
# "Event": "frappe.desk.doctype.event.event.has_permission",
# }

has_upload_permission = {"Employee": "erpnext.setup.doctype.employee.employee.has_upload_permission"}
has_upload_permission = {
"Employee": "erpnext.setup.doctype.employee.employee.has_upload_permission"
}

# DocType Class
# ---------------
# Override standard doctype classes

# override_doctype_class = {
# "ToDo": "custom_app.overrides.CustomToDo"
# }
override_doctype_class = {"Timesheet": "hrms.overrides.payroll_timesheet.PayrollTimesheet"}

# Document Events
# ---------------
Expand All @@ -113,9 +111,7 @@
"validate": "erpnext.setup.doctype.employee.employee.validate_employee_role",
"on_update": "erpnext.setup.doctype.employee.employee.update_user_permissions",
},
"Timesheet": {
"validate": "hrms.hr.utils.validate_active_employee"
},
"Timesheet": {"validate": "hrms.hr.utils.validate_active_employee"},
"Payment Entry": {
"on_submit": "hrms.hr.doctype.expense_claim.expense_claim.update_payment_for_expense_claim",
"on_cancel": "hrms.hr.doctype.expense_claim.expense_claim.update_payment_for_expense_claim",
Expand All @@ -126,8 +122,8 @@
"on_cancel": [
"hrms.hr.doctype.expense_claim.expense_claim.update_payment_for_expense_claim",
"hrms.payroll.doctype.salary_slip.salary_slip.unlink_ref_doc_from_salary_slip",
]
}
],
},
}

# Scheduled Tasks
Expand Down
18 changes: 18 additions & 0 deletions hrms/overrides/payroll_timesheet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt

from erpnext.projects.doctype.timesheet.timesheet import Timesheet


class PayrollTimesheet(Timesheet):
def set_status(self):
self.status = {"0": "Draft", "1": "Submitted", "2": "Cancelled"}[str(self.docstatus or 0)]

if self.per_billed == 100:
self.status = "Billed"

if self.salary_slip:
self.status = "Payslip"

if self.sales_invoice and self.salary_slip:
self.status = "Completed"
3 changes: 0 additions & 3 deletions hrms/payroll/doctype/salary_slip/salary_slip.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

cur_frm.add_fetch('employee', 'company', 'company');
cur_frm.add_fetch('time_sheet', 'total_hours', 'working_hours');

frappe.ui.form.on("Salary Slip", {
setup: function(frm) {
$.each(["earnings", "deductions"], function(i, table_fieldname) {
Expand Down
4 changes: 3 additions & 1 deletion hrms/payroll/doctype/salary_slip/salary_slip.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@
"read_only": 1
},
{
"fetch_from": "employee.company",
"fieldname": "company",
"fieldtype": "Link",
"in_list_view": 1,
Expand Down Expand Up @@ -277,6 +278,7 @@
"fieldtype": "Column Break"
},
{
"fetch_from": "timesheet.total_hours",
"fieldname": "total_working_hours",
"fieldtype": "Float",
"label": "Total Working Hours",
Expand Down Expand Up @@ -637,7 +639,7 @@
"idx": 9,
"is_submittable": 1,
"links": [],
"modified": "2022-01-19 12:45:54.999345",
"modified": "2022-06-15 18:36:54.999345",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Slip",
Expand Down
27 changes: 24 additions & 3 deletions hrms/payroll/doctype/salary_slip/salary_slip.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@

import erpnext
from erpnext.accounts.utils import get_fiscal_year
from hrms.hr.utils import get_holiday_dates_for_employee, validate_active_employee
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
calculate_amounts,
create_repayment_entry,
)
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
process_loan_interest_accrual_for_term_loans,
)
from erpnext.utilities.transaction_base import TransactionBase

from hrms.hr.utils import get_holiday_dates_for_employee, validate_active_employee
from hrms.payroll.doctype.additional_salary.additional_salary import get_additional_salaries
from hrms.payroll.doctype.employee_benefit_application.employee_benefit_application import (
get_benefit_component_amount,
Expand All @@ -45,8 +47,6 @@
get_payroll_period,
get_period_factor,
)
from erpnext.utilities.transaction_base import TransactionBase


class SalarySlip(TransactionBase):
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -1763,3 +1763,24 @@ def get_lwp_or_ppl_for_date(date, employee, holidays):
query = query.where((LeaveType.include_holiday == "1"))

return query.run(as_dict=True)


@frappe.whitelist()
def make_salary_slip_from_timesheet(source_name, target_doc=None):
target = frappe.new_doc("Salary Slip")
set_missing_values(source_name, target)
target.run_method("get_emp_and_working_day_details")

return target


def set_missing_values(time_sheet, target):
doc = frappe.get_doc("Timesheet", time_sheet)
target.employee = doc.employee
target.employee_name = doc.employee_name
target.salary_slip_based_on_timesheet = 1
target.start_date = doc.start_date
target.end_date = doc.end_date
target.posting_date = doc.modified
target.total_working_hours = doc.total_hours
target.append("timesheets", {"time_sheet": doc.name, "working_hours": doc.total_hours})
57 changes: 49 additions & 8 deletions hrms/payroll/doctype/salary_slip/test_salary_slip.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
create_payroll_period,
)
from hrms.payroll.doctype.payroll_entry.payroll_entry import get_month_details
from hrms.payroll.doctype.salary_slip.salary_slip import make_salary_slip_from_timesheet
from hrms.payroll.doctype.salary_structure.salary_structure import make_salary_slip


Expand Down Expand Up @@ -350,13 +351,7 @@ def test_payment_days_based_on_leave_application(self):

@change_settings("Payroll Settings", {"payroll_based_on": "Attendance"})
def test_payment_days_in_salary_slip_based_on_timesheet(self):
from erpnext.projects.doctype.timesheet.test_timesheet import (
make_salary_structure_for_timesheet,
make_timesheet,
)
from erpnext.projects.doctype.timesheet.timesheet import (
make_salary_slip as make_salary_slip_for_timesheet,
)
from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet

from hrms.hr.doctype.attendance.attendance import mark_attendance

Expand Down Expand Up @@ -388,7 +383,7 @@ def test_payment_days_in_salary_slip_based_on_timesheet(self):
# salary structure based on timesheet
make_salary_structure_for_timesheet(emp)
timesheet = make_timesheet(emp, simulate=True, is_billable=1)
salary_slip = make_salary_slip_for_timesheet(timesheet.name)
salary_slip = make_salary_slip_from_timesheet(timesheet.name)
salary_slip.start_date = month_start_date
salary_slip.end_date = month_end_date
salary_slip.save()
Expand Down Expand Up @@ -1007,6 +1002,27 @@ def test_tax_for_recurring_additional_salary(self):

frappe.db.rollback()

def test_salary_slip_from_timesheet(self):
emp = make_employee("test_employee_6@salary.com", company="_Test Company")
salary_structure = make_salary_structure_for_timesheet(emp)
timesheet = make_timesheet(emp, simulate=True, is_billable=1)
salary_slip = make_salary_slip_from_timesheet(timesheet.name)
salary_slip.submit()

self.assertEqual(salary_slip.total_working_hours, 2)
self.assertEqual(salary_slip.hour_rate, 50)
self.assertEqual(salary_slip.earnings[0].salary_component, "Timesheet Component")
self.assertEqual(salary_slip.earnings[0].amount, 100)
self.assertEqual(salary_slip.timesheets[0].time_sheet, timesheet.name)
self.assertEqual(salary_slip.timesheets[0].working_hours, 2)

timesheet = frappe.get_doc("Timesheet", timesheet.name)
self.assertEqual(timesheet.status, "Payslip")
salary_slip.cancel()

timesheet = frappe.get_doc("Timesheet", timesheet.name)
self.assertEqual(timesheet.status, "Submitted")

def make_activity_for_employee(self):
activity_type = frappe.get_doc("Activity Type", "_Test Activity Type")
activity_type.billing_rate = 50
Expand Down Expand Up @@ -1584,3 +1600,28 @@ def create_recurring_additional_salary(
"currency": erpnext.get_default_currency(),
}
).submit()


def make_salary_structure_for_timesheet(employee, company=None):
salary_structure_name = "Timesheet Salary Structure Test"
frequency = "Monthly"

if not frappe.db.exists("Salary Component", "Timesheet Component"):
frappe.get_doc(
{"doctype": "Salary Component", "salary_component": "Timesheet Component"}
).insert()

salary_structure = make_salary_structure(
salary_structure_name, frequency, company=company, dont_submit=True
)
salary_structure.salary_component = "Timesheet Component"
salary_structure.salary_slip_based_on_timesheet = 1
salary_structure.hour_rate = 50.0
salary_structure.save()
salary_structure.submit()

if not frappe.db.get_value("Salary Structure Assignment", {"employee": employee, "docstatus": 1}):
frappe.db.set_value("Employee", employee, "date_of_joining", add_months(nowdate(), -5))
create_salary_structure_assignment(employee, salary_structure.name)

return salary_structure
19 changes: 19 additions & 0 deletions hrms/public/js/timesheet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

frappe.ui.form.on("Timesheet", {
refresh(frm) {
if (frm.doc.docstatus == 1) {
if (!frm.doc.salary_slip && frm.doc.employee) {
frm.add_custom_button(__("Create Salary Slip"), function() {
frm.trigger("make_salary_slip");
});
}
}
},

make_salary_slip: function(frm) {
frappe.model.open_mapped_doc({
method: "hrms.payroll.doctype.salary_slip.salary_slip.make_salary_slip_from_timesheet",
frm: frm
});
},
});
18 changes: 15 additions & 3 deletions hrms/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def get_custom_fields():
"options": "Job Applicant",
"insert_after": "employment_details",
},
{
{
"fieldname": "grade",
"fieldtype": "Link",
"label": "Grade",
Expand Down Expand Up @@ -120,5 +120,17 @@ def get_custom_fields():
"options": "Department Approver",
"insert_after": "leave_approvers",
},
]
}
],
"Timesheet": [
{
"fieldname": "salary_slip",
"fieldtype": "Link",
"label": "Salary Slip",
"no_copy": 1,
"options": "Salary Slip",
"print_hide": 1,
"read_only": 1,
"insert_after": "column_break_3",
},
],
}

0 comments on commit 5956f2d

Please sign in to comment.