예제 #1
0
def get_employee_shift_working_hours(emp_id):
    working_hours = []
    if emp_id:
        working_hours = frappe.db.sql("""
            SELECT
               FLOOR(emp.working_hours / total) AS working_hours
            FROM
               (SELECT
                   COUNT(0) AS total,
                       SUM(TIME_FORMAT(TIMEDIFF(st.shift_end_time, st.shift_start_time), '%h')) AS working_hours
               FROM
                   `tabAttendance` AS st
               WHERE
                   st.attendance_date >= DATE_FORMAT(NOW(), '%Y-01-01')
                       AND employee = '{0}'
               GROUP BY employee) AS emp
            """.format(emp_id),
                                      as_dict=True)

    if len(working_hours) == 0:
        default_time = dict({
            "start_time":
            get_config_by_name("default_shift_start_time", "09:00:00"),
            "end_time":
            get_config_by_name("default_shift_end_time", "18:00:00"),
            "date":
            datetime.datetime.today().strftime('%Y-%m-%d')
        })
        working_hours = calculate_working_hours_for_default(default_time)
    else:
        working_hours = working_hours[0]['working_hours']
    return working_hours
예제 #2
0
    def prepare_employee_data(self):

        _df_start_time = get_config_by_name("default_shift_start_time",
                                            "09:00:00")
        _df_end_time = get_config_by_name("default_shift_end_time", "18:00:00")
        _employee_cond = ""

        if self.employee:
            _employee_cond = """ AND e.name = "{0}" """.format(self.employee)

        self.employee_attendance = frappe.db.sql("""
            SELECT
            e.name,  
            IF(st.start_time IS NULL,%(start_time)s,st.start_time) as start_time,
            IF(st.end_time IS NULL,%(end_time)s,st.end_time) as end_time,
            e.company, mc.week_day_date as week_day,
            e.department,
            IF(h.name IS NULL AND ch.name IS NULL AND erch.name IS NULL, 
                IF(week_day_date = e.date_of_joining, "{1}", "{2}"), 
                IF(ch.name IS NOT NULL, "{3}", "{4}")
                ) as attendance_status,            
            IF(h.name IS NULL AND ch.name IS NULL AND erch.name IS NULL, "Absent", "Present") as status
            FROM `month_calendar` mc 
            INNER JOIN `tabEmployee` e ON mc.emp_fk = "employee" AND e.status = "Active" {0}
            LEFT JOIN `tabAttendance` att ON att.employee = e.name 
            AND att.attendance_date = mc.week_day_date
            LEFT JOIN `tabEmployee` es
            ON es.name = e.name
            INNER JOIN `tabShift Type` st
            ON st.name = es.shift_type
            LEFT JOIN
            `tabHoliday List` hl ON hl.name = e.holiday_list
            LEFT JOIN
            `tabHoliday` h ON h.parent = hl.name AND h.holiday_date = mc.week_day_date
            LEFT JOIN
            `tabCompany` c ON c.name = e.company
            LEFT JOIN
            `tabHoliday List` chl ON chl.name = c.default_holiday_list
            LEFT JOIN
            `tabHoliday` ch ON ch.parent = chl.name AND ch.holiday_date = mc.week_day_date
            LEFT JOIN
            `tabHoliday List` erhl ON erhl.name = st.holiday_list
            LEFT JOIN
            `tabHoliday` erch ON erch.parent = erhl.name AND erch.holiday_date = mc.week_day_date

            WHERE att.name IS NULL
            AND mc.week_day_date >= e.date_of_joining
            GROUP BY e.name, mc.week_day_date
        """.format(_employee_cond, ROSTER_STATUS_FLEXIBLE, ROSTER_STATUS_ON,
                   ROSTER_STATUS_HOLIDAY, ROSTER_STATUS_OFF), {
                       "start_time": _df_start_time,
                       "end_time": _df_end_time
                   },
                                                 as_dict=True)
예제 #3
0
def get_reversal_button(emp_id, processed_id, attendance_date):

    if (check_advance_privilege_by_name(
            get_config_by_name('CAN_REVERSE_ATTENDANCE_PENALTY')) == False
            or EmployeeRoster.is_cutoff_passed(
                attendance_date,
                get_config_by_name('PENALTY_REVERSAL_MONTHS_LIMIT',
                                   2)) == True):
        return "Processed"
    else:
        return """<button type="button" class="btn-primary btn btn-sm process-style" onclick="reverse_penalty('{0}','{1}')">{2}</button>""".format(
            emp_id, processed_id, "Reverse")
