chore: Remove HR and Payroll modules from ERPNext

This commit is contained in:
Rucha Mahabal 2022-06-10 12:51:29 +05:30
parent a117af253b
commit 0d34e589fd
830 changed files with 0 additions and 63757 deletions

View File

@ -1,6 +0,0 @@
Key features:
- Leave and Attendance
- Payroll
- Appraisal
- Expense Claim

View File

View File

@ -1,27 +0,0 @@
{
"chart_name": "Attendance Count",
"chart_type": "Report",
"creation": "2020-07-22 11:56:32.730068",
"custom_options": "{\n\t\t\"type\": \"line\",\n\t\t\"axisOptions\": {\n\t\t\t\"shortenYAxisNumbers\": 1\n\t\t},\n\t\t\"tooltipOptions\": {}\n\t}",
"docstatus": 0,
"doctype": "Dashboard Chart",
"dynamic_filters_json": "{\"month\":\"frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth() + 1\",\"year\":\"frappe.datetime.str_to_obj(frappe.datetime.get_today()).getFullYear();\",\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\"}",
"filters_json": "{}",
"group_by_type": "Count",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"modified": "2020-07-22 14:32:40.334424",
"modified_by": "Administrator",
"module": "HR",
"name": "Attendance Count",
"number_of_groups": 0,
"owner": "Administrator",
"report_name": "Monthly Attendance Sheet",
"time_interval": "Yearly",
"timeseries": 0,
"timespan": "Last Year",
"type": "Line",
"use_report_chart": 1,
"y_axis": []
}

View File

@ -1,29 +0,0 @@
{
"chart_name": "Department Wise Employee Count",
"chart_type": "Group By",
"creation": "2020-07-22 11:56:32.760730",
"custom_options": "",
"docstatus": 0,
"doctype": "Dashboard Chart",
"document_type": "Employee",
"dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]",
"group_by_based_on": "department",
"group_by_type": "Count",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"last_synced_on": "2020-07-22 14:27:40.574194",
"modified": "2020-07-22 14:33:38.036794",
"modified_by": "Administrator",
"module": "HR",
"name": "Department Wise Employee Count",
"number_of_groups": 0,
"owner": "Administrator",
"time_interval": "Yearly",
"timeseries": 0,
"timespan": "Last Year",
"type": "Donut",
"use_report_chart": 0,
"y_axis": []
}

View File

@ -1,29 +0,0 @@
{
"aggregate_function_based_on": "planned_vacancies",
"chart_name": "Department Wise Openings",
"chart_type": "Group By",
"creation": "2020-07-22 11:56:32.849775",
"custom_options": "",
"docstatus": 0,
"doctype": "Dashboard Chart",
"document_type": "Job Opening",
"filters_json": "[]",
"group_by_based_on": "department",
"group_by_type": "Sum",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"last_synced_on": "2020-07-22 14:33:44.834801",
"modified": "2020-07-22 14:34:45.273591",
"modified_by": "Administrator",
"module": "HR",
"name": "Department Wise Openings",
"number_of_groups": 0,
"owner": "Administrator",
"time_interval": "Monthly",
"timeseries": 0,
"timespan": "Last Year",
"type": "Bar",
"use_report_chart": 0,
"y_axis": []
}

View File

@ -1,29 +0,0 @@
{
"chart_name": "Designation Wise Employee Count",
"chart_type": "Group By",
"creation": "2020-07-22 11:56:32.790337",
"custom_options": "",
"docstatus": 0,
"doctype": "Dashboard Chart",
"document_type": "Employee",
"dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]",
"group_by_based_on": "designation",
"group_by_type": "Count",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"last_synced_on": "2020-07-22 14:27:40.602783",
"modified": "2020-07-22 14:31:49.665555",
"modified_by": "Administrator",
"module": "HR",
"name": "Designation Wise Employee Count",
"number_of_groups": 0,
"owner": "Administrator",
"time_interval": "Yearly",
"timeseries": 0,
"timespan": "Last Year",
"type": "Donut",
"use_report_chart": 0,
"y_axis": []
}

View File

@ -1,30 +0,0 @@
{
"aggregate_function_based_on": "planned_vacancies",
"chart_name": "Designation Wise Openings",
"chart_type": "Group By",
"creation": "2020-07-22 11:56:32.820217",
"custom_options": "",
"docstatus": 0,
"doctype": "Dashboard Chart",
"document_type": "Job Opening",
"dynamic_filters_json": "",
"filters_json": "[]",
"group_by_based_on": "designation",
"group_by_type": "Sum",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"last_synced_on": "2020-07-22 14:33:44.806626",
"modified": "2020-07-22 14:34:32.711881",
"modified_by": "Administrator",
"module": "HR",
"name": "Designation Wise Openings",
"number_of_groups": 0,
"owner": "Administrator",
"time_interval": "Monthly",
"timeseries": 0,
"timespan": "Last Year",
"type": "Bar",
"use_report_chart": 0,
"y_axis": []
}

View File

@ -1,29 +0,0 @@
{
"chart_name": "Gender Diversity Ratio",
"chart_type": "Group By",
"creation": "2020-07-22 11:56:32.667291",
"custom_options": "",
"docstatus": 0,
"doctype": "Dashboard Chart",
"document_type": "Employee",
"dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]",
"group_by_based_on": "gender",
"group_by_type": "Count",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"last_synced_on": "2020-07-22 14:27:40.143783",
"modified": "2020-07-22 14:32:50.962459",
"modified_by": "Administrator",
"module": "HR",
"name": "Gender Diversity Ratio",
"number_of_groups": 0,
"owner": "Administrator",
"time_interval": "Yearly",
"timeseries": 0,
"timespan": "Last Year",
"type": "Pie",
"use_report_chart": 0,
"y_axis": []
}

View File

@ -1,29 +0,0 @@
{
"chart_name": "Job Application Status",
"chart_type": "Group By",
"creation": "2020-07-22 11:56:32.699696",
"custom_options": "",
"docstatus": 0,
"doctype": "Dashboard Chart",
"document_type": "Job Applicant",
"dynamic_filters_json": "",
"filters_json": "[[\"Job Applicant\",\"creation\",\"Timespan\",\"last month\",false]]",
"group_by_based_on": "status",
"group_by_type": "Count",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"last_synced_on": "2020-07-28 16:19:12.109979",
"modified": "2020-07-28 16:19:45.279490",
"modified_by": "Administrator",
"module": "HR",
"name": "Job Application Status",
"number_of_groups": 0,
"owner": "Administrator",
"time_interval": "Yearly",
"timeseries": 0,
"timespan": "Last Year",
"type": "Pie",
"use_report_chart": 0,
"y_axis": []
}

View File

@ -1,30 +0,0 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Appointment Letter', {
appointment_letter_template: function(frm){
if (frm.doc.appointment_letter_template){
frappe.call({
method: 'erpnext.hr.doctype.appointment_letter.appointment_letter.get_appointment_letter_details',
args : {
template : frm.doc.appointment_letter_template
},
callback: function(r){
if(r.message){
let message_body = r.message;
frm.set_value("introduction", message_body[0].introduction);
frm.set_value("closing_notes", message_body[0].closing_notes);
frm.doc.terms = []
for (var i in message_body[1].description){
frm.add_child("terms");
frm.fields_dict.terms.get_value()[i].title = message_body[1].description[i].title;
frm.fields_dict.terms.get_value()[i].description = message_body[1].description[i].description;
}
frm.refresh();
}
}
});
}
},
});

View File

@ -1,128 +0,0 @@
{
"actions": [],
"autoname": "HR-APP-LETTER-.#####",
"creation": "2019-12-26 12:35:49.574828",
"default_print_format": "Standard Appointment Letter",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"job_applicant",
"applicant_name",
"column_break_3",
"company",
"appointment_date",
"appointment_letter_template",
"body_section",
"introduction",
"terms",
"closing_notes"
],
"fields": [
{
"fetch_from": "job_applicant.applicant_name",
"fieldname": "applicant_name",
"fieldtype": "Data",
"in_global_search": 1,
"in_list_view": 1,
"label": "Applicant Name",
"read_only": 1,
"reqd": 1
},
{
"fieldname": "appointment_date",
"fieldtype": "Date",
"label": "Appointment Date",
"reqd": 1
},
{
"fieldname": "appointment_letter_template",
"fieldtype": "Link",
"label": "Appointment Letter Template",
"options": "Appointment Letter Template",
"reqd": 1
},
{
"fetch_from": "appointment_letter_template.introduction",
"fieldname": "introduction",
"fieldtype": "Long Text",
"label": "Introduction",
"reqd": 1
},
{
"fieldname": "body_section",
"fieldtype": "Section Break",
"label": "Body"
},
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
{
"fieldname": "job_applicant",
"fieldtype": "Link",
"label": "Job Applicant",
"options": "Job Applicant",
"reqd": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"reqd": 1
},
{
"fieldname": "closing_notes",
"fieldtype": "Text",
"label": "Closing Notes"
},
{
"fieldname": "terms",
"fieldtype": "Table",
"label": "Terms",
"options": "Appointment Letter content",
"reqd": 1
}
],
"links": [],
"modified": "2022-01-18 19:27:35.649424",
"modified_by": "Administrator",
"module": "HR",
"name": "Appointment Letter",
"name_case": "Title Case",
"naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"share": 1,
"write": 1
}
],
"search_fields": "applicant_name, company",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "applicant_name",
"track_changes": 1
}

View File

@ -1,29 +0,0 @@
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
class AppointmentLetter(Document):
pass
@frappe.whitelist()
def get_appointment_letter_details(template):
body = []
intro = frappe.get_list(
"Appointment Letter Template",
fields=["introduction", "closing_notes"],
filters={"name": template},
)[0]
content = frappe.get_all(
"Appointment Letter content",
fields=["title", "description"],
filters={"parent": template},
order_by="idx",
)
body.append(intro)
body.append({"description": content})
return body

View File

@ -1,9 +0,0 @@
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# import frappe
import unittest
class TestAppointmentLetter(unittest.TestCase):
pass

View File

@ -1,39 +0,0 @@
{
"actions": [],
"creation": "2019-12-26 12:22:16.575767",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"title",
"description"
],
"fields": [
{
"fieldname": "title",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Title",
"reqd": 1
},
{
"fieldname": "description",
"fieldtype": "Long Text",
"in_list_view": 1,
"label": "Description",
"reqd": 1
}
],
"istable": 1,
"links": [],
"modified": "2019-12-26 12:24:09.824084",
"modified_by": "Administrator",
"module": "HR",
"name": "Appointment Letter content",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -1,10 +0,0 @@
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class AppointmentLettercontent(Document):
pass

View File

@ -1,8 +0,0 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Appointment Letter Template', {
// refresh: function(frm) {
// }
});

View File

@ -1,81 +0,0 @@
{
"actions": [],
"autoname": "field:template_name",
"creation": "2019-12-26 12:20:14.219578",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"template_name",
"introduction",
"terms",
"closing_notes"
],
"fields": [
{
"fieldname": "introduction",
"fieldtype": "Long Text",
"in_list_view": 1,
"label": "Introduction",
"reqd": 1
},
{
"fieldname": "closing_notes",
"fieldtype": "Text",
"label": "Closing Notes"
},
{
"fieldname": "terms",
"fieldtype": "Table",
"label": "Terms",
"options": "Appointment Letter content",
"reqd": 1
},
{
"fieldname": "template_name",
"fieldtype": "Data",
"label": "Template Name",
"reqd": 1,
"unique": 1
}
],
"links": [],
"modified": "2022-01-18 19:25:14.614616",
"modified_by": "Administrator",
"module": "HR",
"name": "Appointment Letter Template",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"share": 1,
"write": 1
}
],
"search_fields": "template_name",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "template_name",
"track_changes": 1
}

View File

@ -1,10 +0,0 @@
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class AppointmentLetterTemplate(Document):
pass

View File

@ -1,9 +0,0 @@
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# import frappe
import unittest
class TestAppointmentLetterTemplate(unittest.TestCase):
pass

View File

@ -1 +0,0 @@
Performance of an Employee in a Time Period against given goals.

View File

@ -1,79 +0,0 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on('Appraisal', {
setup: function(frm) {
frm.add_fetch('employee', 'company', 'company');
frm.add_fetch('employee', 'employee_name', 'employee_name');
frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
return{ query: "erpnext.controllers.queries.employee_query" }
};
},
onload: function(frm) {
if(!frm.doc.status) {
frm.set_value('status', 'Draft');
}
},
kra_template: function(frm) {
frm.doc.goals = [];
erpnext.utils.map_current_doc({
method: "erpnext.hr.doctype.appraisal.appraisal.fetch_appraisal_template",
source_name: frm.doc.kra_template,
frm: frm
});
},
calculate_total: function(frm) {
let goals = frm.doc.goals || [];
let total = 0;
if (goals == []) {
frm.set_value('total_score', 0);
return;
}
for (let i = 0; i<goals.length; i++) {
total = flt(total)+flt(goals[i].score_earned)
}
if (!isNaN(total)) {
frm.set_value('total_score', total);
frm.refresh_field('calculate_total');
}
},
set_score_earned: function(frm) {
let goals = frm.doc.goals || [];
for (let i = 0; i<goals.length; i++) {
var d = locals[goals[i].doctype][goals[i].name];
if (d.score && d.per_weightage) {
d.score_earned = flt(d.per_weightage*d.score, precision("score_earned", d))/100;
}
else {
d.score_earned = 0;
}
refresh_field('score_earned', d.name, 'goals');
}
frm.trigger('calculate_total');
}
});
frappe.ui.form.on('Appraisal Goal', {
score: function(frm, cdt, cdn) {
var d = locals[cdt][cdn];
if (flt(d.score) > 5) {
frappe.msgprint(__("Score must be less than or equal to 5"));
d.score = 0;
refresh_field('score', d.name, 'goals');
}
else {
frm.trigger('set_score_earned');
}
},
per_weightage: function(frm) {
frm.trigger('set_score_earned');
},
goals_remove: function(frm) {
frm.trigger('set_score_earned');
}
});

View File

