Merge pull request #13951 from ESS-LLP/jam_enterprise_sprint
Attendance Request, Leave Period, Compensatory Leave Request, Leave Policy
BIN
erpnext/docs/assets/img/human-resources/earned-leave.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
erpnext/docs/assets/img/human-resources/employee-grade.png
Normal file
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 24 KiB |
BIN
erpnext/docs/assets/img/human-resources/leave-encashment.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
erpnext/docs/assets/img/human-resources/leave-period-1.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
erpnext/docs/assets/img/human-resources/leave-period-2.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
erpnext/docs/assets/img/human-resources/leave-period-grant.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
erpnext/docs/assets/img/human-resources/leave-policy.png
Normal file
After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 88 KiB |
@ -5,7 +5,7 @@ of co-workers. Most important feature here is processing the payroll by using
|
|||||||
Payroll Entry to generate Salary Slips. Most countries have complex tax
|
Payroll Entry to generate Salary Slips. Most countries have complex tax
|
||||||
rules stating which expenses the company can make on behalf of the Employees.
|
rules stating which expenses the company can make on behalf of the Employees.
|
||||||
There are a set of rules for the company to deduct taxes and social security
|
There are a set of rules for the company to deduct taxes and social security
|
||||||
from employee payroll. ERPNext allows to accomodate all types of taxes and
|
from employee payroll. ERPNext allows to accommodate all types of taxes and
|
||||||
their calculation.
|
their calculation.
|
||||||
|
|
||||||
It also maintains a complete employee database including contact information,
|
It also maintains a complete employee database including contact information,
|
||||||
|
@ -5,7 +5,7 @@ to be able to qualify as paid leaveas, you can create Leave Application to
|
|||||||
track approval and usage of leaves. You have to mention the Employee, Leave
|
track approval and usage of leaves. You have to mention the Employee, Leave
|
||||||
Type and the period for which the leave is taken.
|
Type and the period for which the leave is taken.
|
||||||
|
|
||||||
> Human Resources > Leave Application > New Leave Application
|
> Human Resources > Leaves and Holiday > Leave Application > New Leave Application
|
||||||
|
|
||||||
<img class="screenshot" alt="Leave Application" src="{{docs_base_url}}/assets/img/human-resources/leave-application.png">
|
<img class="screenshot" alt="Leave Application" src="{{docs_base_url}}/assets/img/human-resources/leave-application.png">
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ their “Employee ID” as a match rule in the Leave Application Permission
|
|||||||
settings. See the earlier discussion on [Setting Up Permissions](/docs/user/manual/en/setting-up/users-and-permissions/user-permissions.html)
|
settings. See the earlier discussion on [Setting Up Permissions](/docs/user/manual/en/setting-up/users-and-permissions/user-permissions.html)
|
||||||
for more info.
|
for more info.
|
||||||
|
|
||||||
You assign Leaves aginast an Employee check [Leave Allocation](/docs/user/manual/en/human-resources/leave.html)
|
To understand how ERPNext allows you configure leaves for employees, check [Leaves - Overview](/docs/user/manual/en/human-resources/leave.html)
|
||||||
|
|
||||||
<div class="embed-container">
|
<div class="embed-container">
|
||||||
<iframe src="https://www.youtube.com/embed/fc0p_AXebc8?rel=0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen>
|
<iframe src="https://www.youtube.com/embed/fc0p_AXebc8?rel=0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen>
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
#Overview
|
#Leaves - Overview
|
||||||
This section enables you to manage leave schedule of your organization. It also explains the way employees can apply for leaves.
|
This section will help you understand how ERPNext enables you to effectively manage the leave schedule of your organization. It also explains the way employees can apply for leaves.
|
||||||
Employees create leave request and manager (leave approver) approves or rejects the request. You can select from a number of leave types such as sick leave, casual leave, privilege leave and so on. You can also allocate leaves to your employees and generate reports to track leaves record.
|
Employees create leave requests, which their respective managers (leave approver) can approve or reject. An Employee can select from a number of leave types such as sick leave, casual leave, privilege leave and so on. The number and type of leaves an Employee can apply is controlled by Leave Allocations. You can create Leave Allocations for a Leave Period based on the company's Leave Policy. You can also allocate additional leaves to your employees and generate reports to track leaves taken by Employees.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#Leave Type
|
#Leave Type
|
||||||
|
> Human Resources > Leaves and Holiday > Leave Type > New Leave Type
|
||||||
> Human Resources > Setup > Leave Type > New Leave Type
|
|
||||||
|
|
||||||
Leave Type refers to types of leave allotted to an employee by a company. An employee can select a particular Leave Type while requesting for a leave. You can create any number of Leave Types based on your company’s
|
Leave Type refers to types of leave allotted to an employee by a company. An employee can select a particular Leave Type while requesting for a leave. You can create any number of Leave Types based on your company’s
|
||||||
requirement.
|
requirement.
|
||||||
@ -14,16 +13,36 @@ requirement.
|
|||||||
<img class="screenshot" alt="New Leave Type"
|
<img class="screenshot" alt="New Leave Type"
|
||||||
src="{{docs_base_url}}/assets/img/human-resources/new-leave-type.png">
|
src="{{docs_base_url}}/assets/img/human-resources/new-leave-type.png">
|
||||||
|
|
||||||
**Max Days Leave Allowed:** It refers to maximum number of days this particular Leave Type can be availed at a stretch. If an employee exceeds the maximum number of days under a particular Leave Type, his/her extended leave may be considered as ‘Leave Without Pay’ and this may affect his/her salary calculation.
|
**Max Leaves Allowed:** This field allows you to set the maximum number of leaves of this Leave Type that Employees can apply within a Leave Period.
|
||||||
|
|
||||||
|
**Applicable After (Working Days):** Employees who have worked with the company for this number of days are only allowed to apply for this Leave Type. Do note that any other leaves availed by the Employee after her joining date is also considered while calculating working days.
|
||||||
|
|
||||||
|
**Maximum Continuous Days Applicable:** It refers to maximum number of days this particular Leave Type can be availed at a stretch. If an employee exceeds the maximum number of days under a particular Leave Type, his/her extended leave may be considered as ‘Leave Without Pay’ and this may affect his/her salary calculation.
|
||||||
|
|
||||||
**Is Carry Forward:** If checked, the balance leave will be carried forwarded to the next allocation period.
|
**Is Carry Forward:** If checked, the balance leave will be carried forwarded to the next allocation period.
|
||||||
|
|
||||||
**Is Leave Without Pay:** This ensures that the Leave Type will be treated as leaves whithout pay and salary will get deducted for this Leave Type.
|
**Is Leave Without Pay:** This ensures that the Leave Type will be treated as leaves without pay and salary will get deducted for this Leave Type.
|
||||||
|
|
||||||
**Allow Nagative Balance:** If checked, system will always allow to approve leave application for the Leave Type, even if there is no leave balance.
|
**Allow Negative Balance:** If checked, system will always allow to approve leave application for the Leave Type, even if there is no leave balance.
|
||||||
|
|
||||||
**Include holidays within leaves as leaves:** Check this option if you wish to count holidays within leaves as a ‘leave’. Such holidays will be deducted from the total number of leaves.
|
**Include holidays within leaves as leaves:** Check this option if you wish to count holidays within leaves as a ‘leave’. Such holidays will be deducted from the total number of leaves.
|
||||||
|
|
||||||
|
**Is Compensatory:** Compensatory leaves are leaves granted for working overtime or on holidays, normally compensated as an encashable leave. You can check this option to mark the Leave Type as compensatory. An Employee can request for compensatory leaves (Compensatory Leave Request) and on approval of such requests, Leave Allocations are created allowing her to apply for leaves of this type later on.
|
||||||
|
|
||||||
|
**Is Optional:** Check this Optional Leaves are holidays which Employees can choose to avail from a list of holidays published by the company. The Holiday List for optional leaves can have any number of holidays but you can restrict the number of such leaves granted to an Employee in a Leave Period by setting the Max Days Leave Allowed field.
|
||||||
|
|
||||||
|
**Encashment:** It is possible that Employees can receive cash from their Employer for unused leaves granted to them in a Leave Period. Not all Leave Types need to be encashable, so you should set "Allow Encashment" for Leave Types which are encashable. Leave encashment is allowed only in the last month of the Leave Period.
|
||||||
|
|
||||||
|
<img class="screenshot" alt="Leave Encashment"
|
||||||
|
src="{{docs_base_url}}/assets/img/human-resources/leave-encashment.png">
|
||||||
|
|
||||||
|
You can set the **Encashment Threshold Days** field so that the Employees wont be able to encash that many days. These days should be carry forwarded to the next Leave Period so that it can be either encashed or availed. You may also want to set the **Earning Component** for use in Salary Slip while paying out the encashed amount to Employees as part of their Salary.
|
||||||
|
|
||||||
|
**Earned Leave:** Earned Leaves are leaves earned by an employee after working with the company for a certain amount of time. Checking "Is Earned Leave" will allot leaves pro rata by automatically updating Leave Allocation for leaves of this type at intervals set by **Earned Leave Frequency**. For example, if an employee earns 2 leaves of type Paid Leaves monthly, ERPNext automatically increments the Leave Allocation for Paid Leave at the end of every month by 2. The leave allotment process (background job) will only allot leaves considering the max leaves for the leave type, and will round to **Rounding** for fractions.
|
||||||
|
|
||||||
|
<img class="screenshot" alt="Earned Leave"
|
||||||
|
src="{{docs_base_url}}/assets/img/human-resources/earned-leave.png">
|
||||||
|
|
||||||
###Default Leave Types
|
###Default Leave Types
|
||||||
There are some pre-loaded Leave Types in the system, as below:
|
There are some pre-loaded Leave Types in the system, as below:
|
||||||
|
|
||||||
@ -35,30 +54,60 @@ There are some pre-loaded Leave Types in the system, as below:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#Leave Allocation
|
#Leave Policy
|
||||||
|
> Human Resources > Leaves and Holiday > Leave Policy > New Leave Policy
|
||||||
|
|
||||||
Leave Allocation enables you to allot a specific number of leaves to a particular employee. You can allocate a number of leaves to different types of leave. You also have the option to allocate leaves to your employees manually or via the Leave Allocation Tool.
|
It is a practice for many enterprises to enforce a general Leave Policy to effectively track and manage Employee leaves. ERPNext allows you to create and manage multiple Leave Policies and allocate leaves to Employees as defined by the policy.
|
||||||
|
|
||||||
###Manual Allocation
|
<img class="screenshot" alt="Leave Policy"
|
||||||
> Human Resources > Setup > Leave Allocation > New Leave Allocation
|
src="{{docs_base_url}}/assets/img/human-resources/leave-policy.png">
|
||||||
|
|
||||||
To allocate leaves to an Employee, select the period and the number of leaves you want to allocate. You can also add unused leaves from previous allocation period.
|
### Enforcing the Leave Policy
|
||||||
|
To enforce the Leave Policy, you can either:
|
||||||
|
* Apply the Leave Policy in Employee Grade
|
||||||
|
<img class="screenshot" alt="Employee Grade"
|
||||||
|
src="{{docs_base_url}}/assets/img/human-resources/employee-grade.png">
|
||||||
|
|
||||||
|
This will ensure all leave allocations for all employees of this grade will be as per the Leave Policy
|
||||||
|
|
||||||
|
* Update Employee record with appropriate Leave Policy. In case you need to selectively update the Leave Policy for a particular Employee, you can do so by updating the Employee record.
|
||||||
|
|
||||||
|
<img class="screenshot" alt="Employee Leave Policy"
|
||||||
|
src="{{docs_base_url}}/assets/img/human-resources/employee-leave-policy.png">
|
||||||
|
|
||||||
|
#Leave Period
|
||||||
|
Most companies manage leaves based on a Leave Period. ERPNext allows you to create a Leave period by going to
|
||||||
|
> Human Resources > Leaves and Holiday > Leave Period > New Leave Period
|
||||||
|
|
||||||
|
<img class="screenshot" alt="Leave Period"
|
||||||
|
src="{{docs_base_url}}/assets/img/human-resources/leave-period.png">
|
||||||
|
|
||||||
|
#Granting Leaves to Employees
|
||||||
|
Leave Management in ERPNext is based on Leave Allocations created for each employee. This means, Employees can only avail as many leaves (of each Leave Type) allocated to them. There are multiple ways by which you can create Leave Allocations for Employees.
|
||||||
|
|
||||||
|
###Leave Allocation
|
||||||
|
Leave Allocation enables you to allot a specific number of leaves to a particular employee. You can allocate a number of leaves to different types of leave.
|
||||||
|
|
||||||
|
###Allocating leaves for a Leave Period
|
||||||
|
> Human Resources > Leaves and Holiday > Leave Period
|
||||||
|
|
||||||
|
Leave Period helps you manage leaves for a period and also doubles up as a tool to help you grant leaves for a category of employees. The **Grant** button will generate Leave Allocations based on the Leave Policy applicable to each Employee. You can allocate leaves based on Employee Grade, Department or Designation. Also, note that **Carry Forward Leaves** check will enable you to carry forward any unused leaves (for Leave Types with Is Carry Forward turned on) from previous allocations to new ones.
|
||||||
|
|
||||||
|
<img class="screenshot" alt="Grant Leaves from Leave Period"
|
||||||
|
src="{{docs_base_url}}/assets/img/human-resources/leave-period-grant.png">
|
||||||
|
|
||||||
|
###Manual Allocation of leaves
|
||||||
|
> Human Resources > Leaves and Holiday > Leave Allocation > New Leave Allocation
|
||||||
|
|
||||||
|
To manually allocate leaves for an Employee, select the period and the number of leaves you want to allocate. You can also add unused leaves from previous allocation period.
|
||||||
|
|
||||||
<img class="screenshot" alt="Manual Leave Allocation"
|
<img class="screenshot" alt="Manual Leave Allocation"
|
||||||
src="{{docs_base_url}}/assets/img/human-resources/manual-leave-allocation.png">
|
src="{{docs_base_url}}/assets/img/human-resources/manual-leave-allocation.png">
|
||||||
|
|
||||||
###Via Leave Allocation Tool
|
|
||||||
> Human Resources > Tools > Leave Allocation Tool
|
|
||||||
|
|
||||||
This tool enables you to allocate leaves for a category of employees, instead of individual ones. You can allocate leaves based on Employee Type, Branch, Department and Designation. Leave Allocation Tool is also known as Leave Control Panel.
|
|
||||||
|
|
||||||
<img class="screenshot" alt="Leave Allocation Tool"
|
|
||||||
src="{{docs_base_url}}/assets/img/human-resources/leave-allocation-tool.png">
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#Leave Application
|
#Leave Application
|
||||||
> Human Resources > Documents > Leave Application > New Leave Application
|
> Human Resources > Leaves and Holiday > Leave Application > New Leave Application
|
||||||
|
|
||||||
Leave Application section enables an employee to apply for leaves. Employee can select the type of leave and the Leave Approver who will authorize the Leave Application. User with "Leave Approver" role are considered as Leave approver. Leave Approvers can also be restricted/pre-defined in the Employee record. Based on selected dates and applicable Holiday List, total leave days is calculated automatically.
|
Leave Application section enables an employee to apply for leaves. Employee can select the type of leave and the Leave Approver who will authorize the Leave Application. User with "Leave Approver" role are considered as Leave approver. Leave Approvers can also be restricted/pre-defined in the Employee record. Based on selected dates and applicable Holiday List, total leave days is calculated automatically.
|
||||||
|
|
||||||
@ -84,7 +133,7 @@ Leave Application section enables an employee to apply for leaves. Employee can
|
|||||||
|
|
||||||
#Leave Block List
|
#Leave Block List
|
||||||
|
|
||||||
> Human Resources > Setup > Leave Block List > New Leave Block List
|
> Human Resources > Leaves and Holiday > Leave Block List > New Leave Block List
|
||||||
|
|
||||||
Leave Block List is a list of dates in a year, on which employees can not apply for leave. You can define a list of users who can approve Leave Application on blocked days, in case of urgency. You can also define whether the list will applied on entire company or any specific departments.
|
Leave Block List is a list of dates in a year, on which employees can not apply for leave. You can define a list of users who can approve Leave Application on blocked days, in case of urgency. You can also define whether the list will applied on entire company or any specific departments.
|
||||||
|
|
||||||
|
@ -295,6 +295,39 @@
|
|||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "attendance_request",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Attendance Request",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Attendance Request",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -339,7 +372,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-06-13 14:29:11.771376",
|
"modified": "2018-05-07 16:56:32.314683",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Attendance",
|
"name": "Attendance",
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
cur_frm.add_fetch('employee', 'company', 'company');
|
||||||
|
|
||||||
frappe.ui.form.on('Attendance Request', {
|
frappe.ui.form.on('Attendance Request', {
|
||||||
refresh: function(frm) {
|
half_day: function(frm) {
|
||||||
|
if(frm.doc.half_day == 1){
|
||||||
|
frm.set_df_property('half_day_date', 'reqd', true);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
frm.set_df_property('half_day_date', 'reqd', false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -145,19 +145,18 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"fieldname": "reason",
|
"fieldname": "half_day",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Check",
|
||||||
"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_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 0,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "Reason",
|
"label": "Half Day",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"options": "Work From Home\nOn Duty",
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
@ -165,7 +164,39 @@
|
|||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"remember_last_selected_value": 0,
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"depends_on": "half_day",
|
||||||
|
"fieldname": "half_day_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Half Day Date",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
@ -232,6 +263,70 @@
|
|||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "reason",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Reason",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Work From Home\nOn Duty",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Company",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Company",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -274,7 +369,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-04-14 15:38:14.344570",
|
"modified": "2018-05-14 18:18:56.936880",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Attendance Request",
|
"name": "Attendance Request",
|
||||||
|
@ -4,7 +4,58 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import date_diff, add_days, getdate
|
||||||
|
from erpnext.hr.doctype.employee.employee import is_holiday
|
||||||
|
from erpnext.hr.utils import validate_dates
|
||||||
|
|
||||||
class AttendanceRequest(Document):
|
class AttendanceRequest(Document):
|
||||||
pass
|
def validate(self):
|
||||||
|
validate_dates(self, self.from_date, self.to_date)
|
||||||
|
if self.half_day:
|
||||||
|
if not getdate(self.from_date)<=getdate(self.half_day_date)<=getdate(self.to_date):
|
||||||
|
frappe.throw(_("Half day date should be in between from date and to date"))
|
||||||
|
|
||||||
|
def on_submit(self):
|
||||||
|
self.create_attendance()
|
||||||
|
|
||||||
|
def on_cancel(self):
|
||||||
|
attendance_list = frappe.get_list("Attendance", {'employee': self.employee, 'attendance_request': self.name})
|
||||||
|
if attendance_list:
|
||||||
|
for attendance in attendance_list:
|
||||||
|
attendance_obj = frappe.get_doc("Attendance", attendance['name'])
|
||||||
|
attendance_obj.cancel()
|
||||||
|
|
||||||
|
def create_attendance(self):
|
||||||
|
request_days = date_diff(self.to_date, self.from_date) + 1
|
||||||
|
for number in range(request_days):
|
||||||
|
attendance_date = add_days(self.from_date, number)
|
||||||
|
skip_attendance = self.validate_if_attendance_not_applicable(attendance_date)
|
||||||
|
if not skip_attendance:
|
||||||
|
attendance = frappe.new_doc("Attendance")
|
||||||
|
attendance.employee = self.employee
|
||||||
|
attendance.employee_name = self.employee_name
|
||||||
|
if self.half_day and date_diff(getdate(self.half_day_date), getdate(attendance_date)) == 0:
|
||||||
|
attendance.status = "Half Day"
|
||||||
|
else:
|
||||||
|
attendance.status = "Present"
|
||||||
|
attendance.attendance_date = attendance_date
|
||||||
|
attendance.company = self.company
|
||||||
|
attendance.attendance_request = self.name
|
||||||
|
attendance.save(ignore_permissions=True)
|
||||||
|
attendance.submit()
|
||||||
|
|
||||||
|
def validate_if_attendance_not_applicable(self, attendance_date):
|
||||||
|
# Check if attendance_date is a Holiday
|
||||||
|
if is_holiday(self.employee, attendance_date):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Check if employee on Leave
|
||||||
|
leave_record = frappe.db.sql("""select half_day from `tabLeave Application`
|
||||||
|
where employee = %s and %s between from_date and to_date
|
||||||
|
and docstatus = 1""", (self.employee, attendance_date), as_dict=True)
|
||||||
|
if leave_record:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
@ -3,6 +3,12 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Compensatory Leave Request', {
|
frappe.ui.form.on('Compensatory Leave Request', {
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
|
frm.set_query("leave_type", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"is_compensatory": true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -4,9 +4,68 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import date_diff, add_days
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period
|
||||||
|
|
||||||
class CompensatoryLeaveRequest(Document):
|
class CompensatoryLeaveRequest(Document):
|
||||||
|
|
||||||
def validate_present(self):
|
def validate(self):
|
||||||
pass
|
validate_dates(self, self.work_from_date, self.work_end_date)
|
||||||
|
validate_overlap(self, self.work_from_date, self.work_end_date)
|
||||||
|
|
||||||
|
def on_submit(self):
|
||||||
|
if not self.leave_type:
|
||||||
|
frappe.throw(_("Please select a leave type to submit the request"))
|
||||||
|
else:
|
||||||
|
company = frappe.db.get_value("Employee", self.employee, "company")
|
||||||
|
date_difference = date_diff(self.work_end_date, self.work_from_date) + 1
|
||||||
|
leave_period = get_leave_period(self.work_from_date, self.work_end_date, company)
|
||||||
|
if leave_period:
|
||||||
|
leave_allocation = self.exists_allocation_for_period(leave_period)
|
||||||
|
if leave_allocation:
|
||||||
|
leave_allocation.new_leaves_allocated += date_difference
|
||||||
|
leave_allocation.submit()
|
||||||
|
else:
|
||||||
|
self.create_leave_allocation(leave_period, date_difference)
|
||||||
|
else:
|
||||||
|
frappe.throw(_("There is no leave period in between {0} and {1}").format(self.work_from_date, self.work_end_date))
|
||||||
|
|
||||||
|
def exists_allocation_for_period(self, leave_period):
|
||||||
|
leave_allocation = frappe.db.sql("""
|
||||||
|
select name
|
||||||
|
from `tabLeave Allocation`
|
||||||
|
where employee=%(employee)s and leave_type=%(leave_type)s
|
||||||
|
and docstatus=1
|
||||||
|
and (from_date between %(from_date)s and %(to_date)s
|
||||||
|
or to_date between %(from_date)s and %(to_date)s
|
||||||
|
or (from_date < %(from_date)s and to_date > %(to_date)s))
|
||||||
|
""", {
|
||||||
|
"from_date": leave_period[0].from_date,
|
||||||
|
"to_date": leave_period[0].to_date,
|
||||||
|
"employee": self.employee,
|
||||||
|
"leave_type": self.leave_type
|
||||||
|
}, as_dict=1)
|
||||||
|
|
||||||
|
if leave_allocation:
|
||||||
|
return frappe.get_doc("Leave Allocation", leave_allocation[0].name)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_leave_allocation(self, leave_period, date_difference):
|
||||||
|
is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward")
|
||||||
|
allocation = frappe.new_doc("Leave Allocation")
|
||||||
|
allocation.employee = self.employee
|
||||||
|
allocation.employee_name = self.employee_name
|
||||||
|
allocation.leave_type = self.leave_type
|
||||||
|
allocation.from_date = add_days(self.work_end_date, 1)
|
||||||
|
allocation.to_date = leave_period[0].to_date
|
||||||
|
allocation.new_leaves_allocated = date_difference
|
||||||
|
allocation.total_leaves_allocated = date_difference
|
||||||
|
allocation.compensatory_request = self.name
|
||||||
|
allocation.description = self.reason
|
||||||
|
if is_carry_forward == 1:
|
||||||
|
allocation.carry_forward = True
|
||||||
|
allocation.save(ignore_permissions = True)
|
||||||
|
allocation.submit()
|
||||||
|
@ -432,6 +432,71 @@
|
|||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "compensatory_request",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Compensatory Leave Request",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Compensatory Leave Request",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "leave_period",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Leave Period",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Leave Period",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -478,7 +543,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-11-10 18:41:38.845159",
|
"modified": "2018-04-19 19:34:59.248428",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Leave Allocation",
|
"name": "Leave Allocation",
|
||||||
|
@ -6,7 +6,7 @@ import frappe
|
|||||||
from frappe.utils import flt, date_diff, formatdate
|
from frappe.utils import flt, date_diff, formatdate
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.hr.utils import set_employee_name
|
from erpnext.hr.utils import set_employee_name, get_leave_period
|
||||||
from erpnext.hr.doctype.leave_application.leave_application import get_approved_leaves_for_period
|
from erpnext.hr.doctype.leave_application.leave_application import get_approved_leaves_for_period
|
||||||
|
|
||||||
class OverlapError(frappe.ValidationError): pass
|
class OverlapError(frappe.ValidationError): pass
|
||||||
@ -25,6 +25,20 @@ class LeaveAllocation(Document):
|
|||||||
self.validate_total_leaves_allocated()
|
self.validate_total_leaves_allocated()
|
||||||
self.validate_lwp()
|
self.validate_lwp()
|
||||||
set_employee_name(self)
|
set_employee_name(self)
|
||||||
|
self.validate_leave_allocation_days()
|
||||||
|
|
||||||
|
def validate_leave_allocation_days(self):
|
||||||
|
company = frappe.db.get_value("Employee", self.employee, "company")
|
||||||
|
leave_period = get_leave_period(self.from_date, self.to_date, company)
|
||||||
|
max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed")
|
||||||
|
if max_leaves_allowed > 0:
|
||||||
|
leave_allocated = 0
|
||||||
|
if leave_period:
|
||||||
|
leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type, leave_period[0].from_date, leave_period[0].to_date)
|
||||||
|
leave_allocated += self.new_leaves_allocated
|
||||||
|
if leave_allocated > max_leaves_allowed:
|
||||||
|
frappe.throw(_("Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period")\
|
||||||
|
.format(self.leave_type, self.employee))
|
||||||
|
|
||||||
def on_update_after_submit(self):
|
def on_update_after_submit(self):
|
||||||
self.validate_new_leaves_allocated_value()
|
self.validate_new_leaves_allocated_value()
|
||||||
@ -97,6 +111,29 @@ class LeaveAllocation(Document):
|
|||||||
else:
|
else:
|
||||||
frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError)
|
frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError)
|
||||||
|
|
||||||
|
def get_leave_allocation_for_period(employee, leave_type, from_date, to_date):
|
||||||
|
leave_allocated = 0
|
||||||
|
leave_allocations = frappe.db.sql("""
|
||||||
|
select employee, leave_type, from_date, to_date, total_leaves_allocated
|
||||||
|
from `tabLeave Allocation`
|
||||||
|
where employee=%(employee)s and leave_type=%(leave_type)s
|
||||||
|
and docstatus=1
|
||||||
|
and (from_date between %(from_date)s and %(to_date)s
|
||||||
|
or to_date between %(from_date)s and %(to_date)s
|
||||||
|
or (from_date < %(from_date)s and to_date > %(to_date)s))
|
||||||
|
""", {
|
||||||
|
"from_date": from_date,
|
||||||
|
"to_date": to_date,
|
||||||
|
"employee": employee,
|
||||||
|
"leave_type": leave_type
|
||||||
|
}, as_dict=1)
|
||||||
|
|
||||||
|
if leave_allocations:
|
||||||
|
for leave_alloc in leave_allocations:
|
||||||
|
leave_allocated += leave_alloc.total_leaves_allocated
|
||||||
|
|
||||||
|
return leave_allocated
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None):
|
def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None):
|
||||||
carry_forwarded_leaves = 0
|
carry_forwarded_leaves = 0
|
||||||
|
@ -35,6 +35,7 @@ class LeaveApplication(Document):
|
|||||||
self.validate_attendance()
|
self.validate_attendance()
|
||||||
if frappe.db.get_value("Leave Type", self.leave_type, 'is_optional_leave'):
|
if frappe.db.get_value("Leave Type", self.leave_type, 'is_optional_leave'):
|
||||||
self.validate_optional_leave()
|
self.validate_optional_leave()
|
||||||
|
self.validate_applicable_after()
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
if self.status == "Open" and self.docstatus < 1:
|
if self.status == "Open" and self.docstatus < 1:
|
||||||
@ -56,6 +57,21 @@ class LeaveApplication(Document):
|
|||||||
# notify leave applier about cancellation
|
# notify leave applier about cancellation
|
||||||
self.notify_employee()
|
self.notify_employee()
|
||||||
|
|
||||||
|
def validate_applicable_after(self):
|
||||||
|
if self.leave_type:
|
||||||
|
leave_type = frappe.get_doc("Leave Type", self.leave_type)
|
||||||
|
if leave_type.applicable_after > 0:
|
||||||
|
date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining")
|
||||||
|
leave_days = get_approved_leaves_for_period(self.employee, False, date_of_joining, self.from_date)
|
||||||
|
number_of_days = date_diff(getdate(self.from_date), date_of_joining)
|
||||||
|
if number_of_days >= 0:
|
||||||
|
holidays = 0
|
||||||
|
if not frappe.db.get_value("Leave Type", self.leave_type, "include_holiday"):
|
||||||
|
holidays = get_holidays(self.employee, date_of_joining, self.from_date)
|
||||||
|
number_of_days = number_of_days - leave_days - holidays
|
||||||
|
if number_of_days < leave_type.applicable_after:
|
||||||
|
frappe.throw(_("{0} applicable after {1} working days").format(self.leave_type, leave_type.applicable_after))
|
||||||
|
|
||||||
def validate_dates(self):
|
def validate_dates(self):
|
||||||
if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)):
|
if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)):
|
||||||
frappe.throw(_("To date cannot be before from date"))
|
frappe.throw(_("To date cannot be before from date"))
|
||||||
|
@ -7,7 +7,7 @@ import unittest
|
|||||||
|
|
||||||
from erpnext.hr.doctype.leave_application.leave_application import LeaveDayBlockedError, OverlapError, NotAnOptionalHoliday, get_leave_balance_on
|
from erpnext.hr.doctype.leave_application.leave_application import LeaveDayBlockedError, OverlapError, NotAnOptionalHoliday, get_leave_balance_on
|
||||||
from frappe.permissions import clear_user_permissions_for_doctype
|
from frappe.permissions import clear_user_permissions_for_doctype
|
||||||
from frappe.utils import add_days, nowdate
|
from frappe.utils import add_days, nowdate, now_datetime
|
||||||
|
|
||||||
test_dependencies = ["Leave Allocation", "Leave Block List"]
|
test_dependencies = ["Leave Allocation", "Leave Block List"]
|
||||||
|
|
||||||
@ -275,16 +275,119 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
self.assertEqual(get_leave_balance_on(employee.name, leave_type.name, today), 9)
|
self.assertEqual(get_leave_balance_on(employee.name, leave_type.name, today), 9)
|
||||||
|
|
||||||
def test_leaves_allowed(self):
|
def test_leaves_allowed(self):
|
||||||
# TODO: test cannot allocate more than max leaves
|
employee = get_employee()
|
||||||
pass
|
leave_period = get_leave_period()
|
||||||
|
frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1)
|
||||||
|
leave_type = frappe.get_doc(dict(
|
||||||
|
leave_type_name = 'Test Leave Type',
|
||||||
|
doctype = 'Leave Type',
|
||||||
|
max_leaves_allowed = 5
|
||||||
|
)).insert()
|
||||||
|
|
||||||
|
date = add_days(nowdate(), -7)
|
||||||
|
|
||||||
|
allocate_leaves(employee, leave_period, leave_type.name, 5)
|
||||||
|
|
||||||
|
leave_application = frappe.get_doc(dict(
|
||||||
|
doctype = 'Leave Application',
|
||||||
|
employee = employee.name,
|
||||||
|
leave_type = leave_type.name,
|
||||||
|
from_date = date,
|
||||||
|
to_date = add_days(date, 2),
|
||||||
|
company = "_Test Company",
|
||||||
|
docstatus = 1,
|
||||||
|
status = "Approved"
|
||||||
|
))
|
||||||
|
|
||||||
|
self.assertTrue(leave_application.insert())
|
||||||
|
|
||||||
|
leave_application = frappe.get_doc(dict(
|
||||||
|
doctype = 'Leave Application',
|
||||||
|
employee = employee.name,
|
||||||
|
leave_type = leave_type.name,
|
||||||
|
from_date = add_days(date, 4),
|
||||||
|
to_date = add_days(date, 7),
|
||||||
|
company = "_Test Company",
|
||||||
|
docstatus = 1,
|
||||||
|
status = "Approved"
|
||||||
|
))
|
||||||
|
self.assertRaises(frappe.ValidationError, leave_application.insert)
|
||||||
|
|
||||||
def test_applicable_after(self):
|
def test_applicable_after(self):
|
||||||
# TODO: test not applicable until applicable working days
|
employee = get_employee()
|
||||||
pass
|
leave_period = get_leave_period()
|
||||||
|
frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1)
|
||||||
|
leave_type = frappe.get_doc(dict(
|
||||||
|
leave_type_name = 'Test Leave Type',
|
||||||
|
doctype = 'Leave Type',
|
||||||
|
applicable_after = 15
|
||||||
|
)).insert()
|
||||||
|
|
||||||
|
date = add_days(nowdate(), -7)
|
||||||
|
|
||||||
|
allocate_leaves(employee, leave_period, leave_type.name, 10)
|
||||||
|
|
||||||
|
leave_application = frappe.get_doc(dict(
|
||||||
|
doctype = 'Leave Application',
|
||||||
|
employee = employee.name,
|
||||||
|
leave_type = leave_type.name,
|
||||||
|
from_date = date,
|
||||||
|
to_date = add_days(date, 4),
|
||||||
|
company = "_Test Company",
|
||||||
|
docstatus = 1,
|
||||||
|
status = "Approved"
|
||||||
|
))
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, leave_application.insert)
|
||||||
|
|
||||||
|
frappe.delete_doc_if_exists("Leave Type", "Test Leave Type 1", force=1)
|
||||||
|
leave_type_1 = frappe.get_doc(dict(
|
||||||
|
leave_type_name = 'Test Leave Type 1',
|
||||||
|
doctype = 'Leave Type'
|
||||||
|
)).insert()
|
||||||
|
|
||||||
|
allocate_leaves(employee, leave_period, leave_type_1.name, 10)
|
||||||
|
|
||||||
|
leave_application = frappe.get_doc(dict(
|
||||||
|
doctype = 'Leave Application',
|
||||||
|
employee = employee.name,
|
||||||
|
leave_type = leave_type_1.name,
|
||||||
|
from_date = date,
|
||||||
|
to_date = add_days(date, 4),
|
||||||
|
company = "_Test Company",
|
||||||
|
docstatus = 1,
|
||||||
|
status = "Approved"
|
||||||
|
))
|
||||||
|
|
||||||
|
self.assertTrue(leave_application.insert())
|
||||||
|
|
||||||
def test_max_continuous_leaves(self):
|
def test_max_continuous_leaves(self):
|
||||||
# TODO: test cannot take continuous leaves more than
|
employee = get_employee()
|
||||||
pass
|
leave_period = get_leave_period()
|
||||||
|
frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1)
|
||||||
|
leave_type = frappe.get_doc(dict(
|
||||||
|
leave_type_name = 'Test Leave Type',
|
||||||
|
doctype = 'Leave Type',
|
||||||
|
max_leaves_allowed = 15,
|
||||||
|
max_days_allowed = 3
|
||||||
|
)).insert()
|
||||||
|
|
||||||
|
date = add_days(nowdate(), -7)
|
||||||
|
|
||||||
|
allocate_leaves(employee, leave_period, leave_type.name, 10)
|
||||||
|
|
||||||
|
leave_application = frappe.get_doc(dict(
|
||||||
|
doctype = 'Leave Application',
|
||||||
|
employee = employee.name,
|
||||||
|
leave_type = leave_type.name,
|
||||||
|
from_date = date,
|
||||||
|
to_date = add_days(date, 4),
|
||||||
|
company = "_Test Company",
|
||||||
|
docstatus = 1,
|
||||||
|
status = "Approved"
|
||||||
|
))
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, leave_application.insert)
|
||||||
|
|
||||||
def test_earned_leave(self):
|
def test_earned_leave(self):
|
||||||
leave_period = get_leave_period()
|
leave_period = get_leave_period()
|
||||||
@ -320,3 +423,36 @@ def make_allocation_record(employee=None, leave_type=None):
|
|||||||
|
|
||||||
allocation.insert(ignore_permissions=True)
|
allocation.insert(ignore_permissions=True)
|
||||||
allocation.submit()
|
allocation.submit()
|
||||||
|
|
||||||
|
def get_employee():
|
||||||
|
return frappe.get_doc("Employee", "_T-Employee-00001")
|
||||||
|
|
||||||
|
def get_leave_period():
|
||||||
|
leave_period_name = frappe.db.exists({
|
||||||
|
"doctype": "Leave Period",
|
||||||
|
"name": "Test Leave Period"
|
||||||
|
})
|
||||||
|
if leave_period_name:
|
||||||
|
return frappe.get_doc("Leave Period", leave_period_name[0][0])
|
||||||
|
else:
|
||||||
|
return frappe.get_doc(dict(
|
||||||
|
name = 'Test Leave Period',
|
||||||
|
doctype = 'Leave Period',
|
||||||
|
from_date = "{0}-01-01".format(now_datetime().year),
|
||||||
|
to_date = "{0}-12-31".format(now_datetime().year),
|
||||||
|
company = "_Test Company",
|
||||||
|
is_active = 1
|
||||||
|
)).insert()
|
||||||
|
|
||||||
|
def allocate_leaves(employee, leave_period, leave_type, new_leaves_allocated, eligible_leaves=0):
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "Leave Allocation",
|
||||||
|
"__islocal": 1,
|
||||||
|
"employee": employee.name,
|
||||||
|
"employee_name": employee.employee_name,
|
||||||
|
"leave_type": leave_type,
|
||||||
|
"from_date": leave_period.from_date,
|
||||||
|
"to_date": leave_period.to_date,
|
||||||
|
"new_leaves_allocated": new_leaves_allocated,
|
||||||
|
"docstatus": 1
|
||||||
|
}).insert()
|
||||||
|
@ -2,13 +2,35 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Leave Period', {
|
frappe.ui.form.on('Leave Period', {
|
||||||
onload: function(frm) {
|
refresh: (frm)=>{
|
||||||
|
frm.set_df_property("grant_leaves", "hidden", frm.doc.__islocal ? 1:0);
|
||||||
|
},
|
||||||
|
from_date: (frm)=>{
|
||||||
|
if (frm.doc.from_date && !frm.doc.to_date) {
|
||||||
|
var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12);
|
||||||
|
frm.set_value("to_date", frappe.datetime.add_days(a_year_from_start, -1));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grant: (frm)=>{
|
||||||
|
frappe.call({
|
||||||
|
doc: frm.doc,
|
||||||
|
method: "grant_leave_allocation",
|
||||||
|
callback: function(r) {
|
||||||
|
if(!r.exc){
|
||||||
|
frm.reload_doc();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: __("Grant allocations......")
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onload: (frm) => {
|
||||||
frm.set_query("department", function() {
|
frm.set_query("department", function() {
|
||||||
return {
|
return {
|
||||||
"filters": {
|
"filters": {
|
||||||
"company": frm.doc.company,
|
"company": frm.doc.company,
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -478,7 +478,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-05-04 18:25:06.719932",
|
"modified": "2018-05-07 18:25:06.719932",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Leave Period",
|
"name": "Leave Period",
|
||||||
|
@ -4,7 +4,78 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import getdate, cstr
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from erpnext.hr.utils import validate_overlap, get_employee_leave_policy
|
||||||
|
|
||||||
class LeavePeriod(Document):
|
class LeavePeriod(Document):
|
||||||
pass
|
def get_employees(self):
|
||||||
|
conditions, values = [], []
|
||||||
|
for field in ["grade", "designation", "department"]:
|
||||||
|
if self.get(field):
|
||||||
|
conditions.append("{0}=%s".format(field))
|
||||||
|
values.append(self.get(field))
|
||||||
|
|
||||||
|
condition_str = " and " + " and ".join(conditions) if len(conditions) else ""
|
||||||
|
|
||||||
|
e = frappe.db.sql("select name from tabEmployee where status='Active' {condition}"
|
||||||
|
.format(condition=condition_str), tuple(values))
|
||||||
|
|
||||||
|
return e
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
self.validate_dates()
|
||||||
|
validate_overlap(self, self.from_date, self.to_date, self.company)
|
||||||
|
|
||||||
|
def grant_leave_allocation(self):
|
||||||
|
if self.employee:
|
||||||
|
self.grant_leave_alloc(self.employee)
|
||||||
|
else:
|
||||||
|
self.grant_leave_alloc_for_employees()
|
||||||
|
|
||||||
|
def grant_leave_alloc_for_employees(self):
|
||||||
|
employees = self.get_employees()
|
||||||
|
if employees:
|
||||||
|
for employee in employees:
|
||||||
|
self.grant_leave_alloc(cstr(employee[0]))
|
||||||
|
else:
|
||||||
|
frappe.msgprint(_("No employee found"))
|
||||||
|
|
||||||
|
def grant_leave_alloc(self, employee):
|
||||||
|
self.validate_allocation_exists(employee)
|
||||||
|
leave_policy = get_employee_leave_policy(employee)
|
||||||
|
if leave_policy:
|
||||||
|
for leave_policy_detail in leave_policy.leave_policy_details:
|
||||||
|
if not frappe.db.get_value("Leave Type", leave_policy_detail.leave_type, "is_lwp"):
|
||||||
|
self.create_leave_allocation(employee, leave_policy_detail.leave_type, leave_policy_detail.annual_allocation)
|
||||||
|
|
||||||
|
def validate_allocation_exists(self, employee):
|
||||||
|
leave_alloc = frappe.db.exists({
|
||||||
|
"doctype": "Leave Allocation",
|
||||||
|
"employee": employee,
|
||||||
|
"leave_period": self.name,
|
||||||
|
"docstatus": 1})
|
||||||
|
if leave_alloc:
|
||||||
|
frappe.throw(_("Employee {0} already have Leave Allocation {1} for this period").format(employee, leave_alloc[0][0])\
|
||||||
|
+ """ <b><a href="#Form/Leave Allocation/{0}">{0}</a></b>""".format(leave_alloc[0][0]))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_dates(self):
|
||||||
|
if getdate(self.from_date) >= getdate(self.to_date):
|
||||||
|
frappe.throw(_("To date can not be equal or less than from date"))
|
||||||
|
|
||||||
|
def create_leave_allocation(self, employee, leave_type, new_leaves_allocated):
|
||||||
|
allocation = frappe.new_doc("Leave Allocation")
|
||||||
|
allocation.employee = employee
|
||||||
|
allocation.employee_name = frappe.db.get_value("Employee", employee, "employee_name")
|
||||||
|
allocation.leave_type = leave_type
|
||||||
|
allocation.from_date = self.from_date
|
||||||
|
allocation.to_date = self.to_date
|
||||||
|
allocation.new_leaves_allocated = new_leaves_allocated
|
||||||
|
allocation.leave_period = self.name
|
||||||
|
if self.carry_forward_leaves:
|
||||||
|
if frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"):
|
||||||
|
allocation.carry_forward = self.carry_forward_leaves
|
||||||
|
allocation.save(ignore_permissions = True)
|
||||||
|
allocation.submit()
|
||||||
|
@ -2,7 +2,30 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Leave Policy', {
|
frappe.ui.form.on('Leave Policy', {
|
||||||
refresh: function(frm) {
|
});
|
||||||
|
|
||||||
|
frappe.ui.form.on('Leave Policy Detail',{
|
||||||
|
leave_type: function(frm, cdt, cdn) {
|
||||||
|
var child = locals[cdt][cdn];
|
||||||
|
if(child.leave_type){
|
||||||
|
frappe.call({
|
||||||
|
method: "frappe.client.get_value",
|
||||||
|
args: {
|
||||||
|
doctype: "Leave Type",
|
||||||
|
fieldname: "max_leaves_allowed",
|
||||||
|
filters: { name: child.leave_type }
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message) {
|
||||||
|
child.annual_allocation = r.message.max_leaves_allowed;
|
||||||
|
refresh_field("leave_policy_details");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
child.annual_allocation = "";
|
||||||
|
refresh_field("leave_policy_details");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -4,7 +4,13 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class LeavePolicy(Document):
|
class LeavePolicy(Document):
|
||||||
pass
|
def validate(self):
|
||||||
|
if self.leave_policy_details:
|
||||||
|
for lp_detail in self.leave_policy_details:
|
||||||
|
max_leaves_allowed = frappe.db.get_value("Leave Type", lp_detail.leave_type, "max_leaves_allowed")
|
||||||
|
if max_leaves_allowed > 0 and lp_detail.annual_allocation > max_leaves_allowed:
|
||||||
|
frappe.throw(_("Maximum leave allowed in the leave type {0} is {1}").format(lp_detail.leave_type, max_leaves_allowed))
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -72,6 +73,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -102,6 +104,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -133,6 +136,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -162,6 +166,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -193,6 +198,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -222,6 +228,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -252,6 +259,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -281,6 +289,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -310,6 +319,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -318,7 +328,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"fieldname": "is_parental_leave",
|
"fieldname": "is_compensatory",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
@ -327,7 +337,7 @@
|
|||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "Is Parental Leave",
|
"label": "Is Compensatory",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -340,6 +350,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -370,6 +381,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -401,6 +413,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -431,6 +444,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -462,6 +476,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -494,6 +509,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -524,6 +540,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -554,6 +571,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -586,6 +604,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -618,6 +637,7 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -632,7 +652,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-05-03 19:42:23.852331",
|
"modified": "2018-05-08 18:32:51.803472",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Leave Type",
|
"name": "Leave Type",
|
||||||
@ -640,7 +660,6 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"amend": 0,
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
"cancel": 0,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
@ -660,7 +679,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"amend": 0,
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
"cancel": 0,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
@ -680,7 +698,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"amend": 0,
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
"cancel": 0,
|
||||||
"create": 0,
|
"create": 0,
|
||||||
"delete": 0,
|
"delete": 0,
|
||||||
|
@ -4,8 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import formatdate, format_datetime
|
from frappe.utils import formatdate, format_datetime, getdate, get_datetime, nowdate
|
||||||
from frappe.utils import getdate, get_datetime
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.desk.form import assign_to
|
from frappe.desk.form import assign_to
|
||||||
|
|
||||||
@ -84,7 +83,6 @@ def get_onboarding_details(parent, parenttype):
|
|||||||
filters={"parent": parent, "parenttype": parenttype},
|
filters={"parent": parent, "parenttype": parenttype},
|
||||||
order_by= "idx")
|
order_by= "idx")
|
||||||
|
|
||||||
|
|
||||||
def set_employee_name(doc):
|
def set_employee_name(doc):
|
||||||
if doc.employee and not doc.employee_name:
|
if doc.employee and not doc.employee_name:
|
||||||
doc.employee_name = frappe.db.get_value("Employee", doc.employee, "employee_name")
|
doc.employee_name = frappe.db.get_value("Employee", doc.employee, "employee_name")
|
||||||
@ -139,6 +137,74 @@ def update_employee(employee, details, cancel=False):
|
|||||||
setattr(employee, item.fieldname, new_data)
|
setattr(employee, item.fieldname, new_data)
|
||||||
return employee
|
return employee
|
||||||
|
|
||||||
|
def validate_dates(doc, from_date, to_date):
|
||||||
|
date_of_joining, relieving_date = frappe.db.get_value("Employee", doc.employee, ["date_of_joining", "relieving_date"])
|
||||||
|
if getdate(from_date) > getdate(to_date):
|
||||||
|
frappe.throw(_("To date can not be less than from date"))
|
||||||
|
elif getdate(from_date) > getdate(nowdate()):
|
||||||
|
frappe.throw(_("Future dates not allowed"))
|
||||||
|
elif date_of_joining and getdate(from_date) < getdate(date_of_joining):
|
||||||
|
frappe.throw(_("From date can not be less than employee's joining date"))
|
||||||
|
elif relieving_date and getdate(to_date) > getdate(relieving_date):
|
||||||
|
frappe.throw(_("To date can not greater than employee's relieving date"))
|
||||||
|
|
||||||
|
def validate_overlap(doc, from_date, to_date, company = None):
|
||||||
|
query = """
|
||||||
|
select name
|
||||||
|
from `tab{0}`
|
||||||
|
where name != %(name)s
|
||||||
|
"""
|
||||||
|
query += get_doc_condition(doc.doctype)
|
||||||
|
|
||||||
|
if not doc.name:
|
||||||
|
# hack! if name is null, it could cause problems with !=
|
||||||
|
doc.name = "New "+doc.doctype
|
||||||
|
|
||||||
|
overlap_doc = frappe.db.sql(query.format(doc.doctype),{
|
||||||
|
"employee": doc.employee,
|
||||||
|
"from_date": from_date,
|
||||||
|
"to_date": to_date,
|
||||||
|
"name": doc.name,
|
||||||
|
"company": company
|
||||||
|
}, as_dict = 1)
|
||||||
|
|
||||||
|
if overlap_doc:
|
||||||
|
exists_for = doc.employee
|
||||||
|
if company:
|
||||||
|
exists_for = company
|
||||||
|
throw_overlap_error(doc, exists_for, overlap_doc[0].name, from_date, to_date)
|
||||||
|
|
||||||
|
def get_doc_condition(doctype):
|
||||||
|
if doctype == "Compensatory Leave Request":
|
||||||
|
return "and employee = %(employee)s and docstatus < 2 \
|
||||||
|
and (work_from_date between %(from_date)s and %(to_date)s \
|
||||||
|
or work_end_date between %(from_date)s and %(to_date)s \
|
||||||
|
or (work_from_date < %(from_date)s and work_end_date > %(to_date)s))"
|
||||||
|
elif doctype == "Leave Period":
|
||||||
|
return "and company = %(company)s and (from_date between %(from_date)s and %(to_date)s \
|
||||||
|
or to_date between %(from_date)s and %(to_date)s \
|
||||||
|
or (from_date < %(from_date)s and to_date > %(to_date)s))"
|
||||||
|
|
||||||
|
def throw_overlap_error(doc, exists_for, overlap_doc, from_date, to_date):
|
||||||
|
msg = _("A {0} exists between {1} and {2} (").format(doc.doctype,
|
||||||
|
formatdate(from_date), formatdate(to_date)) \
|
||||||
|
+ """ <b><a href="#Form/{0}/{1}">{1}</a></b>""".format(doc.doctype, overlap_doc) \
|
||||||
|
+ _(") for {0}").format(exists_for)
|
||||||
|
frappe.throw(msg)
|
||||||
|
|
||||||
|
def get_employee_leave_policy(employee):
|
||||||
|
leave_policy = frappe.db.get_value("Employee", employee, "leave_policy")
|
||||||
|
if not leave_policy:
|
||||||
|
employee_grade = frappe.db.get_value("Employee", employee, "grade")
|
||||||
|
if employee_grade:
|
||||||
|
leave_policy = frappe.db.get_value("Employee Grade", employee_grade, "default_leave_policy")
|
||||||
|
if not leave_policy:
|
||||||
|
frappe.throw(_("Employee {0} of grade {1} have no default leave policy").format(employee, employee_grade))
|
||||||
|
else:
|
||||||
|
frappe.throw(_("Employee {0} has no grade to get default leave policy").format(employee))
|
||||||
|
if leave_policy:
|
||||||
|
return frappe.get_doc("Leave Policy", leave_policy)
|
||||||
|
|
||||||
def validate_tax_declaration(declarations):
|
def validate_tax_declaration(declarations):
|
||||||
subcategories = []
|
subcategories = []
|
||||||
for declaration in declarations:
|
for declaration in declarations:
|
||||||
@ -168,4 +234,3 @@ def get_leave_period(from_date, to_date, company):
|
|||||||
|
|
||||||
if leave_period:
|
if leave_period:
|
||||||
return leave_period
|
return leave_period
|
||||||
|
|
||||||
|