예제 #4
0
def before_save_attendance(doc, event):

    if doc.attendance_status == ROSTER_STATUS_HOLIDAY and doc.flags.ignore_permissions == False:
        frappe.throw(_("You cannot manually update roster status to holiday"))

    calculate_late_arrivals(doc)

    doc.shift_start_time = doc.shift_start_time if doc.shift_start_time != None else get_config_by_name("default_shift_start_time", "09:00:00")
    doc.shift_end_time = doc.shift_end_time if doc.shift_end_time != None else get_config_by_name("default_shift_end_time", "18:00:00")

    user_id = frappe.session.user
    auth_role = frappe.get_roles(user_id)


    emp_attendance = []
    if doc.amended_from is not None:
        emp_attendance = frappe.db.get_value("Attendance", {"name": doc.amended_from}, "*")

    if ADMINISTRATOR != user_id:
        # if(emp_attendance):
        #     emp_attendance.attendance_date = emp_attendance.attendance_date.strftime('%Y-%m-%d')
        #     # Only employee with advanced privilege can update privious date attendance for roster
        #     today_date = datetime.strftime(datetime.today(), '%Y-%m-%d')
        #     if emp_attendance.attendance_date < today_date and check_advance_privilege_by_name(get_config_by_name('CAN_UPDATE_PREVIOUS_DAY_ROSTER')) == False:
        #         frappe.throw(_("You cannot update previous date roster"))

        # Validate not to update attendance except AttendanceManager
        if ATTENDANCE_MANAGER not in auth_role and doc.flags.ignore_permissions == False:
            if emp_attendance:

            # validate for attendance_date
                if doc.attendance_date is not None:
                    if str(doc.attendance_date) != str(emp_attendance.attendance_date):
                        frappe.throw(_("You cannot update attendance date"))
            # validate for check_in
                if doc.check_in is not None:
                    if str(doc.check_in) != str(emp_attendance.check_in):
                        frappe.throw(_("You cannot update attendance check in time"))

            # validate for check_out
                if doc.check_out is not None:
                    if str(doc.check_out) != str(emp_attendance.check_out):
                        frappe.throw(_("You cannot update attendance check out time"))
            # validate for employee_id
                if doc.employee is not None:
                    if doc.employee != emp_attendance.employee:
                        frappe.throw(_("You cannot update employee code"))


        # Restrict self update for Attendance
        if doc.employee is not None:
            if doc.employee == get_employee_code():
                frappe.throw(_("You cannot update your attendance"))
예제 #5
0
def validate_dates(doctype, event_name):
    ''' validate cutoff dates '''

    try:
        date_obj = datetime.datetime.now()
        la_date = str_to_date(date_obj.strftime("%Y-%m-%d"), '%Y-%m-%d')
        processding_date = str_to_date(
            date_obj.strftime("%Y-%m-") +
            str(get_config_by_name('MONTH_PROCESSING_DATE', '20')), '%Y-%m-%d')
        restrict_leave_date = str_to_date(
            date_obj.strftime("%Y-%m-") +
            str(get_config_by_name('RESTRICTED_LEAVES_DATE', '22')),
            '%Y-%m-%d')
        from_date = str_to_date(doctype.from_date, '%Y-%m-%d')
        to_date = str_to_date(doctype.to_date, '%Y-%m-%d')
    except Exception as err:
        print(err)
        ErrorHandler.log_error(str(err))
    ''' if there is creation date then we consider creation date as leave application date la_date '''
    if hasattr(doctype, 'creation'):
        la_date = str_to_date(get_datetime(doctype.creation), '%Y-%m-%d')

    new_la = False
    if hasattr(doctype, '__islocal'):
        new_la = True
    ''' Check advance privilege and validate dates of cutoff'''
    if (check_advance_privilege_by_name(
            get_config_by_name('ADVANCE_PRIV_APPLY_LEAVE_AFTER_CUTOFF_DATE'))
            == False and new_la == True):
        if (la_date >= restrict_leave_date and
            (from_date <= processding_date or to_date <= processding_date)):
            frappe.throw(
                _("You cannot apply for a previous day leave after the cutoff date"
                  ))
        elif (
            (from_date <=
             processding_date - dateutil.relativedelta.relativedelta(months=1)
             or to_date <= processding_date -
             dateutil.relativedelta.relativedelta(months=1))):
            frappe.throw(
                _("You cannot apply for a previous day leave after the cutoff date"
                  ))
    #TODO: Make validation configurable
    ''' Check advance privilege Validation for casual leave '''
