Merge branch 'develop' of https://github.com/frappe/erpnext into project-link-for-all-accounts

This commit is contained in:
Deepesh Garg
2020-06-19 11:41:21 +05:30
176 changed files with 4541 additions and 2499 deletions

View File

@@ -18,7 +18,7 @@
{
"hidden": 0,
"label": "Leaves",
"links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Application\",\n \"name\": \"Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Allocation\",\n \"name\": \"Leave Allocation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Type\"\n ],\n \"label\": \"Leave Policy\",\n \"name\": \"Leave Policy\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Period\",\n \"name\": \"Leave Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Type\",\n \"name\": \"Leave Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Holiday List\",\n \"name\": \"Holiday List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Compensatory Leave Request\",\n \"name\": \"Compensatory Leave Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Encashment\",\n \"name\": \"Leave Encashment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Block List\",\n \"name\": \"Leave Block List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Application\"\n ],\n \"doctype\": \"Leave Application\",\n \"is_query_report\": true,\n \"label\": \"Employee Leave Balance\",\n \"name\": \"Employee Leave Balance\",\n \"type\": \"report\"\n }\n]"
"links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Application\",\n \"name\": \"Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Allocation\",\n \"name\": \"Leave Allocation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Type\"\n ],\n \"label\": \"Leave Policy\",\n \"name\": \"Leave Policy\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Period\",\n \"name\": \"Leave Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Type\",\n \"name\": \"Leave Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Holiday List\",\n \"name\": \"Holiday List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Compensatory Leave Request\",\n \"name\": \"Compensatory Leave Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Encashment\",\n \"name\": \"Leave Encashment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Block List\",\n \"name\": \"Leave Block List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Application\"\n ],\n \"doctype\": \"Leave Application\",\n \"is_query_report\": true,\n \"label\": \"Employee Leave Balance\",\n \"name\": \"Employee Leave Balance\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
@@ -93,7 +93,7 @@
"idx": 0,
"is_standard": 1,
"label": "HR",
"modified": "2020-05-28 13:36:07.710600",
"modified": "2020-06-16 19:20:50.976045",
"modified_by": "Administrator",
"module": "HR",
"name": "HR",
@@ -126,7 +126,7 @@
},
{
"label": "Salary Structure",
"link_to": "Payroll Entry",
"link_to": "Salary Structure",
"type": "DocType"
},
{

View File

@@ -19,11 +19,15 @@
"attendance_date",
"company",
"department",
"shift",
"attendance_request",
"amended_from",
"details_section",
"shift",
"in_time",
"out_time",
"column_break_18",
"late_entry",
"early_exit"
"early_exit",
"amended_from"
],
"fields": [
{
@@ -172,13 +176,36 @@
"fieldname": "early_exit",
"fieldtype": "Check",
"label": "Early Exit"
},
{
"fieldname": "details_section",
"fieldtype": "Section Break",
"label": "Details"
},
{
"depends_on": "shift",
"fieldname": "in_time",
"fieldtype": "Datetime",
"label": "In Time",
"read_only": 1
},
{
"depends_on": "shift",
"fieldname": "out_time",
"fieldtype": "Datetime",
"label": "Out Time",
"read_only": 1
},
{
"fieldname": "column_break_18",
"fieldtype": "Column Break"
}
],
"icon": "fa fa-ok",
"idx": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-04-11 11:40:14.319496",
"modified": "2020-05-29 13:51:37.177231",
"modified_by": "Administrator",
"module": "HR",
"name": "Attendance",

View File

@@ -19,7 +19,7 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters):
approvers = []
department_details = {}
department_list = []
employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver"], as_dict=True)
employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver", "expense_approver"], as_dict=True)
employee_department = filters.get("department") or employee.department
if employee_department:
@@ -33,10 +33,16 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters):
if filters.get("doctype") == "Leave Application" and employee.leave_approver:
approvers.append(frappe.db.get_value("User", employee.leave_approver, ['name', 'first_name', 'last_name']))
if filters.get("doctype") == "Expense Claim" and employee.expense_approver:
approvers.append(frappe.db.get_value("User", employee.expense_approver, ['name', 'first_name', 'last_name']))
if filters.get("doctype") == "Leave Application":
parentfield = "leave_approvers"
field_name = "Leave Approver"
else:
parentfield = "expense_approvers"
field_name = "Expense Approver"
if department_list:
for d in department_list:
approvers += frappe.db.sql("""select user.name, user.first_name, user.last_name from
@@ -46,4 +52,12 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters):
and approver.parentfield = %s
and approver.approver=user.name""",(d, "%" + txt + "%", parentfield), as_list=True)
if len(approvers) == 0:
frappe.throw(_("Please set {0} for the Employee or for Department: {1}").
format(
field_name, frappe.bold(employee_department),
frappe.bold(employee.name)
),
title=_(field_name + " Missing"))
return set(tuple(approver) for approver in approvers)