@ -1,254 +0,0 @@
{
"actions": [],
"autoname": "naming_series:",
"creation": "2013-01-10 16:34:12",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
"field_order": [
"employee_details",
"naming_series",
"kra_template",
"employee",
"employee_name",
"column_break0",
"status",
"start_date",
"end_date",
"department",
"section_break0",
"goals",
"total_score",
"section_break1",
"remarks",
"other_details",
"company",
"column_break_17",
"amended_from"
],
"fields": [
{
"fieldname": "employee_details",
"fieldtype": "Section Break",
"oldfieldtype": "Section Break"
},
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"no_copy": 1,
"options": "HR-APR-.YY.-.MM.",
"print_hide": 1,
"reqd": 1,
"set_only_once": 1
},
{
"fieldname": "kra_template",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Appraisal Template",
"oldfieldname": "kra_template",
"oldfieldtype": "Link",
"options": "Appraisal Template",
"reqd": 1
},
{
"depends_on": "kra_template",
"fieldname": "employee",
"fieldtype": "Link",
"in_global_search": 1,
"in_standard_filter": 1,
"label": "For Employee",
"oldfieldname": "employee",
"oldfieldtype": "Link",
"options": "Employee",
"reqd": 1,
"search_index": 1
},
{
"depends_on": "kra_template",
"fieldname": "employee_name",
"fieldtype": "Data",
"in_global_search": 1,
"label": "For Employee Name",
"oldfieldname": "employee_name",
"oldfieldtype": "Data",
"read_only": 1
},
{
"depends_on": "kra_template",
"fieldname": "column_break0",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"width": "50%"
},
{
"default": "Draft",
"depends_on": "kra_template",
"fieldname": "status",
"fieldtype": "Select",
"in_standard_filter": 1,
"label": "Status",
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nDraft\nSubmitted\nCompleted\nCancelled",
"read_only": 1,
"reqd": 1,
"search_index": 1
},
{
"depends_on": "kra_template",
"fieldname": "start_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Start Date",
"oldfieldname": "start_date",
"oldfieldtype": "Date",
"reqd": 1
},
{
"depends_on": "kra_template",
"fieldname": "end_date",
"fieldtype": "Date",
"label": "End Date",
"oldfieldname": "end_date",
"oldfieldtype": "Date",
"reqd": 1
},
{
"fetch_from": "employee.department",
"fieldname": "department",
"fieldtype": "Link",
"label": "Department",
"options": "Department",
"read_only": 1
},
{
"depends_on": "kra_template",
"fieldname": "section_break0",
"fieldtype": "Section Break",
"label": "Goals",
"oldfieldtype": "Section Break",
"options": "Simple"
},
{
"fieldname": "goals",
"fieldtype": "Table",
"label": "Goals",
"oldfieldname": "appraisal_details",
"oldfieldtype": "Table",
"options": "Appraisal Goal"
},
{
"fieldname": "total_score",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Total Score (Out of 5)",
"no_copy": 1,
"oldfieldname": "total_score",
"oldfieldtype": "Currency",
"read_only": 1
},
{
"depends_on": "kra_template",
"fieldname": "section_break1",
"fieldtype": "Section Break"
},
{
"description": "Any other remarks, noteworthy effort that should go in the records.",
"fieldname": "remarks",
"fieldtype": "Text",
"label": "Remarks"
},
{
"depends_on": "kra_template",
"fieldname": "other_details",
"fieldtype": "Section Break"
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"remember_last_selected_value": 1,
"reqd": 1
},
{
"fieldname": "column_break_17",
"fieldtype": "Column Break"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 1,
"label": "Amended From",
"no_copy": 1,
"oldfieldname": "amended_from",
"oldfieldtype": "Data",
"options": "Appraisal",
"print_hide": 1,
"read_only": 1,
"report_hide": 1,
"width": "150px"
}
],
"icon": "fa fa-thumbs-up",
"idx": 1,
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-10-03 21:48:33.297065",
"modified_by": "Administrator",
"module": "HR",
"name": "Appraisal",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"email": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Employee",
"share": 1,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"share": 1,
"submit": 1,
"write": 1
}
],
"search_fields": "status, employee, employee_name",
"sort_field": "modified",
"sort_order": "DESC",
"timeline_field": "employee",
"title_field": "employee_name"
}

View File

@ -1,94 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
from frappe.utils import flt, getdate
from erpnext.hr.utils import set_employee_name, validate_active_employee
class Appraisal(Document):
def validate(self):
if not self.status:
self.status = "Draft"
if not self.goals:
frappe.throw(_("Goals cannot be empty"))
validate_active_employee(self.employee)
set_employee_name(self)
self.validate_dates()
self.validate_existing_appraisal()
self.calculate_total()
def get_employee_name(self):
self.employee_name = frappe.db.get_value("Employee", self.employee, "employee_name")
return self.employee_name
def validate_dates(self):
if getdate(self.start_date) > getdate(self.end_date):
frappe.throw(_("End Date can not be less than Start Date"))
def validate_existing_appraisal(self):
chk = frappe.db.sql(
"""select name from `tabAppraisal` where employee=%s
and (status='Submitted' or status='Completed')
and ((start_date>=%s and start_date<=%s)
or (end_date>=%s and end_date<=%s))""",
(self.employee, self.start_date, self.end_date, self.start_date, self.end_date),
)
if chk:
frappe.throw(
_("Appraisal {0} created for Employee {1} in the given date range").format(
chk[0][0], self.employee_name
)
)
def calculate_total(self):
total, total_w = 0, 0
for d in self.get("goals"):
if d.score:
d.score_earned = flt(d.score) * flt(d.per_weightage) / 100
total = total + d.score_earned
total_w += flt(d.per_weightage)
if int(total_w) != 100:
frappe.throw(
_("Total weightage assigned should be 100%.<br>It is {0}").format(str(total_w) + "%")
)
if (
frappe.db.get_value("Employee", self.employee, "user_id") != frappe.session.user and total == 0
):
frappe.throw(_("Total cannot be zero"))
self.total_score = total
def on_submit(self):
frappe.db.set(self, "status", "Submitted")
def on_cancel(self):
frappe.db.set(self, "status", "Cancelled")
@frappe.whitelist()
def fetch_appraisal_template(source_name, target_doc=None):
target_doc = get_mapped_doc(
"Appraisal Template",
source_name,
{
"Appraisal Template": {
"doctype": "Appraisal",
},
"Appraisal Template Goal": {
"doctype": "Appraisal Goal",
},
},
target_doc,
)
return target_doc

View File

@ -1,10 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
import unittest
# test_records = frappe.get_test_records('Appraisal')
class TestAppraisal(unittest.TestCase):
pass

View File

@ -1 +0,0 @@
Goal for the parent Appraisal.

View File

@ -1,220 +0,0 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "hash",
"beta": 0,
"creation": "2013-02-22 01:27:44",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"editable_grid": 1,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"description": "Key Responsibility Area",
"fieldname": "kra",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Goal",
"length": 0,
"no_copy": 0,
"oldfieldname": "kra",
"oldfieldtype": "Small Text",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "240px",
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "240px"
},
{
"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,
"fieldname": "per_weightage",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Weightage (%)",
"length": 0,
"no_copy": 0,
"oldfieldname": "per_weightage",
"oldfieldtype": "Currency",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "70px",
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "70px"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_4",
"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": "score",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Score (0-5)",
"length": 0,
"no_copy": 1,
"oldfieldname": "score",
"oldfieldtype": "Select",
"options": "",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "70px",
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "70px"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "section_break_6",
"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,
"fieldname": "score_earned",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Score Earned",
"length": 0,
"no_copy": 1,
"oldfieldname": "score_earned",
"oldfieldtype": "Currency",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "70px",
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "70px"
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Appraisal Goal",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"track_seen": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from frappe.model.document import Document
class AppraisalGoal(Document):
pass

View File

@ -1 +0,0 @@
Standard set of goals for an Employee / Designation / Job Profile. New Appraisal transactions can be created from the Template.

View File

@ -1,8 +0,0 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Appraisal Template', {
refresh: function(frm) {
}
});

View File

@ -1,170 +0,0 @@
{
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:kra_title",
"beta": 0,
"creation": "2012-07-03 13:30:39",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 0,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "kra_title",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Appraisal Template Title",
"length": 0,
"no_copy": 0,
"oldfieldname": "kra_title",
"oldfieldtype": "Data",
"permlevel": 0,
"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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"oldfieldname": "description",
"oldfieldtype": "Small Text",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "300px",
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "300px"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "goals",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Goals",
"length": 0,
"no_copy": 0,
"oldfieldname": "kra_sheet",
"oldfieldtype": "Table",
"options": "Appraisal Template Goal",
"permlevel": 0,
"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,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "icon-file-text",
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Appraisal Template",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Employee",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_order": "DESC",
"track_seen": 0
}

View File

@ -1,21 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import cint, flt
class AppraisalTemplate(Document):
def validate(self):
self.check_total_points()
def check_total_points(self):
total_points = 0
for d in self.get("goals"):
total_points += flt(d.per_weightage)
if cint(total_points) != 100:
frappe.throw(_("Sum of points for all goals should be 100. It is {0}").format(total_points))

View File

@ -1,7 +0,0 @@
def get_data():
return {
"fieldname": "kra_template",
"transactions": [
{"items": ["Appraisal"]},
],
}

View File

@ -1,10 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
import unittest
# test_records = frappe.get_test_records('Appraisal Template')
class TestAppraisalTemplate(unittest.TestCase):
pass

View File

@ -1 +0,0 @@
Goal details for the parent Appraisal Template.

View File

@ -1,91 +0,0 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "hash",
"beta": 0,
"creation": "2013-02-22 01:27:44",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"editable_grid": 1,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"description": "Key Performance Area",
"fieldname": "kra",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "KRA",
"length": 0,
"no_copy": 0,
"oldfieldname": "kra",
"oldfieldtype": "Small Text",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "200px",
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "200px"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "per_weightage",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Weightage (%)",
"length": 0,
"no_copy": 0,
"oldfieldname": "per_weightage",
"oldfieldtype": "Currency",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "100px",
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "100px"
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Appraisal Template Goal",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"track_seen": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from frappe.model.document import Document
class AppraisalTemplateGoal(Document):
pass

View File

@ -1 +0,0 @@
Attendance record of an Employee on a particular date.

View File

@ -1,15 +0,0 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
cur_frm.add_fetch('employee', 'company', 'company');
cur_frm.add_fetch('employee', 'employee_name', 'employee_name');
cur_frm.cscript.onload = function(doc, cdt, cdn) {
if(doc.__islocal) cur_frm.set_value("attendance_date", frappe.datetime.get_today());
}
cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
return{
query: "erpnext.controllers.queries.employee_query"
}
}

View File

@ -1,260 +0,0 @@
{
"actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-01-10 16:34:13",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
"field_order": [
"attendance_details",
"naming_series",
"employee",
"employee_name",
"working_hours",
"status",
"leave_type",
"leave_application",
"column_break0",
"attendance_date",
"company",
"department",
"attendance_request",
"details_section",
"shift",
"in_time",
"out_time",
"column_break_18",
"late_entry",
"early_exit",
"amended_from"
],
"fields": [
{
"fieldname": "attendance_details",
"fieldtype": "Section Break",
"oldfieldtype": "Section Break",
"options": "Simple"
},
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"no_copy": 1,
"oldfieldname": "naming_series",
"oldfieldtype": "Select",
"options": "HR-ATT-.YYYY.-",
"reqd": 1,
"set_only_once": 1
},
{
"fieldname": "employee",
"fieldtype": "Link",
"in_global_search": 1,
"in_standard_filter": 1,
"label": "Employee",
"oldfieldname": "employee",
"oldfieldtype": "Link",
"options": "Employee",
"reqd": 1,
"search_index": 1
},
{
"fetch_from": "employee.employee_name",
"fieldname": "employee_name",
"fieldtype": "Data",
"in_global_search": 1,
"label": "Employee Name",
"oldfieldname": "employee_name",
"oldfieldtype": "Data",
"read_only": 1
},
{
"depends_on": "working_hours",
"fieldname": "working_hours",
"fieldtype": "Float",
"label": "Working Hours",
"precision": "1",
"read_only": 1
},
{
"default": "Present",
"fieldname": "status",
"fieldtype": "Select",
"in_standard_filter": 1,
"label": "Status",
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nPresent\nAbsent\nOn Leave\nHalf Day\nWork From Home",
"reqd": 1,
"search_index": 1
},
{
"depends_on": "eval:in_list([\"On Leave\", \"Half Day\"], doc.status)",
"fieldname": "leave_type",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Leave Type",
"mandatory_depends_on": "eval:in_list([\"On Leave\", \"Half Day\"], doc.status)",
"oldfieldname": "leave_type",
"oldfieldtype": "Link",
"options": "Leave Type"
},
{
"fieldname": "leave_application",
"fieldtype": "Link",
"label": "Leave Application",
"no_copy": 1,
"options": "Leave Application",
"read_only": 1
},
{
"fieldname": "column_break0",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"width": "50%"
},
{
"fieldname": "attendance_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Attendance Date",
"oldfieldname": "attendance_date",
"oldfieldtype": "Date",
"reqd": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"remember_last_selected_value": 1,
"reqd": 1
},
{
"fetch_from": "employee.department",
"fieldname": "department",
"fieldtype": "Link",
"label": "Department",
"options": "Department",
"read_only": 1
},
{
"fieldname": "shift",
"fieldtype": "Link",
"label": "Shift",
"options": "Shift Type"
},
{
"fieldname": "attendance_request",
"fieldtype": "Link",
"label": "Attendance Request",
"options": "Attendance Request",
"read_only": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Amended From",
"no_copy": 1,
"options": "Attendance",
"print_hide": 1,
"read_only": 1
},
{
"default": "0",
"fieldname": "late_entry",
"fieldtype": "Check",
"label": "Late Entry"
},
{
"default": "0",
"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-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Attendance",
"owner": "Administrator",
"permissions": [
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"import": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"share": 1,
"submit": 1,
"write": 1
},
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"share": 1,
"submit": 1,
"write": 1
}
],
"search_fields": "employee,employee_name,attendance_date,status",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "employee_name"
}

View File