예제 #6
0
def attendance_api(attendance=[]):

    if attendance:
        body = attendance
    else:
        body = get_post_body()

    AttendanceCalculation.MIN_EARLY = get_config_by_name("MIN_EARLY", 0)
    AttendanceCalculation.MAX_LATE = get_config_by_name("MAX_LATE", 0)
    AttendanceCalculation.GRACE_MINS = get_config_by_name("GRACE_MINS", 0)
    AttendanceCalculation.EMPLOYEE_PREFIX = getenv("EMPLOYEE_PREFIX", "EMPG")

    response = list()

    if body:

        for att_data in body:
            if "user_id" in att_data:
                attendance = AttendanceCalculation(att_data["user_id"])
                response.append(attendance.add_update_attendance(att_data))

    return response
예제 #7
0
def get():
    args = get_form_params()

    try:
        args["filters"] = args["filters"] + frappe.get_doc(
            {
                "doctype": "Doctype List Rules",
                "doc": args["doctype"]
            }).get_filters()
    except Exception as err:
        frappe.log_error(err)

    # OVERRIDE START HERE
    # Show only custom reports which are defined in array
    role = get_user_role()
    report_list = get_config_by_name(
        'COMMON_REPORT_ALLOWED') + get_config_by_name('REPORTS_ALLOWED_LIST')
    if args['doctype'] == 'Report':
        if role is not ADMINISTRATOR:
            args['filters'].append(['Report', 'disabled', '=', 0])
            args['page_length'] = 500
    results = execute(**args)
    exec_arr = []
    if args['doctype'] == 'Report':
        if role is not ADMINISTRATOR:
            for result in results:
                if result.name in report_list:
                    exec_arr.append(result)
                elif 'total_count' in result:
                    exec_arr.append(result)
        else:
            exec_arr = results
    else:
        exec_arr = results
    # OVERRIDE END HERE

    data = compress(exec_arr, args=args)
    return data
예제 #8
0
def employee_leave_allocation(doctype, employee_detail):
    ''' Restrict leave allocation call for HR User, HR Manager and administrator'''
    if employee_detail and any(x in frappe.get_roles(
    ) for x in [LINE_MANAGER, HR_USER, HR_MANAGER, ADMINISTRATOR]) == True:

        if (doctype.employment_type == EMPLOYMENT_TYPE_PERMANENT):
            allot_leaves(doctype)
        elif (doctype.employment_type == get_config_by_name(
                'EMPLOYMENT_TYPE_PROBATION', "Probationary")):
            allot_leaves(doctype, True)
        if doctype.holiday_list:
            update_holiday_status(doctype.holiday_list, [doctype.name])
        ''' Update leave approver if line manager changed for employee'''
        if employee_detail[2] is not None or doctype.reports_to:
            if doctype.reports_to and doctype.reports_to != employee_detail[2]:
                update_leave_approver(doctype.name, doctype.reports_to)
예제 #9
0
def get_leave_status_list(islocal,employee=None):

    # Check User with Special Privillige or not
    if(check_advance_privilege_by_name(get_config_by_name('ADVANCE_PRIV_AUTO_APPROVE_LEAVE'))):
        return [LEAVE_STATUS_OPEN, LEAVE_STATUS_APPROVED, LEAVE_STATUS_REJECTED, LEAVE_STATUS_CANCELLED]
    
    if(any(x in frappe.get_roles() for x in [HR_MANAGER, HR_USER, LINE_MANAGER,ADMINISTRATOR]) and islocal != "new" ):
        if (employee is not None and employee == get_employee_code()):
            return [LEAVE_STATUS_OPEN, LEAVE_STATUS_CANCELLED]

        return [LEAVE_STATUS_OPEN, LEAVE_STATUS_APPROVED, LEAVE_STATUS_REJECTED, LEAVE_STATUS_CANCELLED]
    
    if(islocal=="new"):
        return [LEAVE_STATUS_OPEN]
    else:
        return [LEAVE_STATUS_OPEN, LEAVE_STATUS_CANCELLED]