View File

@@ -62,6 +62,7 @@
"salary_mode",
"payroll_cost_center",
"column_break_52",
"expense_approver",
"bank_name",
"bank_ac_no",
"health_insurance_section",
@@ -205,7 +206,7 @@
"label": "Status",
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nActive\nLeft",
"options": "Active\nLeft",
"reqd": 1,
"search_index": 1
},
@@ -667,6 +668,7 @@
"oldfieldtype": "Date"
},
{
"depends_on": "eval:doc.status == \"Left\"",
"fieldname": "relieving_date",
"fieldtype": "Date",
"label": "Relieving Date",
@@ -797,13 +799,21 @@
{
"fieldname": "column_break_52",
"fieldtype": "Column Break"
},
{
"fieldname": "expense_approver",
"fieldtype": "Link",
"label": "Expense Approver",
"options": "User",
"show_days": 1,
"show_seconds": 1
}
],
"icon": "fa fa-user",
"idx": 24,
"image_field": "image",
"links": [],
"modified": "2020-05-05 18:51:03.152503",
"modified": "2020-06-18 18:01:27.223535",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee",

View File

@@ -139,13 +139,13 @@ frappe.ui.form.on('Employee Advance', {
employee: function (frm) {
if (frm.doc.employee) {
return frappe.call({
method: "erpnext.hr.doctype.employee_advance.employee_advance.get_due_advance_amount",
method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount",
args: {
"employee": frm.doc.employee,
"posting_date": frm.doc.posting_date
},
callback: function(r) {
frm.set_value("due_advance_amount",r.message);
frm.set_value("pending_amount",r.message);
}
});
}

View File

@@ -19,7 +19,7 @@
"column_break_11",
"advance_amount",
"paid_amount",
"due_advance_amount",
"pending_amount",
"claimed_amount",
"return_amount",
"section_break_7",
@@ -102,14 +102,6 @@
"options": "Company:company:default_currency",
"read_only": 1
},
{
"depends_on": "eval:cur_frm.doc.employee",
"fieldname": "due_advance_amount",
"fieldtype": "Currency",
"label": "Due Advance Amount",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "claimed_amount",
"fieldtype": "Currency",
@@ -177,11 +169,19 @@
"fieldname": "repay_unclaimed_amount_from_salary",
"fieldtype": "Check",
"label": "Repay unclaimed amount from salary"
},
{
"depends_on": "eval:cur_frm.doc.employee",
"fieldname": "pending_amount",
"fieldtype": "Currency",
"label": "Pending Amount",
"options": "Company:company:default_currency",
"read_only": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2020-03-06 15:11:33.747535",
"modified": "2020-06-12 12:42:39.833818",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Advance",

View File

@@ -95,7 +95,7 @@ class EmployeeAdvance(Document):
frappe.db.set_value("Employee Advance", self.name, "status", self.status)
@frappe.whitelist()
def get_due_advance_amount(employee, posting_date):
def get_pending_amount(employee, posting_date):
employee_due_amount = frappe.get_all("Employee Advance", \
filters = {"employee":employee, "docstatus":1, "posting_date":("<=", posting_date)}, \
fields = ["advance_amount", "paid_amount"])

View File

@@ -72,7 +72,7 @@ def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=N
return doc
def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, shift=None):
def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, in_time=None, out_time=None, shift=None):
"""Creates an attendance and links the attendance to the Employee Checkin.
Note: If attendance is already present for the given date, the logs are marked as skipped and no exception is thrown.
@@ -100,7 +100,9 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki
'company': employee_doc.company,
'shift': shift,
'late_entry': late_entry,
'early_exit': early_exit
'early_exit': early_exit,
'in_time': in_time,
'out_time': out_time
}
attendance = frappe.get_doc(doc_dict).insert()
attendance.submit()

View File

@@ -243,7 +243,6 @@ frappe.ui.form.on("Expense Claim", {
},
update_employee_advance_claimed_amount: function(frm) {
console.log("update_employee_advance_claimed_amount")
let amount_to_be_allocated = frm.doc.grand_total;
$.each(frm.doc.advances || [], function(i, advance){
if (amount_to_be_allocated >= advance.unclaimed_amount){
@@ -295,6 +294,16 @@ frappe.ui.form.on("Expense Claim", {
frm.events.get_advances(frm);
},
cost_center: function(frm) {
frm.events.set_child_cost_center(frm);
},
set_child_cost_center: function(frm){
(frm.doc.expenses || []).forEach(function(d) {
if (!d.cost_center){
d.cost_center = frm.doc.cost_center;
}
});
},
get_taxes: function(frm) {
if(frm.doc.taxes) {
frappe.call({
@@ -338,8 +347,7 @@ frappe.ui.form.on("Expense Claim", {
frappe.ui.form.on("Expense Claim Detail", {
expenses_add: function(frm, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
frm.script_manager.copy_from_first_row("expenses", row, ["cost_center"]);
frm.events.set_child_cost_center(frm);
},
amount: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];

View File

@@ -66,6 +66,7 @@
"fieldname": "employee",
"fieldtype": "Link",
"in_global_search": 1,
"in_standard_filter": 1,
"label": "From Employee",
"oldfieldname": "employee",
"oldfieldtype": "Link",
@@ -164,6 +165,7 @@
"default": "Today",
"fieldname": "posting_date",
"fieldtype": "Date",
"in_standard_filter": 1,
"label": "Posting Date",
"oldfieldname": "posting_date",
"oldfieldtype": "Date",
@@ -236,6 +238,7 @@
{
"fieldname": "company",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Company",
"oldfieldname": "company",
"oldfieldtype": "Link",
@@ -368,7 +371,7 @@
"idx": 1,
"is_submittable": 1,
"links": [],
"modified": "2019-12-14 23:52:05.388458",
"modified": "2020-06-15 12:43:04.099803",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim",

View File

@@ -38,11 +38,15 @@ frappe.ui.form.on("Leave Application", {
},
validate: function(frm) {
if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1){
frm.doc.half_day_date = frm.doc.from_date;
}
frm.toggle_reqd("half_day_date", frm.doc.half_day == 1);
},
make_dashboard: function(frm) {
var leave_details;
let lwps;
if (frm.doc.employee) {
frappe.call({
method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_details",
@@ -58,6 +62,7 @@ frappe.ui.form.on("Leave Application", {
if (!r.exc && r.message['leave_approver']) {
frm.set_value('leave_approver', r.message['leave_approver']);
}
lwps = r.message["lwps"];
}
});
$("div").remove(".form-dashboard-section.custom");
@@ -67,6 +72,18 @@ frappe.ui.form.on("Leave Application", {
})
);
frm.dashboard.show();
let allowed_leave_types = Object.keys(leave_details);
// lwps should be allowed, lwps don't have any allocation
allowed_leave_types = allowed_leave_types.concat(lwps);
frm.set_query('leave_type', function(){
return {
filters : [
['leave_type_name', 'in', allowed_leave_types]
]
};
});
}
},

View File

@@ -19,7 +19,6 @@ class NotAnOptionalHoliday(frappe.ValidationError): pass
from frappe.model.document import Document
class LeaveApplication(Document):
def get_feed(self):
return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type)
@@ -33,6 +32,7 @@ class LeaveApplication(Document):
self.validate_block_days()
self.validate_salary_processed_days()
self.validate_attendance()
self.set_half_day_date()
if frappe.db.get_value("Leave Type", self.leave_type, 'is_optional_leave'):
self.validate_optional_leave()
self.validate_applicable_after()
@@ -131,8 +131,6 @@ class LeaveApplication(Document):
for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
date = dt.strftime("%Y-%m-%d")
status = "Half Day" if getdate(date) == getdate(self.half_day_date) else "On Leave"
print("-------->>>", status)
# frappe.throw("Hello")
attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee,
attendance_date = date, docstatus = ('!=', 2)))
@@ -292,6 +290,10 @@ class LeaveApplication(Document):
frappe.throw(_("{0} is not in Optional Holiday List").format(formatdate(day)), NotAnOptionalHoliday)
day = add_days(day, 1)
def set_half_day_date(self):
if self.from_date == self.to_date and self.half_day == 1:
self.half_day_date = self.from_date
def notify_employee(self):
employee = frappe.get_doc("Employee", self.employee)
if not employee.user_id:
@@ -442,6 +444,7 @@ def get_leave_details(employee, date):
total_allocated_leaves = frappe.db.get_value('Leave Allocation', {
'from_date': ('<=', date),
'to_date': ('>=', date),
'employee': employee,
'leave_type': allocation.leave_type,
}, 'SUM(total_leaves_allocated)') or 0
@@ -459,9 +462,14 @@ def get_leave_details(employee, date):
"pending_leaves": leaves_pending,
"remaining_leaves": remaining_leaves}
#is used in set query
lwps = frappe.get_list("Leave Type", filters = {"is_lwp": 1})
lwps = [lwp.name for lwp in lwps]
ret = {
'leave_allocation': leave_allocation,
'leave_approver': get_leave_approver(employee)
'leave_approver': get_leave_approver(employee),
'lwps': lwps
}
return ret

View File

@@ -1,5 +1,5 @@
{% if data %}
{% if not jQuery.isEmptyObject(data) %}
<h5 style="margin-top: 20px;"> {{ __("Allocated Leaves") }} </h5>
<table class="table table-bordered small">
<thead>
@@ -11,7 +11,6 @@
<th style="width: 16%" class="text-right">{{ __("Pending Leaves") }}</th>
<th style="width: 16%" class="text-right">{{ __("Available Leaves") }}</th>
</tr>
</thead>
<tbody>
{% for(const [key, value] of Object.entries(data)) { %}
@@ -26,6 +25,6 @@
{% } %}
</tbody>
</table>
{% } else { %}
{% else %}
<p style="margin-top: 30px;"> No Leaves have been allocated. </p>
{% } %}
{% endif %}

View File

@@ -28,13 +28,14 @@ class ShiftType(Document):
logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time")
for key, group in itertools.groupby(logs, key=lambda x: (x['employee'], x['shift_actual_start'])):
single_shift_logs = list(group)
attendance_status, working_hours, late_entry, early_exit = self.get_attendance(single_shift_logs)
mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, self.name)
attendance_status, working_hours, late_entry, early_exit, in_time, out_time = self.get_attendance(single_shift_logs)
mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, in_time, out_time, self.name)
for employee in self.get_assigned_employee(self.process_attendance_after, True):
self.mark_absent_for_dates_with_no_attendance(employee)
def get_attendance(self, logs):
"""Return attendance_status, working_hours for a set of logs belonging to a single shift.
"""Return attendance_status, working_hours, late_entry, early_exit, in_time, out_time
for a set of logs belonging to a single shift.
Assumtion:
1. These logs belongs to an single shift, single employee and is not in a holiday date.
2. Logs are in chronological order
@@ -48,10 +49,10 @@ class ShiftType(Document):
early_exit = True
if self.working_hours_threshold_for_absent and total_working_hours < self.working_hours_threshold_for_absent:
return 'Absent', total_working_hours, late_entry, early_exit
return 'Absent', total_working_hours, late_entry, early_exit, in_time, out_time
if self.working_hours_threshold_for_half_day and total_working_hours < self.working_hours_threshold_for_half_day:
return 'Half Day', total_working_hours, late_entry, early_exit
return 'Present', total_working_hours, late_entry, early_exit
return 'Half Day', total_working_hours, late_entry, early_exit, in_time, out_time
return 'Present', total_working_hours, late_entry, early_exit, in_time, out_time
def mark_absent_for_dates_with_no_attendance(self, employee):
"""Marks Absents for the given employee on working days in this shift which have no attendance marked.

View File

@@ -73,11 +73,11 @@ class EmployeeBoardingController(Document):
def assign_task_to_users(self, task, users):
for user in users:
args = {
'assign_to' : user,
'doctype' : task.doctype,
'name' : task.name,
'description' : task.description or task.subject,
'notify': self.notify_users_by_email
'assign_to': [user],
'doctype': task.doctype,
'name': task.name,
'description': task.description or task.subject,
'notify': self.notify_users_by_email
}
assign_to.add(args)