@ -1,390 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.query_builder import Criterion
from frappe.utils import cint, cstr, formatdate, get_datetime, get_link_to_form, getdate, nowdate
from erpnext.hr.doctype.shift_assignment.shift_assignment import has_overlapping_timings
from erpnext.hr.utils import get_holiday_dates_for_employee, validate_active_employee
class DuplicateAttendanceError(frappe.ValidationError):
pass
class OverlappingShiftAttendanceError(frappe.ValidationError):
pass
class Attendance(Document):
def validate(self):
from erpnext.controllers.status_updater import validate_status
validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
validate_active_employee(self.employee)
self.validate_attendance_date()
self.validate_duplicate_record()
self.validate_overlapping_shift_attendance()
self.validate_employee_status()
self.check_leave_record()
def on_cancel(self):
self.unlink_attendance_from_checkins()
def validate_attendance_date(self):
date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining")
# leaves can be marked for future dates
if (
self.status != "On Leave"
and not self.leave_application
and getdate(self.attendance_date) > getdate(nowdate())
):
frappe.throw(_("Attendance can not be marked for future dates"))
elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining):
frappe.throw(_("Attendance date can not be less than employee's joining date"))
def validate_duplicate_record(self):
duplicate = get_duplicate_attendance_record(
self.employee, self.attendance_date, self.shift, self.name
)
if duplicate:
frappe.throw(
_("Attendance for employee {0} is already marked for the date {1}: {2}").format(
frappe.bold(self.employee),
frappe.bold(self.attendance_date),
get_link_to_form("Attendance", duplicate[0].name),
),
title=_("Duplicate Attendance"),
exc=DuplicateAttendanceError,
)
def validate_overlapping_shift_attendance(self):
attendance = get_overlapping_shift_attendance(
self.employee, self.attendance_date, self.shift, self.name
)
if attendance:
frappe.throw(
_("Attendance for employee {0} is already marked for an overlapping shift {1}: {2}").format(
frappe.bold(self.employee),
frappe.bold(attendance.shift),
get_link_to_form("Attendance", attendance.name),
),
title=_("Overlapping Shift Attendance"),
exc=OverlappingShiftAttendanceError,
)
def validate_employee_status(self):
if frappe.db.get_value("Employee", self.employee, "status") == "Inactive":
frappe.throw(_("Cannot mark attendance for an Inactive employee {0}").format(self.employee))
def check_leave_record(self):
leave_record = frappe.db.sql(
"""
select leave_type, half_day, half_day_date
from `tabLeave Application`
where employee = %s
and %s between from_date and to_date
and status = 'Approved'
and docstatus = 1
""",
(self.employee, self.attendance_date),
as_dict=True,
)
if leave_record:
for d in leave_record:
self.leave_type = d.leave_type
if d.half_day_date == getdate(self.attendance_date):
self.status = "Half Day"
frappe.msgprint(
_("Employee {0} on Half day on {1}").format(self.employee, formatdate(self.attendance_date))
)
else:
self.status = "On Leave"
frappe.msgprint(
_("Employee {0} is on Leave on {1}").format(self.employee, formatdate(self.attendance_date))
)
if self.status in ("On Leave", "Half Day"):
if not leave_record:
frappe.msgprint(
_("No leave record found for employee {0} on {1}").format(
self.employee, formatdate(self.attendance_date)
),
alert=1,
)
elif self.leave_type:
self.leave_type = None
self.leave_application = None
def validate_employee(self):
emp = frappe.db.sql(
"select name from `tabEmployee` where name = %s and status = 'Active'", self.employee
)
if not emp:
frappe.throw(_("Employee {0} is not active or does not exist").format(self.employee))
def unlink_attendance_from_checkins(self):
EmployeeCheckin = frappe.qb.DocType("Employee Checkin")
linked_logs = (
frappe.qb.from_(EmployeeCheckin)
.select(EmployeeCheckin.name)
.where(EmployeeCheckin.attendance == self.name)
.for_update()
.run(as_dict=True)
)
if linked_logs:
(
frappe.qb.update(EmployeeCheckin)
.set("attendance", "")
.where(EmployeeCheckin.attendance == self.name)
).run()
frappe.msgprint(
msg=_("Unlinked Attendance record from Employee Checkins: {}").format(
", ".join(get_link_to_form("Employee Checkin", log.name) for log in linked_logs)
),
title=_("Unlinked logs"),
indicator="blue",
is_minimizable=True,
wide=True,
)
def get_duplicate_attendance_record(employee, attendance_date, shift, name=None):
attendance = frappe.qb.DocType("Attendance")
query = (
frappe.qb.from_(attendance)
.select(attendance.name)
.where((attendance.employee == employee) & (attendance.docstatus < 2))
)
if shift:
query = query.where(
Criterion.any(
[
Criterion.all(
[
((attendance.shift.isnull()) | (attendance.shift == "")),
(attendance.attendance_date == attendance_date),
]
),
Criterion.all(
[
((attendance.shift.isnotnull()) | (attendance.shift != "")),
(attendance.attendance_date == attendance_date),
(attendance.shift == shift),
]
),
]
)
)
else:
query = query.where((attendance.attendance_date == attendance_date))
if name:
query = query.where(attendance.name != name)
return query.run(as_dict=True)
def get_overlapping_shift_attendance(employee, attendance_date, shift, name=None):
if not shift:
return {}
attendance = frappe.qb.DocType("Attendance")
query = (
frappe.qb.from_(attendance)
.select(attendance.name, attendance.shift)
.where(
(attendance.employee == employee)
& (attendance.docstatus < 2)
& (attendance.attendance_date == attendance_date)
& (attendance.shift != shift)
)
)
if name:
query = query.where(attendance.name != name)
overlapping_attendance = query.run(as_dict=True)
if overlapping_attendance and has_overlapping_timings(shift, overlapping_attendance[0].shift):
return overlapping_attendance[0]
return {}
@frappe.whitelist()
def get_events(start, end, filters=None):
events = []
employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user})
if not employee:
return events
from frappe.desk.reportview import get_filters_cond
conditions = get_filters_cond("Attendance", filters, [])
add_attendance(events, start, end, conditions=conditions)
return events
def add_attendance(events, start, end, conditions=None):
query = """select name, attendance_date, status
from `tabAttendance` where
attendance_date between %(from_date)s and %(to_date)s
and docstatus < 2"""
if conditions:
query += conditions
for d in frappe.db.sql(query, {"from_date": start, "to_date": end}, as_dict=True):
e = {
"name": d.name,
"doctype": "Attendance",
"start": d.attendance_date,
"end": d.attendance_date,
"title": cstr(d.status),
"docstatus": d.docstatus,
}
if e not in events:
events.append(e)
def mark_attendance(
employee,
attendance_date,
status,
shift=None,
leave_type=None,
ignore_validate=False,
late_entry=False,
early_exit=False,
):
if get_duplicate_attendance_record(employee, attendance_date, shift):
return
if get_overlapping_shift_attendance(employee, attendance_date, shift):
return
company = frappe.db.get_value("Employee", employee, "company")
attendance = frappe.get_doc(
{
"doctype": "Attendance",
"employee": employee,
"attendance_date": attendance_date,
"status": status,
"company": company,
"shift": shift,
"leave_type": leave_type,
"late_entry": late_entry,
"early_exit": early_exit,
}
)
attendance.flags.ignore_validate = ignore_validate
attendance.insert()
attendance.submit()
return attendance.name
@frappe.whitelist()
def mark_bulk_attendance(data):
import json
if isinstance(data, str):
data = json.loads(data)
data = frappe._dict(data)
company = frappe.get_value("Employee", data.employee, "company")
if not data.unmarked_days:
frappe.throw(_("Please select a date."))
return
for date in data.unmarked_days:
doc_dict = {
"doctype": "Attendance",
"employee": data.employee,
"attendance_date": get_datetime(date),
"status": data.status,
"company": company,
}
attendance = frappe.get_doc(doc_dict).insert()
attendance.submit()
def get_month_map():
return frappe._dict(
{
"January": 1,
"February": 2,
"March": 3,
"April": 4,
"May": 5,
"June": 6,
"July": 7,
"August": 8,
"September": 9,
"October": 10,
"November": 11,
"December": 12,
}
)
@frappe.whitelist()
def get_unmarked_days(employee, month, exclude_holidays=0):
import calendar
month_map = get_month_map()
today = get_datetime()
joining_date, relieving_date = frappe.get_cached_value(
"Employee", employee, ["date_of_joining", "relieving_date"]
)
start_day = 1
end_day = calendar.monthrange(today.year, month_map[month])[1] + 1
if joining_date and joining_date.month == month_map[month]:
start_day = joining_date.day
if relieving_date and relieving_date.month == month_map[month]:
end_day = relieving_date.day + 1
dates_of_month = [
"{}-{}-{}".format(today.year, month_map[month], r) for r in range(start_day, end_day)
]
month_start, month_end = dates_of_month[0], dates_of_month[-1]
records = frappe.get_all(
"Attendance",
fields=["attendance_date", "employee"],
filters=[
["attendance_date", ">=", month_start],
["attendance_date", "<=", month_end],
["employee", "=", employee],
["docstatus", "!=", 2],
],
)
marked_days = [get_datetime(record.attendance_date) for record in records]
if cint(exclude_holidays):
holiday_dates = get_holiday_dates_for_employee(employee, month_start, month_end)
holidays = [get_datetime(record) for record in holiday_dates]
marked_days.extend(holidays)
unmarked_days = []
for date in dates_of_month:
date_time = get_datetime(date)
if today.day <= date_time.day and today.month <= date_time.month:
break
if date_time not in marked_days:
unmarked_days.append(date)
return unmarked_days

View File

@ -1,12 +0,0 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.views.calendar["Attendance"] = {
options: {
header: {
left: 'prev,next today',
center: 'title',
right: 'month'
}
},
get_events_method: "erpnext.hr.doctype.attendance.attendance.get_events"
};

View File

@ -1,2 +0,0 @@
def get_data():
return {"fieldname": "attendance", "transactions": [{"label": "", "items": ["Employee Checkin"]}]}

View File

@ -1,164 +0,0 @@
frappe.listview_settings['Attendance'] = {
add_fields: ["status", "attendance_date"],
get_indicator: function (doc) {
if (["Present", "Work From Home"].includes(doc.status)) {
return [__(doc.status), "green", "status,=," + doc.status];
} else if (["Absent", "On Leave"].includes(doc.status)) {
return [__(doc.status), "red", "status,=," + doc.status];
} else if (doc.status == "Half Day") {
return [__(doc.status), "orange", "status,=," + doc.status];
}
},
onload: function(list_view) {
let me = this;
const months = moment.months();
list_view.page.add_inner_button(__("Mark Attendance"), function() {
let dialog = new frappe.ui.Dialog({
title: __("Mark Attendance"),
fields: [{
fieldname: 'employee',
label: __('For Employee'),
fieldtype: 'Link',
options: 'Employee',
get_query: () => {
return {query: "erpnext.controllers.queries.employee_query"};
},
reqd: 1,
onchange: function() {
dialog.set_df_property("unmarked_days", "hidden", 1);
dialog.set_df_property("status", "hidden", 1);
dialog.set_df_property("exclude_holidays", "hidden", 1);
dialog.set_df_property("month", "value", '');
dialog.set_df_property("unmarked_days", "options", []);
dialog.no_unmarked_days_left = false;
}
},
{
label: __("For Month"),
fieldtype: "Select",
fieldname: "month",
options: months,
reqd: 1,
onchange: function() {
if (dialog.fields_dict.employee.value && dialog.fields_dict.month.value) {
dialog.set_df_property("status", "hidden", 0);
dialog.set_df_property("exclude_holidays", "hidden", 0);
dialog.set_df_property("unmarked_days", "options", []);
dialog.no_unmarked_days_left = false;
me.get_multi_select_options(
dialog.fields_dict.employee.value,
dialog.fields_dict.month.value,
dialog.fields_dict.exclude_holidays.get_value()
).then(options => {
if (options.length > 0) {
dialog.set_df_property("unmarked_days", "hidden", 0);
dialog.set_df_property("unmarked_days", "options", options);
} else {
dialog.no_unmarked_days_left = true;
}
});
}
}
},
{
label: __("Status"),
fieldtype: "Select",
fieldname: "status",
options: ["Present", "Absent", "Half Day", "Work From Home"],
hidden: 1,
reqd: 1,
},
{
label: __("Exclude Holidays"),
fieldtype: "Check",
fieldname: "exclude_holidays",
hidden: 1,
onchange: function() {
if (dialog.fields_dict.employee.value && dialog.fields_dict.month.value) {
dialog.set_df_property("status", "hidden", 0);
dialog.set_df_property("unmarked_days", "options", []);
dialog.no_unmarked_days_left = false;
me.get_multi_select_options(
dialog.fields_dict.employee.value,
dialog.fields_dict.month.value,
dialog.fields_dict.exclude_holidays.get_value()
).then(options => {
if (options.length > 0) {
dialog.set_df_property("unmarked_days", "hidden", 0);
dialog.set_df_property("unmarked_days", "options", options);
} else {
dialog.no_unmarked_days_left = true;
}
});
}
}
},
{
label: __("Unmarked Attendance for days"),
fieldname: "unmarked_days",
fieldtype: "MultiCheck",
options: [],
columns: 2,
hidden: 1
}],
primary_action(data) {
if (cur_dialog.no_unmarked_days_left) {
frappe.msgprint(__("Attendance for the month of {0} , has already been marked for the Employee {1}",
[dialog.fields_dict.month.value, dialog.fields_dict.employee.value]));
} else {
frappe.confirm(__('Mark attendance as {0} for {1} on selected dates?', [data.status, data.month]), () => {
frappe.call({
method: "erpnext.hr.doctype.attendance.attendance.mark_bulk_attendance",
args: {
data: data
},
callback: function (r) {
if (r.message === 1) {
frappe.show_alert({
message: __("Attendance Marked"),
indicator: 'blue'
});
cur_dialog.hide();
}
}
});
});
}
dialog.hide();
list_view.refresh();
},
primary_action_label: __('Mark Attendance')
});
dialog.show();
});
},
get_multi_select_options: function(employee, month, exclude_holidays) {
return new Promise(resolve => {
frappe.call({
method: 'erpnext.hr.doctype.attendance.attendance.get_unmarked_days',
async: false,
args: {
employee: employee,
month: month,
exclude_holidays: exclude_holidays
}
}).then(r => {
var options = [];
for (var d in r.message) {
var momentObj = moment(r.message[d], 'YYYY-MM-DD');
var date = momentObj.format('DD-MM-YYYY');
options.push({
"label": date,
"value": r.message[d],
"checked": 1
});
}
resolve(options);
});
});
}
};

View File