예제 #10
0
def validate_leave_approver(doctype):

    reports_to = None
    if(check_advance_privilege_by_name(get_config_by_name('ADVANCE_PRIV_AUTO_APPROVE_LEAVE')) == False):
        # reports_to = ''
        reports_to_code = frappe.get_value("Employee", doctype.employee, "reports_to")
        if(reports_to_code):
            reports_to = frappe.get_value("Employee", reports_to_code, "user_id")
        dept_leave_approver = frappe.db.get_value('Department Approver', {'parent': doctype.department,'parentfield': 'leave_approvers','approver':doctype.leave_approver}, 'approver')
        hr_role = None
        if HR_MANAGER in frappe.get_roles(frappe.session.user) or HR_USER in frappe.get_roles(frappe.session.user):
            hr_role = True
        ''' Also validating HR User and HR Manager can approve leaves'''
        if(doctype.status in [LEAVE_STATUS_APPROVED, LEAVE_STATUS_REJECTED] and doctype.employee == get_employee_code()):
            frappe.throw(_("You cannot approve/reject your own leaves"))
        elif (doctype.status in [LEAVE_STATUS_APPROVED, LEAVE_STATUS_REJECTED] and doctype.employee not in get_auth_team_ids(True) and hr_role is None):
            frappe.throw(_("You cannot approve/reject this leaves"))
예제 #11
0
def allocate_probationary_leaves():
    employees = frappe.get_all("Employee",
                               filters={
                                   'status':
                                   'Active',
                                   'employment_type':
                                   get_config_by_name(
                                       'EMPLOYMENT_TYPE_PROBATION',
                                       "Probationary")
                               })
    for employee in employees:
        try:
            doc = frappe.get_doc("Employee", employee.name)
            allot_leaves(doc, True)
            print("employee:", employee.name)
            frappe.db.commit()
        except Exception as err:
            frappe.log_error(err)
            pass
예제 #12
0
def sync():
    ''' Fucntion to sync other departments roster with empghr roster '''
    _max_iterations = 10000
    _errors = []
    _success = []
    _iteration = 0
    response = dict()
    global _BODY
    global _VALID_EMPLOYEES
    _BODY = get_post_body()
    ''' Get allowed user to sync attendance '''
    users = get_config_by_name("ATTENDANCE_SYNC_USERS", [])
    if frappe.session.user in users:
        if _BODY:
            validation_err, _errors = validate_required_fields()
            if validation_err == False and len(_errors) < 1:
                _chunk = divide_chunks(_BODY, 20)
                while True or _iteration <= _max_iterations:
                    try:
                        _data = next(_chunk)
                        for att in _data:
                            ''' Add or update attendance'''
                            status, result_obj = add_or_update_attendance(
                                _VALID_EMPLOYEES[att.get("email")], att)
                            if status == False and result_obj:
                                att["error"] = result_obj
                                _errors.append(att)
                            else:
                                _success.append(result_obj)
                        _iteration += 1
                        frappe.db.commit()
                    except StopIteration:
                        break
                response = {"code": 200, "success": _success}
            else:
                response = {"code": 201, "error": _errors}
        return response
    else:
        return {
            "code": 403,
            "error": [_("You are not allowed to sync roster/attendance.")]
        }
예제 #13
0
def get_leave_approver(employee, department=None):
    leave_approver = ""
    employee_detail = frappe.db.get_value(
        'Employee', employee, ['department', 'reports_to', 'user_id'])

    if employee_detail and employee_detail[1]:
        leave_approver = frappe.db.get_value('Employee', employee_detail[1],
                                             'user_id')

    if not leave_approver and employee_detail and employee_detail[0]:
        leave_approver = frappe.db.get_value(
            'Department Approver', {
                'parent': employee_detail[0],
                'parentfield': 'leave_approvers',
                'idx': 1
            }, 'approver')

    if leave_approver is None and check_advance_privilege_by_name(
            get_config_by_name('ADVANCE_PRIV_AUTO_APPROVE_LEAVE')):
        if employee_detail and employee_detail[
                2] and frappe.session.user != ADMINISTRATOR:
            leave_approver = employee_detail[2]
    return leave_approver
예제 #14
0
 def get_config_by_name(self, *args):
     return get_config_by_name(args[0])
