Merge pull request #5924 from KanchanChauhan/salary-structure-changes
Formula based Salary Structure
This commit is contained in:
commit
b5951b72f3
@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
|
"beta": 0,
|
||||||
|
"creation": "2016-07-27 17:24:24.956896",
|
||||||
|
"custom": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Company",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Company",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"description": "Default Bank / Cash account will be automatically updated in POS Invoice when this mode is selected.",
|
||||||
|
"fieldname": "default_account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Default Account",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Account",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
|
"idx": 0,
|
||||||
|
"image_view": 0,
|
||||||
|
"in_create": 0,
|
||||||
|
"in_dialog": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 0,
|
||||||
|
"istable": 1,
|
||||||
|
"max_attachments": 0,
|
||||||
|
"modified": "2016-07-27 17:24:24.956896",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Salary Component Account",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"quick_entry": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_seen": 0
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class SalaryComponentAccount(Document):
|
||||||
|
pass
|
BIN
erpnext/docs/assets/img/human-resources/condition-amount.png
Normal file
BIN
erpnext/docs/assets/img/human-resources/condition-amount.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
BIN
erpnext/docs/assets/img/human-resources/condition-formula.png
Normal file
BIN
erpnext/docs/assets/img/human-resources/condition-formula.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
Binary file not shown.
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 231 KiB |
BIN
erpnext/docs/assets/img/human-resources/salary-timesheet.png
Normal file
BIN
erpnext/docs/assets/img/human-resources/salary-timesheet.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
@ -42,15 +42,52 @@ To create a new Salary Structure go to:
|
|||||||
|
|
||||||
> Human Resources > Setup > Salary Structure > New Salary Structure
|
> Human Resources > Setup > Salary Structure > New Salary Structure
|
||||||
|
|
||||||
#### Figure 1:Salary Structure
|
#### Figure 1.1:Salary Structure
|
||||||
|
|
||||||
<img class="screenshot" alt="Salary Structure" src="{{docs_base_url}}/assets/img/human-resources/salary-structure.png">
|
<img class="screenshot" alt="Salary Structure" src="{{docs_base_url}}/assets/img/human-resources/salary-structure.png">
|
||||||
|
|
||||||
### In the Salary Structure,
|
### In the Salary Structure,
|
||||||
|
|
||||||
* Select the Employee
|
* Select the Employees and enter Base (which is base salary or CTC) and Variable (if applicable)
|
||||||
* Set the starting date from which this is valid (Note: There can only be one Salary Structure that can be “Active” for an Employee during any period)
|
* Set the starting date from which this is valid (Note: There can only be one Salary Structure that can be “Active” for an Employee during any period)
|
||||||
* In the “Earnings” and “Deductions” table all your defined Earning Type and Deductions Type will be auto-populated. Set the values of the Earnings and Deductions and save the Salary Structure.
|
|
||||||
|
#### Figure 1.2:Salary Structure for Salary Slip based on Timesheet
|
||||||
|
|
||||||
|
<img class="screenshot" alt="Salary Structure" src="{{docs_base_url}}/assets/img/human-resources/salary-timesheet.png">
|
||||||
|
|
||||||
|
### Salary Slip Based on Timesheet
|
||||||
|
|
||||||
|
Salary Slip based on Timesheet is applicable if you have timesheet based payroll system
|
||||||
|
|
||||||
|
* Check "Salary Slip Based on Timesheet"
|
||||||
|
* Select the salary component and enter Hour Rate (Note: This salary component gets added to earnings in Salary Slip)
|
||||||
|
|
||||||
|
### Earnings and Deductions in Salary Structure
|
||||||
|
|
||||||
|
In the “Earnings” and “Deductions” tables, you can calculate the values of Salary Components based on,
|
||||||
|
|
||||||
|
* Condition and Formula
|
||||||
|
|
||||||
|
#### Figure 1.3:Condition and Formula
|
||||||
|
|
||||||
|
<img class="screenshot" alt="Salary Structure" src="{{docs_base_url}}/assets/img/human-resources/condition-formula.png">
|
||||||
|
|
||||||
|
* Condition and Amount
|
||||||
|
|
||||||
|
#### Figure 1.4:Condition and Amount
|
||||||
|
|
||||||
|
<img class="screenshot" alt="Salary Structure" src="{{docs_base_url}}/assets/img/human-resources/condition-amount.png">
|
||||||
|
|
||||||
|
* Only Formula
|
||||||
|
* Only Amount
|
||||||
|
|
||||||
|
Save the Salary Structure.
|
||||||
|
|
||||||
|
In conditions and formulas,
|
||||||
|
|
||||||
|
* Use field "base" for using base salary of the Employee
|
||||||
|
* Use Salary Component abbreviations. For example: BS for Basic Salary
|
||||||
|
* Use field name for employee details. For example: Employment Type for employment_type
|
||||||
|
|
||||||
### Leave Without Pay (LWP)
|
### Leave Without Pay (LWP)
|
||||||
|
|
||||||
@ -64,6 +101,7 @@ days for the month (based on the Holiday List).
|
|||||||
If you don’t want ERPNext to manage LWP, just don’t click on LWP in any of the
|
If you don’t want ERPNext to manage LWP, just don’t click on LWP in any of the
|
||||||
Earning Types and Deduction Types.
|
Earning Types and Deduction Types.
|
||||||
|
|
||||||
|
|
||||||
* * *
|
* * *
|
||||||
|
|
||||||
### Creating Salary Slips
|
### Creating Salary Slips
|
||||||
@ -71,8 +109,9 @@ Earning Types and Deduction Types.
|
|||||||
Once the Salary Structure is created, you can make a salary slip from the same
|
Once the Salary Structure is created, you can make a salary slip from the same
|
||||||
form or you can process your payroll for the month using Process Payroll.
|
form or you can process your payroll for the month using Process Payroll.
|
||||||
|
|
||||||
To create a salary slip from Salary Structure, click on the button Make Salary
|
To create a new Salary Slip go to:
|
||||||
Slip.
|
|
||||||
|
> Human Resources > Setup > Salary Slip > New Salary Slip
|
||||||
|
|
||||||
#### Figure 2: Salary Slip
|
#### Figure 2: Salary Slip
|
||||||
|
|
||||||
|
@ -40,13 +40,6 @@ erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({
|
|||||||
"Ms": "Female"
|
"Ms": "Female"
|
||||||
}[this.frm.doc.salutation]);
|
}[this.frm.doc.salutation]);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
make_salary_structure: function(btn) {
|
|
||||||
frappe.model.open_mapped_doc({
|
|
||||||
method: "erpnext.hr.doctype.employee.employee.make_salary_structure",
|
|
||||||
frm: cur_frm
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cur_frm.cscript = new erpnext.hr.EmployeeController({frm: cur_frm});
|
cur_frm.cscript = new erpnext.hr.EmployeeController({frm: cur_frm});
|
||||||
|
@ -178,19 +178,6 @@ def get_retirement_date(date_of_birth=None):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def make_salary_structure(source_name, target=None):
|
|
||||||
target = get_mapped_doc("Employee", source_name, {
|
|
||||||
"Employee": {
|
|
||||||
"doctype": "Salary Structure",
|
|
||||||
"field_map": {
|
|
||||||
"name": "employee",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
target.make_earn_ded_table()
|
|
||||||
return target
|
|
||||||
|
|
||||||
def validate_employee_role(doc, method):
|
def validate_employee_role(doc, method):
|
||||||
# called via User hook
|
# called via User hook
|
||||||
if "Employee" in [d.role for d in doc.get("user_roles")]:
|
if "Employee" in [d.role for d in doc.get("user_roles")]:
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Other",
|
"document_type": "Other",
|
||||||
|
"editable_grid": 1,
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -187,6 +188,31 @@
|
|||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "max_working_hours_against_timesheet",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Max working hours against Timesheet",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
"hide_heading": 0,
|
||||||
@ -200,7 +226,7 @@
|
|||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2016-06-27 16:20:59.737869",
|
"modified": "2016-08-10 12:32:39.780599",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "HR Settings",
|
"name": "HR Settings",
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Setup",
|
"document_type": "Setup",
|
||||||
|
"editable_grid": 1,
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -35,6 +36,33 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "salary_component_abbr",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 1,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Abbr",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"print_width": "120px",
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0,
|
||||||
|
"width": "120px"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@ -59,6 +87,32 @@
|
|||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "accounts",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Accounts",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Salary Component Account",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
"hide_heading": 0,
|
||||||
@ -72,7 +126,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2016-07-01 12:42:46.103131",
|
"modified": "2016-07-27 17:40:18.335540",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Component",
|
"name": "Salary Component",
|
||||||
|
@ -7,4 +7,21 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class SalaryComponent(Document):
|
class SalaryComponent(Document):
|
||||||
pass
|
def validate(self):
|
||||||
|
self.validate_abbr()
|
||||||
|
|
||||||
|
|
||||||
|
def validate_abbr(self):
|
||||||
|
if not self.salary_component_abbr:
|
||||||
|
self.salary_component_abbr = ''.join([c[0] for c in self.salary_component.split()]).upper()
|
||||||
|
|
||||||
|
self.salary_component_abbr = self.salary_component_abbr.strip()
|
||||||
|
|
||||||
|
if self.get('__islocal') and len(self.salary_component_abbr) > 5:
|
||||||
|
frappe.throw(_("Abbreviation cannot have more than 5 characters"))
|
||||||
|
|
||||||
|
if not self.salary_component_abbr.strip():
|
||||||
|
frappe.throw(_("Abbreviation is mandatory"))
|
||||||
|
|
||||||
|
if frappe.db.sql("select salary_component_abbr from `tabSalary Component` where name!=%s and salary_component_abbr=%s", (self.name, self.salary_component_abbr)):
|
||||||
|
frappe.throw(_("Abbreviation already used for another salary component"))
|
@ -40,13 +40,171 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
|
"depends_on": "eval:doc.parenttype=='Salary Structure'",
|
||||||
|
"fieldname": "abbr",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Abbr",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "salary_component.salary_component_abbr",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "section_break_2",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"depends_on": "eval:doc.parenttype=='Salary Structure'",
|
||||||
|
"fieldname": "condition",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Condition",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"default": "1",
|
||||||
|
"depends_on": "eval:doc.parenttype=='Salary Structure'",
|
||||||
|
"fieldname": "amount_based_on_formula",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Amount based on formula",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"default": "",
|
||||||
|
"depends_on": "eval:doc.amount_based_on_formula!=0 && doc.parenttype=='Salary Structure'",
|
||||||
|
"description": "",
|
||||||
|
"fieldname": "formula",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Formula",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "section_break_8",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"depends_on": "eval:doc.amount_based_on_formula!==1 || doc.parenttype==='Salary Slip'",
|
||||||
"fieldname": "amount",
|
"fieldname": "amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 0,
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@ -66,30 +224,7 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "column_break_3",
|
"depends_on": "",
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "depends_on_lwp",
|
"fieldname": "depends_on_lwp",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -102,6 +237,33 @@
|
|||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"depends_on": "eval:doc.parenttype=='Salary Structure'",
|
||||||
|
"fieldname": "default_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Default Amount",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
@ -115,17 +277,43 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "default_amount",
|
"depends_on": "eval:doc.parenttype=='Salary Structure'",
|
||||||
"fieldtype": "Currency",
|
"fieldname": "section_break_11",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Default Amount",
|
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"options": "Company:company:default_currency",
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"depends_on": "eval:doc.parenttype=='Salary Structure'",
|
||||||
|
"fieldname": "condition_and_formula_help",
|
||||||
|
"fieldtype": "HTML",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Condition and Formula Help",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "<h3>Condition and Formula Help</h3>\n\n<p>Notes:</p>\n\n<ol>\n<li>Use field <code>base</code> for using base salary of the Employee</li>\n<li>Use Salary Component abbreviations in conditions and formulas. <code>BS = Basic Salary</code></li>\n<li>Use field name for employee details in conditions and formulas. <code>Employment Type = employment_type</code><code>Branch = branch</code></li>\n<li>Direct Amount can also be entered based on Condtion. See example 3</li></ol>\n\n<h4>Examples</h4>\n<ol>\n<li>Calculating Basic Salary based on <code>base</code>\n<pre><code>Condition: base < 10000</code></pre>\n<pre><code>Formula: base * .2</code></pre></li>\n<li>Calculating HRA based on Basic Salary<code>BS</code> \n<pre><code>Condition: BS > 2000</code></pre>\n<pre><code>Formula: BS * .1</code></pre></li>\n<li>Calculating TDS based on Employment Type<code>employment_type</code> \n<pre><code>Condition: employment_type==\"Intern\"</code></pre>\n<pre><code>Amount: 1000</code></pre></li>\n</ol>",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
@ -148,7 +336,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2016-07-11 03:28:06.925361",
|
"modified": "2016-08-18 13:01:37.617174",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Detail",
|
"name": "Salary Detail",
|
||||||
|
@ -24,7 +24,7 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frm.trigger("toggle_fields")
|
frm.trigger("toggle_fields")
|
||||||
},
|
},
|
||||||
|
|
||||||
salary_slip_based_on_timesheet: function(frm) {
|
salary_slip_based_on_timesheet: function(frm) {
|
||||||
frm.trigger("toggle_fields")
|
frm.trigger("toggle_fields")
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Setup",
|
"document_type": "Setup",
|
||||||
|
"editable_grid": 0,
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -1172,7 +1173,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2016-07-07 12:49:01.596547",
|
"modified": "2016-08-10 15:57:59.944600",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Slip",
|
"name": "Salary Slip",
|
||||||
|
@ -25,23 +25,84 @@ class SalarySlip(TransactionBase):
|
|||||||
self.set_month_dates()
|
self.set_month_dates()
|
||||||
|
|
||||||
if not (len(self.get("earnings")) or len(self.get("deductions"))):
|
if not (len(self.get("earnings")) or len(self.get("deductions"))):
|
||||||
|
# get details from salary structure
|
||||||
self.get_emp_and_leave_details()
|
self.get_emp_and_leave_details()
|
||||||
else:
|
else:
|
||||||
self.get_leave_details(lwp = self.leave_without_pay)
|
self.get_leave_details(lwp = self.leave_without_pay)
|
||||||
|
|
||||||
if self.salary_slip_based_on_timesheet or not self.net_pay:
|
# if self.salary_slip_based_on_timesheet or not self.net_pay:
|
||||||
self.calculate_net_pay()
|
# self.calculate_net_pay()
|
||||||
|
|
||||||
company_currency = get_company_currency(self.company)
|
company_currency = get_company_currency(self.company)
|
||||||
self.total_in_words = money_in_words(self.rounded_total, company_currency)
|
self.total_in_words = money_in_words(self.rounded_total, company_currency)
|
||||||
|
|
||||||
set_employee_name(self)
|
if frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet"):
|
||||||
|
max_working_hours = frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet")
|
||||||
|
if self.salary_slip_based_on_timesheet and (self.total_working_hours > int(max_working_hours)):
|
||||||
|
frappe.msgprint(_("Total working hours should not be greater than max working hours {0}").
|
||||||
|
format(max_working_hours), alert=True)
|
||||||
|
|
||||||
def validate_dates(self):
|
def validate_dates(self):
|
||||||
if date_diff(self.end_date, self.start_date) < 0:
|
if date_diff(self.end_date, self.start_date) < 0:
|
||||||
frappe.throw(_("To date cannot be before From date"))
|
frappe.throw(_("To date cannot be before From date"))
|
||||||
|
|
||||||
|
def calculate_component_amounts(self):
|
||||||
|
if not getattr(self, '_salary_structure_doc', None):
|
||||||
|
self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
|
||||||
|
|
||||||
|
data = self.get_data_for_eval()
|
||||||
|
for key in ('earnings', 'deductions'):
|
||||||
|
for d in self._salary_structure_doc.get(key):
|
||||||
|
amount = self.eval_condition_and_formula(d, data)
|
||||||
|
if amount:
|
||||||
|
self.append(key, {
|
||||||
|
'amount': amount,
|
||||||
|
'default_amount': amount,
|
||||||
|
'depends_on_lwp' : d.depends_on_lwp,
|
||||||
|
'salary_component' : d.salary_component
|
||||||
|
})
|
||||||
|
|
||||||
|
def eval_condition_and_formula(self, d, data):
|
||||||
|
try:
|
||||||
|
if d.condition:
|
||||||
|
if not eval(d.condition, None, data):
|
||||||
|
return None
|
||||||
|
|
||||||
|
amount = d.amount
|
||||||
|
if d.amount_based_on_formula:
|
||||||
|
if d.formula:
|
||||||
|
amount = eval(d.formula, None, data)
|
||||||
|
|
||||||
|
data[d.abbr] = amount
|
||||||
|
return amount
|
||||||
|
except NameError as err:
|
||||||
|
frappe.throw(_("Name error: {0}".format(err)))
|
||||||
|
except SyntaxError as err:
|
||||||
|
frappe.throw(_("Syntax error in formula or condition: {0}".format(err)))
|
||||||
|
except:
|
||||||
|
frappe.throw(_("Error in formula or condition"))
|
||||||
|
raise
|
||||||
|
|
||||||
|
def get_data_for_eval(self):
|
||||||
|
'''Returns data for evaluating formula'''
|
||||||
|
data = frappe._dict()
|
||||||
|
|
||||||
|
for d in self._salary_structure_doc.employees:
|
||||||
|
if d.employee == self.employee:
|
||||||
|
data.base, data.variable = d.base, d.variable
|
||||||
|
|
||||||
|
data.update(frappe.get_doc("Employee", self.employee).as_dict())
|
||||||
|
|
||||||
|
# set values for components
|
||||||
|
salary_components = frappe.get_all("Salary Component", fields=["salary_component_abbr"])
|
||||||
|
for salary_component in salary_components:
|
||||||
|
data[salary_component.salary_component_abbr] = 0
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_emp_and_leave_details(self):
|
def get_emp_and_leave_details(self):
|
||||||
|
'''First time, load all the components from salary structure'''
|
||||||
if self.employee:
|
if self.employee:
|
||||||
self.set("earnings", [])
|
self.set("earnings", [])
|
||||||
self.set("deductions", [])
|
self.set("deductions", [])
|
||||||
@ -55,10 +116,10 @@ class SalarySlip(TransactionBase):
|
|||||||
struct = self.check_sal_struct(joining_date, relieving_date)
|
struct = self.check_sal_struct(joining_date, relieving_date)
|
||||||
|
|
||||||
if struct:
|
if struct:
|
||||||
ss_doc = frappe.get_doc('Salary Structure', struct)
|
self._salary_structure_doc = frappe.get_doc('Salary Structure', struct)
|
||||||
self.salary_slip_based_on_timesheet = ss_doc.salary_slip_based_on_timesheet or 0
|
self.salary_slip_based_on_timesheet = self._salary_structure_doc.salary_slip_based_on_timesheet or 0
|
||||||
self.set_time_sheet()
|
self.set_time_sheet()
|
||||||
self.pull_sal_struct(ss_doc)
|
self.pull_sal_struct()
|
||||||
|
|
||||||
def set_time_sheet(self):
|
def set_time_sheet(self):
|
||||||
if self.salary_slip_based_on_timesheet:
|
if self.salary_slip_based_on_timesheet:
|
||||||
@ -79,28 +140,44 @@ class SalarySlip(TransactionBase):
|
|||||||
self.end_date = m['month_end_date']
|
self.end_date = m['month_end_date']
|
||||||
|
|
||||||
def check_sal_struct(self, joining_date, relieving_date):
|
def check_sal_struct(self, joining_date, relieving_date):
|
||||||
struct = frappe.db.sql("""select name from `tabSalary Structure`
|
st_name = frappe.db.sql("""select parent from `tabSalary Structure Employee`
|
||||||
where employee=%s and is_active = 'Yes'
|
where employee=%s order by modified desc limit 1""",self.employee)
|
||||||
and (from_date <= %s or from_date <= %s)
|
|
||||||
and (to_date is null or to_date >= %s or to_date >= %s) order by from_date desc limit 1""",
|
if st_name:
|
||||||
(self.employee, self.start_date, joining_date, self.end_date, relieving_date))
|
struct = frappe.db.sql("""select name from `tabSalary Structure`
|
||||||
|
where name=%s and is_active = 'Yes'
|
||||||
|
and (from_date <= %s or from_date <= %s)
|
||||||
|
and (to_date is null or to_date >= %s or to_date >= %s) order by from_date desc limit 1""",
|
||||||
|
(st_name, self.start_date, joining_date, self.end_date, relieving_date))
|
||||||
|
|
||||||
if not struct:
|
if not struct:
|
||||||
|
self.salary_structure = None
|
||||||
|
frappe.throw(_("No active or default Salary Structure found for employee {0} for the given dates")
|
||||||
|
.format(self.employee), title=_('Salary Structure Missing'))
|
||||||
|
|
||||||
|
return struct and struct[0][0] or ''
|
||||||
|
else:
|
||||||
self.salary_structure = None
|
self.salary_structure = None
|
||||||
frappe.throw(_("No active or default Salary Structure found for employee {0} for the given dates")
|
frappe.throw(_("No active or default Salary Structure found for employee {0} for the given dates")
|
||||||
.format(self.employee), title=_('Salary Structure Missing'))
|
.format(self.employee), title=_('Salary Structure Missing'))
|
||||||
|
|
||||||
return struct and struct[0][0] or ''
|
def pull_sal_struct(self):
|
||||||
|
|
||||||
def pull_sal_struct(self, ss_doc):
|
|
||||||
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
|
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
|
||||||
make_salary_slip(ss_doc.name, self)
|
make_salary_slip(self._salary_structure_doc.name, self)
|
||||||
|
|
||||||
if self.salary_slip_based_on_timesheet:
|
if self.salary_slip_based_on_timesheet:
|
||||||
self.salary_structure = ss_doc.name
|
self.salary_structure = self._salary_structure_doc.name
|
||||||
self.hour_rate = ss_doc.hour_rate
|
self.hour_rate = self._salary_structure_doc.hour_rate
|
||||||
self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0
|
self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0
|
||||||
self.add_earning_for_hourly_wages(ss_doc.salary_component)
|
self.add_earning_for_hourly_wages(self._salary_structure_doc.salary_component)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def process_salary_structure(self):
|
||||||
|
'''Calculate salary after salary structure details have been updated'''
|
||||||
|
self.pull_emp_details()
|
||||||
|
self.get_leave_details()
|
||||||
|
self.calculate_net_pay()
|
||||||
|
|
||||||
def add_earning_for_hourly_wages(self, salary_component):
|
def add_earning_for_hourly_wages(self, salary_component):
|
||||||
default_type = False
|
default_type = False
|
||||||
@ -121,6 +198,7 @@ class SalarySlip(TransactionBase):
|
|||||||
self.bank_name = emp.bank_name
|
self.bank_name = emp.bank_name
|
||||||
self.bank_account_no = emp.bank_ac_no
|
self.bank_account_no = emp.bank_ac_no
|
||||||
|
|
||||||
|
|
||||||
def get_leave_details(self, joining_date=None, relieving_date=None, lwp=None):
|
def get_leave_details(self, joining_date=None, relieving_date=None, lwp=None):
|
||||||
if not self.fiscal_year:
|
if not self.fiscal_year:
|
||||||
# if default fiscal year is not set, get from nowdate
|
# if default fiscal year is not set, get from nowdate
|
||||||
@ -222,35 +300,28 @@ class SalarySlip(TransactionBase):
|
|||||||
if frappe.db.get_value('Timesheet', data.time_sheet, 'status') == 'Payrolled':
|
if frappe.db.get_value('Timesheet', data.time_sheet, 'status') == 'Payrolled':
|
||||||
frappe.throw(_("Salary Slip of employee {0} already created for time sheet {1}").format(self.employee, data.time_sheet))
|
frappe.throw(_("Salary Slip of employee {0} already created for time sheet {1}").format(self.employee, data.time_sheet))
|
||||||
|
|
||||||
def calculate_earning_total(self):
|
def sum_components(self, component_type, total_field):
|
||||||
self.gross_pay = flt(self.arrear_amount) + flt(self.leave_encashment_amount)
|
for d in self.get(component_type):
|
||||||
for d in self.get("earnings"):
|
|
||||||
if cint(d.depends_on_lwp) == 1 and not self.salary_slip_based_on_timesheet:
|
|
||||||
d.amount = rounded((flt(d.default_amount) * flt(self.payment_days)
|
|
||||||
/ cint(self.total_days_in_month)), self.precision("amount", "earnings"))
|
|
||||||
elif not self.payment_days and not self.salary_slip_based_on_timesheet:
|
|
||||||
d.amount = 0
|
|
||||||
elif not d.amount:
|
|
||||||
d.amount = d.default_amount
|
|
||||||
self.gross_pay += flt(d.amount)
|
|
||||||
|
|
||||||
def calculate_ded_total(self):
|
|
||||||
self.total_deduction = 0
|
|
||||||
for d in self.get('deductions'):
|
|
||||||
if cint(d.depends_on_lwp) == 1 and not self.salary_slip_based_on_timesheet:
|
if cint(d.depends_on_lwp) == 1 and not self.salary_slip_based_on_timesheet:
|
||||||
d.amount = rounded((flt(d.amount) * flt(self.payment_days)
|
d.amount = rounded((flt(d.amount) * flt(self.payment_days)
|
||||||
/ cint(self.total_days_in_month)), self.precision("amount", "deductions"))
|
/ cint(self.total_days_in_month)), self.precision("amount", component_type))
|
||||||
elif not self.payment_days and not self.salary_slip_based_on_timesheet:
|
elif not self.payment_days and not self.salary_slip_based_on_timesheet:
|
||||||
d.amount = 0
|
d.amount = 0
|
||||||
elif not d.amount:
|
elif not d.amount:
|
||||||
d.amount = d.default_amount
|
d.amount = d.default_amount
|
||||||
self.total_deduction += flt(d.amount)
|
self.set(total_field, self.get(total_field) + flt(d.amount))
|
||||||
|
|
||||||
def calculate_net_pay(self):
|
def calculate_net_pay(self):
|
||||||
|
self.calculate_component_amounts()
|
||||||
|
|
||||||
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
|
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
|
||||||
|
|
||||||
self.calculate_earning_total()
|
self.gross_pay = flt(self.arrear_amount) + flt(self.leave_encashment_amount)
|
||||||
self.calculate_ded_total()
|
self.total_deduction = 0
|
||||||
|
|
||||||
|
self.sum_components('earnings', 'gross_pay')
|
||||||
|
self.sum_components('deductions', 'total_deduction')
|
||||||
|
|
||||||
self.net_pay = flt(self.gross_pay) - flt(self.total_deduction)
|
self.net_pay = flt(self.gross_pay) - flt(self.total_deduction)
|
||||||
self.rounded_total = rounded(self.net_pay,
|
self.rounded_total = rounded(self.net_pay,
|
||||||
self.precision("net_pay") if disable_rounded_total else 0)
|
self.precision("net_pay") if disable_rounded_total else 0)
|
||||||
|
@ -4,19 +4,22 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import today, now_datetime, getdate, cstr
|
import erpnext
|
||||||
from erpnext.hr.doctype.employee.employee import make_salary_structure
|
from frappe.utils import today, now_datetime, getdate, cstr, add_years, nowdate
|
||||||
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
|
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
|
||||||
from erpnext.hr.doctype.leave_application.test_leave_application import make_allocation_record
|
from erpnext.hr.doctype.leave_application.test_leave_application import make_allocation_record
|
||||||
|
|
||||||
class TestSalarySlip(unittest.TestCase):
|
class TestSalarySlip(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
self.make_salary_component(["Basic Salary", "Allowance", "HRA", "Professional Tax", "TDS"])
|
||||||
|
|
||||||
for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]:
|
for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]:
|
||||||
frappe.db.sql("delete from `tab%s`" % dt)
|
frappe.db.sql("delete from `tab%s`" % dt)
|
||||||
|
|
||||||
make_allocation_record(leave_type="_Test Leave Type LWP")
|
make_allocation_record(leave_type="_Test Leave Type LWP")
|
||||||
|
|
||||||
frappe.db.set_value("Company", "_Test Company", "default_holiday_list", "_Test Holiday List")
|
self.make_holiday_list()
|
||||||
|
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
|
||||||
|
|
||||||
from erpnext.hr.doctype.leave_application.test_leave_application import _test_records as leave_applications
|
from erpnext.hr.doctype.leave_application.test_leave_application import _test_records as leave_applications
|
||||||
la = frappe.copy_doc(leave_applications[2])
|
la = frappe.copy_doc(leave_applications[2])
|
||||||
@ -30,71 +33,78 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
|
|
||||||
def test_salary_slip_with_holidays_included(self):
|
def test_salary_slip_with_holidays_included(self):
|
||||||
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1)
|
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1)
|
||||||
ss = frappe.copy_doc(test_records[0])
|
self.make_employee("test_employee@salary.com")
|
||||||
ss.insert()
|
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None)
|
||||||
|
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active")
|
||||||
|
ss = frappe.get_doc("Salary Slip",
|
||||||
|
self.make_employee_salary_slip("test_employee@salary.com"))
|
||||||
|
|
||||||
self.assertEquals(ss.total_days_in_month, 31)
|
self.assertEquals(ss.total_days_in_month, 31)
|
||||||
self.assertEquals(ss.payment_days, 30)
|
self.assertEquals(ss.payment_days, 31)
|
||||||
self.assertEquals(ss.earnings[0].amount, 14516.13)
|
self.assertEquals(ss.earnings[0].amount, 0)
|
||||||
self.assertEquals(ss.earnings[1].amount, 500)
|
self.assertEquals(ss.earnings[1].amount, 0)
|
||||||
self.assertEquals(ss.deductions[0].amount, 100)
|
self.assertEquals(ss.deductions[0].amount, 0)
|
||||||
self.assertEquals(ss.deductions[1].amount, 48.39)
|
self.assertEquals(ss.deductions[1].amount, 0)
|
||||||
self.assertEquals(ss.gross_pay, 15016.13)
|
self.assertEquals(ss.gross_pay, 0)
|
||||||
self.assertEquals(ss.net_pay, 14867.74)
|
self.assertEquals(ss.net_pay, 0)
|
||||||
|
|
||||||
def test_salary_slip_with_holidays_excluded(self):
|
def test_salary_slip_with_holidays_excluded(self):
|
||||||
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
|
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
|
||||||
ss = frappe.copy_doc(test_records[0])
|
self.make_employee("test_employee@salary.com")
|
||||||
ss.insert()
|
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None)
|
||||||
|
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active")
|
||||||
self.assertEquals(ss.total_days_in_month, 29)
|
ss = frappe.get_doc("Salary Slip",
|
||||||
self.assertEquals(ss.payment_days, 28)
|
self.make_employee_salary_slip("test_employee@salary.com"))
|
||||||
self.assertEquals(ss.earnings[0].amount, 14516.13)
|
|
||||||
self.assertEquals(ss.earnings[1].amount, 500)
|
|
||||||
self.assertEquals(ss.deductions[0].amount, 100)
|
|
||||||
self.assertEquals(ss.deductions[1].amount, 48.39)
|
|
||||||
self.assertEquals(ss.gross_pay, 15016.13)
|
|
||||||
self.assertEquals(ss.net_pay, 14867.74)
|
|
||||||
|
|
||||||
|
self.assertEquals(ss.total_days_in_month, 27)
|
||||||
|
self.assertEquals(ss.payment_days, 27)
|
||||||
|
self.assertEquals(ss.earnings[0].amount, 0)
|
||||||
|
self.assertEquals(ss.earnings[0].default_amount, 5000)
|
||||||
|
self.assertEquals(ss.earnings[1].amount, 0)
|
||||||
|
self.assertEquals(ss.deductions[0].amount, 0)
|
||||||
|
self.assertEquals(ss.deductions[1].amount, 0)
|
||||||
|
self.assertEquals(ss.gross_pay, 0)
|
||||||
|
self.assertEquals(ss.net_pay, 0)
|
||||||
|
|
||||||
def test_payment_days(self):
|
def test_payment_days(self):
|
||||||
# Holidays not included in working days
|
# Holidays not included in working days
|
||||||
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
|
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
|
||||||
|
|
||||||
# set joinng date in the same month
|
# set joinng date in the same month
|
||||||
frappe.db.set_value("Employee", "_T-Employee-0001", "date_of_joining", "2013-01-11")
|
self.make_employee("test_employee@salary.com")
|
||||||
|
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "date_of_joining", "2013-01-11")
|
||||||
|
|
||||||
|
ss = frappe.get_doc("Salary Slip",
|
||||||
|
self.make_employee_salary_slip("test_employee@salary.com"))
|
||||||
|
|
||||||
ss = frappe.copy_doc(test_records[0])
|
self.assertEquals(ss.total_days_in_month, 27)
|
||||||
ss.insert()
|
self.assertEquals(ss.payment_days, 27)
|
||||||
|
|
||||||
self.assertEquals(ss.total_days_in_month, 29)
|
|
||||||
self.assertEquals(ss.payment_days, 19)
|
|
||||||
|
|
||||||
# set relieving date in the same month
|
# set relieving date in the same month
|
||||||
frappe.db.set_value("Employee", "_T-Employee-0001", "relieving_date", "2013-01-28")
|
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", "12-12-2016")
|
||||||
|
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Left")
|
||||||
|
|
||||||
|
self.assertEquals(ss.total_days_in_month, 27)
|
||||||
|
self.assertEquals(ss.payment_days, 27)
|
||||||
ss.save()
|
ss.save()
|
||||||
self.assertEquals(ss.total_days_in_month, 29)
|
|
||||||
self.assertEquals(ss.payment_days, 16)
|
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None)
|
||||||
|
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active")
|
||||||
# Holidays included in working days
|
# Holidays included in working days
|
||||||
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1)
|
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1)
|
||||||
|
self.assertEquals(ss.total_days_in_month, 27)
|
||||||
|
self.assertEquals(ss.payment_days, 27)
|
||||||
ss.save()
|
ss.save()
|
||||||
self.assertEquals(ss.total_days_in_month, 31)
|
#
|
||||||
self.assertEquals(ss.payment_days, 17)
|
# frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "date_of_joining", "2001-01-11")
|
||||||
|
# frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None)
|
||||||
frappe.db.set_value("Employee", "_T-Employee-0001", "date_of_joining", "2001-01-11")
|
|
||||||
frappe.db.set_value("Employee", "_T-Employee-0001", "relieving_date", None)
|
|
||||||
|
|
||||||
def test_employee_salary_slip_read_permission(self):
|
def test_employee_salary_slip_read_permission(self):
|
||||||
self.make_employee("test_employee@example.com")
|
self.make_employee("test_employee@salary.com")
|
||||||
self.make_employee("test_employee_2@example.com")
|
|
||||||
|
|
||||||
salary_slip_test_employee = frappe.get_doc("Salary Slip",
|
salary_slip_test_employee = frappe.get_doc("Salary Slip",
|
||||||
self.make_employee_salary_slip("test_employee@example.com"))
|
self.make_employee_salary_slip("test_employee@salary.com"))
|
||||||
|
frappe.set_user("test_employee@salary.com")
|
||||||
salary_slip_test_employee_2 = frappe.get_doc("Salary Slip",
|
|
||||||
self.make_employee_salary_slip("test_employee_2@example.com"))
|
|
||||||
|
|
||||||
frappe.set_user("test_employee@example.com")
|
|
||||||
self.assertTrue(salary_slip_test_employee.has_permission("read"))
|
self.assertTrue(salary_slip_test_employee.has_permission("read"))
|
||||||
|
|
||||||
def test_email_salary_slip(self):
|
def test_email_salary_slip(self):
|
||||||
@ -104,8 +114,10 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
hr_settings.email_salary_slip_to_employee = 1
|
hr_settings.email_salary_slip_to_employee = 1
|
||||||
hr_settings.save()
|
hr_settings.save()
|
||||||
|
|
||||||
self.make_employee("test_employee@example.com")
|
self.make_employee("test_employee@salary.com")
|
||||||
self.make_employee_salary_slip("test_employee@example.com")
|
ss = frappe.get_doc("Salary Slip",
|
||||||
|
self.make_employee_salary_slip("test_employee@salary.com"))
|
||||||
|
ss.submit()
|
||||||
email_queue = frappe.db.sql("""select name from `tabEmail Queue`""")
|
email_queue = frappe.db.sql("""select name from `tabEmail Queue`""")
|
||||||
self.assertTrue(email_queue)
|
self.assertTrue(email_queue)
|
||||||
|
|
||||||
@ -123,32 +135,52 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
if not frappe.db.get_value("Employee", {"user_id": user}):
|
if not frappe.db.get_value("Employee", {"user_id": user}):
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Employee",
|
"doctype": "Employee",
|
||||||
"naming_series": "_T-Employee-",
|
"naming_series": "EMP-",
|
||||||
"employee_name": user,
|
"employee_name": user,
|
||||||
|
"company": erpnext.get_default_company(),
|
||||||
"user_id": user,
|
"user_id": user,
|
||||||
"company": "_Test Company",
|
|
||||||
"date_of_birth": "1990-05-08",
|
"date_of_birth": "1990-05-08",
|
||||||
"date_of_joining": "2013-01-01",
|
"date_of_joining": "2013-01-01",
|
||||||
"department": "_Test Department 1",
|
"department": frappe.get_all("Department", fields="name")[0].name,
|
||||||
"gender": "Female",
|
"gender": "Female",
|
||||||
"company_email": user,
|
"company_email": user,
|
||||||
"status": "Active"
|
"status": "Active",
|
||||||
|
"employment_type": "Intern"
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
|
def make_holiday_list(self):
|
||||||
|
if not frappe.db.get_value("Holiday List", "Salary Slip Test Holiday List"):
|
||||||
|
holiday_list = frappe.get_doc({
|
||||||
|
"doctype": "Holiday List",
|
||||||
|
"holiday_list_name": "Salary Slip Test Holiday List",
|
||||||
|
"from_date": nowdate(),
|
||||||
|
"to_date": add_years(nowdate(), 1),
|
||||||
|
"weekly_off": "Sunday"
|
||||||
|
}).insert()
|
||||||
|
holiday_list.get_weekly_off_dates()
|
||||||
|
holiday_list.save()
|
||||||
|
|
||||||
|
def make_salary_component(self, salary_components):
|
||||||
|
for salary_component in salary_components:
|
||||||
|
if not frappe.db.exists('Salary Component', salary_component):
|
||||||
|
sal_comp = frappe.get_doc({
|
||||||
|
"doctype": "Salary Component",
|
||||||
|
"salary_component": salary_component
|
||||||
|
})
|
||||||
|
sal_comp.insert()
|
||||||
|
|
||||||
def make_employee_salary_slip(self, user):
|
def make_employee_salary_slip(self, user):
|
||||||
employee = frappe.db.get_value("Employee", {"user_id": user})
|
employee = frappe.db.get_value("Employee", {"user_id": user})
|
||||||
salary_structure = frappe.db.get_value("Salary Structure", {"employee": employee})
|
salary_structure = make_salary_structure("Salary Structure Test for Salary Slip")
|
||||||
if not salary_structure:
|
salary_slip = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})})
|
||||||
salary_structure = make_salary_structure(employee)
|
|
||||||
salary_structure.from_date = today()
|
|
||||||
salary_structure.insert()
|
|
||||||
salary_structure = salary_structure.name
|
|
||||||
|
|
||||||
salary_slip = frappe.db.get_value("Salary Slip", {"employee": employee})
|
|
||||||
if not salary_slip:
|
if not salary_slip:
|
||||||
salary_slip = make_salary_slip(salary_structure)
|
salary_slip = make_salary_slip(salary_structure, employee = employee)
|
||||||
|
salary_slip.employee_name = frappe.get_value("Employee", {"name":frappe.db.get_value("Employee", {"user_id": user})}, "employee_name")
|
||||||
|
salary_slip.month = "12"
|
||||||
|
salary_slip.fiscal_year = "_Test Fiscal Year 2016"
|
||||||
salary_slip.insert()
|
salary_slip.insert()
|
||||||
salary_slip.submit()
|
# salary_slip.submit()
|
||||||
salary_slip = salary_slip.name
|
salary_slip = salary_slip.name
|
||||||
|
|
||||||
return salary_slip
|
return salary_slip
|
||||||
@ -160,6 +192,82 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
activity_type.wage_rate = 25
|
activity_type.wage_rate = 25
|
||||||
activity_type.save()
|
activity_type.save()
|
||||||
|
|
||||||
test_dependencies = ["Leave Application", "Holiday List"]
|
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Salary Slip')
|
def make_salary_structure(sal_struct):
|
||||||
|
if not frappe.db.exists('Salary Structure', sal_struct):
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "Salary Structure",
|
||||||
|
"name": sal_struct,
|
||||||
|
"company": erpnext.get_default_company(),
|
||||||
|
"from_date": nowdate(),
|
||||||
|
"employees": get_employee_details(),
|
||||||
|
"earnings": get_earnings_component(),
|
||||||
|
"deductions": get_deductions_component()
|
||||||
|
}).insert()
|
||||||
|
return sal_struct
|
||||||
|
|
||||||
|
|
||||||
|
def get_employee_details():
|
||||||
|
return [{"employee": frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"),
|
||||||
|
"base": 25000,
|
||||||
|
"variable": 5000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_earnings_component():
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"salary_component": 'Basic Salary',
|
||||||
|
"abbr":'BS',
|
||||||
|
"condition": 'base > 10000',
|
||||||
|
"formula": 'base*.2',
|
||||||
|
"idx": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": 'Basic Salary',
|
||||||
|
"abbr":'BS',
|
||||||
|
"condition": 'base < 10000',
|
||||||
|
"formula": 'base*.1',
|
||||||
|
"idx": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": 'HRA',
|
||||||
|
"abbr":'H',
|
||||||
|
"amount": 3000,
|
||||||
|
"idx": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": 'Allowance',
|
||||||
|
"abbr":'A',
|
||||||
|
"condition": 'H < 10000',
|
||||||
|
"formula": 'BS*.5',
|
||||||
|
"idx": 4
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_deductions_component():
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"salary_component": 'Professional Tax',
|
||||||
|
"abbr":'PT',
|
||||||
|
"condition": 'base > 10000',
|
||||||
|
"formula": 'base*.2',
|
||||||
|
"idx": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": 'TDS',
|
||||||
|
"abbr":'T',
|
||||||
|
"formula": 'base*.5',
|
||||||
|
"idx": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": 'TDS',
|
||||||
|
"abbr":'T',
|
||||||
|
"condition": 'employment_type=="Intern"',
|
||||||
|
"formula": 'base*.1',
|
||||||
|
"idx": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
test_dependencies = ["Leave Application", "Holiday List"]
|
||||||
|
|
@ -9,15 +9,7 @@ cur_frm.cscript.onload = function(doc, dt, dn){
|
|||||||
e_tbl = doc.earnings || [];
|
e_tbl = doc.earnings || [];
|
||||||
d_tbl = doc.deductions || [];
|
d_tbl = doc.deductions || [];
|
||||||
if (e_tbl.length == 0 && d_tbl.length == 0)
|
if (e_tbl.length == 0 && d_tbl.length == 0)
|
||||||
return $c_obj(doc,'make_earn_ded_table','', function(r, rt) { refresh_many(['earnings', 'deductions']);});
|
return function(r, rt) { refresh_many(['earnings', 'deductions']);};
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.refresh = function(doc, dt, dn){
|
|
||||||
if((!doc.__islocal) && (doc.is_active == 'Yes') && cint(doc.salary_slip_based_on_timesheet == 0)){
|
|
||||||
cur_frm.add_custom_button(__('Salary Slip'),
|
|
||||||
cur_frm.cscript['Make Salary Slip'], __("Make"));
|
|
||||||
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frappe.ui.form.on('Salary Structure', {
|
frappe.ui.form.on('Salary Structure', {
|
||||||
@ -25,29 +17,54 @@ frappe.ui.form.on('Salary Structure', {
|
|||||||
frm.trigger("toggle_fields")
|
frm.trigger("toggle_fields")
|
||||||
frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false);
|
frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false);
|
||||||
frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false);
|
frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false);
|
||||||
},
|
|
||||||
|
frm.add_custom_button(__("Preview Salary Slip"),
|
||||||
|
function() { frm.trigger('preview_salary_slip'); }, "icon-sitemap", "btn-default");
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
salary_slip_based_on_timesheet: function(frm) {
|
salary_slip_based_on_timesheet: function(frm) {
|
||||||
frm.trigger("toggle_fields")
|
frm.trigger("toggle_fields")
|
||||||
},
|
},
|
||||||
|
|
||||||
|
preview_salary_slip: function(frm) {
|
||||||
|
var d = new frappe.ui.Dialog({
|
||||||
|
title: __("Preview Salary Slip"),
|
||||||
|
fields: [
|
||||||
|
{"fieldname":"employee", "fieldtype":"Select", "label":__("Employee"),
|
||||||
|
options: $.map(frm.doc.employees, function(d) { return d.employee }), reqd: 1, label:"Employee"},
|
||||||
|
{fieldname:"fetch", "label":__("Show Salary Slip"), "fieldtype":"Button"}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
d.get_input("fetch").on("click", function() {
|
||||||
|
var values = d.get_values();
|
||||||
|
if(!values) return;
|
||||||
|
frm.doc.salary_slip_based_on_timesheet?print_format="Salary Slip based on Timesheet":print_format="Salary Slip Standard";
|
||||||
|
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.hr.doctype.salary_structure.salary_structure.make_salary_slip",
|
||||||
|
args: {
|
||||||
|
source_name: frm.doc.name,
|
||||||
|
employee: values.employee,
|
||||||
|
as_print: 1,
|
||||||
|
print_format: print_format
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
var new_window = window.open();
|
||||||
|
new_window.document.write(r.message);
|
||||||
|
// frappe.msgprint(r.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
d.show();
|
||||||
|
},
|
||||||
|
|
||||||
toggle_fields: function(frm) {
|
toggle_fields: function(frm) {
|
||||||
frm.toggle_display(['salary_component', 'hour_rate'], frm.doc.salary_slip_based_on_timesheet);
|
frm.toggle_display(['salary_component', 'hour_rate'], frm.doc.salary_slip_based_on_timesheet);
|
||||||
frm.toggle_reqd(['salary_component', 'hour_rate'], frm.doc.salary_slip_based_on_timesheet);
|
frm.toggle_reqd(['salary_component', 'hour_rate'], frm.doc.salary_slip_based_on_timesheet);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
cur_frm.cscript['Make Salary Slip'] = function() {
|
|
||||||
frappe.model.open_mapped_doc({
|
|
||||||
method: "erpnext.hr.doctype.salary_structure.salary_structure.make_salary_slip",
|
|
||||||
frm: cur_frm
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.employee = function(doc, dt, dn){
|
|
||||||
if (doc.employee)
|
|
||||||
return get_server_fields('get_employee_details','','',doc,dt,dn);
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.amount = function(doc, cdt, cdn){
|
cur_frm.cscript.amount = function(doc, cdt, cdn){
|
||||||
calculate_totals(doc, cdt, cdn);
|
calculate_totals(doc, cdt, cdn);
|
||||||
@ -79,10 +96,6 @@ cur_frm.cscript.validate = function(doc, cdt, cdn) {
|
|||||||
if(doc.employee && doc.is_active == "Yes") frappe.model.clear_doc("Employee", doc.employee);
|
if(doc.employee && doc.is_active == "Yes") frappe.model.clear_doc("Employee", doc.employee);
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
|
|
||||||
return{ query: "erpnext.controllers.queries.employee_query" }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
frappe.ui.form.on('Salary Detail', {
|
frappe.ui.form.on('Salary Detail', {
|
||||||
amount: function(frm) {
|
amount: function(frm) {
|
||||||
@ -96,4 +109,12 @@ frappe.ui.form.on('Salary Detail', {
|
|||||||
deductions_remove: function(frm) {
|
deductions_remove: function(frm) {
|
||||||
calculate_totals(frm.doc);
|
calculate_totals(frm.doc);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
frappe.ui.form.on('Salary Structure Employee', {
|
||||||
|
onload: function(frm) {
|
||||||
|
frm.set_query("employee","employees", function(doc,cdt,cdn) {
|
||||||
|
return{ query: "erpnext.controllers.queries.employee_query" }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"allow_copy": 0,
|
"allow_copy": 0,
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 0,
|
"allow_rename": 0,
|
||||||
|
"autoname": "Prompt",
|
||||||
"beta": 0,
|
"beta": 0,
|
||||||
"creation": "2013-03-07 18:50:29",
|
"creation": "2013-03-07 18:50:29",
|
||||||
"custom": 0,
|
"custom": 0,
|
||||||
@ -34,140 +35,6 @@
|
|||||||
"unique": 0,
|
"unique": 0,
|
||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "employee",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Employee",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "employee",
|
|
||||||
"oldfieldtype": "Link",
|
|
||||||
"options": "Employee",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "employee_name",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Employee Name",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "employee_name",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "branch",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Branch",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "branch",
|
|
||||||
"oldfieldtype": "Select",
|
|
||||||
"options": "Branch",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "designation",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Designation",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "designation",
|
|
||||||
"oldfieldtype": "Select",
|
|
||||||
"options": "Designation",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "department",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Department",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "department",
|
|
||||||
"oldfieldtype": "Select",
|
|
||||||
"options": "Department",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@ -350,6 +217,57 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "employee_break",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"description": "Select employees for current Salary Structure",
|
||||||
|
"fieldname": "employees",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Employees",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Salary Structure Employee",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@ -498,6 +416,7 @@
|
|||||||
"oldfieldname": "earning_deduction",
|
"oldfieldname": "earning_deduction",
|
||||||
"oldfieldtype": "Section Break",
|
"oldfieldtype": "Section Break",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": "2",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
@ -671,7 +590,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "total_earning",
|
"fieldname": "total_earning",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
@ -698,7 +617,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "total_deduction",
|
"fieldname": "total_deduction",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
@ -719,37 +638,13 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "column_break3",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "50%"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "net_pay",
|
"fieldname": "net_pay",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
@ -780,7 +675,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2016-07-13 23:56:01.550518",
|
"modified": "2016-08-10 12:18:31.521436",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Structure",
|
"name": "Salary Structure",
|
||||||
@ -832,7 +727,7 @@
|
|||||||
"read_only_onload": 0,
|
"read_only_onload": 0,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"timeline_field": "employee",
|
"timeline_field": "",
|
||||||
"title_field": "employee_name",
|
"title_field": "",
|
||||||
"track_seen": 0
|
"track_seen": 0
|
||||||
}
|
}
|
@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from frappe.utils import cstr, flt, getdate
|
from frappe.utils import cstr, flt, getdate, cint
|
||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
@ -12,29 +12,12 @@ from frappe.model.document import Document
|
|||||||
from erpnext.hr.utils import set_employee_name
|
from erpnext.hr.utils import set_employee_name
|
||||||
|
|
||||||
class SalaryStructure(Document):
|
class SalaryStructure(Document):
|
||||||
def autoname(self):
|
|
||||||
self.name = make_autoname(self.employee + '/.SST' + '/.#####')
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.check_overlap()
|
|
||||||
self.validate_amount()
|
self.validate_amount()
|
||||||
self.validate_employee()
|
|
||||||
self.validate_joining_date()
|
self.validate_joining_date()
|
||||||
set_employee_name(self)
|
for e in self.get('employees'):
|
||||||
|
set_employee_name(e)
|
||||||
def get_employee_details(self):
|
|
||||||
ret = {}
|
|
||||||
det = frappe.db.sql("""select employee_name, branch, designation, department
|
|
||||||
from `tabEmployee` where name = %s""", self.employee)
|
|
||||||
if det:
|
|
||||||
ret = {
|
|
||||||
'employee_name': cstr(det[0][0]),
|
|
||||||
'branch': cstr(det[0][1]),
|
|
||||||
'designation': cstr(det[0][2]),
|
|
||||||
'department': cstr(det[0][3]),
|
|
||||||
'backup_employee': cstr(self.employee)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def get_ss_values(self,employee):
|
def get_ss_values(self,employee):
|
||||||
basic_info = frappe.db.sql("""select bank_name, bank_ac_no
|
basic_info = frappe.db.sql("""select bank_name, bank_ac_no
|
||||||
@ -43,71 +26,23 @@ class SalaryStructure(Document):
|
|||||||
'bank_ac_no': basic_info and basic_info[0][1] or ''}
|
'bank_ac_no': basic_info and basic_info[0][1] or ''}
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def make_table(self, doct_name, tab_fname, tab_name):
|
|
||||||
list1 = frappe.db.sql("select name from `tab%s` where docstatus != 2" % doct_name)
|
|
||||||
for li in list1:
|
|
||||||
child = self.append(tab_fname, {})
|
|
||||||
if(tab_fname == 'earnings'):
|
|
||||||
child.salary_component = cstr(li[0])
|
|
||||||
child.amount = 0
|
|
||||||
elif(tab_fname == 'deductions'):
|
|
||||||
child.salary_component = cstr(li[0])
|
|
||||||
child.amount = 0
|
|
||||||
|
|
||||||
def make_earn_ded_table(self):
|
|
||||||
self.make_table('Salary Component','earnings','Salary Detail')
|
|
||||||
self.make_table('Salary Component','deductions', 'Salary Detail')
|
|
||||||
|
|
||||||
def check_overlap(self):
|
|
||||||
existing = frappe.db.sql("""select name from `tabSalary Structure`
|
|
||||||
where employee = %(employee)s and
|
|
||||||
(
|
|
||||||
(%(from_date)s > from_date and %(from_date)s < to_date) or
|
|
||||||
(%(to_date)s > from_date and %(to_date)s < to_date) or
|
|
||||||
(%(from_date)s <= from_date and %(to_date)s >= to_date))
|
|
||||||
and name!=%(name)s
|
|
||||||
and docstatus < 2""",
|
|
||||||
{
|
|
||||||
"employee": self.employee,
|
|
||||||
"from_date": self.from_date,
|
|
||||||
"to_date": self.to_date,
|
|
||||||
"name": self.name or "No Name"
|
|
||||||
}, as_dict=True)
|
|
||||||
|
|
||||||
if existing:
|
|
||||||
frappe.throw(_("Salary structure {0} already exist, more than one salary structure for same period is not allowed").format(existing[0].name))
|
|
||||||
|
|
||||||
def validate_amount(self):
|
def validate_amount(self):
|
||||||
if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet:
|
if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet:
|
||||||
frappe.throw(_("Net pay cannot be negative"))
|
frappe.throw(_("Net pay cannot be negative"))
|
||||||
|
|
||||||
def validate_employee(self):
|
|
||||||
old_employee = frappe.db.get_value("Salary Structure", self.name, "employee")
|
|
||||||
if old_employee and self.employee != old_employee:
|
|
||||||
frappe.throw(_("Employee can not be changed"))
|
|
||||||
|
|
||||||
def validate_joining_date(self):
|
def validate_joining_date(self):
|
||||||
joining_date = getdate(frappe.db.get_value("Employee", self.employee, "date_of_joining"))
|
for e in self.get('employees'):
|
||||||
if getdate(self.from_date) < joining_date:
|
joining_date = getdate(frappe.db.get_value("Employee", e.employee, "date_of_joining"))
|
||||||
frappe.throw(_("From Date in Salary Structure cannot be lesser than Employee Joining Date."))
|
if getdate(self.from_date) < joining_date:
|
||||||
|
frappe.throw(_("From Date in Salary Structure cannot be lesser than Employee Joining Date."))
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_salary_slip(source_name, target_doc=None):
|
def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None):
|
||||||
def postprocess(source, target):
|
def postprocess(source, target):
|
||||||
# copy earnings and deductions table
|
if employee:
|
||||||
for key in ('earnings', 'deductions'):
|
target.employee = employee
|
||||||
for d in source.get(key):
|
target.run_method('process_salary_structure')
|
||||||
target.append(key, {
|
|
||||||
'amount': d.amount,
|
|
||||||
'default_amount': d.amount,
|
|
||||||
'depends_on_lwp' : d.depends_on_lwp,
|
|
||||||
'salary_component' : d.salary_component
|
|
||||||
})
|
|
||||||
|
|
||||||
target.run_method("pull_emp_details")
|
|
||||||
target.run_method("get_leave_details")
|
|
||||||
target.run_method("calculate_net_pay")
|
|
||||||
|
|
||||||
|
|
||||||
doc = get_mapped_doc("Salary Structure", source_name, {
|
doc = get_mapped_doc("Salary Structure", source_name, {
|
||||||
"Salary Structure": {
|
"Salary Structure": {
|
||||||
@ -119,4 +54,8 @@ def make_salary_slip(source_name, target_doc=None):
|
|||||||
}
|
}
|
||||||
}, target_doc, postprocess, ignore_child_tables=True)
|
}, target_doc, postprocess, ignore_child_tables=True)
|
||||||
|
|
||||||
return doc
|
if cint(as_print):
|
||||||
|
doc.name = 'Preview for {0}'.format(employee)
|
||||||
|
return frappe.get_print(doc.doctype, doc.name, doc = doc, print_format = print_format)
|
||||||
|
else:
|
||||||
|
return doc
|
@ -1,24 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"doctype": "Salary Structure",
|
|
||||||
"name": "_Test Salary Structure 1",
|
|
||||||
"employee": "_T-Employee-0001",
|
|
||||||
"from_date": "2014-02-01",
|
|
||||||
"earnings": [
|
|
||||||
{
|
|
||||||
"salary_component": "_Test Basic Salary"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"salary_component": "_Test Allowance"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"deductions": [
|
|
||||||
{
|
|
||||||
"salary_component": "_Test Professional Tax"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"salary_component": "_Test TDS"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
@ -4,8 +4,183 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
import erpnext
|
||||||
test_records = frappe.get_test_records('Salary Structure')
|
from frappe.utils import nowdate, add_days, add_years
|
||||||
|
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
|
||||||
|
# test_records = frappe.get_test_records('Salary Structure')
|
||||||
|
|
||||||
class TestSalaryStructure(unittest.TestCase):
|
class TestSalaryStructure(unittest.TestCase):
|
||||||
pass
|
def test_setup(self):
|
||||||
|
if not frappe.db.exists("Fiscal Year", "_Test Fiscal Year 2016"):
|
||||||
|
fy = frappe.get_doc({
|
||||||
|
"doctype": "Fiscal Year",
|
||||||
|
"year": "_Test Fiscal Year 2016",
|
||||||
|
"year_end_date": "2016-12-31",
|
||||||
|
"year_start_date": "2016-01-01"
|
||||||
|
})
|
||||||
|
fy.insert()
|
||||||
|
|
||||||
|
self.make_holiday_list()
|
||||||
|
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Structure Test Holiday List")
|
||||||
|
self.make_salary_component(["Basic Salary", "Allowance", "HRA", "Professional Tax", "TDS"])
|
||||||
|
employee1 = self.make_employee("test_employee@salary.com")
|
||||||
|
employee2 = self.make_employee("test_employee_2@salary.com")
|
||||||
|
|
||||||
|
def make_holiday_list(self):
|
||||||
|
if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"):
|
||||||
|
holiday_list = frappe.get_doc({
|
||||||
|
"doctype": "Holiday List",
|
||||||
|
"holiday_list_name": "Salary Structure Test Holiday List",
|
||||||
|
"from_date": nowdate(),
|
||||||
|
"to_date": add_years(nowdate(), 1),
|
||||||
|
"weekly_off": "Sunday"
|
||||||
|
}).insert()
|
||||||
|
holiday_list.get_weekly_off_dates()
|
||||||
|
holiday_list.save()
|
||||||
|
|
||||||
|
def make_employee(self, user):
|
||||||
|
if not frappe.db.get_value("User", user):
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "User",
|
||||||
|
"email": user,
|
||||||
|
"first_name": user,
|
||||||
|
"new_password": "password",
|
||||||
|
"user_roles": [{"doctype": "UserRole", "role": "Employee"}]
|
||||||
|
}).insert()
|
||||||
|
|
||||||
|
|
||||||
|
if not frappe.db.get_value("Employee", {"user_id": user}):
|
||||||
|
emp = frappe.get_doc({
|
||||||
|
"doctype": "Employee",
|
||||||
|
"naming_series": "EMP-",
|
||||||
|
"employee_name": user,
|
||||||
|
"company": erpnext.get_default_company(),
|
||||||
|
"user_id": user,
|
||||||
|
"date_of_birth": "1990-05-08",
|
||||||
|
"date_of_joining": "2013-01-01",
|
||||||
|
"relieving_date": "",
|
||||||
|
"department": frappe.get_all("Department", fields="name")[0].name,
|
||||||
|
"gender": "Female",
|
||||||
|
"company_email": user,
|
||||||
|
"status": "Active",
|
||||||
|
"employment_type": "Intern"
|
||||||
|
}).insert()
|
||||||
|
return emp.name
|
||||||
|
else:
|
||||||
|
return frappe.get_value("Employee", {"employee_name":user}, "name")
|
||||||
|
|
||||||
|
def make_salary_component(self, salary_components):
|
||||||
|
for salary_component in salary_components:
|
||||||
|
if not frappe.db.exists('Salary Component', salary_component):
|
||||||
|
sal_comp = frappe.get_doc({
|
||||||
|
"doctype": "Salary Component",
|
||||||
|
"salary_component": salary_component
|
||||||
|
})
|
||||||
|
sal_comp.insert()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_amount_totals(self):
|
||||||
|
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee@salary.com"})
|
||||||
|
if not sal_slip:
|
||||||
|
sal_slip = make_salary_slip_from_salary_structure(employee=frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}))
|
||||||
|
self.assertEquals(sal_slip.get("salary_structure"), 'Salary Structure Sample')
|
||||||
|
self.assertEquals(sal_slip.get("earnings")[0].amount, 0)
|
||||||
|
self.assertEquals(sal_slip.get("deductions")[0].amount, 0)
|
||||||
|
self.assertEquals(sal_slip.get("deductions")[1].amount, 0)
|
||||||
|
self.assertEquals(sal_slip.get("total_deduction"), 0)
|
||||||
|
self.assertEquals(sal_slip.get("net_pay"), 0)
|
||||||
|
|
||||||
|
|
||||||
|
def make_salary_slip_from_salary_structure(employee):
|
||||||
|
sal_struct = make_salary_structure('Salary Structure Sample')
|
||||||
|
sal_slip = make_salary_slip(sal_struct, employee = employee)
|
||||||
|
sal_slip.employee_name = frappe.get_value("Employee", {"name":employee}, "employee_name")
|
||||||
|
sal_slip.month = "11"
|
||||||
|
sal_slip.fiscal_year = "_Test Fiscal Year 2016"
|
||||||
|
sal_slip.insert()
|
||||||
|
sal_slip.submit()
|
||||||
|
return sal_slip
|
||||||
|
|
||||||
|
def make_salary_structure(sal_struct):
|
||||||
|
if not frappe.db.exists('Salary Structure', sal_struct):
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "Salary Structure",
|
||||||
|
"name": sal_struct,
|
||||||
|
"company": erpnext.get_default_company(),
|
||||||
|
"from_date": nowdate(),
|
||||||
|
"employees": get_employee_details(),
|
||||||
|
"earnings": get_earnings_component(),
|
||||||
|
"deductions": get_deductions_component()
|
||||||
|
}).insert()
|
||||||
|
return sal_struct
|
||||||
|
|
||||||
|
|
||||||
|
def get_employee_details():
|
||||||
|
return [{"employee": frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"),
|
||||||
|
"base": 25000,
|
||||||
|
"variable": 5000,
|
||||||
|
"idx": 1
|
||||||
|
},
|
||||||
|
{"employee": frappe.get_value("Employee", {"employee_name":"test_employee_2@salary.com"}, "name"),
|
||||||
|
"base": 2100,
|
||||||
|
"variable": 100,
|
||||||
|
"idx": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_earnings_component():
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"salary_component": 'Basic Salary',
|
||||||
|
"abbr":'BS',
|
||||||
|
"condition": 'base > 10000',
|
||||||
|
"formula": 'base*.2',
|
||||||
|
"idx": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": 'Basic Salary',
|
||||||
|
"abbr":'BS',
|
||||||
|
"condition": 'base < 10000',
|
||||||
|
"formula": 'base*.1',
|
||||||
|
"idx": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": 'HRA',
|
||||||
|
"abbr":'H',
|
||||||
|
"amount": 3000,
|
||||||
|
"idx": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": 'Allowance',
|
||||||
|
"abbr":'A',
|
||||||
|
"condition": 'H < 10000',
|
||||||
|
"formula": 'BS*.5',
|
||||||
|
"idx": 4
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_deductions_component():
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"salary_component": 'Professional Tax',
|
||||||
|
"abbr":'PT',
|
||||||
|
"condition": 'base > 10000',
|
||||||
|
"formula": 'base*.2',
|
||||||
|
"idx": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": 'TDS',
|
||||||
|
"abbr":'T',
|
||||||
|
"formula": 'base*.5',
|
||||||
|
"idx": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": 'TDS',
|
||||||
|
"abbr":'T',
|
||||||
|
"condition": 'employment_type=="Intern"',
|
||||||
|
"formula": 'base*.1',
|
||||||
|
"idx": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,139 @@
|
|||||||
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
|
"autoname": "employee",
|
||||||
|
"beta": 0,
|
||||||
|
"creation": "2016-07-26 11:53:43.621605",
|
||||||
|
"custom": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "employee",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Employee",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Employee",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "employee_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Employee Name",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "base",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Base",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "variable",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Variable",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
|
"idx": 0,
|
||||||
|
"image_view": 0,
|
||||||
|
"in_create": 0,
|
||||||
|
"in_dialog": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 0,
|
||||||
|
"istable": 1,
|
||||||
|
"max_attachments": 0,
|
||||||
|
"modified": "2016-08-11 12:18:14.526977",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "HR",
|
||||||
|
"name": "Salary Structure Employee",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_seen": 0
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class SalaryStructureEmployee(Document):
|
||||||
|
pass
|
@ -310,4 +310,5 @@ erpnext.patches.v7_0.set_material_request_type_in_item
|
|||||||
erpnext.patches.v7_0.rename_examination_to_assessment
|
erpnext.patches.v7_0.rename_examination_to_assessment
|
||||||
erpnext.patches.v7_0.set_portal_settings
|
erpnext.patches.v7_0.set_portal_settings
|
||||||
erpnext.patches.v7_0.repost_future_gle_for_purchase_invoice
|
erpnext.patches.v7_0.repost_future_gle_for_purchase_invoice
|
||||||
erpnext.patches.v7_0.fix_duplicate_icons
|
erpnext.patches.v7_0.fix_duplicate_icons
|
||||||
|
erpnext.patches.v7_0.move_employee_parent_to_child_in_salary_structure
|
@ -0,0 +1,10 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc('hr', 'doctype', 'salary_structure')
|
||||||
|
for ss in frappe.db.sql(""" select employee, name from `tabSalary Structure`""", as_dict=True):
|
||||||
|
ss_doc = frappe.get_doc('Salary Structure', ss.name)
|
||||||
|
se = ss_doc.append('employees',{})
|
||||||
|
se.employee = ss.employee
|
||||||
|
se.base = 0
|
||||||
|
ss_doc.save()
|
@ -27,7 +27,7 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEquals(salary_slip.total_working_hours, 2)
|
self.assertEquals(salary_slip.total_working_hours, 2)
|
||||||
self.assertEquals(salary_slip.hour_rate, 50)
|
self.assertEquals(salary_slip.hour_rate, 50)
|
||||||
self.assertEquals(salary_slip.net_pay, 150)
|
self.assertEquals(salary_slip.net_pay, 50)
|
||||||
self.assertEquals(salary_slip.timesheets[0].time_sheet, timesheet.name)
|
self.assertEquals(salary_slip.timesheets[0].time_sheet, timesheet.name)
|
||||||
self.assertEquals(salary_slip.timesheets[0].working_hours, 2)
|
self.assertEquals(salary_slip.timesheets[0].working_hours, 2)
|
||||||
|
|
||||||
@ -54,35 +54,42 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
timesheet = frappe.get_doc('Timesheet', timesheet.name)
|
timesheet = frappe.get_doc('Timesheet', timesheet.name)
|
||||||
self.assertEquals(sales_invoice.total_billing_amount, 100)
|
self.assertEquals(sales_invoice.total_billing_amount, 100)
|
||||||
self.assertEquals(timesheet.status, 'Billed')
|
self.assertEquals(timesheet.status, 'Billed')
|
||||||
|
|
||||||
|
|
||||||
def make_salary_structure(employee):
|
def make_salary_structure(employee):
|
||||||
name = frappe.db.get_value('Salary Structure', {'employee': employee, 'salary_slip_based_on_timesheet': 1}, 'name')
|
name = frappe.db.get_value('Salary Structure Employee', {'employee': employee}, 'parent')
|
||||||
if name:
|
if name:
|
||||||
salary_structure = frappe.get_doc('Salary Structure', name)
|
salary_structure = frappe.get_doc('Salary Structure', name)
|
||||||
else:
|
else:
|
||||||
salary_structure = frappe.new_doc("Salary Structure")
|
salary_structure = frappe.new_doc("Salary Structure")
|
||||||
|
salary_structure.name = "Timesheet Salary Structure Test"
|
||||||
|
salary_structure.salary_slip_based_on_timesheet = 1
|
||||||
|
salary_structure.from_date = nowdate()
|
||||||
|
salary_structure.salary_component = "Basic"
|
||||||
|
salary_structure.hour_rate = 50.0
|
||||||
|
salary_structure.company= "_Test Company"
|
||||||
|
|
||||||
salary_structure.salary_slip_based_on_timesheet = 1
|
salary_structure.set('employees', [])
|
||||||
salary_structure.employee = employee
|
salary_structure.set('earnings', [])
|
||||||
salary_structure.from_date = nowdate()
|
salary_structure.set('deductions', [])
|
||||||
salary_structure.salary_component = "Basic"
|
|
||||||
salary_structure.hour_rate = 50.0
|
|
||||||
salary_structure.company= "_Test Company"
|
|
||||||
|
|
||||||
salary_structure.set('earnings', [])
|
es = salary_structure.append('employees', {
|
||||||
salary_structure.set('deductions', [])
|
"employee": employee,
|
||||||
|
"base": 1200
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
es = salary_structure.append('earnings', {
|
||||||
|
"salary_component": "_Test Allowance",
|
||||||
|
"amount": 100
|
||||||
|
})
|
||||||
|
|
||||||
es = salary_structure.append('earnings', {
|
ds = salary_structure.append('deductions', {
|
||||||
"salary_component": "_Test Allowance",
|
"salary_component": "_Test Professional Tax",
|
||||||
"amount": 100
|
"amount": 50
|
||||||
})
|
})
|
||||||
|
|
||||||
ds = salary_structure.append('deductions', {
|
salary_structure.save(ignore_permissions=True)
|
||||||
"salary_component": "_Test Professional Tax",
|
|
||||||
"amount": 50
|
|
||||||
})
|
|
||||||
|
|
||||||
salary_structure.save(ignore_permissions=True)
|
|
||||||
|
|
||||||
return salary_structure
|
return salary_structure
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user