@ -1,232 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import (
add_days,
add_months,
get_last_day,
get_year_ending,
get_year_start,
getdate,
nowdate,
)
from erpnext.hr.doctype.attendance.attendance import (
DuplicateAttendanceError,
OverlappingShiftAttendanceError,
get_month_map,
get_unmarked_days,
mark_attendance,
)
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.hr.doctype.leave_application.test_leave_application import get_first_sunday
test_records = frappe.get_test_records("Attendance")
class TestAttendance(FrappeTestCase):
def setUp(self):
from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
from_date = get_year_start(getdate())
to_date = get_year_ending(getdate())
self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date)
frappe.db.delete("Attendance")
def test_duplicate_attendance(self):
employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company")
date = nowdate()
mark_attendance(employee, date, "Present")
attendance = frappe.get_doc(
{
"doctype": "Attendance",
"employee": employee,
"attendance_date": date,
"status": "Absent",
"company": "_Test Company",
}
)
self.assertRaises(DuplicateAttendanceError, attendance.insert)
def test_duplicate_attendance_with_shift(self):
from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type
employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company")
date = nowdate()
shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
mark_attendance(employee, date, "Present", shift=shift_1.name)
# attendance record with shift
attendance = frappe.get_doc(
{
"doctype": "Attendance",
"employee": employee,
"attendance_date": date,
"status": "Absent",
"company": "_Test Company",
"shift": shift_1.name,
}
)
self.assertRaises(DuplicateAttendanceError, attendance.insert)
# attendance record without any shift
attendance = frappe.get_doc(
{
"doctype": "Attendance",
"employee": employee,
"attendance_date": date,
"status": "Absent",
"company": "_Test Company",
}
)
self.assertRaises(DuplicateAttendanceError, attendance.insert)
def test_overlapping_shift_attendance_validation(self):
from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type
employee = make_employee("test_overlap_attendance@example.com", company="_Test Company")
date = nowdate()
shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
shift_2 = setup_shift_type(shift_type="Shift 2", start_time="09:30:00", end_time="11:00:00")
mark_attendance(employee, date, "Present", shift=shift_1.name)
# attendance record with overlapping shift
attendance = frappe.get_doc(
{
"doctype": "Attendance",
"employee": employee,
"attendance_date": date,
"status": "Absent",
"company": "_Test Company",
"shift": shift_2.name,
}
)
self.assertRaises(OverlappingShiftAttendanceError, attendance.insert)
def test_allow_attendance_with_different_shifts(self):
# allows attendance with 2 different non-overlapping shifts
from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type
employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company")
date = nowdate()
shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
shift_2 = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="12:00:00")
mark_attendance(employee, date, "Present", shift_1.name)
frappe.get_doc(
{
"doctype": "Attendance",
"employee": employee,
"attendance_date": date,
"status": "Absent",
"company": "_Test Company",
"shift": shift_2.name,
}
).insert()
def test_mark_absent(self):
employee = make_employee("test_mark_absent@example.com")
date = nowdate()
attendance = mark_attendance(employee, date, "Absent")
fetch_attendance = frappe.get_value(
"Attendance", {"employee": employee, "attendance_date": date, "status": "Absent"}
)
self.assertEqual(attendance, fetch_attendance)
def test_unmarked_days(self):
first_sunday = get_first_sunday(
self.holiday_list, for_date=get_last_day(add_months(getdate(), -1))
)
attendance_date = add_days(first_sunday, 1)
employee = make_employee(
"test_unmarked_days@example.com", date_of_joining=add_days(attendance_date, -1)
)
frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list)
mark_attendance(employee, attendance_date, "Present")
month_name = get_month_name(attendance_date)
unmarked_days = get_unmarked_days(employee, month_name)
unmarked_days = [getdate(date) for date in unmarked_days]
# attendance already marked for the day
self.assertNotIn(attendance_date, unmarked_days)
# attendance unmarked
self.assertIn(getdate(add_days(attendance_date, 1)), unmarked_days)
# holiday considered in unmarked days
self.assertIn(first_sunday, unmarked_days)
def test_unmarked_days_excluding_holidays(self):
first_sunday = get_first_sunday(
self.holiday_list, for_date=get_last_day(add_months(getdate(), -1))
)
attendance_date = add_days(first_sunday, 1)
employee = make_employee(
"test_unmarked_days@example.com", date_of_joining=add_days(attendance_date, -1)
)
frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list)
mark_attendance(employee, attendance_date, "Present")
month_name = get_month_name(attendance_date)
unmarked_days = get_unmarked_days(employee, month_name, exclude_holidays=True)
unmarked_days = [getdate(date) for date in unmarked_days]
# attendance already marked for the day
self.assertNotIn(attendance_date, unmarked_days)
# attendance unmarked
self.assertIn(getdate(add_days(attendance_date, 1)), unmarked_days)
# holidays not considered in unmarked days
self.assertNotIn(first_sunday, unmarked_days)
def test_unmarked_days_as_per_joining_and_relieving_dates(self):
first_sunday = get_first_sunday(
self.holiday_list, for_date=get_last_day(add_months(getdate(), -1))
)
date = add_days(first_sunday, 1)
doj = add_days(date, 1)
relieving_date = add_days(date, 5)
employee = make_employee(
"test_unmarked_days_as_per_doj@example.com", date_of_joining=doj, relieving_date=relieving_date
)
frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list)
attendance_date = add_days(date, 2)
mark_attendance(employee, attendance_date, "Present")
month_name = get_month_name(attendance_date)
unmarked_days = get_unmarked_days(employee, month_name)
unmarked_days = [getdate(date) for date in unmarked_days]
# attendance already marked for the day
self.assertNotIn(attendance_date, unmarked_days)
# date before doj not in unmarked days
self.assertNotIn(add_days(doj, -1), unmarked_days)
# date after relieving not in unmarked days
self.assertNotIn(add_days(relieving_date, 1), unmarked_days)
def tearDown(self):
frappe.db.rollback()
def get_month_name(date):
month_number = date.month
for month, number in get_month_map().items():
if number == month_number:
return month

View File

@ -1,10 +0,0 @@
[
{
"doctype": "Attendance",
"name": "_Test Attendance 1",
"employee": "_T-Employee-00001",
"status": "Present",
"attendance_date": "2014-02-01",
"company": "_Test Company"
}
]

View File

@ -1,14 +0,0 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
cur_frm.add_fetch('employee', 'company', 'company');
frappe.ui.form.on('Attendance Request', {
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);
}
}
});

View File

@ -1,190 +0,0 @@
{
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "HR-ARQ-.YY.-.MM.-.#####",
"creation": "2018-04-13 15:37:40.918990",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"employee",
"employee_name",
"department",
"column_break_5",
"company",
"from_date",
"to_date",
"half_day",
"half_day_date",
"reason_section",
"reason",
"column_break_4",
"explanation",
"amended_from"
],
"fields": [
{
"fieldname": "employee",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Employee",
"options": "Employee",
"reqd": 1
},
{
"fetch_from": "employee.employee_name",
"fieldname": "employee_name",
"fieldtype": "Data",
"label": "Employee Name",
"read_only": 1
},
{
"fetch_from": "employee.department",
"fieldname": "department",
"fieldtype": "Link",
"label": "Department",
"options": "Department",
"read_only": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"remember_last_selected_value": 1,
"reqd": 1
},
{
"fieldname": "column_break_5",
"fieldtype": "Column Break"
},
{
"fieldname": "from_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "From Date",
"reqd": 1
},
{
"fieldname": "to_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "To Date",
"reqd": 1
},
{
"default": "0",
"fieldname": "half_day",
"fieldtype": "Check",
"label": "Half Day"
},
{
"depends_on": "half_day",
"fieldname": "half_day_date",
"fieldtype": "Date",
"label": "Half Day Date"
},
{
"fieldname": "reason_section",
"fieldtype": "Section Break",
"label": "Reason"
},
{
"fieldname": "reason",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Reason",
"options": "Work From Home\nOn Duty",
"reqd": 1
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"fieldname": "explanation",
"fieldtype": "Small Text",
"label": "Explanation"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Attendance Request",
"print_hide": 1,
"read_only": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2019-12-16 11:49:26.943173",
"modified_by": "Administrator",
"module": "HR",
"name": "Attendance Request",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"share": 1,
"submit": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Employee",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "employee_name",
"track_changes": 1
}

View File

@ -1,78 +0,0 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import add_days, date_diff, getdate
from erpnext.hr.doctype.employee.employee import is_holiday
from erpnext.hr.utils import validate_active_employee, validate_dates
class AttendanceRequest(Document):
def validate(self):
validate_active_employee(self.employee)
validate_dates(self, self.from_date, self.to_date)
if self.half_day:
if not getdate(self.from_date) <= getdate(self.half_day_date) <= getdate(self.to_date):
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"
elif self.reason == "Work From Home":
attendance.status = "Work From Home"
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):
frappe.msgprint(
_("Attendance not submitted for {0} as it is a Holiday.").format(attendance_date), alert=1
)
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:
frappe.msgprint(
_("Attendance not submitted for {0} as {1} on leave.").format(attendance_date, self.employee),
alert=1,
)
return True
return False

View File

@ -1,2 +0,0 @@
def get_data():
return {"fieldname": "attendance_request", "transactions": [{"items": ["Attendance"]}]}

View File

@ -1,100 +0,0 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
from datetime import date
import frappe
from frappe.utils import nowdate
test_dependencies = ["Employee"]
class TestAttendanceRequest(unittest.TestCase):
def setUp(self):
for doctype in ["Attendance Request", "Attendance"]:
frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
def tearDown(self):
frappe.db.rollback()
def test_on_duty_attendance_request(self):
"Test creation/updation of Attendace from Attendance Request, on duty."
today = nowdate()
employee = get_employee()
attendance_request = frappe.new_doc("Attendance Request")
attendance_request.employee = employee.name
attendance_request.from_date = date(date.today().year, 1, 1)
attendance_request.to_date = date(date.today().year, 1, 2)
attendance_request.reason = "On Duty"
attendance_request.company = "_Test Company"
attendance_request.insert()
attendance_request.submit()
attendance = frappe.db.get_value(
"Attendance",
filters={
"attendance_request": attendance_request.name,
"attendance_date": date(date.today().year, 1, 1),
},
fieldname=["status", "docstatus"],
as_dict=True,
)
self.assertEqual(attendance.status, "Present")
self.assertEqual(attendance.docstatus, 1)
# cancelling attendance request cancels linked attendances
attendance_request.cancel()
# cancellation alters docname
# fetch attendance value again to avoid stale docname
attendance_docstatus = frappe.db.get_value(
"Attendance",
filters={
"attendance_request": attendance_request.name,
"attendance_date": date(date.today().year, 1, 1),
},
fieldname="docstatus",
)
self.assertEqual(attendance_docstatus, 2)
def test_work_from_home_attendance_request(self):
"Test creation/updation of Attendace from Attendance Request, work from home."
today = nowdate()
employee = get_employee()
attendance_request = frappe.new_doc("Attendance Request")
attendance_request.employee = employee.name
attendance_request.from_date = date(date.today().year, 1, 1)
attendance_request.to_date = date(date.today().year, 1, 2)
attendance_request.reason = "Work From Home"
attendance_request.company = "_Test Company"
attendance_request.insert()
attendance_request.submit()
attendance_status = frappe.db.get_value(
"Attendance",
filters={
"attendance_request": attendance_request.name,
"attendance_date": date(date.today().year, 1, 1),
},
fieldname="status",
)
self.assertEqual(attendance_status, "Work From Home")
attendance_request.cancel()
# cancellation alters docname
# fetch attendance value again to avoid stale docname
attendance_docstatus = frappe.db.get_value(
"Attendance",
filters={
"attendance_request": attendance_request.name,
"attendance_date": date(date.today().year, 1, 1),
},
fieldname="docstatus",
)
self.assertEqual(attendance_docstatus, 2)
def get_employee():
return frappe.get_doc("Employee", "_T-Employee-00001")

View File

@ -1,22 +0,0 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Compensatory Leave Request', {
refresh: function(frm) {
frm.set_query("leave_type", function() {
return {
filters: {
"is_compensatory": true
}
};
});
},
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);
}
}
});

View File

@ -1,575 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "HR-CMP-.YY.-.MM.-.#####",
"beta": 0,
"creation": "2018-04-13 14:51:39.326768",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "employee",
"fieldtype": "Link",
"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": "Employee",
"length": 0,
"no_copy": 0,
"options": "Employee",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.employee_name",
"fieldname": "employee_name",
"fieldtype": "Data",
"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": "Employee Name",
"length": 0,
"no_copy": 0,
"options": "",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.department",
"fieldname": "department",
"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": "Department",
"length": 0,
"no_copy": 0,
"options": "Department",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"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,
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "leave_type",
"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 Type",
"length": 0,
"no_copy": 0,
"options": "Leave Type",
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "leave_allocation",
"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 Allocation",
"length": 0,
"no_copy": 0,
"options": "Leave Allocation",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "worked_on",
"fieldtype": "Section Break",
"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": "Worked On Holiday",
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "work_from_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": "Work From 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": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "work_end_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": "Work End 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": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "half_day",
"fieldtype": "Check",
"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",
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"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,
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reason",
"fieldtype": "Small Text",
"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": "Reason",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"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": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Compensatory Leave Request",
"permlevel": 0,
"print_hide": 1,
"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
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 16:15:36.266924",
"modified_by": "Administrator",
"module": "HR",
"name": "Compensatory Leave Request",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Employee",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "employee_name",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -1,155 +0,0 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import add_days, cint, date_diff, format_date, getdate
from erpnext.hr.utils import (
create_additional_leave_ledger_entry,
get_holiday_dates_for_employee,
get_leave_period,
validate_active_employee,
validate_dates,
validate_overlap,
)
class CompensatoryLeaveRequest(Document):
def validate(self):
validate_active_employee(self.employee)
validate_dates(self, self.work_from_date, self.work_end_date)
if self.half_day:
if not self.half_day_date:
frappe.throw(_("Half Day Date is mandatory"))
if (
not getdate(self.work_from_date) <= getdate(self.half_day_date) <= getdate(self.work_end_date)
):
frappe.throw(_("Half Day Date should be in between Work From Date and Work End Date"))
validate_overlap(self, self.work_from_date, self.work_end_date)
self.validate_holidays()
self.validate_attendance()
if not self.leave_type:
frappe.throw(_("Leave Type is madatory"))
def validate_attendance(self):
attendance = frappe.get_all(
"Attendance",
filters={
"attendance_date": ["between", (self.work_from_date, self.work_end_date)],
"status": "Present",
"docstatus": 1,
"employee": self.employee,
},
fields=["attendance_date", "status"],
)
if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1:
frappe.throw(_("You are not present all day(s) between compensatory leave request days"))
def validate_holidays(self):
holidays = get_holiday_dates_for_employee(self.employee, self.work_from_date, self.work_end_date)
if len(holidays) < date_diff(self.work_end_date, self.work_from_date) + 1:
if date_diff(self.work_end_date, self.work_from_date):
msg = _("The days between {0} to {1} are not valid holidays.").format(
frappe.bold(format_date(self.work_from_date)), frappe.bold(format_date(self.work_end_date))
)
else:
msg = _("{0} is not a holiday.").format(frappe.bold(format_date(self.work_from_date)))
frappe.throw(msg)
def on_submit(self):
company = frappe.db.get_value("Employee", self.employee, "company")
date_difference = date_diff(self.work_end_date, self.work_from_date) + 1
if self.half_day:
date_difference -= 0.5
leave_period = get_leave_period(self.work_from_date, self.work_end_date, company)
if leave_period:
leave_allocation = self.get_existing_allocation_for_period(leave_period)
if leave_allocation:
leave_allocation.new_leaves_allocated += date_difference
leave_allocation.validate()
leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated)
leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated)
# generate additional ledger entry for the new compensatory leaves off
create_additional_leave_ledger_entry(
leave_allocation, date_difference, add_days(self.work_end_date, 1)
)
else:
leave_allocation = self.create_leave_allocation(leave_period, date_difference)
self.db_set("leave_allocation", leave_allocation.name)
else:
frappe.throw(
_("There is no leave period in between {0} and {1}").format(
format_date(self.work_from_date), format_date(self.work_end_date)
)
)
def on_cancel(self):
if self.leave_allocation:
date_difference = date_diff(self.work_end_date, self.work_from_date) + 1
if self.half_day:
date_difference -= 0.5
leave_allocation = frappe.get_doc("Leave Allocation", self.leave_allocation)
if leave_allocation:
leave_allocation.new_leaves_allocated -= date_difference
if leave_allocation.new_leaves_allocated - date_difference <= 0:
leave_allocation.new_leaves_allocated = 0
leave_allocation.validate()
leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated)
leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated)
# create reverse entry on cancelation
create_additional_leave_ledger_entry(
leave_allocation, date_difference * -1, add_days(self.work_end_date, 1)
)
def get_existing_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.get_doc(
dict(
doctype="Leave Allocation",
employee=self.employee,
employee_name=self.employee_name,
leave_type=self.leave_type,
from_date=add_days(self.work_end_date, 1),
to_date=leave_period[0].to_date,
carry_forward=cint(is_carry_forward),
new_leaves_allocated=date_difference,
total_leaves_allocated=date_difference,
description=self.reason,
)
)
allocation.insert(ignore_permissions=True)
allocation.submit()
return allocation