예제 #15
0
def validate_required_fields():
    _params = [
        "object_id", "shift_start_time", "shift_end_time", "attendance_status",
        "email", "attendance_date"
    ]
    _required_err = False
    _errors = []
    _invalid_employees = []
    _idx = -1
    _all_roster_statuses = dict()
    global _BODY
    global _VALID_EMPLOYEES
    _roster_mapping = get_config_by_name("ROSTER_MAPPINGS", {})
    _roster_statuses = _roster_mapping.get(frappe.session.user)
    if _roster_statuses is not None and _roster_statuses:
        for att in _BODY:
            _idx += 1
            _err = False
            for key in _params:
                if key not in att:
                    _required_err = True
                    _err = True
                    att["error"] = "{} is required.".format(key)
                    _errors.append(att)
                    break

            if _err == False:
                _err = True
                if att.get("email") not in _VALID_EMPLOYEES:
                    ''' If user is not in valid employees then get employee code by email and update valid_employees and invalid_employees'''

                    if att.get("email") in _invalid_employees:
                        ''' If not employee associated with email then break current itration of loop with error'''
                        continue
                    employee_obj = frappe.db.get_value(
                        'Employee', {'user_id': att.get("email")}, [
                            'name', 'employee_name', 'department',
                            'sub_department'
                        ],
                        as_dict=True)

                    if employee_obj:
                        _VALID_EMPLOYEES[att.get("email")] = employee_obj
                    else:
                        _invalid_employees.append(att.get("email"))
                        att["error"] = "No Employee associated with {0}.".format(
                            att.get("email"))
                        _errors.append(att)
                        continue
                ''' Validate shift start,end time and attendance date'''
                if att.get("shift_start_time"):
                    try:
                        time.strptime(att.get("shift_start_time"),
                                      '%Y-%m-%d %H:%M:%S')
                    except ValueError:
                        att["error"] = "Invalid shift start time {0}.".format(
                            att.get("shift_start_time"))
                        _errors.append(att)
                        continue

                if att.get("shift_end_time"):
                    try:
                        time.strptime(att.get("shift_end_time"),
                                      '%Y-%m-%d %H:%M:%S')
                    except ValueError:
                        att["error"] = "Invalid shift end time {0}.".format(
                            att.get("shift_end_time"))
                        _errors.append(att)
                        continue
                ''' Validate shift start,end time'''
                if att.get("shift_start_time") and att.get("shift_end_time"):
                    try:
                        if time.strptime(att.get("shift_end_time"),
                                         '%Y-%m-%d %H:%M:%S') < time.strptime(
                                             att.get("shift_start_time"),
                                             '%Y-%m-%d %H:%M:%S'):
                            att["error"] = "Shift end time must be greater than shift start time."
                            _errors.append(att)
                            continue
                    except ValueError:
                        att["error"] = "Invalid shift start or end time {0}, {1}.".format(
                            att.get("shift_start_time"),
                            att.get("shift_end_time"))
                        _errors.append(att)
                        continue

                if att.get("attendance_date"):
                    try:
                        att["attendance_date"] = datetime.strptime(
                            att.get("attendance_date"), '%Y-%m-%d').date()
                    except ValueError:
                        att["error"] = "Invalid attendance date {0}.".format(
                            att.get("attendance_date"))
                        _errors.append(att)
                        continue
                ''' Check attendance date is before or after cutoff date'''
                if EmployeeRoster.is_cutoff_passed(
                        att.get("attendance_date")) == True:
                    att["error"] = "Cannot update roster on date {0}  before cutoff Date.".format(
                        att.get("attendance_date"))
                    _errors.append(att)
                    continue
                ''' Check maping of roster status with attendance status'''
                _status = _roster_statuses.get(
                    str(att.get("attendance_status")))
                if _status and _status not in _all_roster_statuses:
                    status = frappe.db.get_value("Roster Status",
                                                 {"name": _status},
                                                 "parent_status")
                    if status is not None and status:
                        _all_roster_statuses[_status] = status
                    else:
                        att["error"] = "Roster status {0} is not linked with any Attendance Status.".format(
                            att.get("attendance_status"))
                        _errors.append(att)
                        continue
                elif not _status:
                    att["error"] = "Roster status {0} is not linked with any Attendance Status.".format(
                        att.get("attendance_status"))
                    _errors.append(att)
                    continue
                _BODY[_idx]["attendance_status"] = _status
                _BODY[_idx]["status"] = _all_roster_statuses[_status]
                _err = False
            if _err == True:
                _required_err = True
    else:
        _errors.append(_("Roster Statuses mapping not found."))
    return _required_err, _errors