View File

@ -1,157 +0,0 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
import frappe
from frappe.utils import add_days, add_months, today
from erpnext.hr.doctype.attendance_request.test_attendance_request import get_employee
from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on
from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
test_dependencies = ["Employee"]
class TestCompensatoryLeaveRequest(unittest.TestCase):
def setUp(self):
frappe.db.sql(""" delete from `tabCompensatory Leave Request`""")
frappe.db.sql(""" delete from `tabLeave Ledger Entry`""")
frappe.db.sql(""" delete from `tabLeave Allocation`""")
frappe.db.sql(
""" delete from `tabAttendance` where attendance_date in {0} """.format(
(today(), add_days(today(), -1))
)
) # nosec
create_leave_period(add_months(today(), -3), add_months(today(), 3), "_Test Company")
create_holiday_list()
employee = get_employee()
employee.holiday_list = "_Test Compensatory Leave"
employee.save()
def test_leave_balance_on_submit(self):
"""check creation of leave allocation on submission of compensatory leave request"""
employee = get_employee()
mark_attendance(employee)
compensatory_leave_request = get_compensatory_leave_request(employee.name)
before = get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, today())
compensatory_leave_request.submit()
self.assertEqual(
get_leave_balance_on(
employee.name, compensatory_leave_request.leave_type, add_days(today(), 1)
),
before + 1,
)
def test_leave_allocation_update_on_submit(self):
employee = get_employee()
mark_attendance(employee, date=add_days(today(), -1))
compensatory_leave_request = get_compensatory_leave_request(
employee.name, leave_date=add_days(today(), -1)
)
compensatory_leave_request.submit()
# leave allocation creation on submit
leaves_allocated = frappe.db.get_value(
"Leave Allocation",
{"name": compensatory_leave_request.leave_allocation},
["total_leaves_allocated"],
)
self.assertEqual(leaves_allocated, 1)
mark_attendance(employee)
compensatory_leave_request = get_compensatory_leave_request(employee.name)
compensatory_leave_request.submit()
# leave allocation updates on submission of second compensatory leave request
leaves_allocated = frappe.db.get_value(
"Leave Allocation",
{"name": compensatory_leave_request.leave_allocation},
["total_leaves_allocated"],
)
self.assertEqual(leaves_allocated, 2)
def test_creation_of_leave_ledger_entry_on_submit(self):
"""check creation of leave ledger entry on submission of leave request"""
employee = get_employee()
mark_attendance(employee)
compensatory_leave_request = get_compensatory_leave_request(employee.name)
compensatory_leave_request.submit()
filters = dict(transaction_name=compensatory_leave_request.leave_allocation)
leave_ledger_entry = frappe.get_all("Leave Ledger Entry", fields="*", filters=filters)
self.assertEqual(len(leave_ledger_entry), 1)
self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
self.assertEqual(leave_ledger_entry[0].leaves, 1)
# check reverse leave ledger entry on cancellation
compensatory_leave_request.cancel()
leave_ledger_entry = frappe.get_all(
"Leave Ledger Entry", fields="*", filters=filters, order_by="creation desc"
)
self.assertEqual(len(leave_ledger_entry), 2)
self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
self.assertEqual(leave_ledger_entry[0].leaves, -1)
def get_compensatory_leave_request(employee, leave_date=today()):
prev_comp_leave_req = frappe.db.get_value(
"Compensatory Leave Request",
dict(
leave_type="Compensatory Off",
work_from_date=leave_date,
work_end_date=leave_date,
employee=employee,
),
"name",
)
if prev_comp_leave_req:
return frappe.get_doc("Compensatory Leave Request", prev_comp_leave_req)
return frappe.get_doc(
dict(
doctype="Compensatory Leave Request",
employee=employee,
leave_type="Compensatory Off",
work_from_date=leave_date,
work_end_date=leave_date,
reason="test",
)
).insert()
def mark_attendance(employee, date=today(), status="Present"):
if not frappe.db.exists(
dict(doctype="Attendance", employee=employee.name, attendance_date=date, status="Present")
):
attendance = frappe.get_doc(
{"doctype": "Attendance", "employee": employee.name, "attendance_date": date, "status": status}
)
attendance.save()
attendance.submit()
def create_holiday_list():
if frappe.db.exists("Holiday List", "_Test Compensatory Leave"):
return
holiday_list = frappe.get_doc(
{
"doctype": "Holiday List",
"from_date": add_months(today(), -3),
"to_date": add_months(today(), 3),
"holidays": [
{"description": "Test Holiday", "holiday_date": today()},
{"description": "Test Holiday 1", "holiday_date": add_days(today(), -1)},
],
"holiday_list_name": "_Test Compensatory Leave",
}
)
holiday_list.save()

View File

@ -1,8 +0,0 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Daily Work Summary', {
refresh: function (frm) {
}
});

View File

@ -1,175 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2016-11-08 04:58:20.001780",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "daily_work_summary_group",
"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": "Daily Work Summary Group",
"length": 0,
"no_copy": 0,
"options": "Daily Work Summary Group",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Open",
"fieldname": "status",
"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": "Status",
"length": 0,
"no_copy": 0,
"options": "Open\nSent",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "email_sent_to",
"fieldtype": "Code",
"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": "Email Sent To",
"length": 0,
"no_copy": 0,
"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,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 1,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-02-16 11:02:06.727749",
"modified_by": "Administrator",
"module": "HR",
"name": "Daily Work Summary",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Employee",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
}
],
"quick_entry": 1,
"read_only": 1,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
}

View File

@ -1,119 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from email_reply_parser import EmailReplyParser
from frappe import _
from frappe.model.document import Document
from frappe.utils import global_date_format
class DailyWorkSummary(Document):
def send_mails(self, dws_group, emails):
"""Send emails to get daily work summary to all users \
in selected daily work summary group"""
incoming_email_account = frappe.db.get_value(
"Email Account", dict(enable_incoming=1, default_incoming=1), "email_id"
)
self.db_set("email_sent_to", "\n".join(emails))
frappe.sendmail(
recipients=emails,
message=dws_group.message,
subject=dws_group.subject,
reference_doctype=self.doctype,
reference_name=self.name,
reply_to=incoming_email_account,
)
def send_summary(self):
"""Send summary of all replies. Called at midnight"""
args = self.get_message_details()
emails = get_user_emails_from_group(self.daily_work_summary_group)
frappe.sendmail(
recipients=emails,
template="daily_work_summary",
args=args,
subject=_(self.daily_work_summary_group),
reference_doctype=self.doctype,
reference_name=self.name,
)
self.db_set("status", "Sent")
def get_message_details(self):
"""Return args for template"""
dws_group = frappe.get_doc("Daily Work Summary Group", self.daily_work_summary_group)
replies = frappe.get_all(
"Communication",
fields=["content", "text_content", "sender"],
filters=dict(
reference_doctype=self.doctype,
reference_name=self.name,
communication_type="Communication",
sent_or_received="Received",
),
order_by="creation asc",
)
did_not_reply = self.email_sent_to.split()
for d in replies:
user = frappe.db.get_values(
"User", {"email": d.sender}, ["full_name", "user_image"], as_dict=True
)
d.sender_name = user[0].full_name if user else d.sender
d.image = user[0].image if user and user[0].image else None
original_image = d.image
# make thumbnail image
try:
if original_image:
file_name = frappe.get_list("File", {"file_url": original_image})
if file_name:
file_name = file_name[0].name
file_doc = frappe.get_doc("File", file_name)
thumbnail_image = file_doc.make_thumbnail(
set_as_thumbnail=False, width=100, height=100, crop=True
)
d.image = thumbnail_image
except Exception:
d.image = original_image
if d.sender in did_not_reply:
did_not_reply.remove(d.sender)
if d.text_content:
d.content = frappe.utils.md_to_html(EmailReplyParser.parse_reply(d.text_content))
did_not_reply = [
(frappe.db.get_value("User", {"email": email}, "full_name") or email) for email in did_not_reply
]
return dict(
replies=replies,
original_message=dws_group.message,
title=_("Work Summary for {0}").format(global_date_format(self.creation)),
did_not_reply=", ".join(did_not_reply) or "",
did_not_reply_title=_("No replies from"),
)
def get_user_emails_from_group(group):
"""Returns list of email of enabled users from the given group
:param group: Daily Work Summary Group `name`"""
group_doc = group
if isinstance(group_doc, str):
group_doc = frappe.get_doc("Daily Work Summary Group", group)
emails = get_users_email(group_doc)
return emails
def get_users_email(doc):
return [d.email for d in doc.users if frappe.db.get_value("User", d.user, "enabled")]

View File

@ -1,105 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import os
import unittest
import frappe
import frappe.utils
# test_records = frappe.get_test_records('Daily Work Summary')
class TestDailyWorkSummary(unittest.TestCase):
def test_email_trigger(self):
self.setup_and_prepare_test()
for d in self.users:
# check that email is sent to users
if d.message:
self.assertTrue(
d.email in [d.recipient for d in self.emails if self.groups.subject in d.message]
)
def test_email_trigger_failed(self):
hour = "00:00"
if frappe.utils.nowtime().split(":")[0] == "00":
hour = "01:00"
self.setup_and_prepare_test(hour)
for d in self.users:
# check that email is not sent to users
self.assertFalse(
d.email in [d.recipient for d in self.emails if self.groups.subject in d.message]
)
def test_incoming(self):
# get test mail with message-id as in-reply-to
self.setup_and_prepare_test()
with open(os.path.join(os.path.dirname(__file__), "test_data", "test-reply.raw"), "r") as f:
if not self.emails:
return
test_mails = [
f.read()
.replace("{{ sender }}", self.users[-1].email)
.replace("{{ message_id }}", self.emails[-1].message_id)
]
# pull the mail
email_account = frappe.get_doc("Email Account", "_Test Email Account 1")
email_account.db_set("enable_incoming", 1)
email_account.receive(test_mails=test_mails)
daily_work_summary = frappe.get_doc(
"Daily Work Summary", frappe.get_all("Daily Work Summary")[0].name
)
args = daily_work_summary.get_message_details()
self.assertTrue("I built Daily Work Summary!" in args.get("replies")[0].content)
def setup_and_prepare_test(self, hour=None):
frappe.db.sql("delete from `tabDaily Work Summary`")
frappe.db.sql("delete from `tabEmail Queue`")
frappe.db.sql("delete from `tabEmail Queue Recipient`")
frappe.db.sql("delete from `tabCommunication`")
frappe.db.sql("delete from `tabDaily Work Summary Group`")
self.users = frappe.get_all(
"User", fields=["email"], filters=dict(email=("!=", "test@example.com"))
)
self.setup_groups(hour)
from erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group import trigger_emails
trigger_emails()
# check if emails are created
self.emails = frappe.db.sql(
"""select r.recipient, q.message, q.message_id \
from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r \
where q.name = r.parent""",
as_dict=1,
)
def setup_groups(self, hour=None):
# setup email to trigger at this hour
if not hour:
hour = frappe.utils.nowtime().split(":")[0]
hour = hour + ":00"
groups = frappe.get_doc(
dict(
doctype="Daily Work Summary Group",
name="Daily Work Summary",
users=self.users,
send_emails_at=hour,
subject="this is a subject for testing summary emails",
message="this is a message for testing summary emails",
)
)
groups.insert()
self.groups = groups
self.groups.save()

View File

@ -1,75 +0,0 @@
From: {{ sender }}
Content-Type: multipart/alternative;
boundary="Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361"
Message-Id: <07D687F6-10AA-4B9F-82DE-27753096164E@gmail.com>
Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\))
X-Smtp-Server: 73CC8281-7E8F-4B47-8324-D5DA86EEDD4F
Subject: Re: What did you work on today?
Date: Thu, 10 Nov 2016 16:04:43 +0530
X-Universally-Unique-Identifier: A4D9669F-179C-42D8-A3D3-AA6A8C49A6F2
References: <{{ message_id }}>
To: test_in@iwebnotes.com
In-Reply-To: <{{ message_id }}>
--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
charset=us-ascii
I built Daily Work Summary!
> On 10-Nov-2016, at 3:20 PM, Frappe <test@erpnext.com> wrote:
>=20
> Please share what did you do today. If you reply by midnight, your =
response will be recorded!
>=20
> This email was sent to rmehta@gmail.com
> Unsubscribe from this list =
<http://demo-test.erpnext.com.dev/api/method/frappe.email.queue.unsubscrib=
e?email=3Drmehta%40gmail.com&name=3D26cc3e5a5d&doctype=3DDaily+Work+Summar=
y&_signature=3D2c7ab37e6d775e5a481e9b4376154a41>
> Sent via ERPNext <https://erpnext.com/?source=3Dvia_email_footer>
--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361
Content-Transfer-Encoding: 7bit
Content-Type: text/html;
charset=us-ascii
<html><head><meta http-equiv="Content-Type" content="text/html charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">I built Daily Work Summary!<div class=""><br class=""><div><blockquote type="cite" class=""><div class="">On 10-Nov-2016, at 3:20 PM, Frappe &lt;<a href="mailto:test@erpnext.com" class="">test@erpnext.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class="">
<meta name="viewport" content="width=device-width" class="">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" class="">
<title class="">What did you work on today?</title>
<div style="line-height: 1.5; color: #36414C;" class="">
<!-- body -->
<div style="font-family: -apple-system, BlinkMacSystemFont,
" segoe="" ui",="" "roboto",="" "oxygen",="" "ubuntu",="" "cantarell",="" "fira="" sans",="" "droid="" "helvetica="" neue",="" sans-serif;="" font-size:="" 14px;="" padding:="" 10px;"="" class=""><p class="">Please share what did you do today. If you reply by midnight, your response will be recorded!</p>
</div>
<!-- footer -->
<div style="margin-top: 30px; font-family: Helvetica, Arial, sans-serif; font-size: 11px;
margin-bottom: 15px; border-top: 1px solid #d1d8dd;" data-email-footer="true" class="">
<div style="margin: 15px auto; padding: 0px 7px; text-align: center; color: #8d99a6;" class="">
This email was sent to <a href="mailto:rmehta@gmail.com" class="">rmehta@gmail.com</a>
<p style="margin: 15px auto;" class="">
<a href="http://demo-test.erpnext.com.dev/api/method/frappe.email.queue.unsubscribe?email=rmehta%40gmail.com&amp;name=26cc3e5a5d&amp;doctype=Daily+Work+Summary&amp;_signature=2c7ab37e6d775e5a481e9b4376154a41" style="color: #8d99a6; text-decoration: underline;
target=" _blank"="" class="">Unsubscribe from this list
</a>
</p>
</div><div style="margin: 15px auto;" class=""><div style="text-align: center;" class="">
<a href="https://erpnext.com/?source=via_email_footer" target="_blank" style="color: #8d99a6;" class="">
Sent via ERPNext
</a>
</div></div>
</div>
<!-- /footer -->
<div class="print-html"></div>
</div>
</div></blockquote></div><br class=""></div></body></html>
--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361--

View File

@ -1,12 +0,0 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Daily Work Summary Group', {
refresh: function (frm) {
if (!frm.is_new()) {
frm.add_custom_button(__('Daily Work Summary'), function () {
frappe.set_route('List', 'Daily Work Summary');
});
}
}
});

View File

@ -1,309 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "Prompt",
"beta": 0,
"creation": "2018-02-12 15:06:18.767239",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fieldname": "enabled",
"fieldtype": "Check",
"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": "Enabled",
"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,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "select_users",
"fieldtype": "Section Break",
"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": "Select Users",
"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,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "users",
"fieldtype": "Table",
"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": "Users",
"length": 0,
"no_copy": 0,
"options": "Daily Work Summary Group User",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "send_emails_at",
"fieldtype": "Select",
"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": "Send Emails At",
"length": 0,
"no_copy": 0,
"options": "00:00\n01:00\n02:00\n03:00\n04:00\n05:00\n06:00\n07:00\n08:00\n09:00\n10:00\n11:00\n12:00\n13:00\n14:00\n15:00\n16:00\n17:00\n18:00\n19:00\n20:00\n21:00\n22:00\n23:00",
"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,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "holiday_list",
"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": "Holiday List",
"length": 0,
"no_copy": 0,
"options": "Holiday List",
"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,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "mail_details",
"fieldtype": "Section Break",
"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": "Reminder",
"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,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "What did you work on today?",
"fieldname": "subject",
"fieldtype": "Data",
"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": "Subject",
"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,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "<p>Please share what did you do today. If you reply by midnight, your response will be recorded!</p>",
"fieldname": "message",
"fieldtype": "Text Editor",
"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": "Message",
"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,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-02-16 10:56:03.998495",
"modified_by": "Administrator",
"module": "HR",
"name": "Daily Work Summary Group",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
}

View File

@ -1,55 +0,0 @@
# # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# # For license information, please see license.txt
import frappe
import frappe.utils
from frappe import _
from frappe.model.document import Document
from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_user_emails_from_group
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
class DailyWorkSummaryGroup(Document):
def validate(self):
if self.users:
if not frappe.flags.in_test and not is_incoming_account_enabled():
frappe.throw(
_("Please enable default incoming account before creating Daily Work Summary Group")
)
def trigger_emails():
"""Send emails to Employees at the given hour asking
them what did they work on today"""
groups = frappe.get_all("Daily Work Summary Group")
for d in groups:
group_doc = frappe.get_doc("Daily Work Summary Group", d)
if (
is_current_hour(group_doc.send_emails_at)
and not is_holiday(group_doc.holiday_list)
and group_doc.enabled
):
emails = get_user_emails_from_group(group_doc)
# find emails relating to a company
if emails:
daily_work_summary = frappe.get_doc(
dict(doctype="Daily Work Summary", daily_work_summary_group=group_doc.name)
).insert()
daily_work_summary.send_mails(group_doc, emails)
def is_current_hour(hour):
return frappe.utils.nowtime().split(":")[0] == hour.split(":")[0]
def send_summary():
"""Send summary to everyone"""
for d in frappe.get_all("Daily Work Summary", dict(status="Open")):
daily_work_summary = frappe.get_doc("Daily Work Summary", d.name)
daily_work_summary.send_summary()
def is_incoming_account_enabled():
return frappe.db.get_value("Email Account", dict(enable_incoming=1, default_incoming=1))

View File

@ -1,106 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-02-12 14:57:38.332692",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "user",
"fieldtype": "Link",
"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": "User",
"length": 0,
"no_copy": 0,
"options": "User",
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "user.email",
"fieldname": "email",
"fieldtype": "Read Only",
"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": "email",
"length": 0,
"no_copy": 0,
"options": "",
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-05-16 22:43:00.481019",
"modified_by": "Administrator",
"module": "HR",
"name": "Daily Work Summary Group User",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class DailyWorkSummaryGroupUser(Document):
pass

View File

@ -1,76 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "hash",
"beta": 0,
"creation": "2018-04-08 16:31:02.433252",
"custom": 0,
"description": "",
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "approver",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Approver",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"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,
"width": "200"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-04-17 11:05:46.294340",
"modified_by": "Administrator",
"module": "HR",
"name": "Department Approver",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
}

View File

@ -1,93 +0,0 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.model.document import Document
class DepartmentApprover(Document):
pass
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_approvers(doctype, txt, searchfield, start, page_len, filters):
if not filters.get("employee"):
frappe.throw(_("Please select Employee first."))
approvers = []
department_details = {}
department_list = []
employee = frappe.get_value(
"Employee",
filters.get("employee"),
["employee_name", "department", "leave_approver", "expense_approver", "shift_request_approver"],
as_dict=True,
)
employee_department = filters.get("department") or employee.department
if employee_department:
department_details = frappe.db.get_value(
"Department", {"name": employee_department}, ["lft", "rgt"], as_dict=True
)
if department_details:
department_list = frappe.db.sql(
"""select name from `tabDepartment` where lft <= %s
and rgt >= %s
and disabled=0
order by lft desc""",
(department_details.lft, department_details.rgt),
as_list=True,
)
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") == "Shift Request" and employee.shift_request_approver:
approvers.append(
frappe.db.get_value(
"User", employee.shift_request_approver, ["name", "first_name", "last_name"]
)
)
if filters.get("doctype") == "Leave Application":
parentfield = "leave_approvers"
field_name = "Leave Approver"
elif filters.get("doctype") == "Expense Claim":
parentfield = "expense_approvers"
field_name = "Expense Approver"
elif filters.get("doctype") == "Shift Request":
parentfield = "shift_request_approver"
field_name = "Shift Request Approver"
if department_list:
for d in department_list:
approvers += frappe.db.sql(
"""select user.name, user.first_name, user.last_name from
tabUser user, `tabDepartment Approver` approver where
approver.parent = %s
and user.name like %s
and approver.parentfield = %s
and approver.approver=user.name""",
(d, "%" + txt + "%", parentfield),
as_list=True,
)
if len(approvers) == 0:
error_msg = _("Please set {0} for the Employee: {1}").format(
field_name, frappe.bold(employee.employee_name)
)
if department_list:
error_msg += " " + _("or for Department: {0}").format(frappe.bold(employee_department))
frappe.throw(error_msg, title=_(field_name + " Missing"))
return set(tuple(approver) for approver in approvers)

View File

@ -1,36 +0,0 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on("Driver", {
setup: function(frm) {
frm.set_query("transporter", function() {
return {
filters: {
is_transporter: 1
}
};
});
},
refresh: function(frm) {
frm.set_query("address", function() {
return {
filters: {
is_your_company_address: !frm.doc.transporter ? 1 : 0
}
};
});
},
transporter: function(frm, cdt, cdn) {
// this assumes that supplier's address has same title as supplier's name
frappe.db
.get_doc("Address", null, { address_title: frm.doc.transporter })
.then(r => {
frappe.model.set_value(cdt, cdn, "address", r.name);
})
.catch(err => {
console.log(err);
});
}
});

View File

@ -1,169 +0,0 @@
{
"allow_import": 1,
"allow_rename": 1,
"autoname": "naming_series:",
"creation": "2017-10-17 08:21:50.489773",
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"naming_series",
"full_name",
"status",
"transporter",
"column_break_2",
"employee",
"cell_number",
"address",
"license_details",
"license_number",
"column_break_8",
"issuing_date",
"column_break_10",
"expiry_date",
"driving_license_categories",
"driving_license_category"
],
"fields": [
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"options": "HR-DRI-.YYYY.-"
},
{
"fieldname": "full_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Full Name",
"reqd": 1
},
{
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Status",
"options": "Active\nSuspended\nLeft",
"reqd": 1
},
{
"allow_in_quick_entry": 1,
"description": "Applicable for external driver",
"fieldname": "transporter",
"fieldtype": "Link",
"label": "Transporter",
"options": "Supplier"
},
{
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
"fieldname": "employee",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Employee",
"options": "Employee"
},
{
"fieldname": "cell_number",
"fieldtype": "Data",
"label": "Cellphone Number"
},
{
"fieldname": "license_details",
"fieldtype": "Section Break",
"label": "License Details"
},
{
"fieldname": "license_number",
"fieldtype": "Data",
"label": "License Number"
},
{
"fieldname": "column_break_8",
"fieldtype": "Column Break"
},
{
"fieldname": "issuing_date",
"fieldtype": "Date",
"label": "Issuing Date"
},
{
"fieldname": "column_break_10",
"fieldtype": "Column Break"
},
{
"fieldname": "expiry_date",
"fieldtype": "Date",
"label": "Expiry Date"
},
{
"fieldname": "driving_license_categories",
"fieldtype": "Section Break",
"label": "Driving License Categories"
},
{
"fieldname": "driving_license_category",
"fieldtype": "Table",
"label": "Driving License Category",
"options": "Driving License Category"
},
{
"fieldname": "address",
"fieldtype": "Link",
"label": "Address",
"options": "Address"
}
],
"icon": "fa fa-user",
"modified": "2019-07-18 16:29:14.151380",
"modified_by": "Administrator",
"module": "HR",
"name": "Driver",
"name_case": "Title Case",
"owner": "Administrator",
"permissions": [
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Fleet Manager",
"share": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"search_fields": "full_name",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "full_name",
"track_changes": 1
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class Driver(Document):
pass

View File

@ -1,8 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
class TestDriver(unittest.TestCase):
pass

View File

@ -1,51 +0,0 @@
{
"actions": [],
"creation": "2017-10-17 08:19:43.142329",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"class",
"description",
"issuing_date",
"expiry_date"
],
"fields": [
{
"fieldname": "class",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Driver licence class"
},
{
"fieldname": "description",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Description"
},
{
"fieldname": "issuing_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Issuing Date"
},
{
"fieldname": "expiry_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Expiry Date"
}
],
"istable": 1,
"links": [],
"modified": "2019-12-12 14:40:55.209022",
"modified_by": "Administrator",
"module": "HR",
"name": "Driving License Category",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class DrivingLicenseCategory(Document):
pass

View File

@ -1,225 +0,0 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Employee Advance', {
setup: function(frm) {
frm.add_fetch("employee", "company", "company");
frm.add_fetch("company", "default_employee_advance_account", "advance_account");
frm.set_query("employee", function() {
return {
filters: {
"status": "Active"
}
};
});
frm.set_query("advance_account", function() {
if (!frm.doc.employee) {
frappe.msgprint(__("Please select employee first"));
}
let company_currency = erpnext.get_currency(frm.doc.company);
let currencies = [company_currency];
if (frm.doc.currency && (frm.doc.currency != company_currency)) {
currencies.push(frm.doc.currency);
}
return {
filters: {
"root_type": "Asset",
"is_group": 0,
"company": frm.doc.company,
"account_currency": ["in", currencies],
}
};
});
frm.set_query('salary_component', function() {
return {
filters: {
"type": "Deduction"
}
};
});
},
refresh: function(frm) {
if (frm.doc.docstatus === 1 &&
(flt(frm.doc.paid_amount) < flt(frm.doc.advance_amount)) &&
frappe.model.can_create("Payment Entry")) {
frm.add_custom_button(__('Payment'),
function () {
frm.events.make_payment_entry(frm);
}, __('Create'));
} else if (
frm.doc.docstatus === 1 &&
flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount) &&
frappe.model.can_create("Expense Claim")
) {
frm.add_custom_button(
__("Expense Claim"),
function () {
frm.events.make_expense_claim(frm);
},
__('Create')
);
}
if (
frm.doc.docstatus === 1
&& (flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount))
) {
if (frm.doc.repay_unclaimed_amount_from_salary == 0 && frappe.model.can_create("Journal Entry")) {
frm.add_custom_button(__("Return"), function() {
frm.trigger('make_return_entry');
}, __('Create'));
} else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")) {
frm.add_custom_button(__("Deduction from Salary"), function() {
frm.events.make_deduction_via_additional_salary(frm);
}, __('Create'));
}
}
},
make_deduction_via_additional_salary: function(frm) {
frappe.call({
method: "erpnext.hr.doctype.employee_advance.employee_advance.create_return_through_additional_salary",
args: {
doc: frm.doc
},
callback: function(r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
});
},
make_payment_entry: function(frm) {
var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
if (frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) {
method = "erpnext.hr.doctype.employee_advance.employee_advance.make_bank_entry";
}
return frappe.call({
method: method,
args: {
"dt": frm.doc.doctype,
"dn": frm.doc.name
},
callback: function(r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
});
},
make_expense_claim: function(frm) {
return frappe.call({
method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim",
args: {
"employee_name": frm.doc.employee,
"company": frm.doc.company,
"employee_advance_name": frm.doc.name,
"posting_date": frm.doc.posting_date,
"paid_amount": frm.doc.paid_amount,
"claimed_amount": frm.doc.claimed_amount
},
callback: function(r) {
const doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
});
},
make_return_entry: function(frm) {
frappe.call({
method: 'erpnext.hr.doctype.employee_advance.employee_advance.make_return_entry',
args: {
'employee': frm.doc.employee,
'company': frm.doc.company,
'employee_advance_name': frm.doc.name,
'return_amount': flt(frm.doc.paid_amount - frm.doc.claimed_amount),
'advance_account': frm.doc.advance_account,
'mode_of_payment': frm.doc.mode_of_payment,
'currency': frm.doc.currency,
'exchange_rate': frm.doc.exchange_rate
},
callback: function(r) {
const doclist = frappe.model.sync(r.message);
frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
}
});
},
employee: function(frm) {
if (frm.doc.employee) {
frappe.run_serially([
() => frm.trigger('get_employee_currency'),
() => frm.trigger('get_pending_amount')
]);
}
},
get_pending_amount: function(frm) {
frappe.call({
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("pending_amount", r.message);
}
});
},
get_employee_currency: function(frm) {
frappe.call({
method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency",
args: {
employee: frm.doc.employee,
},
callback: function(r) {
if (r.message) {
frm.set_value('currency', r.message);
frm.refresh_fields();
}
}
});
},
currency: function(frm) {
if (frm.doc.currency) {
var from_currency = frm.doc.currency;
var company_currency;
if (!frm.doc.company) {
company_currency = erpnext.get_currency(frappe.defaults.get_default("Company"));
} else {
company_currency = erpnext.get_currency(frm.doc.company);
}
if (from_currency != company_currency) {
frm.events.set_exchange_rate(frm, from_currency, company_currency);
} else {
frm.set_value("exchange_rate", 1.0);
frm.set_df_property('exchange_rate', 'hidden', 1);
frm.set_df_property("exchange_rate", "description", "");
}
frm.refresh_fields();
}
},
set_exchange_rate: function(frm, from_currency, company_currency) {
frappe.call({
method: "erpnext.setup.utils.get_exchange_rate",
args: {
from_currency: from_currency,
to_currency: company_currency,
},
callback: function(r) {
frm.set_value("exchange_rate", flt(r.message));
frm.set_df_property('exchange_rate', 'hidden', 0);
frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency +
" = [?] " + company_currency);
}
});
}
});