예제 #16
0
def assign_employee_shift(employee, shift_type):

    if shift_type == None:
        return

    _today = datetime.today()
    today_date = _today.strftime("%Y-%m-%d")
    month = _today.strftime("%m")
    year = _today.strftime("%Y")

    attendace_processed = frappe.db.get_value("Attendance Penalties Processed",
                                              filters={
                                                  "employee": employee,
                                                  "month": month,
                                                  "year": year
                                              })

    if attendace_processed != None:
        today_date = """{0}-{1}-{2}""".format(year, month,
                                              MONTHLY_ATTENDANCE_DATE)

    emp_future_leaves = frappe.db.sql(
        """select * from `tabLeave Application`
    					where employee = %s and (from_date >= %s OR to_date >= %s)
    					and docstatus = 1""", (employee, today_date, today_date))

    if len(emp_future_leaves) > 0:
        frappe.throw(
            _("You cannot add/change shift because employee have applied for future leave"
              ))

    _df_start_time = get_config_by_name("default_shift_start_time", "09:00:00")
    _df_end_time = get_config_by_name("default_shift_end_time", "18:00:00")

    try:
        emp_attendance_list = frappe.db.sql("""
            SELECT
                att.name,
                att.attendance_date,
                IF(st.start_time IS NULL,%(start_time)s,st.start_time) as start_time,
                IF(st.end_time IS NULL,%(end_time)s,st.end_time) as end_time,
                IF(h.name IS NULL AND ch.name IS NULL AND erch.name IS NULL, "On", "Off") as attendance_status,
                IF(h.name IS NULL AND ch.name IS NULL AND erch.name IS NULL, "Absent", "Present") as status

                FROM `tabAttendance` att 
                LEFT JOIN `tabEmployee` emp
                ON att.employee = emp.name
                LEFT JOIN `tabShift Type` st
                ON st.name = %(shift_name)s
                LEFT JOIN
                `tabHoliday List` hl ON hl.name = emp.holiday_list
                LEFT JOIN
                `tabHoliday` h ON h.parent = hl.name AND h.holiday_date = att.attendance_date
                LEFT JOIN
                `tabCompany` c ON c.name = emp.company
                LEFT JOIN
                `tabHoliday List` chl ON chl.name = c.default_holiday_list
                LEFT JOIN
                `tabHoliday` ch ON ch.parent = chl.name AND ch.holiday_date = att.attendance_date
                LEFT JOIN
                `tabHoliday List` erhl ON erhl.name = st.holiday_list
                LEFT JOIN
                `tabHoliday` erch ON erch.parent = erhl.name AND erch.holiday_date = att.attendance_date

                WHERE att.attendance_date > %(today_date)s AND att.docstatus = 1 AND att.employee = %(employee)s
        
            """, {
            "shift_name": shift_type,
            "start_time": _df_start_time,
            "end_time": _df_end_time,
            "employee": employee,
            "today_date": today_date
        },
                                            as_dict=True)

    except Exception as err:
        ErrorHandler.log_error(str(err))
        frappe.throw(_("did not find roster against new employee shift"))

    if len(emp_attendance_list) > 0:

        _att_list = [emp_sh["name"] for emp_sh in emp_attendance_list]

        unlock_doc_list("Attendance", _att_list)

        for attendance in emp_attendance_list:

            try:
                emp_att = frappe.get_doc("Attendance", attendance["name"])
                emp_att.shift_start_time = attendance["start_time"]
                emp_att.shift_end_time = attendance["end_time"]
                emp_att.status = attendance["status"]
                emp_att.attendance_status = attendance["attendance_status"]
                emp_att.docstatus = 1
                emp_att.save()
            except Exception as err:
                ErrorHandler.log_error(str(err))
                frappe.throw(_("There is an error while updating Attendance"))
    def update_leave_quota(self, year, month, employee_ids):
        errors = list()
        if len(employee_ids) == 0:

            return False

        _df_start_time = get_config_by_name("default_shift_start_time", "09:00:00")
        _df_end_time = get_config_by_name("default_shift_end_time", "18:00:00")

        _sql = """
            SELECT 
                `eap`.*,
                SUM(eap.penalty) AS `calculated_val`,
                app.name as processed_id,
                `apr`.*,
                `emp`.`department`,
                `emp`.`employee_name`,
                `emp`.`date_of_joining`,
                HOUR(TIMEDIFF(
                if(shift.end_time IS NOT NULL,shift.end_time, "{3}"),
                if(shift.start_time IS NOT NULL,shift.start_time, "{4}")
                )) as working_hours
            FROM
                `tabEmployee Attendance Penalties` AS `eap`
                INNER JOIN `tabAttendance Policy Rules` AS `apr` ON eap.attendance_policy_rules = apr.name
                INNER JOIN `tabEmployee` AS `emp` ON eap.employee = emp.employee
				LEFT JOIN `tabShift Type` as shift ON shift.name = emp.shift_type
                LEFT JOIN `tabAttendance Penalties Processed` app on app.employee = emp.employee
                AND app.month = eap.month AND app.year = eap.year
                
            WHERE
                (eap.year = '{0}' AND eap.month = '{1}'
                    AND eap.employee IN ({2}))
            GROUP BY `eap`.`employee`  
        """.format(year, month, "'" + "','".join(employee_ids) + "'", _df_end_time, _df_start_time)

        employee_list = frappe.db.sql(_sql, as_dict=True)
        
        date_start = datetime.today()
        from_date = posting_date = str_to_date(date_start, "%Y-%m-%d")
        month_start_date = str_to_date(datetime(int(year), int(month), 01),'%Y-%m-%d')

        for emp in employee_list:

            penalty_min = emp["calculated_val"]
            penalty_days = round((penalty_min/60) / int(emp["working_hours"]) , 2)

            response = self.create_earned_leave_quota(emp["employee"])
            if(response):
                errors.append("{0} ---> {1}.".format(response,emp["employee"]))
                continue

            if emp["policy_multiplier"] > 0:

                _itr = 20
                date_of_joining = str_to_date(emp["date_of_joining"],'%Y-%m-%d')
                if(date_of_joining > month_start_date):
                    month_start_date = date_of_joining

                while penalty_days > 0 and _itr > 0:

                    leaves = self.get_prioritized_pending_leaves(emp["employee"], month_start_date)
                    
                    if bool(leaves):

                        _quota = 0

                        if leaves["remaining_leaves"] <= 0 :
                            break

                        elif leaves["remaining_leaves"] > penalty_days:
                            _quota = penalty_days
                            penalty_days = 0
                        else:
                            # if remaining leaves < penalty days
                            _quota = leaves["remaining_leaves"]
                            penalty_days = penalty_days - leaves["remaining_leaves"]

                        to_date = datetime.strftime(month_start_date + timedelta(days=math.ceil(_quota) -1), "%Y-%m-%d")

                        try :

                            leave_application = frappe.get_doc({"doctype" : "Leave Application"})
                            set_name_by_naming_series(leave_application)
                            leave_approver = get_report_to(emp["employee"],True)

                            _sql = """
                                INSERT INTO `tabLeave Application` (name, employee, employee_name, from_date, to_date, status,
                                total_leave_days, docstatus, leave_type, description, department, leave_balance, follow_via_email, leave_approver, posting_date, creation, modified) 
                                VALUES ("{}", "{}", "{}", "{}", "{}", "Approved", "{}", 1, "{}", "Penalty Deduction", "{}", "{}", 0, "{}", "{}", NOW(), NOW() )
                                """.format(leave_application.name, emp["employee"], emp["employee_name"], month_start_date, to_date, _quota, 
                                leaves["leave_type"], emp["department"], leaves["leave_balance"],leave_approver ,posting_date)

                            frappe.db.sql(_sql)

                        except Exception as err:
                            frappe.log_error(err)
                            errors.append("{0} ---> {1}.".format(_("Unable to update quota"),emp["employee"]))
                            break
                        try :

                            frappe.get_doc({
                                "doctype" : "Employee Penalty Leaves",
                                "leave_application" : leave_application.name,
                                "attendance_penalties_processed" : emp["processed_id"]
                            }).submit()

                        except Exception as err:
                            frappe.log_error(err)
                            errors.append("{0} ---> {1}.".format(_("Unable to log quota updation"),emp["employee"]))
                            break
                    else:
                        errors.append("{0} ---> {1}.".format(_("didn't find any leave to deduct quota"),emp["employee"]))
                        break
                    _itr -= 1

            else:

                reward_minutes = emp["unit_value_to"] - emp["value"]
                reward_days = round(float(reward_minutes/60) / int(emp["working_hours"]) , 2)

                start_date, end_date = get_monthly_diff_dates()

                earned_leaves = get_leave_allocation_detail(emp["employee"], "Earned Leave", start_date, end_date)

                if len(earned_leaves) > 0:
                    
                    try :

                        leave_allocation = frappe.get_doc("Leave Allocation", earned_leaves[0]["name"])
                        
                        leave_allocation_log = frappe.get_doc({
                            "doctype" : "Leave Allocation Log",
                        })

                        leave_allocation_log.total_leaves_allocated = leave_allocation.total_leaves_allocated
                        leave_allocation_log.employee = leave_allocation.employee
                        leave_allocation_log.leave_type = leave_allocation.leave_type
                        leave_allocation_log.to_date = leave_allocation.to_date
                        leave_allocation_log.from_date = leave_allocation.from_date

                        leave_allocation_log.insert()

                        leave_allocation.update({"new_leaves_allocated": float(reward_days) + float(earned_leaves[0]["total_leaves_allocated"])})
                        leave_allocation.flags.ignore_permissions = True
                        leave_allocation.save()

                    except Exception as err:
                        errors.append("{0} ---> {1}.".format(_("Unable to update quota"),emp["employee"]))                     

        return errors