View File

@ -1,277 +0,0 @@
{
"actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2022-01-17 18:36:51.450395",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"naming_series",
"employee",
"employee_name",
"department",
"column_break_4",
"posting_date",
"currency",
"exchange_rate",
"repay_unclaimed_amount_from_salary",
"section_break_8",
"purpose",
"column_break_11",
"advance_amount",
"paid_amount",
"pending_amount",
"claimed_amount",
"return_amount",
"section_break_7",
"status",
"company",
"amended_from",
"column_break_18",
"advance_account",
"mode_of_payment"
],
"fields": [
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"options": "HR-EAD-.YYYY.-"
},
{
"fieldname": "employee",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Employee",
"options": "Employee",
"reqd": 1
},
{
"fetch_from": "employee.employee_name",
"fieldname": "employee_name",
"fieldtype": "Read Only",
"label": "Employee Name"
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"default": "Today",
"fieldname": "posting_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Posting Date",
"reqd": 1
},
{
"fetch_from": "employee.department",
"fieldname": "department",
"fieldtype": "Link",
"label": "Department",
"options": "Department",
"read_only": 1
},
{
"fieldname": "section_break_8",
"fieldtype": "Section Break"
},
{
"fieldname": "purpose",
"fieldtype": "Small Text",
"in_list_view": 1,
"label": "Purpose",
"reqd": 1
},
{
"fieldname": "column_break_11",
"fieldtype": "Column Break"
},
{
"fieldname": "advance_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Advance Amount",
"options": "currency",
"reqd": 1
},
{
"fieldname": "paid_amount",
"fieldtype": "Currency",
"label": "Paid Amount",
"no_copy": 1,
"options": "currency",
"read_only": 1
},
{
"fieldname": "claimed_amount",
"fieldtype": "Currency",
"label": "Claimed Amount",
"no_copy": 1,
"options": "currency",
"read_only": 1
},
{
"fieldname": "section_break_7",
"fieldtype": "Section Break"
},
{
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
"no_copy": 1,
"options": "Draft\nPaid\nUnpaid\nClaimed\nReturned\nPartly Claimed and Returned\nCancelled",
"read_only": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"reqd": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Employee Advance",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "column_break_18",
"fieldtype": "Column Break"
},
{
"fieldname": "advance_account",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Advance Account",
"options": "Account",
"reqd": 1
},
{
"fieldname": "mode_of_payment",
"fieldtype": "Link",
"label": "Mode of Payment",
"options": "Mode of Payment"
},
{
"allow_on_submit": 1,
"fieldname": "return_amount",
"fieldtype": "Currency",
"label": "Returned Amount",
"options": "currency",
"read_only": 1
},
{
"default": "0",
"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": "currency",
"read_only": 1
},
{
"depends_on": "eval:(doc.docstatus==1 || doc.employee)",
"fieldname": "currency",
"fieldtype": "Link",
"label": "Currency",
"options": "Currency",
"reqd": 1
},
{
"depends_on": "currency",
"fieldname": "exchange_rate",
"fieldtype": "Float",
"label": "Exchange Rate",
"precision": "9",
"print_hide": 1,
"reqd": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2022-01-17 19:33:52.345823",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Advance",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Employee",
"share": 1,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Expense Approver",
"share": 1,
"submit": 1,
"write": 1
}
],
"search_fields": "employee,employee_name",
"sort_field": "modified",
"sort_order": "DESC",
"states": [
{
"color": "Red",
"custom": 1,
"title": "Draft"
},
{
"color": "Green",
"custom": 1,
"title": "Paid"
},
{
"color": "Orange",
"custom": 1,
"title": "Unpaid"
},
{
"color": "Blue",
"custom": 1,
"title": "Claimed"
},
{
"color": "Gray",
"title": "Returned"
},
{
"color": "Yellow",
"title": "Partly Claimed and Returned"
},
{
"color": "Red",
"custom": 1,
"title": "Cancelled"
}
],
"title_field": "employee_name",
"track_changes": 1
}

View File

@ -1,323 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.query_builder.functions import Sum
from frappe.utils import flt, nowdate
import erpnext
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
from erpnext.hr.utils import validate_active_employee
class EmployeeAdvanceOverPayment(frappe.ValidationError):
pass
class EmployeeAdvance(Document):
def onload(self):
self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value(
"Accounts Settings", "make_payment_via_journal_entry"
)
def validate(self):
validate_active_employee(self.employee)
self.set_status()
def on_cancel(self):
self.ignore_linked_doctypes = "GL Entry"
self.set_status(update=True)
def set_status(self, update=False):
precision = self.precision("paid_amount")
total_amount = flt(flt(self.claimed_amount) + flt(self.return_amount), precision)
status = None
if self.docstatus == 0:
status = "Draft"
elif self.docstatus == 1:
if flt(self.claimed_amount) > 0 and flt(self.claimed_amount, precision) == flt(
self.paid_amount, precision
):
status = "Claimed"
elif flt(self.return_amount) > 0 and flt(self.return_amount, precision) == flt(
self.paid_amount, precision
):
status = "Returned"
elif (
flt(self.claimed_amount) > 0
and (flt(self.return_amount) > 0)
and total_amount == flt(self.paid_amount, precision)
):
status = "Partly Claimed and Returned"
elif flt(self.paid_amount) > 0 and flt(self.advance_amount, precision) == flt(
self.paid_amount, precision
):
status = "Paid"
else:
status = "Unpaid"
elif self.docstatus == 2:
status = "Cancelled"
if update:
self.db_set("status", status)
else:
self.status = status
def set_total_advance_paid(self):
gle = frappe.qb.DocType("GL Entry")
paid_amount = (
frappe.qb.from_(gle)
.select(Sum(gle.debit).as_("paid_amount"))
.where(
(gle.against_voucher_type == "Employee Advance")
& (gle.against_voucher == self.name)
& (gle.party_type == "Employee")
& (gle.party == self.employee)
& (gle.docstatus == 1)
& (gle.is_cancelled == 0)
)
).run(as_dict=True)[0].paid_amount or 0
return_amount = (
frappe.qb.from_(gle)
.select(Sum(gle.credit).as_("return_amount"))
.where(
(gle.against_voucher_type == "Employee Advance")
& (gle.voucher_type != "Expense Claim")
& (gle.against_voucher == self.name)
& (gle.party_type == "Employee")
& (gle.party == self.employee)
& (gle.docstatus == 1)
& (gle.is_cancelled == 0)
)
).run(as_dict=True)[0].return_amount or 0
if paid_amount != 0:
paid_amount = flt(paid_amount) / flt(self.exchange_rate)
if return_amount != 0:
return_amount = flt(return_amount) / flt(self.exchange_rate)
if flt(paid_amount) > self.advance_amount:
frappe.throw(
_("Row {0}# Paid Amount cannot be greater than requested advance amount"),
EmployeeAdvanceOverPayment,
)
if flt(return_amount) > self.paid_amount - self.claimed_amount:
frappe.throw(_("Return amount cannot be greater unclaimed amount"))
self.db_set("paid_amount", paid_amount)
self.db_set("return_amount", return_amount)
self.set_status(update=True)
def update_claimed_amount(self):
claimed_amount = (
frappe.db.sql(
"""
SELECT sum(ifnull(allocated_amount, 0))
FROM `tabExpense Claim Advance` eca, `tabExpense Claim` ec
WHERE
eca.employee_advance = %s
AND ec.approval_status="Approved"
AND ec.name = eca.parent
AND ec.docstatus=1
AND eca.allocated_amount > 0
""",
self.name,
)[0][0]
or 0
)
frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount))
self.reload()
self.set_status(update=True)
@frappe.whitelist()
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"],
)
return sum([(emp.advance_amount - emp.paid_amount) for emp in employee_due_amount])
@frappe.whitelist()
def make_bank_entry(dt, dn):
doc = frappe.get_doc(dt, dn)
payment_account = get_default_bank_cash_account(
doc.company, account_type="Cash", mode_of_payment=doc.mode_of_payment
)
if not payment_account:
frappe.throw(_("Please set a Default Cash Account in Company defaults"))
advance_account_currency = frappe.db.get_value("Account", doc.advance_account, "account_currency")
advance_amount, advance_exchange_rate = get_advance_amount_advance_exchange_rate(
advance_account_currency, doc
)
paying_amount, paying_exchange_rate = get_paying_amount_paying_exchange_rate(payment_account, doc)
je = frappe.new_doc("Journal Entry")
je.posting_date = nowdate()
je.voucher_type = "Bank Entry"
je.company = doc.company
je.remark = "Payment against Employee Advance: " + dn + "\n" + doc.purpose
je.multi_currency = 1 if advance_account_currency != payment_account.account_currency else 0
je.append(
"accounts",
{
"account": doc.advance_account,
"account_currency": advance_account_currency,
"exchange_rate": flt(advance_exchange_rate),
"debit_in_account_currency": flt(advance_amount),
"reference_type": "Employee Advance",
"reference_name": doc.name,
"party_type": "Employee",
"cost_center": erpnext.get_default_cost_center(doc.company),
"party": doc.employee,
"is_advance": "Yes",
},
)
je.append(
"accounts",
{
"account": payment_account.account,
"cost_center": erpnext.get_default_cost_center(doc.company),
"credit_in_account_currency": flt(paying_amount),
"account_currency": payment_account.account_currency,
"account_type": payment_account.account_type,
"exchange_rate": flt(paying_exchange_rate),
},
)
return je.as_dict()
def get_advance_amount_advance_exchange_rate(advance_account_currency, doc):
if advance_account_currency != doc.currency:
advance_amount = flt(doc.advance_amount) * flt(doc.exchange_rate)
advance_exchange_rate = 1
else:
advance_amount = doc.advance_amount
advance_exchange_rate = doc.exchange_rate
return advance_amount, advance_exchange_rate
def get_paying_amount_paying_exchange_rate(payment_account, doc):
if payment_account.account_currency != doc.currency:
paying_amount = flt(doc.advance_amount) * flt(doc.exchange_rate)
paying_exchange_rate = 1
else:
paying_amount = doc.advance_amount
paying_exchange_rate = doc.exchange_rate
return paying_amount, paying_exchange_rate
@frappe.whitelist()
def create_return_through_additional_salary(doc):
import json
if isinstance(doc, str):
doc = frappe._dict(json.loads(doc))
additional_salary = frappe.new_doc("Additional Salary")
additional_salary.employee = doc.employee
additional_salary.currency = doc.currency
additional_salary.amount = doc.paid_amount - doc.claimed_amount
additional_salary.company = doc.company
additional_salary.ref_doctype = doc.doctype
additional_salary.ref_docname = doc.name
return additional_salary
@frappe.whitelist()
def make_return_entry(
employee,
company,
employee_advance_name,
return_amount,
advance_account,
currency,
exchange_rate,
mode_of_payment=None,
):
bank_cash_account = get_default_bank_cash_account(
company, account_type="Cash", mode_of_payment=mode_of_payment
)
if not bank_cash_account:
frappe.throw(_("Please set a Default Cash Account in Company defaults"))
advance_account_currency = frappe.db.get_value("Account", advance_account, "account_currency")
je = frappe.new_doc("Journal Entry")
je.posting_date = nowdate()
je.voucher_type = get_voucher_type(mode_of_payment)
je.company = company
je.remark = "Return against Employee Advance: " + employee_advance_name
je.multi_currency = 1 if advance_account_currency != bank_cash_account.account_currency else 0
advance_account_amount = (
flt(return_amount)
if advance_account_currency == currency
else flt(return_amount) * flt(exchange_rate)
)
je.append(
"accounts",
{
"account": advance_account,
"credit_in_account_currency": advance_account_amount,
"account_currency": advance_account_currency,
"exchange_rate": flt(exchange_rate) if advance_account_currency == currency else 1,
"reference_type": "Employee Advance",
"reference_name": employee_advance_name,
"party_type": "Employee",
"party": employee,
"is_advance": "Yes",
"cost_center": erpnext.get_default_cost_center(company),
},
)
bank_amount = (
flt(return_amount)
if bank_cash_account.account_currency == currency
else flt(return_amount) * flt(exchange_rate)
)
je.append(
"accounts",
{
"account": bank_cash_account.account,
"debit_in_account_currency": bank_amount,
"account_currency": bank_cash_account.account_currency,
"account_type": bank_cash_account.account_type,
"exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1,
"cost_center": erpnext.get_default_cost_center(company),
},
)
return je.as_dict()
def get_voucher_type(mode_of_payment=None):
voucher_type = "Cash Entry"
if mode_of_payment:
mode_of_payment_type = frappe.get_cached_value("Mode of Payment", mode_of_payment, "type")
if mode_of_payment_type == "Bank":
voucher_type = "Bank Entry"
return voucher_type

View File

@ -1,9 +0,0 @@
def get_data():
return {
"fieldname": "employee_advance",
"non_standard_fieldnames": {
"Payment Entry": "reference_name",
"Journal Entry": "reference_name",
},
"transactions": [{"items": ["Expense Claim"]}, {"items": ["Payment Entry", "Journal Entry"]}],
}

View File