예제 #18
0
def allot_leaves(doctype, probationary=False):
    date_obj = datetime.datetime.now()
    total_days_in_year = 365

    joining_date = str_to_date(doctype.date_of_joining, "%Y-%m-%d")
    j_year = joining_date.year
    curr_year = conf_year = date_obj.year
    system_launching_year = get_config_by_name('SYSTEM_LAUNCH_YEAR', 2019)

    if (j_year < system_launching_year):
        j_year = system_launching_year

    if probationary:
        confirmation_date = str_to_date(doctype.final_confirmation_date,
                                        "%Y-%m-%d")
        conf_year = confirmation_date.year

    while conf_year >= j_year:
        leave_period_start = year_start = str_to_date(
            date_obj.strftime(str(j_year) + "-01-01"), "%Y-%m-%d")
        year_end = str_to_date(date_obj.strftime(str(j_year) + "-12-31"),
                               "%Y-%m-%d")

        if (joining_date > year_start):
            leave_days = flt(total_days_in_year -
                             get_date_diff_in_days(year_start, joining_date))
            leave_period_start = joining_date
        else:
            leave_days = total_days_in_year

        if probationary and conf_year == j_year:
            if conf_year == joining_date.year:
                leave_days = flt(
                    get_date_diff_in_days(joining_date, confirmation_date))
            else:
                leave_days = flt(
                    get_date_diff_in_days(year_start, confirmation_date))

        leaves = get_auto_assigned_leaves_detail(probationary)

        for leave in leaves:
            if (leave and leave.max_leaves_allowed > 0):
                alloted_leave_balance = round(
                    flt(leave_days * leave.max_leaves_allowed) /
                    total_days_in_year, 2)
                Leave_allocation_obj = frappe.get_doc(
                    {"doctype": "Leave Allocation"})
                Leave_allocation_obj.employee = doctype.employee
                Leave_allocation_obj.leave_type = leave.name
                Leave_allocation_obj.from_date = leave_period_start
                Leave_allocation_obj.to_date = year_end
                Leave_allocation_obj.new_leaves_allocated = alloted_leave_balance
                alloted_leave_by_type = get_leave_allocation_record(
                    doctype.employee, leave.name, leave_period_start, year_end)
                if (alloted_leave_balance > 0):
                    if (not alloted_leave_by_type):
                        Leave_allocation_obj.docstatus = 1
                        Leave_allocation_obj.flags.ignore_permissions = True
                        try:
                            Leave_allocation_obj.insert()
                        except Exception as err:
                            frappe.throw(_(err))
                            ErrorHandler.log_error(str(err))
                    elif (alloted_leave_by_type
                          and leave.prorated_on_probation):
                        if alloted_leave_by_type[
                                0].total_leaves_allocated != alloted_leave_balance:
                            la_doc = frappe.get_doc(
                                "Leave Allocation",
                                alloted_leave_by_type[0].name)
                            la_doc.update({
                                "new_leaves_allocated":
                                alloted_leave_balance
                            })
                            la_doc.flags.ignore_permissions = True
                            la_doc.save()

        j_year = j_year + 1