@ -1,257 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
import frappe
from frappe.utils import flt, nowdate
import erpnext
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.hr.doctype.employee_advance.employee_advance import (
EmployeeAdvanceOverPayment,
create_return_through_additional_salary,
make_bank_entry,
make_return_entry,
)
from erpnext.hr.doctype.expense_claim.expense_claim import get_advances
from erpnext.hr.doctype.expense_claim.test_expense_claim import (
get_payable_account,
make_expense_claim,
)
from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
class TestEmployeeAdvance(unittest.TestCase):
def setUp(self):
frappe.db.delete("Employee Advance")
def test_paid_amount_and_status(self):
employee_name = make_employee("_T@employe.advance")
advance = make_employee_advance(employee_name)
journal_entry = make_payment_entry(advance)
journal_entry.submit()
advance.reload()
self.assertEqual(advance.paid_amount, 1000)
self.assertEqual(advance.status, "Paid")
# try making over payment
journal_entry1 = make_payment_entry(advance)
self.assertRaises(EmployeeAdvanceOverPayment, journal_entry1.submit)
def test_paid_amount_on_pe_cancellation(self):
employee_name = make_employee("_T@employe.advance")
advance = make_employee_advance(employee_name)
pe = make_payment_entry(advance)
pe.submit()
advance.reload()
self.assertEqual(advance.paid_amount, 1000)
self.assertEqual(advance.status, "Paid")
pe.cancel()
advance.reload()
self.assertEqual(advance.paid_amount, 0)
self.assertEqual(advance.status, "Unpaid")
advance.cancel()
advance.reload()
self.assertEqual(advance.status, "Cancelled")
def test_claimed_status(self):
# CLAIMED Status check, full amount claimed
payable_account = get_payable_account("_Test Company")
claim = make_expense_claim(
payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True
)
advance = make_employee_advance(claim.employee)
pe = make_payment_entry(advance)
pe.submit()
claim = get_advances_for_claim(claim, advance.name)
claim.save()
claim.submit()
advance.reload()
self.assertEqual(advance.claimed_amount, 1000)
self.assertEqual(advance.status, "Claimed")
# advance should not be shown in claims
advances = get_advances(claim.employee)
advances = [entry.name for entry in advances]
self.assertTrue(advance.name not in advances)
# cancel claim; status should be Paid
claim.cancel()
advance.reload()
self.assertEqual(advance.claimed_amount, 0)
self.assertEqual(advance.status, "Paid")
def test_partly_claimed_and_returned_status(self):
payable_account = get_payable_account("_Test Company")
claim = make_expense_claim(
payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True
)
advance = make_employee_advance(claim.employee)
pe = make_payment_entry(advance)
pe.submit()
# PARTLY CLAIMED AND RETURNED status check
# 500 Claimed, 500 Returned
claim = make_expense_claim(
payable_account, 500, 500, "_Test Company", "Travel Expenses - _TC", do_not_submit=True
)
advance = make_employee_advance(claim.employee)
pe = make_payment_entry(advance)
pe.submit()
claim = get_advances_for_claim(claim, advance.name, amount=500)
claim.save()
claim.submit()
advance.reload()
self.assertEqual(advance.claimed_amount, 500)
self.assertEqual(advance.status, "Paid")
entry = make_return_entry(
employee=advance.employee,
company=advance.company,
employee_advance_name=advance.name,
return_amount=flt(advance.paid_amount - advance.claimed_amount),
advance_account=advance.advance_account,
mode_of_payment=advance.mode_of_payment,
currency=advance.currency,
exchange_rate=advance.exchange_rate,
)
entry = frappe.get_doc(entry)
entry.insert()
entry.submit()
advance.reload()
self.assertEqual(advance.return_amount, 500)
self.assertEqual(advance.status, "Partly Claimed and Returned")
# advance should not be shown in claims
advances = get_advances(claim.employee)
advances = [entry.name for entry in advances]
self.assertTrue(advance.name not in advances)
# Cancel return entry; status should change to PAID
entry.cancel()
advance.reload()
self.assertEqual(advance.return_amount, 0)
self.assertEqual(advance.status, "Paid")
# advance should be shown in claims
advances = get_advances(claim.employee)
advances = [entry.name for entry in advances]
self.assertTrue(advance.name in advances)
def test_repay_unclaimed_amount_from_salary(self):
employee_name = make_employee("_T@employe.advance")
advance = make_employee_advance(employee_name, {"repay_unclaimed_amount_from_salary": 1})
pe = make_payment_entry(advance)
pe.submit()
args = {"type": "Deduction"}
create_salary_component("Advance Salary - Deduction", **args)
make_salary_structure(
"Test Additional Salary for Advance Return", "Monthly", employee=employee_name
)
# additional salary for 700 first
advance.reload()
additional_salary = create_return_through_additional_salary(advance)
additional_salary.salary_component = "Advance Salary - Deduction"
additional_salary.payroll_date = nowdate()
additional_salary.amount = 700
additional_salary.insert()
additional_salary.submit()
advance.reload()
self.assertEqual(advance.return_amount, 700)
# additional salary for remaining 300
additional_salary = create_return_through_additional_salary(advance)
additional_salary.salary_component = "Advance Salary - Deduction"
additional_salary.payroll_date = nowdate()
additional_salary.amount = 300
additional_salary.insert()
additional_salary.submit()
advance.reload()
self.assertEqual(advance.return_amount, 1000)
self.assertEqual(advance.status, "Returned")
# update advance return amount on additional salary cancellation
additional_salary.cancel()
advance.reload()
self.assertEqual(advance.return_amount, 700)
self.assertEqual(advance.status, "Paid")
def tearDown(self):
frappe.db.rollback()
def make_payment_entry(advance):
journal_entry = frappe.get_doc(make_bank_entry("Employee Advance", advance.name))
journal_entry.cheque_no = "123123"
journal_entry.cheque_date = nowdate()
journal_entry.save()
return journal_entry
def make_employee_advance(employee_name, args=None):
doc = frappe.new_doc("Employee Advance")
doc.employee = employee_name
doc.company = "_Test company"
doc.purpose = "For site visit"
doc.currency = erpnext.get_company_currency("_Test company")
doc.exchange_rate = 1
doc.advance_amount = 1000
doc.posting_date = nowdate()
doc.advance_account = "_Test Employee Advance - _TC"
if args:
doc.update(args)
doc.insert()
doc.submit()
return doc
def get_advances_for_claim(claim, advance_name, amount=None):
advances = get_advances(claim.employee, advance_name)
for entry in advances:
if amount:
allocated_amount = amount
else:
allocated_amount = flt(entry.paid_amount) - flt(entry.claimed_amount)
claim.append(
"advances",
{
"employee_advance": entry.name,
"posting_date": entry.posting_date,
"advance_account": entry.advance_account,
"advance_paid": entry.paid_amount,
"unclaimed_amount": allocated_amount,
"allocated_amount": allocated_amount,
},
)
return claim

View File

@ -1,21 +0,0 @@
.top-toolbar{
padding-bottom: 30px;
margin-left: -17px;
}
.bottom-toolbar{
margin-left: -17px;
margin-top: 20px;
}
.btn{
margin-right: 5px;
}
.marked-employee-label{
font-weight: normal;
}
.checkbox{
margin-top: -3px;
}

View File

@ -1,269 +0,0 @@
frappe.ui.form.on("Employee Attendance Tool", {
refresh: function(frm) {
frm.disable_save();
},
onload: function(frm) {
frm.set_value("date", frappe.datetime.get_today());
erpnext.employee_attendance_tool.load_employees(frm);
},
date: function(frm) {
erpnext.employee_attendance_tool.load_employees(frm);
},
department: function(frm) {
erpnext.employee_attendance_tool.load_employees(frm);
},
branch: function(frm) {
erpnext.employee_attendance_tool.load_employees(frm);
},
company: function(frm) {
erpnext.employee_attendance_tool.load_employees(frm);
}
});
erpnext.employee_attendance_tool = {
load_employees: function(frm) {
if(frm.doc.date) {
frappe.call({
method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.get_employees",
args: {
date: frm.doc.date,
department: frm.doc.department,
branch: frm.doc.branch,
company: frm.doc.company
},
callback: function(r) {
if(r.message['unmarked'].length > 0) {
unhide_field('unmarked_attendance_section')
if(!frm.employee_area) {
frm.employee_area = $('<div>')
.appendTo(frm.fields_dict.employees_html.wrapper);
}
frm.EmployeeSelector = new erpnext.EmployeeSelector(frm, frm.employee_area, r.message['unmarked'])
}
else{
hide_field('unmarked_attendance_section')
}
if(r.message['marked'].length > 0) {
unhide_field('marked_attendance_section')
if(!frm.marked_employee_area) {
frm.marked_employee_area = $('<div>')
.appendTo(frm.fields_dict.marked_attendance_html.wrapper);
}
frm.marked_employee = new erpnext.MarkedEmployee(frm, frm.marked_employee_area, r.message['marked'])
}
else{
hide_field('marked_attendance_section')
}
}
});
}
}
}
erpnext.MarkedEmployee = class MarkedEmployee {
constructor(frm, wrapper, employee) {
this.wrapper = wrapper;
this.frm = frm;
this.make(frm, employee);
}
make(frm, employee) {
var me = this;
$(this.wrapper).empty();
var row;
$.each(employee, function(i, m) {
var attendance_icon = "fa fa-check";
var color_class = "";
if(m.status == "Absent") {
attendance_icon = "fa fa-check-empty"
color_class = "text-muted";
}
else if(m.status == "Half Day") {
attendance_icon = "fa fa-check-minus"
}
if (i===0 || i % 4===0) {
row = $('<div class="row"></div>').appendTo(me.wrapper);
}
$(repl('<div class="col-sm-3 %(color_class)s">\
<label class="marked-employee-label"><span class="%(icon)s"></span>\
%(employee)s</label>\
</div>', {
employee: m.employee_name,
icon: attendance_icon,
color_class: color_class
})).appendTo(row);
});
}
};
erpnext.EmployeeSelector = class EmployeeSelector {
constructor(frm, wrapper, employee) {
this.wrapper = wrapper;
this.frm = frm;
this.make(frm, employee);
}
make(frm, employee) {
var me = this;
$(this.wrapper).empty();
var employee_toolbar = $('<div class="col-sm-12 top-toolbar">\
<button class="btn btn-default btn-add btn-xs"></button>\
<button class="btn btn-xs btn-default btn-remove"></button>\
</div>').appendTo($(this.wrapper));
var mark_employee_toolbar = $('<div class="col-sm-12 bottom-toolbar">\
<button class="btn btn-primary btn-mark-present btn-xs"></button>\
<button class="btn btn-primary btn-mark-work-from-home btn-xs"></button>\
<button class="btn btn-warning btn-mark-half-day btn-xs"></button>\
<button class="btn btn-danger btn-mark-absent btn-xs"></button>\
</div>');
employee_toolbar.find(".btn-add")
.html(__('Check all'))
.on("click", function() {
$(me.wrapper).find('input[type="checkbox"]').each(function(i, check) {
if(!$(check).is(":checked")) {
check.checked = true;
}
});
});
employee_toolbar.find(".btn-remove")
.html(__('Uncheck all'))
.on("click", function() {
$(me.wrapper).find('input[type="checkbox"]').each(function(i, check) {
if($(check).is(":checked")) {
check.checked = false;
}
});
});
mark_employee_toolbar.find(".btn-mark-present")
.html(__('Mark Present'))
.on("click", function() {
var employee_present = [];
$(me.wrapper).find('input[type="checkbox"]').each(function(i, check) {
if($(check).is(":checked")) {
employee_present.push(employee[i]);
}
});
frappe.call({
method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance",
args:{
"employee_list":employee_present,
"status":"Present",
"date":frm.doc.date,
"company":frm.doc.company
},
callback: function(r) {
erpnext.employee_attendance_tool.load_employees(frm);
}
});
});
mark_employee_toolbar.find(".btn-mark-absent")
.html(__('Mark Absent'))
.on("click", function() {
var employee_absent = [];
$(me.wrapper).find('input[type="checkbox"]').each(function(i, check) {
if($(check).is(":checked")) {
employee_absent.push(employee[i]);
}
});
frappe.call({
method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance",
args:{
"employee_list":employee_absent,
"status":"Absent",
"date":frm.doc.date,
"company":frm.doc.company
},
callback: function(r) {
erpnext.employee_attendance_tool.load_employees(frm);
}
});
});
mark_employee_toolbar.find(".btn-mark-half-day")
.html(__('Mark Half Day'))
.on("click", function() {
var employee_half_day = [];
$(me.wrapper).find('input[type="checkbox"]').each(function(i, check) {
if($(check).is(":checked")) {
employee_half_day.push(employee[i]);
}
});
frappe.call({
method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance",
args:{
"employee_list":employee_half_day,
"status":"Half Day",
"date":frm.doc.date,
"company":frm.doc.company
},
callback: function(r) {
erpnext.employee_attendance_tool.load_employees(frm);
}
});
});
mark_employee_toolbar.find(".btn-mark-work-from-home")
.html(__('Mark Work From Home'))
.on("click", function() {
var employee_work_from_home = [];
$(me.wrapper).find('input[type="checkbox"]').each(function(i, check) {
if($(check).is(":checked")) {
employee_work_from_home.push(employee[i]);
}
});
frappe.call({
method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance",
args:{
"employee_list":employee_work_from_home,
"status":"Work From Home",
"date":frm.doc.date,
"company":frm.doc.company
},
callback: function(r) {
erpnext.employee_attendance_tool.load_employees(frm);
}
});
});
var row;
$.each(employee, function(i, m) {
if (i===0 || (i % 4) === 0) {
row = $('<div class="row"></div>').appendTo(me.wrapper);
}
$(repl('<div class="col-sm-3 unmarked-employee-checkbox">\
<div class="checkbox">\
<label><input type="checkbox" class="employee-check" employee="%(employee)s"/>\
%(employee)s</label>\
</div></div>', {employee: m.employee_name})).appendTo(row);
});
mark_employee_toolbar.appendTo($(this.wrapper));
}
};

View File

@ -1,275 +0,0 @@
{
"allow_copy": 1,
"allow_import": 0,
"allow_rename": 0,
"creation": "2016-01-27 14:59:47.849379",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "Today",
"fieldname": "date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Date",
"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": "department",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Department",
"length": 0,
"no_copy": 0,
"options": "Department",
"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": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "",
"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": "branch",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Branch",
"length": 0,
"no_copy": 0,
"options": "Branch",
"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": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 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,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "date",
"fieldname": "unmarked_attendance_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Unmarked Attendance",
"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": "employees_html",
"fieldtype": "HTML",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Employees HTML",
"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,
"depends_on": "date",
"fieldname": "marked_attendance_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Marked Attendance",
"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": "marked_attendance_html",
"fieldtype": "HTML",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Marked Attendance HTML",
"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": 1,
"hide_toolbar": 1,
"idx": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2016-01-29 02:14:36.034952",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Attendance Tool",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "HR Manager",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 1
}
],
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -1,69 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
import frappe
from frappe.model.document import Document
from frappe.utils import getdate
class EmployeeAttendanceTool(Document):
pass
@frappe.whitelist()
def get_employees(date, department=None, branch=None, company=None):
attendance_not_marked = []
attendance_marked = []
filters = {"status": "Active", "date_of_joining": ["<=", date]}
for field, value in {"department": department, "branch": branch, "company": company}.items():
if value:
filters[field] = value
employee_list = frappe.get_list(
"Employee", fields=["employee", "employee_name"], filters=filters, order_by="employee_name"
)
marked_employee = {}
for emp in frappe.get_list(
"Attendance", fields=["employee", "status"], filters={"attendance_date": date}
):
marked_employee[emp["employee"]] = emp["status"]
for employee in employee_list:
employee["status"] = marked_employee.get(employee["employee"])
if employee["employee"] not in marked_employee:
attendance_not_marked.append(employee)
else:
attendance_marked.append(employee)
return {"marked": attendance_marked, "unmarked": attendance_not_marked}
@frappe.whitelist()
def mark_employee_attendance(employee_list, status, date, leave_type=None, company=None):
employee_list = json.loads(employee_list)
for employee in employee_list:
if status == "On Leave" and leave_type:
leave_type = leave_type
else:
leave_type = None
company = frappe.db.get_value("Employee", employee["employee"], "Company", cache=True)
attendance = frappe.get_doc(
dict(
doctype="Attendance",
employee=employee.get("employee"),
employee_name=employee.get("employee_name"),
attendance_date=getdate(date),
status=status,
leave_type=leave_type,
company=company,
)
)
attendance.insert()
attendance.submit()

Some files were not shown because too many files have changed in this diff Show More