diff --git a/.github/workflows/attendance.yml b/.github/workflows/attendance.yml
new file mode 100644
index 0000000000..0def926f8e
--- /dev/null
+++ b/.github/workflows/attendance.yml
@@ -0,0 +1,36 @@
+name: Deploy CI/CD to Attendance
+
+on:
+ push: # tells github to run this on any push to the repository
+ branches:
+ - attendance
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+ # This workflow contains a single job called "build"
+ build:
+ # The type of runner that the job will run on
+ runs-on: ubuntu-latest
+ timeout-minutes: 90
+
+ # Steps represent a sequence of tasks that will be executed as part of the job
+ steps:
+ - name: Deploy to Attendance
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.STAGING_HOST }}
+ username: ${{ secrets.USERNAME }}
+ key: ${{ secrets.STAGING_KEY }}
+ port: 22
+ script: |
+ cd /home/frappe/attendance-bench/apps/one_fm # we move into our app's folder
+ # git pull upstream attendance # we pull any changes from git
+ git pull
+ cd /home/frappe
+ pip3 install --upgrade attendance-bench
+ cd /home/frappe/attendance-bench
+ bench setup requirements one_fm
+ bench migrate # sync database
+ bench restart #${{secrets.PASSKEY}}
+ # we remove any unused dependencies
+ bench doctor
diff --git a/one_fm/api/doc_events.py b/one_fm/api/doc_events.py
index 58cffd371b..050492b34c 100644
--- a/one_fm/api/doc_events.py
+++ b/one_fm/api/doc_events.py
@@ -6,7 +6,6 @@
from frappe import _
from frappe.utils import cstr, cint, get_datetime, getdate, add_to_date
from frappe.core.doctype.version.version import get_diff
-from one_fm.utils import get_current_shift
from one_fm.operations.doctype.operations_site.operations_site import create_notification_log
import datetime
from frappe.permissions import remove_user_permission
diff --git a/one_fm/api/mobile/Leave_application.py b/one_fm/api/mobile/Leave_application.py
index 26b08bb74d..cc42dcfeea 100644
--- a/one_fm/api/mobile/Leave_application.py
+++ b/one_fm/api/mobile/Leave_application.py
@@ -8,7 +8,6 @@
import base64, json
from one_fm.api.v1.utils import response, validate_date
from frappe.utils import cint, cstr, getdate
-from one_fm.utils import get_current_shift
from one_fm.api.tasks import get_action_user
from one_fm.api.api import push_notification_rest_api_for_leave_application
from one_fm.processor import sendemail
diff --git a/one_fm/api/mobile/face_recognition.py b/one_fm/api/mobile/face_recognition.py
index 4aeaad361c..348b85e838 100644
--- a/one_fm/api/mobile/face_recognition.py
+++ b/one_fm/api/mobile/face_recognition.py
@@ -10,14 +10,17 @@
# setup channel for face recognition
face_recognition_service_url = frappe.local.conf.face_recognition_service_url
-channels = [
- grpc.secure_channel(i, grpc.ssl_channel_credentials()) for i in face_recognition_service_url
-]
+# channels = [
+# grpc.secure_channel(i, grpc.ssl_channel_credentials()) for i in face_recognition_service_url
+# ]
# setup stub for face recognition
-stubs = [
- facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
-]
+# stubs = [
+# facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
+# ]
+
+stubs = list()
+
@frappe.whitelist()
diff --git a/one_fm/api/tasks.py b/one_fm/api/tasks.py
index 76ba2da39e..3ab7321a26 100644
--- a/one_fm/api/tasks.py
+++ b/one_fm/api/tasks.py
@@ -20,7 +20,6 @@
from one_fm.utils import get_current_shift
from one_fm.processor import sendemail
from one_fm.api.api import push_notification_for_checkin, push_notification_rest_api_for_checkin
-from one_fm.operations.doctype.employee_checkin_issue.employee_checkin_issue import approve_open_employee_checkin_issue
from hrms.hr.utils import get_holidays_for_employee
class DeltaTemplate(Template):
@@ -227,6 +226,7 @@ def checkin_checkout_supervisor_reminder():
"""
frappe.enqueue(supervisor_reminder,shift = shift,today_datetime = today_datetime ,now_time=now_time, is_async=True, queue='long')
+
def supervisor_reminder(shift, today_datetime, now_time):
title = "Checkin Report"
category = "Attendance"
@@ -234,6 +234,7 @@ def supervisor_reminder(shift, today_datetime, now_time):
if shift.start_time < shift.end_time and nowtime() < cstr(shift.start_time):
date = getdate() - timedelta(days=1)
+
if (strfdelta(shift.start_time, '%H:%M:%S') == cstr((get_datetime(now_time) - timedelta(minutes=cint(shift.supervisor_reminder_shift_start))).time())) or (shift.has_split_shift == 1 and strfdelta(shift.second_shift_start_time, '%H:%M:%S') == cstr((get_datetime(now_time) - timedelta(minutes=cint(shift.supervisor_reminder_shift_start))).time())):
checkin_time = today_datetime + " " + strfdelta(shift.start_time, '%H:%M:%S')
recipients = checkin_checkout_query(date=cstr(date), shift_type=shift.name, log_type="IN")
@@ -324,9 +325,9 @@ def get_active_shifts(now_time):
def checkin_checkout_query(date, shift_type, log_type):
if log_type == "IN":
- permission_type = ("Arrive Late", "Forget to Checkin", "Checkin Issue")
+ permission_type = ("Arrive Late", )
else:
- permission_type = ("Leave Early", "Forget to Checkout", "Checkout Issue")
+ permission_type = ("Leave Early",)
query = frappe.db.sql("""SELECT DISTINCT emp.user_id, emp.name , emp.employee_name, emp.holiday_list, tSA.shift
FROM `tabShift Assignment` tSA, `tabEmployee` emp
@@ -721,11 +722,13 @@ def fetch_non_shift(date, s_type):
def assign_am_shift():
date = cstr(getdate())
end_previous_shifts("AM")
+ employment_type = ["Full-time", "Part-time", "Intern", "Subcontractor"]
roster = frappe.db.sql("""
SELECT * from `tabEmployee Schedule` ES
WHERE
ES.date = '{date}'
AND ES.employee_availability = "Working"
+ AND ES.is_replaced = 0
AND ES.roster_type = "Basic"
AND ES.shift_type IN(
SELECT name from `tabShift Type` st
@@ -733,8 +736,9 @@ def assign_am_shift():
AND st.start_time < '13:00:00')
AND ES.employee IN(
SELECT name from `tabEmployee`
- WHERE status = "Active")
- """.format(date=cstr(date)), as_dict=1)
+ WHERE status = "Active"
+ AND employment_type IN {employment_type})
+ """.format(date=cstr(date), employment_type=tuple(employment_type)), as_dict=1)
non_shift = fetch_non_shift(date, "AM")
if non_shift:
@@ -746,11 +750,13 @@ def assign_am_shift():
def assign_pm_shift():
date = cstr(getdate())
end_previous_shifts("PM")
+ employment_type = ["Full-time", "Part-time", "Intern", "Subcontractor"]
roster = frappe.db.sql("""
SELECT * from `tabEmployee Schedule` ES
WHERE
ES.date = '{date}'
AND ES.employee_availability = "Working"
+ AND ES.is_replaced = 0
AND ES.roster_type = "Basic"
AND ES.shift_type IN(
SELECT name from `tabShift Type` st
@@ -758,8 +764,9 @@ def assign_pm_shift():
)
AND ES.employee IN(
SELECT name from `tabEmployee`
- WHERE status = "Active")
- """.format(date=cstr(date)), as_dict=1)
+ WHERE status = "Active"
+ AND employment_type IN {employment_type})
+ """.format(date=cstr(date), employment_type=tuple(employment_type)), as_dict=1)
non_shift = fetch_non_shift(date, "PM")
if non_shift:
@@ -788,6 +795,8 @@ def get_shift_type(time):
def create_shift_assignment(roster, date, time):
try:
+
+
owner = frappe.session.user
creation = now()
shift_type = get_shift_type(time)
@@ -849,19 +858,21 @@ def create_shift_assignment(roster, date, time):
INSERT INTO `tabShift Assignment` (`name`, `company`, `docstatus`, `employee`, `employee_name`, `shift_type`, `site`, `project`, `status`,
`shift_classification`, `site_location`, `start_date`, `start_datetime`, `end_datetime`, `department`,
`shift`, `operations_role`, `post_abbrv`, `roster_type`, `owner`, `modified_by`, `creation`, `modified`,
- `shift_request`, `check_in_site`, `check_out_site`)
+ `shift_request`, `check_in_site`, `check_out_site`,`custom_day_off_ot`)
VALUES
"""
query_body = """"""
# check if all roster has been done
has_rostered = []
for r in roster:
+ _day_off_ot = 0
if not r.employee in existing_shift_list:
if shift_request_dict.get(r.employee):
_shift_request = shift_request_dict.get(r.employee)
_shift_type = shift_types_dict.get(_shift_request.shift_type) or default_shift
_project_r = frappe.db.get_value("Operations Shift", {'name':_shift_request.operations_shift}, ['project'])
shift_r_start_time = date+ " " + str(_shift_type.start_time)
+ _day_off_ot = 1 if shift_request_dict.get(r.employee).get('purpose') == 'Day Off Overtime' else 0
if _shift_type.start_time > _shift_type.end_time:
shift_r_end_time = str(add_to_date(date, days=1))+ " " + str(_shift_type.end_time)
@@ -874,7 +885,7 @@ def create_shift_assignment(roster, date, time):
"{_shift_request.site or ''}", "{_project_r or ''}", 'Active', '{_shift_request.shift_type}', "{sites_list_dict.get(_shift_request.site) or ''}", "{date}",
"{shift_r_start_time or str(date)+' 08:00:00'}", "{shift_r_end_time or str(date)+' 17:00:00'}", "{r.department}",
"{_shift_request.operations_shift or ''}", "{_shift_request.operations_role or ''}", "{r.post_abbrv or ''}", "{_shift_request.roster_type}",
- "{owner}", "{owner}", "{creation}", "{creation}", "{_shift_request.name}", "{_shift_request.check_in_site}", "{_shift_request.check_out_site}"),"""
+ "{owner}", "{owner}", "{creation}", "{creation}", "{_shift_request.name}", "{_shift_request.check_in_site}", "{_shift_request.check_out_site}","{_day_off_ot}"),"""
else:
_shift_type = shift_types_dict.get(r.shift_type) or default_shift
query_body += f"""
@@ -883,7 +894,7 @@ def create_shift_assignment(roster, date, time):
"{r.site or ''}", "{r.project or ''}", 'Active', '{_shift_type.shift_type}', "{sites_list_dict.get(r.site) or ''}", "{date}",
"{_shift_type.start_datetime or str(date)+' 08:00:00'}",
"{_shift_type.end_datetime or str(date)+' 17:00:00'}", "{r.department}", "{r.shift or ''}", "{r.operations_role or ''}", "{r.post_abbrv or ''}", "{r.roster_type}",
- "{owner}", "{owner}", "{creation}", "{creation}", '', '', ''),"""
+ "{owner}", "{owner}", "{creation}", "{creation}", '', '', '',"{_day_off_ot}"),"""
else:
has_rostered.append(r.employee_name)
@@ -911,7 +922,8 @@ def create_shift_assignment(roster, date, time):
check_in_site = VALUES(check_in_site),
check_out_site = VALUES(check_out_site),
shift_classification = VALUES(shift_classification),
- status = VALUES(status)
+ status = VALUES(status),
+ custom_day_off_ot = '{_day_off_ot}'
"""
frappe.db.sql(query, values=[], as_dict=1)
frappe.db.commit()
@@ -934,7 +946,6 @@ def validate_shift_assignment(is_scheduled_event=True):
shift_request = frappe.db.sql("""
SELECT * from `tabShift Request` SR
WHERE '{date}' BETWEEN SR.from_date AND SR.to_date
- AND SR.assign_day_off = 0
AND SR.workflow_state = 'Approved'
AND SR.employee
NOT IN (Select employee from `tabShift Assignment` tSA
@@ -947,11 +958,12 @@ def validate_shift_assignment(is_scheduled_event=True):
IN (Select employee from `tabEmployee` E
WHERE E.name = SR.employee
AND E.status = 'Active')""".format(now_time=now_time,date=cstr(date), now=now), as_dict=1)
-
+
roster = frappe.db.sql("""
SELECT * from `tabEmployee Schedule` ES
WHERE ES.start_datetime = '{now}'
AND ES.employee_availability = "Working"
+ AND ES.is_replaced = 0
AND ES.employee
NOT IN (Select employee from `tabShift Assignment` tSA
WHERE tSA.employee = ES.employee
@@ -979,7 +991,7 @@ def validate_shift_assignment(is_scheduled_event=True):
if non_shift:
roster.extend(non_shift)
-
+
if shift_request:
roster_emp = shift_request
employee_list = [i.employee for i in shift_request]
@@ -1006,6 +1018,7 @@ def validate_am_shift_assignment(is_scheduled_event=True):
ES.date = '{date}'
AND ES.employee_availability = "Working"
AND ES.roster_type = "Basic"
+ AND ES.is_replaced = 0
AND ES.shift_type IN(
SELECT name from `tabShift Type` st
WHERE st.start_time >= '01:00:00'
@@ -1051,6 +1064,7 @@ def validate_pm_shift_assignment():
ES.date = '{date}'
AND ES.employee_availability = "Working"
AND ES.roster_type = "Basic"
+ AND ES.is_replaced = 0
AND ES.shift_type IN(
SELECT name from `tabShift Type` st
WHERE st.start_time < '01:00:00' OR st.start_time >= '13:00:00'
@@ -1095,7 +1109,7 @@ def overtime_shift_assignment():
"""
date = cstr(getdate())
now_time = add_to_date(now_datetime(), hours=1).strftime("%H:%M:00")
- roster = frappe.get_all("Employee Schedule", {"date": date, "employee_availability": "Working" , "roster_type": "Over-Time"}, ["*"])
+ roster = frappe.get_all("Employee Schedule", {"date": date, "employee_availability": "Working" , "roster_type": "Over-Time", "is_replaced": 0}, ["*"])
shift_request = frappe.db.sql(f"""SELECT sr.*, 'Shift Request' as doctype FROM `tabShift Request` sr
WHERE '{date}' between sr.from_date and sr.to_date
AND sr.roster_type = 'Over-Time'
@@ -1108,7 +1122,7 @@ def process_overtime_shift(roster, date, time):
for schedule in roster:
#Check for employee's shift assignment of the day, if he has any.
try:
- if frappe.db.exists('Employee', {'employee':schedule.employee, 'status':'Active'}):
+ if frappe.db.exists('Employee', {'employee':schedule.employee, 'status':'Active', "employment_type": ["IN", ["Full-time", "Part-time", "Intern", "Subcontractor"]]}):
shift_assignment = frappe.db.sql(f"""SELECT name, shift_type, end_datetime, roster_type from `tabShift Assignment` WHERE employee = '{schedule.employee}' AND date(end_datetime) = '{date}'""", as_dict=1)
if shift_assignment:
shift_end_time = frappe.get_value("Shift Type",shift_assignment[0].shift_type, "end_time")
@@ -1123,7 +1137,7 @@ def process_overtime_shift(roster, date, time):
except Exception as e:
continue
-def create_overtime_shift_assignment(schedule, date):
+def create_overtime_shift_assignment(schedule, date):
try:
shift_assignment = frappe.new_doc("Shift Assignment")
shift_assignment.start_date = date
@@ -1620,7 +1634,9 @@ def get_current_schedules(employee, log_type=None):
# Attendance Request, Leaves
employee_doc = frappe.db.get_value("Employee", employee, ['name', 'holiday_list'])
start_date = str(getdate())
- curr_shift = get_current_shift(employee)
+ shift_exists = get_current_shift(employee)
+ if shift_exists['type'] == "On Time":
+ curr_shift = shift_exists['data']
# check day off
if frappe.db.exists('Employee Schedule', {
'employee':employee,
@@ -1701,27 +1717,23 @@ def fetch_employees_not_in_checkin():
for log_type in log_types:
# capture current minute
if log_type=='IN':
- reminder_minutes = [i.minute for i in frappe.db.sql("""
+ initial_reminder_minutes = [i.minute for i in frappe.db.sql("""
SELECT notification_reminder_after_shift_start as minute FROM `tabShift Type`
WHERE notification_reminder_after_shift_start>0
GROUP BY notification_reminder_after_shift_start;
""", as_dict=1)]
-
-
- late_entry_reminder_minutes = [i.minute for i in frappe.db.sql("""
+ grace_period_minute = [i.minute for i in frappe.db.sql("""
SELECT late_entry_grace_period as minute FROM `tabShift Type`
WHERE late_entry_grace_period>0
GROUP BY late_entry_grace_period;
""", as_dict=1)]
-
-
supervisor_reminder_minutes = [i.minute for i in frappe.db.sql("""
SELECT supervisor_reminder_shift_start as minute FROM `tabShift Type`
WHERE supervisor_reminder_shift_start>0
GROUP BY supervisor_reminder_shift_start;
""", as_dict=1)]
else:
- reminder_minutes = [i.minute for i in frappe.db.sql("""
+ initial_reminder_minutes = [i.minute for i in frappe.db.sql("""
SELECT notification_reminder_after_shift_end as minute FROM `tabShift Type`
WHERE notification_reminder_after_shift_end>0
GROUP BY notification_reminder_after_shift_end;
@@ -1738,14 +1750,12 @@ def fetch_employees_not_in_checkin():
shift_assignments_employees_list = frappe.db.sql(f"""
SELECT DISTINCT sa.employee, sa.shift_type, sa.start_datetime, sa.end_datetime,
sa.shift as operations_shift, st.notification_reminder_after_shift_start,
- st.notification_reminder_after_shift_end, st.supervisor_reminder_shift_start,
- st.supervisor_reminder_start_ends,st.late_entry_grace_period, os.supervisor as shift_supervisor,
+ st.notification_reminder_after_shift_end, st.late_entry_grace_period, st.supervisor_reminder_shift_start,
+ st.supervisor_reminder_start_ends, os.supervisor as shift_supervisor,
osi.account_supervisor as site_supervisor,sa.name as shift_assignment_id
-
FROM `tabShift Assignment` sa RIGHT JOIN `tabShift Type` st ON sa.shift_type=st.name
RIGHT JOIN `tabOperations Shift` os ON sa.shift=os.name RIGHT JOIN `tabOperations Site` osi
ON sa.site=osi.name
-
WHERE {'sa.start_datetime' if log_type=='IN' else 'sa.end_datetime'}='{cur_date} {shift_start_time}'
AND sa.status='Active' AND sa.docstatus=1
GROUP BY sa.employee
@@ -1767,7 +1777,6 @@ def fetch_employees_not_in_checkin():
GROUP BY employee
""", as_dict=1)]
employees_yet_to_checkin = [i for i in shift_assignments_employees if not i in checkins]
-
# check shift permissions, attendance request, leave application
shift_permissions = [i.employee for i in frappe.db.sql(f"""
SELECT employee FROM `tabShift Permission`
@@ -1804,37 +1813,38 @@ def fetch_employees_not_in_checkin():
# holiday list
holiday_list = [i for i,j in get_holiday_today(cur_date).items()]
- holiday_list_employees = [i.name for i in frappe.db.get_list("Employee", filters={
- 'name': ['IN', employees_yet_to_checkin],
- 'status':'Active',
- 'holiday_list': ['IN', holiday_list]
- })]
+ holiday_list_tuple = str(tuple(holiday_list)).replace(',)', ')')
+ holiday_list_employees = [i.name for i in frappe.db.sql(f"""SELECT name from `tabEmployee` WHERE
+ status = 'Active' AND
+ holiday_list IN {holiday_list_tuple}
+ """)]
employees_yet_to_checkin = [i for i in employees_yet_to_checkin if not i in holiday_list_employees]
-
+
employee_details = frappe.db.get_list("Employee", filters={
'name': ['IN', employees_yet_to_checkin]},
fields=['name', 'employee_id', 'employee_name', 'user_id', 'prefered_contact_email',
'prefered_email', 'reports_to']
)
-
if log_type=='IN':
for i in employee_details:
if shift_assignments_employees_dict.get(i.name):
i = {**i, **shift_assignments_employees_dict.get(i.name), **{'log_type':'IN'}}
del i['name']
-
# check if is_after_grace_period
- if minute in reminder_minutes:i['is_after_grace_checkin'] = True
- #Setup this way because the users do not want both emails(late_entry and is_after_grace_checkin) sent
- else:i['is_after_grace_checkin'] = False
- if minute in late_entry_reminder_minutes:i['is_late_entry_checkin'] = True
- else:i['is_late_entry_checkin'] = False
+ if minute in initial_reminder_minutes:
+ i['initial_checkin_reminder']=True
+ else:
+ i['initial_checkin_reminder']=False
# check if supervisor reminder
- if minute in supervisor_reminder_minutes:i['is_supervisor_checkin_reminder'] = True
- else:i['is_supervisor_checkin_reminder'] = False
+ if minute in supervisor_reminder_minutes:
+ i['is_supervisor_checkin_reminder'] = True
+ else:
+ i['is_supervisor_checkin_reminder'] = False
# check initial
- if (minute==i['start_datetime'].minute and hour==i['start_datetime'].hour):i['initial_checkin_reminder']=True
- else:i['initial_checkin_reminder']=False
+ if minute in grace_period_minute:
+ i['is_after_grace_checkin'] = True
+ else:
+ i['is_after_grace_checkin'] = False
return_data.append(frappe._dict(i))
else:
for i in employee_details:
@@ -1842,16 +1852,16 @@ def fetch_employees_not_in_checkin():
i = {**i, **shift_assignments_employees_dict.get(i.name), **{'log_type':'OUT'}}
del i['name']
# check if is_after_grace_period
- if minute in reminder_minutes:i['is_after_grace_checkout'] = True
- else:i['is_after_grace_checkout'] = False
+ if minute in initial_reminder_minutes:
+ i['initial_checkout_reminder']=True
+ else:
+ i['initial_checkout_reminder']=False
# check if supervisor reminder
- if minute in supervisor_reminder_minutes:i['is_supervisor_checkout_reminder'] = True
- else:i['is_supervisor_checkout_reminder'] = False
- # check initial
- if (minute==i['end_datetime'].minute and hour==i['end_datetime'].hour):i['initial_checkout_reminder']=True
- else:i['initial_checkout_reminder']=False
+ if minute in supervisor_reminder_minutes:
+ i['is_supervisor_checkout_reminder'] = True
+ else:
+ i['is_supervisor_checkout_reminder'] = False
return_data.append(frappe._dict(i))
-
return frappe._dict({
'employees':return_data,
# 'reminder_minutes':reminder_minutes,
@@ -2115,8 +2125,8 @@ def notify_approver_about_pending_shift_request(is_scheduled_event=True):
FROM `tabShift Request` sr
LEFT JOIN `tabOperations Shift` os ON sr.operations_shift = os.name
LEFT JOIN `tabShift Request Approvers` ap ON sr.name = ap.parent
- WHERE sr.workflow_state = 'Pending Approval'
- AND ap.parentfield="shift_request_approver"
+ WHERE sr.workflow_state = 'Pending Approver'
+ AND ap.parentfield="custom_shift_approvers"
AND sr.from_date = %s
AND os.start_time BETWEEN %s AND %s
""", (date_time.date(), date_time.time(), one_hour.time()), as_dict=1)
@@ -2132,9 +2142,8 @@ def notify_approver_about_pending_shift_request(is_scheduled_event=True):
data_dict.get(obj["shift_approver"]).append({obj.get("employee_name"): get_url_to_form("Shift Request", obj.get("name"))})
-
for key, value in data_dict.items():
title = "Pending Shift Request for upcoming shift"
-
msg = frappe.render_template('one_fm/templates/emails/notify_shift_request_approver.html', context={"data": value})
sendemail(recipients=key, subject=title, content=msg, is_scheduler_email=is_scheduled_event)
+
diff --git a/one_fm/api/v1/employee_checkin_issue.py b/one_fm/api/v1/employee_checkin_issue.py
index fdcbdf2f0b..c8097d9507 100644
--- a/one_fm/api/v1/employee_checkin_issue.py
+++ b/one_fm/api/v1/employee_checkin_issue.py
@@ -5,17 +5,18 @@
from one_fm.api.notification import create_notification_log
from one_fm.api.v1.utils import response, validate_date, validate_time
from one_fm.operations.doctype.employee_checkin_issue.employee_checkin_issue import fetch_approver
+from frappe.model.workflow import apply_workflow
+from frappe.utils import getdate
@frappe.whitelist()
-def create_employee_checkin_issue(employee_id: str = None, log_type: str = None, issue_type: str = None, date: str = None,
- issue_details: str = None, latitude: str = None, longitude: str = None) -> dict:
+def create_employee_checkin_issue(employee_id: str = None, log_type: str = None, issue_type: str = None,
+ issue_details: str = None, latitude: float = None, longitude: float = None) -> dict:
"""This method creates a employee checkin issue for a given employee.
Args:
employee (str): employee id
log_type (str): type of log(IN/OUT).
issue_type (str): type of issue requested.
- date (str): yyyy-mm-dd
issue_details (str): reason to create a employee checkin issue
latitude (float, optional): Latitude od user.
longitude (float, optional): Longitude od user.
@@ -29,6 +30,7 @@ def create_employee_checkin_issue(employee_id: str = None, log_type: str = None,
}
"""
try:
+ date = getdate()
if not employee_id:
return response("Bad Request", 400, None, "employee_id required.")
@@ -38,8 +40,6 @@ def create_employee_checkin_issue(employee_id: str = None, log_type: str = None,
if not issue_type:
return response("Bad Request", 400, None, "issue_type required.")
- if not date:
- return response("Bad Request", 400, None, "date required.")
if issue_type == "Other":
if not issue_details:
@@ -65,11 +65,6 @@ def create_employee_checkin_issue(employee_id: str = None, log_type: str = None,
except:
return response("Bad Request", 400, None, "Latitude and longitude must be float.")
- if not isinstance(date, str):
- return response("Bad Request", 400, None, "date must be of type str.")
-
- if not validate_date(date):
- return response("Bad Request", 400, None, "date must be of type yyyy-mm-dd.")
employee = frappe.db.get_value("Employee", {"employee_id": employee_id})
@@ -77,10 +72,13 @@ def create_employee_checkin_issue(employee_id: str = None, log_type: str = None,
return response("Resource Not Found", 404, None, "No employee found with {employee_id}"
.format(employee_id=employee_id))
- shift_details = get_shift_details(employee)
+ shift_details = fetch_approver(employee)
- if shift_details.found:
- shift, shift_type, shift_assignment, shift_supervisor = shift_details.data
+ if shift_details:
+ shift_assignment = shift_details['assigned_shift']
+ shift_supervisor = shift_details['shift_supervisor']
+ shift = shift_details['shift']
+ shift_type = shift_details['shift_type']
else:
return response("Resource Not Found", 404, None, "shift not found in employee schedule for {employee}"
.format(employee=employee))
@@ -96,6 +94,10 @@ def create_employee_checkin_issue(employee_id: str = None, log_type: str = None,
if not shift_supervisor:
return response("Resource Not Found", 404, None, "shift supervisor not found for {employee}"
.format(employee=employee_id))
+
+ if frappe.db.exists("Attendance", {"shift_assignment": shift_assignment}):
+ return response("Attendance Exists", 400, None, f"Attendance already marked for {date}")
+
if not frappe.db.exists("Employee Checkin Issue", {"employee": employee, "date": date,
"assigned_shift": shift_assignment, "log_type": log_type, "issue_type": issue_type}):
@@ -112,7 +114,10 @@ def create_employee_checkin_issue(employee_id: str = None, log_type: str = None,
employee_checkin_issue_doc.shift_supervisor = shift_supervisor
employee_checkin_issue_doc.shift = shift
employee_checkin_issue_doc.shift_type = shift_type
+ employee_checkin_issue_doc.flags.ignore_permissions = 1
employee_checkin_issue_doc.save()
+ apply_workflow(employee_checkin_issue_doc, "Submit for Approval")
+
frappe.db.commit()
return response("Success", 201, employee_checkin_issue_doc.as_dict())
diff --git a/one_fm/api/v1/face_recognition.py b/one_fm/api/v1/face_recognition.py
index a2f07cb07a..1b2d3babbb 100644
--- a/one_fm/api/v1/face_recognition.py
+++ b/one_fm/api/v1/face_recognition.py
@@ -3,14 +3,14 @@
from one_fm.one_fm.page.face_recognition.face_recognition import (
update_onboarding_employee, check_existing,
)
-from one_fm.utils import get_current_shift
+from one_fm.utils import get_current_shift, is_holiday
from one_fm.api.v1.utils import (
response, verify_via_face_recogniton_service
)
from frappe.utils import cstr, getdate,now_datetime
from one_fm.proto import facial_recognition_pb2, facial_recognition_pb2_grpc, enroll_pb2, enroll_pb2_grpc
from one_fm.api.doc_events import haversine
-
+from one_fm.overrides.employee import has_day_off, is_employee_on_leave
# setup channel for face recognition
@@ -34,12 +34,11 @@ def base64_to_mp4(base64_string):
@frappe.whitelist()
-def enroll(employee_id: str = None, video: str = None, filename: str = None) -> dict:
+def enroll(employee_id: str = None, filename: str = None, video: str = None) -> dict:
"""This method enrolls the user face into the system for future face recognition use cases.
Args:
employee_id (str): employee_id of user
- video (str): Base64 encoded string of the video captured of user's face.
Returns:
response (dict): {
@@ -81,29 +80,27 @@ def enroll(employee_id: str = None, video: str = None, filename: str = None) ->
return response("Bad Request", 400, None, message)
# Set a context flag to indicate an API update (It will affect in 'Employee' validate method)
- frappe.flags.allow_enrollment_update = True
-
+ frappe.flags.allow_enrollment_update = True
doc.enrolled = 1
doc.save(ignore_permissions=True)
update_onboarding_employee(doc)
frappe.db.commit()
- return response("Success", 201, "User enrolled successfully.
Please wait for 10sec, you will be redirected to checkin.")
-
+ return response("Success", 201,
+ "User enrolled successfully.
Please wait for 10sec, you will be redirected to checkin.")
except Exception as error:
frappe.log_error(message=frappe.get_traceback(), title="Enrollment")
return response("Internal Server Error", 500, None, error)
@frappe.whitelist()
-def verify_checkin_checkout(employee_id: str = None, video : str = None, log_type: str = None,
- skip_attendance: str = None, latitude: str = None, longitude: str = None,
- filename=None):
+def verify_checkin_checkout(employee_id: str = None, log_type: str = None,
+ skip_attendance: str = None, latitude: str = None, longitude: str = None,
+ filename: str = None):
"""This method verifies user checking in/checking out.
Args:
employee_id (srt): employee_id of user
- video (str, optional): base64 encoded video of user checking in/checking out.
log_type (str, optional): IN/OUT
skip_attendance (int, optional): 0/1.
latitude (float, optional): Latitude od user.
@@ -123,15 +120,13 @@ def verify_checkin_checkout(employee_id: str = None, video : str = None, log_typ
skip_attendance = int(skip_attendance) if skip_attendance else 0
latitude = float(latitude)
longitude = float(longitude)
- except Exception as e:
- return response("Bad Request", 400, None, "skip_attendance must be an integer, latitude and longitude must be float.")
+ except:
+ return response("Bad Request", 400, None,
+ "skip_attendance must be an integer, latitude and longitude must be float.")
if not employee_id:
return response("Bad Request", 400, None, "employee_id required.")
- if not video:
- return response("Bad Request", 400, None, "video required.")
-
if not log_type:
return response("Bad Request", 400, None, "log_type required.")
@@ -144,19 +139,19 @@ def verify_checkin_checkout(employee_id: str = None, video : str = None, log_typ
if not longitude:
return response("Bad Request", 400, None, "longitude required.")
- if not isinstance(video, str):
- return response("Bad Request", 400, None, "video must be of type str.")
+ if not filename:
+ return response("Bad Request", 400, None, "Filename is required.")
if not isinstance(log_type, str):
return response("Bad Request", 400, None, "log_type must be of type str.")
- if log_type not in ["IN", "OUT"]:
+ if log_type not in {"IN", "OUT"}:
return response("Bad Request", 400, None, "Invalid log_type. log_type must be IN/OUT.")
if not isinstance(skip_attendance, int):
return response("Bad Request", 400, None, "skip_attendance must be of type int.")
- if skip_attendance not in [0, 1]:
+ if skip_attendance not in {0, 1}:
return response("Bad Request", 400, "Invalid skip_attendance. skip_attendance must be 0 or 1.")
if not isinstance(latitude, float):
@@ -165,6 +160,10 @@ def verify_checkin_checkout(employee_id: str = None, video : str = None, log_typ
if not isinstance(longitude, float):
return response("Bad Request", 400, None, "longitude must be of type float.")
+ video_file = frappe.request.files.get("video_file")
+ if not video_file:
+ return response("Bad Request", 400, None, "Video File is required.")
+
employee = frappe.db.get_value("Employee", {"employee_id": employee_id})
if not employee:
@@ -172,7 +171,7 @@ def verify_checkin_checkout(employee_id: str = None, video : str = None, log_typ
# check Face Recognition Endpoint
endpoint_state = frappe.db.get_single_value("ONEFM General Setting", 'enable_face_recognition_endpoint')
- video_file = video or frappe.request.files.get("video_file") or frappe.request.files.get("video")
+ video_file = frappe.request.files.get("video_file") or frappe.request.files.get("video")
if not filename:
filename = frappe.session.user+'.mp4'
if endpoint_state:
@@ -194,20 +193,22 @@ def verify_checkin_checkout(employee_id: str = None, video : str = None, log_typ
return response("Internal Server Error", 500, None, error)
-def create_checkin_log(employee: str, log_type: str, skip_attendance: int, latitude: float, longitude: float, source: str) -> dict:
+def create_checkin_log(employee: str, log_type: str, skip_attendance: int, latitude: float, longitude: float,
+ source: str) -> dict:
checkin = frappe.new_doc("Employee Checkin")
checkin.employee = employee
checkin.log_type = log_type
- checkin.device_id = frappe.utils.cstr(latitude)+","+frappe.utils.cstr(longitude)
- checkin.skip_auto_attendance = 0 #skip_attendance
+ checkin.device_id = frappe.utils.cstr(latitude) + "," + frappe.utils.cstr(longitude)
+ checkin.skip_auto_attendance = 0 #skip_attendance
checkin.source = source
checkin.save()
frappe.db.commit()
return checkin.as_dict()
+
def check_employee_non_shift(employee):
- shift_working, employement_type = frappe.get_value("Employee", employee, ["shift_working","employment_type"])
- if shift_working==0 and employement_type!="Contract":
+ shift_working, employement_type = frappe.get_value("Employee", employee, ["shift_working", "employment_type"])
+ if shift_working == 0 and employement_type != "Contract":
return True
return False
@@ -215,19 +216,11 @@ def has_day_off(employee,date):
"""
Confirm if the employee schedule for that day and employee is set to day off
"""
- is_day_off = False
+ return frappe.db.exists("Employee Schedule", {"employee": employee, "date": date, "employee_availability": "Day Off"})
- schedule = frappe.db.exists("Employee Schedule", {'employee':employee,'date':date})
- existing_schedule = frappe.get_value("Employee Schedule", schedule, ['employee_availability']) if schedule else None
-
- if existing_schedule:
- if existing_schedule == 'Day Off':
- is_day_off = True
- return is_day_off
@frappe.whitelist()
def get_site_location(employee_id: str = None, latitude: float = None, longitude: float = None) -> dict:
-
try:
if not employee_id:
return response("Bad Request", 400, None, "employee_id required.")
@@ -240,91 +233,205 @@ def get_site_location(employee_id: str = None, latitude: float = None, longitude
if not isinstance(employee_id, str):
return response("Bad Request", 400, None, "employee must be of type str.")
-
- employee = frappe.db.get_value("Employee", {"employee_id": employee_id})
+
+ employee = frappe.db.get_value("Employee", {"employee_id": employee_id}, ["name", "employee_name", "shift_working"], as_dict=1)
if not employee:
- return response("Resource Not Found", 404, None, "No employee found with {employee_id}".format(employee_id=employee_id))
+ return response("Resource Not Found", 404, None,
+ "No employee found with {employee_id}".format(employee_id=employee_id))
+
+ shift = False
+ shift_details = get_current_shift(employee.name)
+ if shift_details:
+ if shift_details['type'] == "Early":
+ # check if user can checkin with the correct time
+ return response("Resource Not Found", 404, None,
+ f"You are checking in too early, checkin is allowed in {shift_details['data']} minutes ")
+ elif shift_details['type'] == "Late":
+ return response("Resource Not Found", 404, None,
+ f"You are checking out too late, checkout was allowed {shift_details['data']} minutes ago ")
+ elif shift_details['type'] == "On Time":
+ shift = shift_details['data'] # Return the object of Shift Assignment
- shift = get_current_shift(employee)
date = cstr(getdate())
-
- site, location = None, None
+
if shift:
- if not shift.can_checkin_out:
- # check if user can checkin with the correct time
- return response("Resource Not Found", 404, None, "Your are outside shift hours")
-
- log_type = shift.check_existing_checking()
- if log_type=='IN':
- if shift.after_4hrs():
- # check if hrs has passed since shift start. Here we can also allow those who checked out tp checkin by checkin if OUT exist for same shift
- return response("Resource Not Found", 404, None, "You are 4 or more hours late, you cannot checkin at this time.")
-
- if frappe.db.exists("Shift Request", {
- "employee":employee, 'from_date':['<=',date],
- 'to_date':['>=',date], "status": "Approved"}
- ):
- check_in_site, check_out_site = frappe.get_value("Shift Request", {
- "employee":employee, 'from_date':['<=',date],'to_date':['>=',date],
- "status": "Approved"},["check_in_site","check_out_site"]
- )
- if log_type == "IN":
- site = check_in_site
- location = frappe.get_list("Location", {'name':check_in_site}, ["latitude","longitude", "geofence_radius"])
- else:
- site = check_out_site
- location = frappe.get_list("Location", {'name':check_out_site}, ["latitude","longitude", "geofence_radius"])
-
- else:
- if shift.site_location:
- site = shift.site_location
- location = frappe.get_list("Location", {'name':shift.site_location}, ["latitude","longitude", "geofence_radius"])
- elif shift.shift:
- site = frappe.get_value("Operations Shift", shift.shift, "site")
- location= frappe.db.sql("""
- SELECT loc.latitude, loc.longitude, loc.geofence_radius
- FROM `tabLocation` as loc
- WHERE
- loc.name IN (SELECT site_location FROM `tabOperations Site` where name="{site}")
- """.format(site=site), as_dict=1)
-
-
- if not site:
- if has_day_off(employee,date):
- employee_name = frappe.get_value("Employee",employee,'employee_name')
- return response("Resource Not Found", 404, None, f"Dear {employee_name}, Today is your day off. Happy Recharging!.")
- return response("Resource Not Found", 404, None, "User not assigned to a shift.")
+ if shift.is_replaced == 1:
+ return response("Resource Not Found", 404, None, f"You have been replaced with another Employee")
- if not location and site:
- return response("Resource Not Found", 404, None, "No site location set for {site}".format(site=site))
+ if is_attendance_request_exists(employee.name, date):
+ return response("Resource Not Found", 404, None,
+ f"You have an attendance request for today. Your attendance will be marked.")
- result=location[0]
- result['user_within_geofence_radius'] = True
+ log_type = shift.get_next_checkin_log_type()
+ if log_type == 'IN':
+ if shift.after_4hrs():
+ # check if hrs has passed since shift start. Here we can also allow those who checked out tp checkin by checkin if OUT exist for same shift
+ return response("Resource Not Found", 404, None,
+ "You are 4 or more hours late, you cannot checkin at this time.")
+
+ location = get_shift_site_location(shift, date, log_type)
+ site = frappe.get_value("Operations Shift", shift.shift, "site")
+
+ if location:
+ result = location
+ result['user_within_geofence_radius'] = True
+
+ distance = float(haversine(result.latitude, result.longitude, latitude, longitude))
+ if distance > float(result.geofence_radius):
+ result['user_within_geofence_radius'] = False
+
+ result['site_name'] = site
+ if shift:
+ result['shift'] = shift
+
+ # log to checkin radius log
+ data = result.copy()
+ data = {
+ **data,
+ **{'employee': employee_id, 'user_latitude': latitude, 'user_longitude': longitude,
+ 'user_distance': distance, 'diff': distance - result.geofence_radius}
+ }
+ if not result['user_within_geofence_radius']:
+ frappe.enqueue(
+ 'one_fm.operations.doctype.checkin_radius_log.checkin_radius_log.create_checkin_radius_log',
+ **{'data': data})
+ result['log_type'] = log_type
+ return response("Success", 200, result)
+
+ elif site:
+ return response("Resource Not Found", 404, None, "No site location set for {site}".format(site=site))
- distance = float(haversine(result.latitude, result.longitude, latitude, longitude))
- if distance > float(result.geofence_radius):
- result['user_within_geofence_radius'] = False
+ else:
+ if employee.shift_working:
+ if has_day_off(employee.name, date):
+ return response("Resource Not Found", 404, None,
+ f"Dear {employee.employee_name}, Today is your day off. Happy Recharging!.")
- result['site_name'] = site
- if shift:
- result['shift'] = shift
+ if is_employee_on_leave(employee.name, date):
+ return response("Resource Not Found", 404, None, "You are currently on leave, see you soon!")
- # log to checkin radius log
- data = result.copy()
- data = {
- **data,
- **{'employee':employee_id, 'user_latitude':latitude, 'user_longitude':longitude, 'user_distance':distance, 'diff':distance-result.geofence_radius}
- }
- if not result['user_within_geofence_radius']:
- frappe.enqueue('one_fm.operations.doctype.checkin_radius_log.checkin_radius_log.create_checkin_radius_log', **{'data':data})
- result['log_type'] = log_type
- return response("Success", 200, result)
+ status, message = is_holiday(employee=employee, date=date)
+ if status:
+ return response("Resource Not Found", 404, None, message)
+ return response("Resource Not Found", 404, None, "User not assigned to a shift.")
except Exception as error:
frappe.log_error(title="API Site location", message=frappe.get_traceback())
return response("Internal Server Error", 500, None, error)
+def is_attendance_request_exists(employee, date):
+ return frappe.db.exists(
+ "Attendance Request",
+ {
+ "employee": employee,
+ "from_date": ["<=", date],
+ "to_date": [">=", date],
+ "docstatus": 1
+ }
+ )
+
+
+def get_shift_site_location(shift, date, log_type):
+ """
+ Method to retrieves the site location details (latitude, longitude, and optionally geofence radius)
+ for a given shift on a specific date, considering both shift and shift request information.
+
+ Args:
+ shift (object): A object of Shift Assignment
+ date (str): The date (YYYY-MM-DD format) for which to retrieve the location.
+ log_type (str): "IN" or "OUT".
+
+ Return:
+ dict (or None): If a valid location is found, a dictionary containing the following keys is returned:
+ latitude (float): The latitude of the site location.
+ longitude (float): The longitude of the site location.
+ geofence_radius (float, optional): The geofence radius of the site location.
+ None: If no valid location information is found.
+ """
+ location = get_shift_request_site_location(shift.employee, date, log_type)
+ if not location:
+ if shift.site_location:
+ return frappe.get_value(
+ "Location",
+ {"name": shift.site_location},
+ ["latitude", "longitude", "geofence_radius"],
+ as_dict=True
+ )
+ elif shift.shift:
+ # Fetch the site from Operations Shift to get the location of the Site
+ site = frappe.get_value("Operations Shift", shift.shift, "site")
+ return frappe.db.sql("""
+ SELECT
+ loc.latitude, loc.longitude, loc.geofence_radius
+ FROM
+ `tabLocation` as loc
+ WHERE
+ loc.name IN (
+ SELECT site_location FROM `tabOperations Site` where name="{site}"
+ )
+ """.format(site=site), as_dict=1)
+ return location
+
+
+def get_shift_request_site_location(employee, date, log_type):
+ """
+ This function retrieves the site location details for an employee's approved shift request on a specific date.
+ It checks for an "Approved" shift request that overlaps with the provided date and returns the corresponding check-in
+ or check-out site location (depending on the log_type) along with its latitude, longitude, and geofence radius.
+
+ Args:
+ employee (str): The employee ID for whom to fetch the shift request details.
+ date (str): The date (YYYY-MM-DD format) for which to check the shift request.
+ log_type (str): "IN" or "OUT". This specifies whether to retrieve the check-in
+ or check-out site location from the shift request.
+
+ Return:
+ dict (or None): If an approved shift request is found overlapping the provided date and log_type,
+ a dictionary containing the following keys is returned:
+ latitude (float): The latitude of the site location.
+ longitude (float): The longitude of the site location.
+ geofence_radius (float): The geofence radius of the site location (optional, depending on your data model).
+ None: If no approved shift request is found or there's an error retrieving the location details.
+ """
+
+ location = False
+ shift_request_exists = frappe.db.exists(
+ "Shift Request",
+ {
+ "employee": employee,
+ "from_date": ["<=", date],
+ "to_date": [">=", date],
+ "status": "Approved"
+ }
+ )
+ if shift_request_exists:
+ shift_request_details = frappe.get_value(
+ "Shift Request",
+ {
+ "employee": employee,
+ "from_date": ["<=", date],
+ "to_date": [">=", date],
+ "status": "Approved"
+ },
+ ["check_in_site", "check_out_site"],
+ as_dict=True
+ )
+ if log_type == "IN":
+ # Fetch check in site location from shift request
+ location = shift_request_details.check_in_site
+ else:
+ # Fetch check out site location from shift request
+ location = shift_request_details.check_out_site
+ if location:
+ # Return the location details
+ return frappe.get_value(
+ "Location",
+ {"name": location},
+ ["latitude", "longitude", "geofence_radius"],
+ as_dict=True
+ )
+ return None
@frappe.whitelist()
def checkin_list(employee_id, from_date, to_date):
"""
@@ -344,4 +451,4 @@ def checkin_list(employee_id, from_date, to_date):
return response("success", 200, checkins)
except Exception as e:
return response("error", 500, None, str(e))
-
\ No newline at end of file
+
diff --git a/one_fm/api/v1/leave_application.py b/one_fm/api/v1/leave_application.py
index 8b23fbd1fa..3f4ec2bc22 100644
--- a/one_fm/api/v1/leave_application.py
+++ b/one_fm/api/v1/leave_application.py
@@ -12,10 +12,8 @@
from one_fm.api.api import upload_file
from one_fm.api.tasks import get_action_user,get_notification_user
from one_fm.api.v1.utils import response, validate_date
-from one_fm.utils import (
- get_current_shift, check_if_backdate_allowed,
- get_approver, get_approver_user,
-)
+from frappe.utils import cint, cstr, getdate
+from one_fm.utils import check_if_backdate_allowed, get_approver
from one_fm.api.utils import validate_sick_leave_attachment
@frappe.whitelist()
diff --git a/one_fm/api/v1/legal.py b/one_fm/api/v1/legal.py
index 0bdcafd01b..f030f9b619 100644
--- a/one_fm/api/v1/legal.py
+++ b/one_fm/api/v1/legal.py
@@ -13,15 +13,17 @@
# setup channel for face recognition
face_recognition_service_url = frappe.local.conf.face_recognition_service_url
-options = [('grpc.max_message_length', 100 * 1024 * 1024* 10)]
-channels = [
- grpc.secure_channel(i, grpc.ssl_channel_credentials(), options=options) for i in face_recognition_service_url
-]
+# options = [('grpc.max_message_length', 100 * 1024 * 1024* 10)]
+# channels = [
+# grpc.secure_channel(i, grpc.ssl_channel_credentials(), options=options) for i in face_recognition_service_url
+# ]
# setup stub for face recognition
-stubs = [
- facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
-]
+# stubs = [
+# facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
+# ]
+
+stubs = list()
@frappe.whitelist()
diff --git a/one_fm/api/v1/roster.py b/one_fm/api/v1/roster.py
index 3dde6a69ae..bdabdee40b 100644
--- a/one_fm/api/v1/roster.py
+++ b/one_fm/api/v1/roster.py
@@ -7,7 +7,6 @@
from frappe.client import attach_file
from one_fm.one_fm.page.roster.roster import get_post_view as _get_post_view # , get_roster_view as _get_roster_view
from one_fm.api.v1.utils import response
-from one_fm.utils import get_current_shift
from one_fm.one_fm.page.roster.employee_map import CreateMap, PostMap
diff --git a/one_fm/api/v1/shift_permission.py b/one_fm/api/v1/shift_permission.py
index bdc050a30f..2772fd92fe 100644
--- a/one_fm/api/v1/shift_permission.py
+++ b/one_fm/api/v1/shift_permission.py
@@ -26,17 +26,16 @@ def get_approver_details(employee_id):
message = frappe.get_traceback()
frappe.log_error(title="Error fetching approver", message=message)
return response("Internal Server Error", 500, None, message)
+
@frappe.whitelist()
-def create_shift_permission(employee_id: str = None, log_type: str = None, permission_type: str = None, date: str = None,
- reason: str = None, leaving_time: str = None, arrival_time: str = None, latitude: str = None,
- longitude: str = None) -> dict:
+def create_shift_permission(employee_id: str = None, log_type: str = None, date: str = None,
+ reason: str = None, leaving_time: str = None, arrival_time: str = None) -> dict:
"""This method creates a shift permission for a given employee.
Args:
employee (str): employee id
log_type (str): type of log(IN/OUT).
- permission_type (str): type of permission requested.
date (str): yyyy-mm-dd
reason (str): reason to create a shift permission
leaving_time (str): time for leave in hh:mm:ss
@@ -58,9 +57,6 @@ def create_shift_permission(employee_id: str = None, log_type: str = None, permi
if not employee_id:
return response("Bad Request", 400, None, "employee_id required.")
- if not permission_type:
- return response("Bad Request", 400, None, "permission_type required.")
-
if not date:
return response("Bad Request", 400, None, "date required.")
@@ -68,52 +64,19 @@ def create_shift_permission(employee_id: str = None, log_type: str = None, permi
return response("Bad Request", 400, None, "reason required.")
if not log_type:
- if permission_type in ['Arrive Late', 'Forget to Checkin', 'Checkin Issue']:
- log_type='IN'
- elif permission_type in ['Leave Early', 'Forget to Checkout', 'Checkout Issue']:
- log_type='OUT'
- else:
- return response("Bad Request", 400, None, "log_type required.")
-
- if log_type == "IN" and permission_type not in ['Arrive Late', 'Forget to Checkin', 'Checkin Issue']:
-
- return response("Bad Request", 400, None, _('Permission Type cannot be {0}. It should be one of \
- "Arrive Late", "Forget to Checkin", "Checkin Issue" for Log Type "IN"'.format(permission_type)))
-
- if log_type == "OUT" and permission_type not in ['Leave Early', 'Forget to Checkout', 'Checkout Issue']:
-
- return response("Bad Request", 400, None, _('Permission Type cannot be {0}. It should be one of \
- "Leave Early", "Forget to Checkout", "Checkout Issue" for Log Type "OUT"'.format(permission_type)))
-
- if permission_type == "Arrive Late" and not arrival_time:
+ return response("Bad Request", 400, None, "log_type required.")
+ if log_type == "IN" and not arrival_time:
return response("Bad Request", 400, None, "Arrival time required for late arrival shift permission.")
- if permission_type == "Leave Early" and not leaving_time:
-
+ if log_type == "OUT" and not leaving_time:
return response("Bad Request", 400, None, "Leaving time required for early exit shift permission")
if not isinstance(employee_id, str):
return response("Bad Request", 400, None, "employee must be of type str.")
- if not isinstance(permission_type, str):
-
- return response("Bad Request", 400, None, "permission_type must be of type str.")
-
- if permission_type not in ["Arrive Late", "Leave Early", "Checkin Issue", "Checkout Issue","Forget to Checkout","Forget to Checkin"]:
-
- return response("Bad Request", 400, None, "permission type must be either 'Arrive Late' or 'Leave Early' or 'Checkin Issue' or 'Checkout Issue'.")
- if permission_type in ["Checkin Issue", "Checkout Issue"] and latitude and longitude:
- try:
- latitude = float(latitude)
- longitude = float(longitude)
- except:
- frappe.log_error(title="API Shift Permission", message= "Latitude and longitude must be float.")
- return response("Bad Request", 400, None, "Latitude and longitude must be float.")
if not isinstance(date, str):
-
- frappe.log_error(title="API Shift Permission", message="date must be of type str.")
return response("Bad Request", 400, None, "date must be of type str.")
if not validate_date(date):
@@ -169,20 +132,16 @@ def create_shift_permission(employee_id: str = None, log_type: str = None, permi
return response("Resource Not Found", 404, None, "shift supervisor not found for {employee}".format(employee=employee_id))
- if not frappe.db.exists("Shift Permission", {"employee": employee, "date": date, "assigned_shift": shift_assignment, "permission_type": permission_type}):
+ if not frappe.db.exists("Shift Permission", {"employee": employee, "date": date, "assigned_shift": shift_assignment, "log_type": log_type}):
shift_permission_doc = frappe.new_doc('Shift Permission')
shift_permission_doc.employee = employee
shift_permission_doc.date = date
shift_permission_doc.log_type = log_type
- shift_permission_doc.permission_type = permission_type
shift_permission_doc.reason = reason
- if permission_type == "Arrive Late" and arrival_time:
+ if log_type == "IN" and arrival_time:
shift_permission_doc.arrival_time = arrival_time
- if permission_type == "Leave Early" and leaving_time:
+ if log_type == "OUT" and leaving_time:
shift_permission_doc.leaving_time = leaving_time
- if permission_type in ["Checkin Issue", "Checkout Issue"]:
- shift_permission_doc.latitude = latitude if latitude else 0.0
- shift_permission_doc.longitude = longitude if longitude else 0.0
shift_permission_doc.assigned_shift = shift_assignment
shift_permission_doc.shift_supervisor = shift_supervisor
shift_permission_doc.shift = shift
@@ -223,7 +182,7 @@ def list_shift_permission(employee_id: str = None):
try:
if not employee_id:
return response("Bad Request", 400, None, "employee_id required.")
-
+
if not isinstance(employee_id, str):
return response("Bad Request", 400, None, "employee_id must be of type str.")
@@ -232,8 +191,8 @@ def list_shift_permission(employee_id: str = None):
if not employee:
return response("Resource Not Found", 404, None, "No employee found with {employee_id}".format(employee_id=employee_id))
- shift_permission_list = frappe.get_list("Shift Permission", filters={'docstatus':['<',2],'employee': employee}, fields=["name", "date",'log_type','permission_type','emp_name', "workflow_state",'docstatus'])
- line_manager_shift_permission = frappe.get_list("Shift Permission", filters={'docstatus':['<',2],'shift_supervisor': employee}, fields=["name", "date",'log_type','permission_type','emp_name', "workflow_state"])
+ shift_permission_list = frappe.get_list("Shift Permission", filters={'docstatus':['<',2],'employee': employee}, fields=["name", "date",'log_type','emp_name', "workflow_state",'docstatus'])
+ line_manager_shift_permission = frappe.get_list("Shift Permission", filters={'docstatus':['<',2],'shift_supervisor': employee}, fields=["name", "date",'log_type','emp_name', "workflow_state"])
return response("Success", 200, shift_permission_list+line_manager_shift_permission)
@@ -300,8 +259,8 @@ def approve_shift_permission(employee_id: str = None, shift_permission_id: str =
frappe.db.commit()
user_id, supervisor_name = frappe.db.get_value('Employee', {'name': shift_supervisor}, ['user_id', 'employee_name'])
- subject = _("{name} has approved the permission to {type} on {date}.".format(name=supervisor_name, type=shift_permission_doc.permission_type.lower(), date=shift_permission_doc.date))
- message = _("{name} has approved the permission to {type} on {date}.".format(name=supervisor_name, type=shift_permission_doc.permission_type.lower(), date=shift_permission_doc.date))
+ subject = _("{name} has approved the permission to Check-{type} on {date}.".format(name=supervisor_name, type=shift_permission_doc.log_type.lower(), date=shift_permission_doc.date))
+ message = _("{name} has approved the permission to Check-{type} on {date}.".format(name=supervisor_name, type=shift_permission_doc.log_type.lower(), date=shift_permission_doc.date))
notify_for_shift_permission_status(subject, message, user_id, shift_permission_doc, 1)
return response("Success", 201, shift_permission_doc.as_dict())
@@ -350,8 +309,8 @@ def reject_shift_permission(employee_id: str = None, shift_permission_id: str =
frappe.db.commit()
user_id, supervisor_name= frappe.db.get_value('Employee',{'name':shift_supervisor},['user_id','employee_name'])
- subject = _("{name} has rejected the permission to {type} on {date}.".format(name=supervisor_name, type=shift_permission_doc.permission_type.lower(), date=shift_permission_doc.date))
- message = _("{name} has rejected the permission to {type} on {date}.".format(name=supervisor_name, type=shift_permission_doc.permission_type.lower(), date=shift_permission_doc.date))
+ subject = _("{name} has rejected the permission to check-{type} on {date}.".format(name=supervisor_name, type=shift_permission_doc.log_type.lower(), date=shift_permission_doc.date))
+ message = _("{name} has rejected the permission to check-{type} on {date}.".format(name=supervisor_name, type=shift_permission_doc.log_type.lower(), date=shift_permission_doc.date))
notify_for_shift_permission_status(subject, message,user_id, shift_permission_doc, 1)
return response("Success", 201, shift_permission_doc.as_dict())
diff --git a/one_fm/api/v1/utils.py b/one_fm/api/v1/utils.py
index e13656ab47..79ad7ddfbe 100644
--- a/one_fm/api/v1/utils.py
+++ b/one_fm/api/v1/utils.py
@@ -1,6 +1,5 @@
-import frappe, requests
+import frappe, datetime, requests
from frappe.utils import getdate, cint, cstr, random_string, now_datetime
-from one_fm.utils import get_current_shift
def response(message, status_code, data=None, error=None):
"""This method generates a response for an API call with appropriate data and status code.
@@ -202,6 +201,13 @@ def get_employee_by_id(employee_id):
except Exception as e:
frappe._dict({'status': False, 'message': str(e), 'http_status_code':500})
+
+
+@frappe.whitelist()
+def log_error_via_api(traceback: str, message: str, medium: str):
+ frappe.log_error(title=f"Error from {medium} -- {frappe.session.user}", message=f"{traceback} -- {message}")
+ return response(message="Error Logged Successfully", status_code=201)
+
@frappe.whitelist()
def google_map_api():
try:
@@ -221,4 +227,4 @@ def verify_via_face_recogniton_service(url: str, data: dict, files: dict) -> tup
frappe.log_error(title=f"Error from face recognition system -- {frappe.session.user}", message=f"{traceback} -- {message}")# if traceback else None
return False, message
return True, ""
- return False, "Facial Recogniton Service is currently available"
\ No newline at end of file
+ return False, "Facial Recogniton Service is currently available"
diff --git a/one_fm/api/v1/web.py b/one_fm/api/v1/web.py
index e006592afa..d6fce25fb2 100644
--- a/one_fm/api/v1/web.py
+++ b/one_fm/api/v1/web.py
@@ -14,7 +14,7 @@
import json
# from imutils import face_utils, paths
from one_fm.api.doc_events import haversine
-from one_fm.api.v1.roster import get_current_shift
+from one_fm.utils import get_current_shift
from one_fm.api.v1.utils import response
from one_fm.api.v1.face_recognition import (
create_checkin_log, verify_checkin_checkout,
diff --git a/one_fm/api/v2/face_recognition.py b/one_fm/api/v2/face_recognition.py
index bd2497932f..ab6ce28c95 100644
--- a/one_fm/api/v2/face_recognition.py
+++ b/one_fm/api/v2/face_recognition.py
@@ -14,14 +14,14 @@
# setup channel for face recognition
face_recognition_service_url = frappe.local.conf.face_recognition_service_url
-channels = [
- grpc.secure_channel(i, grpc.ssl_channel_credentials()) for i in face_recognition_service_url
-]
+# channels = [
+# grpc.secure_channel(i, grpc.ssl_channel_credentials()) for i in face_recognition_service_url
+# ]
# setup stub for face recognition
-stubs = [
- facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
-]
+# stubs = [
+# facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
+# ]
@frappe.whitelist()
@@ -324,7 +324,16 @@ def get_site_location(employee_id: str = None, latitude: float = None, longitude
if has_attendance(employee, date):
return response("Resource Not Found", 404, None, "Your attendance has been marked For the Day")
- shift = get_current_shift(employee)
+ shift_exists = get_current_shift(employee)
+ if shift_exists['type'] == "Early":
+ # check if user can checkin with the correct time
+ return response("Resource Not Found", 404, None, f"You are checking in too early, checkin is allowed in {shift_exists['data']} minutes ")
+ elif shift_exists['type'] == "Late":
+ return response("Resource Not Found", 404, None, f"You are checking out too late, checkout was allowed {shift_exists['data']} minutes ago ")
+ elif shift_exists['type'] == "On Time":
+ shift = shift_exists['data']
+ else:
+ shift = None
site, location, shift_assignment = None, None, None
if shift:
diff --git a/one_fm/api/v2/leave_application.py b/one_fm/api/v2/leave_application.py
index b4e9c8e80d..ea918af2b6 100644
--- a/one_fm/api/v2/leave_application.py
+++ b/one_fm/api/v2/leave_application.py
@@ -7,7 +7,6 @@
from one_fm.api.api import upload_file
from one_fm.api.tasks import get_action_user,get_notification_user
from one_fm.api.v1.utils import response, validate_date
-from one_fm.utils import get_current_shift
from frappe.utils import cint, cstr, getdate
from one_fm.utils import check_if_backdate_allowed
from one_fm.api.utils import validate_sick_leave_attachment
diff --git a/one_fm/api/v2/shift_permission.py b/one_fm/api/v2/shift_permission.py
index 0f9f476875..ff78206ef4 100644
--- a/one_fm/api/v2/shift_permission.py
+++ b/one_fm/api/v2/shift_permission.py
@@ -46,13 +46,13 @@ def create_shift_permission(employee_id: str = None, log_type: str = None, permi
if not reason:
return response("Bad Request", 400, None, "reason required.")
- if log_type == "IN" and permission_type not in ['Arrive Late', 'Forget to Checkin', 'Checkin Issue']:
- return response("Bad Request", 400, None, _('Permission Type cannot be {0}. It should be one of \
- "Arrive Late", "Forget to Checkin", "Checkin Issue" for Log Type "IN"'.format(permission_type)))
+ if log_type == "IN" and permission_type not in ['Arrive Late', ]:
+ return response("Bad Request", 400, None, _('Permission Type cannot be {0}. It should be \
+ "Arrive Late" for Log Type "IN"'.format(permission_type)))
- if log_type == "OUT" and permission_type not in ['Leave Early', 'Forget to Checkout', 'Checkout Issue']:
+ if log_type == "OUT" and permission_type not in ['Leave Early', ]:
return response("Bad Request", 400, None, _('Permission Type cannot be {0}. It should be one of \
- "Leave Early", "Forget to Checkout", "Checkout Issue" for Log Type "OUT"'.format(permission_type)))
+ "Leave Early" for Log Type "OUT"'.format(permission_type)))
if permission_type == "Arrive Late" and not arrival_time:
return response("Bad Request", 400, None, "Arrival time required for late arrival shift permission.")
@@ -66,15 +66,8 @@ def create_shift_permission(employee_id: str = None, log_type: str = None, permi
if not isinstance(permission_type, str):
return response("Bad Request", 400, None, "permission_type must be of type str.")
- if permission_type not in ["Arrive Late", "Leave Early", "Checkin Issue", "Checkout Issue"]:
- return response("Bad Request", 400, None, "permission type must be either 'Arrive Late' or 'Leave Early' or 'Checkin Issue' or 'Checkout Issue'.")
-
- if permission_type in ["Checkin Issue", "Checkout Issue"] and latitude and longitude:
- try:
- latitude = float(latitude)
- longitude = float(longitude)
- except:
- return response("Bad Request", 400, None, "Latitude and longitude must be float.")
+ if permission_type not in ["Arrive Late", "Leave Early", ]:
+ return response("Bad Request", 400, None, "permission type must be either 'Arrive Late' or 'Leave Early'.")
if not isinstance(date, str):
return response("Bad Request", 400, None, "date must be of type str.")
diff --git a/one_fm/api/v2/web.py b/one_fm/api/v2/web.py
index 65b84b4f6c..dbcc7f9edc 100644
--- a/one_fm/api/v2/web.py
+++ b/one_fm/api/v2/web.py
@@ -20,14 +20,16 @@
# setup channel for face recognition
face_recognition_service_url = frappe.local.conf.face_recognition_service_url
-channels = [
- grpc.secure_channel(i, grpc.ssl_channel_credentials()) for i in face_recognition_service_url
-]
+# channels = [
+# grpc.secure_channel(i, grpc.ssl_channel_credentials()) for i in face_recognition_service_url
+# ]
# setup stub for face recognition
-stubs = [
- facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
-]
+# stubs = [
+# facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
+# ]
+
+stubs = list()
class NumpyArrayEncoder(JSONEncoder):
def default(self, obj):
diff --git a/one_fm/api/v3/web.py b/one_fm/api/v3/web.py
index ff56565913..5f3a4f0be6 100644
--- a/one_fm/api/v3/web.py
+++ b/one_fm/api/v3/web.py
@@ -19,14 +19,16 @@
# setup channel for face recognition
face_recognition_service_url = frappe.local.conf.face_recognition_service_url
-channels = [
- grpc.secure_channel(i, grpc.ssl_channel_credentials()) for i in face_recognition_service_url
-]
+# channels = [
+# grpc.secure_channel(i, grpc.ssl_channel_credentials()) for i in face_recognition_service_url
+# ]
# setup stub for face recognition
-stubs = [
- facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
-]
+# stubs = [
+# facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
+# ]
+
+stubs = list()
channel = frappe.local.conf.face_recognition_channel.get('url')
bucketpath = frappe.local.conf.face_recognition_channel.get('bucket')
diff --git a/one_fm/events/issue.py b/one_fm/events/issue.py
index 86211ebb77..53253be17a 100644
--- a/one_fm/events/issue.py
+++ b/one_fm/events/issue.py
@@ -19,6 +19,12 @@ def send_google_chat_notification(doc, method):
# Fetch the Key and Token for the API
default_api_integration = frappe.get_doc("Default API Integration")
+ if not default_api_integration:
+ return
+
+ if not default_api_integration.integration_setting:
+ return
+
google_chat = frappe.get_doc("API Integration",
[i for i in default_api_integration.integration_setting
if i.app_name=='Google Chat'][0].app_name)
diff --git a/one_fm/fixtures/assignment_rule.json b/one_fm/fixtures/assignment_rule.json
index a08547c37d..2ecaa2e995 100644
--- a/one_fm/fixtures/assignment_rule.json
+++ b/one_fm/fixtures/assignment_rule.json
@@ -1,4 +1,132 @@
[
+ {
+ "assign_condition": "workflow_state == 'Pending Approver'",
+ "assignment_days": [
+ {
+ "day": "Monday",
+ "parent": "Shift Request Pending Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Tuesday",
+ "parent": "Shift Request Pending Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Wednesday",
+ "parent": "Shift Request Pending Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Thursday",
+ "parent": "Shift Request Pending Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Friday",
+ "parent": "Shift Request Pending Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Saturday",
+ "parent": "Shift Request Pending Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Sunday",
+ "parent": "Shift Request Pending Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ }
+ ],
+ "close_condition": "workflow_state in (\"Approved\", \"Rejected\", \"Draft\")",
+ "custom_routine_task": null,
+ "description": "Automatic Assignment",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Assignment Rule",
+ "document_type": "Shift Request",
+ "due_date_based_on": null,
+ "field": "shift_approver",
+ "is_assignment_rule_with_workflow": 0,
+ "last_user": "abdullah@one-fm.com",
+ "modified": "2024-07-22 13:32:41.603305",
+ "name": "Shift Request Pending Approval",
+ "priority": 0,
+ "rule": "Based on Field",
+ "unassign_condition": "workflow_state in (\"Approved\", \"Rejected\", \"Draft\")",
+ "users": []
+ },
+ {
+ "assign_condition": "workflow_state == 'Pending Approval'",
+ "assignment_days": [
+ {
+ "day": "Monday",
+ "parent": "Attendance Request Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Tuesday",
+ "parent": "Attendance Request Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Wednesday",
+ "parent": "Attendance Request Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Thursday",
+ "parent": "Attendance Request Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Friday",
+ "parent": "Attendance Request Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Saturday",
+ "parent": "Attendance Request Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Sunday",
+ "parent": "Attendance Request Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ }
+ ],
+ "close_condition": "workflow_state in ('Approved', 'Draft', 'Rejected')",
+ "custom_routine_task": null,
+ "description": "
Here is to inform you that the following {{ doctype }}({{ name }}) requires your attention/action.\n
\n The details of the request are as follows:\n
\n
\n \n \n Label | \n Value | \n
\n \n \n \n \n Employee | \n {{employee}} | \n
\n \n \n Company | \n {{company}} | \n
\n \n \n From Date | \n {{from_date}} | \n
\n \n \n To Date | \n {{to_date}} | \n
\n \n \n Reason | \n {{reason}} | \n
\n \n \n Explanation | \n {{explanation}} | \n
\n
",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Assignment Rule",
+ "document_type": "Attendance Request",
+ "due_date_based_on": null,
+ "field": "approver_user",
+ "is_assignment_rule_with_workflow": 1,
+ "last_user": "",
+ "modified": "2024-07-17 17:59:49.745883",
+ "name": "Attendance Request Approval",
+ "priority": 0,
+ "rule": "Based on Field",
+ "unassign_condition": "workflow_state in ('Approved', 'Draft', 'Rejected')",
+ "users": []
+ },
{
"assign_condition": "workflow_state in ('Approved', \"Rejected\")",
"assignment_days": [
@@ -120,7 +248,7 @@
"field": "reports_to_user",
"is_assignment_rule_with_workflow": 0,
"last_user": "",
- "modified": "2024-01-24 18:00:54.349067",
+ "modified": "2024-03-22 09:51:17.332071",
"name": "Attendance Check Reports To",
"priority": 2,
"rule": "Based on Field",
@@ -320,7 +448,7 @@
"users": []
},
{
- "assign_condition": "workflow_state == 'Pending'",
+ "assign_condition": "workflow_state == 'Pending Approver'",
"assignment_days": [
{
"day": "Monday",
@@ -367,20 +495,20 @@
],
"close_condition": "workflow_state in ('Approved', 'Rejected')",
"custom_routine_task": null,
- "description": "Here is to inform you that the following {{ doctype }}({{ name }}) requires your attention/action.\n\t\t\t
\n\t\t\tThe details of the request are as follows:
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tLabel | \n\t\t\t\t\t\tValue | \n\t\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\n\t\t\t\t\n\t\t\t\t\tEmployee Id | \n\t\t\t\t\t{{employee}} | \n\t\t\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tDate | \n\t\t\t\t\t{{date}} | \n\t\t\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tLog Type | \n\t\t\t\t\t{{log_type}} | \n\t\t\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tPermission Type | \n\t\t\t\t\t{{permission_type}} | \n\t\t\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tReason | \n\t\t\t\t\t{{reason}} | \n\t\t\t\t
\n\t\t\t
",
- "disabled": 0,
+ "description": "Here is to inform you that the following {{ doctype }}({{ name }}) requires your attention/action.\n\t\t\t
\n\t\t\tThe details of the request are as follows:
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tLabel | \n\t\t\t\t\t\tValue | \n\t\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\n\t\t\t\t\n\t\t\t\t\tEmployee Id | \n\t\t\t\t\t{{employee}} | \n\t\t\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tDate | \n\t\t\t\t\t{{date}} | \n\t\t\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tLog Type | \n\t\t\t\t\t{{log_type}} | \n\t\t\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tReason | \n\t\t\t\t\t{{reason}} | \n\t\t\t\t
\n\t\t\t
",
+ "disabled" :0,
"docstatus": 0,
"doctype": "Assignment Rule",
"document_type": "Shift Permission",
"due_date_based_on": null,
"field": "approver_user_id",
"is_assignment_rule_with_workflow": 0,
- "last_user": "j.poil@one-fm.com",
- "modified": "2023-06-04 11:45:04.269847",
+ "last_user": "",
+ "modified": "2024-03-10 15:19:46.392833",
"name": "Shift Permission Approver",
"priority": 0,
"rule": "Based on Field",
- "unassign_condition": "workflow_state == 'Approved'",
+ "unassign_condition": "workflow_state in ('Approved', 'Rejected')",
"users": []
},
{
@@ -431,7 +559,7 @@
],
"close_condition": null,
"custom_routine_task": null,
- "description": "Dear {{ custom_purchase_order_approver_name }}
\nHere is to inform you that the following {{ doctype }}({{ name }}) requires your attention/action.\n
\n The details of the request are as follows:\n
\n
\n \n \n Label | \n Value | \n
\n \n \n \n \n Name | \n {{ name }} | \n
\n \n \n Supplier | \n {{supplier}} | \n
\n \n \n Date | \n {{transaction_date}} | \n
\n \n \n Company | \n {{company}} | \n
\n \n \n Currency | \n {{currency}} | \n
\n \n \n Exchange Rate | \n {{conversion_rate}} | \n
\n \n \n Status | \n {{status}} | \n
\n
",
+ "description": "Dear {{ custom_purchase_order_approver_name }}
\nHere is to inform you that the following {{ doctype }}({{ name }}) requires your attention/action.\n
\n The details of the request are as follows:\n
\n
\n \n \n Label | \n Value | \n
\n \n \n \n \n Name | \n {{ name }} | \n
\n \n \n Supplier | \n {{supplier}} | \n
\n \n \n Date | \n {{transaction_date}} | \n
\n \n \n Company | \n {{company}} | \n
\n \n \n Currency | \n {{currency}} | \n
\n \n \n Exchange Rate | \n {{conversion_rate}} | \n
\n \n \n Status | \n {{status}} | \n
\n
",
"disabled": 0,
"docstatus": 0,
"doctype": "Assignment Rule",
@@ -495,7 +623,7 @@
],
"close_condition": null,
"custom_routine_task": null,
- "description": "Here is to inform you that the following {{ doctype }}({{ name }}) requires your attention/action.\n
\n The details of the request are as follows:\n
\n
\n \n \n Label | \n Value | \n
\n \n \n \n \n Name | \n {{ name }} | \n
\n \n \n Supplier | \n {{supplier}} | \n
\n \n \n Date | \n {{transaction_date}} | \n
\n \n \n Company | \n {{company}} | \n
\n \n \n Currency | \n {{currency}} | \n
\n \n \n Exchange Rate | \n {{conversion_rate}} | \n
\n \n \n Status | \n {{status}} | \n
\n
",
+ "description": "Here is to inform you that the following {{ doctype }}({{ name }}) requires your attention/action.\n
\n The details of the request are as follows:\n
\n
\n \n \n Label | \n Value | \n
\n \n \n \n \n Name | \n {{ name }} | \n
\n \n \n Supplier | \n {{supplier}} | \n
\n \n \n Date | \n {{transaction_date}} | \n
\n \n \n Company | \n {{company}} | \n
\n \n \n Currency | \n {{currency}} | \n
\n \n \n Exchange Rate | \n {{conversion_rate}} | \n
\n \n \n Status | \n {{status}} | \n
\n
",
"disabled": 0,
"docstatus": 0,
"doctype": "Assignment Rule",
@@ -566,7 +694,7 @@
],
"close_condition": null,
"custom_routine_task": null,
- "description": "Here is to inform you that the following {{ doctype }}({{ name }}) requires your attention/action.\n
\n The details of the request are as follows:\n
\n
\n \n \n Label | \n Value | \n
\n \n \n \n \n Name | \n {{ name }} | \n
\n \n \n Supplier | \n {{supplier}} | \n
\n \n \n Date | \n {{transaction_date}} | \n
\n \n \n Company | \n {{company}} | \n
\n \n \n Currency | \n {{currency}} | \n
\n \n \n Exchange Rate | \n {{conversion_rate}} | \n
\n \n \n Status | \n {{status}} | \n
\n
",
+ "description": "Here is to inform you that the following {{ doctype }}({{ name }}) requires your attention/action.\n
\n The details of the request are as follows:\n
\n
\n \n \n Label | \n Value | \n
\n \n \n \n \n Name | \n {{ name }} | \n
\n \n \n Supplier | \n {{supplier}} | \n
\n \n \n Date | \n {{transaction_date}} | \n
\n \n \n Company | \n {{company}} | \n
\n \n \n Currency | \n {{currency}} | \n
\n \n \n Exchange Rate | \n {{conversion_rate}} | \n
\n \n \n Status | \n {{status}} | \n
\n
",
"disabled": 0,
"docstatus": 0,
"doctype": "Assignment Rule",
@@ -588,5 +716,133 @@
"user": "saoud@one-fm.com"
}
]
+ },
+ {
+ "assign_condition": "workflow_state == 'Pending Approval'",
+ "assignment_days": [
+ {
+ "day": "Monday",
+ "parent": "Employee Checkin Issue Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Tuesday",
+ "parent": "Employee Checkin Issue Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Wednesday",
+ "parent": "Employee Checkin Issue Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Thursday",
+ "parent": "Employee Checkin Issue Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Friday",
+ "parent": "Employee Checkin Issue Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Saturday",
+ "parent": "Employee Checkin Issue Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Sunday",
+ "parent": "Employee Checkin Issue Approval",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ }
+ ],
+ "close_condition": "workflow_state in ('Approved', 'Draft')",
+ "custom_routine_task": null,
+ "description": "Here is to inform you that the following {{ doctype }}({{ name }}) requires your attention/action.\n
\n The details of the request are as follows:\n
\n
\n \n \n Label | \n Value | \n
\n \n \n \n \n Employee | \n {{employee}} | \n
\n \n \n Date | \n {{date}} | \n
\n \n \n Log Type | \n {{log_type}} | \n
\n
",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Assignment Rule",
+ "document_type": "Employee Checkin Issue",
+ "due_date_based_on": null,
+ "field": "approver_user_id",
+ "is_assignment_rule_with_workflow": 1,
+ "last_user": "",
+ "modified": "2024-03-25 11:55:45.809300",
+ "name": "Employee Checkin Issue Approval",
+ "priority": 0,
+ "rule": "Based on Field",
+ "unassign_condition": "workflow_state in ('Approved', 'Draft')",
+ "users": []
+ },
+ {
+ "assign_condition": "workflow_state ==\"Draft\" and creation < modified",
+ "assignment_days": [
+ {
+ "day": "Monday",
+ "parent": "Shift Request Draft",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Tuesday",
+ "parent": "Shift Request Draft",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Wednesday",
+ "parent": "Shift Request Draft",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Thursday",
+ "parent": "Shift Request Draft",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Friday",
+ "parent": "Shift Request Draft",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Saturday",
+ "parent": "Shift Request Draft",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ },
+ {
+ "day": "Sunday",
+ "parent": "Shift Request Draft",
+ "parentfield": "assignment_days",
+ "parenttype": "Assignment Rule"
+ }
+ ],
+ "close_condition": "workflow_state in ('Pending Approver', )",
+ "custom_routine_task": null,
+ "description": "Automatic Assignment",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Assignment Rule",
+ "document_type": "Shift Request",
+ "due_date_based_on": null,
+ "field": "owner",
+ "is_assignment_rule_with_workflow": 0,
+ "last_user": "Administrator",
+ "modified": "2024-07-22 13:46:56.508148",
+ "name": "Shift Request Draft",
+ "priority": 0,
+ "rule": "Based on Field",
+ "unassign_condition": "workflow_state in ('Pending Approver', )",
+ "users": []
}
]
\ No newline at end of file
diff --git a/one_fm/fixtures/property_setter.json b/one_fm/fixtures/property_setter.json
index 26a24295ff..d28ca68476 100755
--- a/one_fm/fixtures/property_setter.json
+++ b/one_fm/fixtures/property_setter.json
@@ -3287,6 +3287,7 @@
"doctype_or_field": "DocField",
"field_name": "shift",
"is_system_generated": 0,
+ "modified": "2024-07-21 17:30:02.315075",
"modified": "2024-08-05 15:06:28.483195",
"module": null,
"name": "Attendance-shift-fetch_from",
@@ -6157,15 +6158,15 @@
"property": "field_order",
"property_type": "Data",
"row_name": null,
- "value": "[\"interview_rounds_sb\", \"interview_rounds\", \"details_section\", \"applicant_name\", \"email_id\", \"one_fm_application_id\", \"phone_number\", \"country\", \"column_break_3\", \"job_title\", \"one_fm_designation\", \"applicant_lead\", \"designation\", \"status\", \"one_fm_reason_for_rejection\", \"one_fm_applicant_status\", \"one_fm_document_verification\", \"source_and_rating_section\", \"source\", \"source_name\", \"one_fm_erf_application_details_section\", \"one_fm_erf\", \"project\", \"department\", \"one_fm_sourcing_team\", \"one_fm_agency\", \"one_fm_is_agency_applying\", \"one_fm_job_applicant_cb_1\", \"one_fm_source_of_hire\", \"one_fm_hiring_method\", \"interview_round\", \"bulk_interview\", \"employment_type\", \"attendance_by_timesheet\", \"one_fm_applicant_personal_details_sb\", \"one_fm_first_name\", \"one_fm_second_name\", \"one_fm_third_name\", \"one_fm_forth_name\", \"one_fm_last_name\", \"one_fm_first_name_in_arabic\", \"one_fm_second_name_in_arabic\", \"one_fm_third_name_in_arabic\", \"one_fm_forth_name_in_arabic\", \"one_fm_last_name_in_arabic\", \"one_fm_height\", \"one_fm_i_am_currently_working\", \"one_fm_applicant_demographics_cb\", \"one_fm_gender\", \"one_fm_religion\", \"one_fm_date_of_birth\", \"one_fm_place_of_birth\", \"one_fm_marital_status\", \"one_fm_nationality\", \"one_fm_date_of_entry\", \"employee_referral\", \"column_break_13\", \"applicant_rating\", \"section_break_6\", \"notes\", \"cover_letter\", \"resume_attachment\", \"children_details_section\", \"one_fm_number_of_kids\", \"one_fm_kids_details\", \"day_off_details\", \"day_off_category\", \"column_break_66\", \"number_of_days_off\", \"one_fm_work_details_section\", \"one_fm_rotation_shift\", \"one_fm_night_shift\", \"one_fm_work_details_cb\", \"one_fm_type_of_travel\", \"one_fm_type_of_driving_license\", \"one_fm_uniform_measurements\", \"one_fm_is_uniform_needed_for_this_job\", \"one_fm_shoulder_width\", \"one_fm_waist_size\", \"one_fm_shoe_size\", \"one_fm_basic_skill_section\", \"one_fm_designation_skill\", \"one_fm_documents_required_section\", \"one_fm_documents_required\", \"one_fm_is_easy_apply\", \"previous_work_details\", \"one_fm_work_permit_number\", \"one_fm_duration_of_work_permit\", \"one_fm_previous_designation\", \"column_break_51\", \"one_fm_work_permit_salary\", \"one_fm_last_working_date\", \"resume_link\", \"section_break_16\", \"currency\", \"column_break_18\", \"lower_range\", \"upper_range\", \"one_fm_contact_details_section\", \"one_fm_email_id\", \"one_fm_country_code\", \"one_fm_contact_number\", \"one_fm_country_code_second\", \"one_fm_secondary_contact_number\", \"one_fm_contact_cb\", \"one_fm_language_section\", \"one_fm_languages\", \"country_and_nationality_section\", \"nationality_no\", \"nationality_subject\", \"nationality_cb\", \"date_of_naturalization\", \"one_fm_passport_section\", \"one_fm_passport_number\", \"one_fm_passport_holder_of\", \"one_fm_passport_issued\", \"one_fm_passport_expire\", \"one_fm_passport_cb\", \"one_fm_passport_type\", \"one_fm_centralized_number\", \"one_fm_visa_and_residency_section\", \"one_fm_have_a_valid_visa_in_kuwait\", \"one_fm_visa_type\", \"one_fm_cid_number\", \"one_fm_cid_expire\", \"one_fm_in_kuwait_at_present\", \"one_fm_visa_cb\", \"one_fm_current_employment_section_\", \"one_fm_current_employer\", \"one_fm_current_employer_website_link\", \"one_fm_employment_start_date\", \"one_fm_employment_end_date\", \"one_fm_current_employment_cb\", \"one_fm_current_job_title\", \"one_fm_current_salary\", \"one_fm_notice_period_in_days\", \"one_fm_educational_qualification_section\", \"one_fm_educational_qualification\", \"other_education\", \"one_fm_education_specialization\", \"one_fm_educational_qualification_cb\", \"one_fm_university\", \"one_fm_country_of_employment\", \"section_break_88\", \"one_fm_are_you_currently_studying\", \"one_fm_current_educational_institution\", \"column_break_91\", \"one_fm_place_of_study\", \"one_fm_entry_date_of_current_educational_institution\", \"section_break_66\", \"one_fm_applicant_is_overseas_or_local\", \"one_fm_country_of_overseas\", \"one_fm_is_transferable\", \"custom_transfer_reminder_date\", \"column_break_72\", \"one_fm_applicant_password\", \"authorized_signatory\", \"one_fm_old_number\", \"one_fm_old_designation\", \"one_fm_erf_pam_file_number\", \"one_fm_erf_pam_designation\", \"send_changes_to_supervisor\", \"accept_changes\", \"reject_changes\", \"suggestions\", \"save_me\", \"no_internal_issues\", \"one_fm_has_issue\", \"one_fm_type_of_issues\", \"column_break_149\", \"one_fm_change_pam_file_number\", \"pam_number_button\", \"pam_designation_button\", \"one_fm_change_pam_designation\", \"column_break_152\", \"one_fm_pam_file_number\", \"one_fm_pam_designation\", \"column_break_154\", \"one_fm_file_number\", \"one_fm_notify_recruiter\", \"previous_company_details\", \"one_fm_previous_company_trade_name_in_arabic\", \"one_fm__previous_company_authorized_signatory_name_arabic\", \"one_fm_previous_company_issuer_number\", \"column_break_142\", \"one_fm_previous_company_pam_file_number\", \"one_fm_government_project\", \"authorized_signatory_section\", \"one_fm_pam_authorized_signatory\", \"one_fm_signatory_name\", \"one_fm_grd_operator\", \"scans\", \"passport_data_page\", \"civil_id_front\", \"civil_id_back\", \"magic_link_details\", \"career_history_ml\", \"career_history_ml_url\", \"career_history_ml_expired\", \"column_break_avxor\", \"applicant_doc_ml\", \"applicant_doc_ml_url\", \"applicant_doc_ml_expired\"]"
+ "value": "[\"interview_rounds_sb\", \"interview_rounds\", \"details_section\", \"applicant_name\", \"email_id\", \"one_fm_application_id\", \"phone_number\", \"country\", \"column_break_3\", \"job_title\", \"one_fm_designation\", \"applicant_lead\", \"designation\", \"status\", \"one_fm_reason_for_rejection\", \"one_fm_applicant_status\", \"one_fm_document_verification\", \"source_and_rating_section\", \"source\", \"source_name\", \"one_fm_erf_application_details_section\", \"one_fm_erf\", \"project\", \"department\", \"one_fm_sourcing_team\", \"one_fm_agency\", \"one_fm_is_agency_applying\", \"one_fm_job_applicant_cb_1\", \"one_fm_source_of_hire\", \"one_fm_hiring_method\", \"interview_round\", \"bulk_interview\", \"employment_type\", \"attendance_by_timesheet\", \"one_fm_applicant_personal_details_sb\", \"one_fm_first_name\", \"one_fm_second_name\", \"one_fm_third_name\", \"one_fm_forth_name\", \"one_fm_last_name\", \"one_fm_first_name_in_arabic\", \"one_fm_second_name_in_arabic\", \"one_fm_third_name_in_arabic\", \"one_fm_forth_name_in_arabic\", \"one_fm_last_name_in_arabic\", \"one_fm_height\", \"one_fm_i_am_currently_working\", \"one_fm_applicant_demographics_cb\", \"one_fm_gender\", \"one_fm_religion\", \"one_fm_date_of_birth\", \"one_fm_place_of_birth\", \"one_fm_marital_status\", \"one_fm_nationality\", \"employee_referral\", \"column_break_13\", \"applicant_rating\", \"section_break_6\", \"notes\", \"cover_letter\", \"resume_attachment\", \"children_details_section\", \"one_fm_number_of_kids\", \"one_fm_kids_details\", \"day_off_details\", \"day_off_category\", \"column_break_66\", \"number_of_days_off\", \"one_fm_work_details_section\", \"one_fm_rotation_shift\", \"one_fm_night_shift\", \"one_fm_work_details_cb\", \"one_fm_type_of_travel\", \"one_fm_type_of_driving_license\", \"one_fm_uniform_measurements\", \"one_fm_is_uniform_needed_for_this_job\", \"one_fm_shoulder_width\", \"one_fm_waist_size\", \"one_fm_shoe_size\", \"one_fm_basic_skill_section\", \"one_fm_designation_skill\", \"one_fm_documents_required_section\", \"one_fm_documents_required\", \"one_fm_is_easy_apply\", \"previous_work_details\", \"one_fm_work_permit_number\", \"one_fm_duration_of_work_permit\", \"one_fm_previous_designation\", \"column_break_51\", \"one_fm_work_permit_salary\", \"one_fm_last_working_date\", \"resume_link\", \"section_break_16\", \"currency\", \"column_break_18\", \"lower_range\", \"upper_range\", \"one_fm_contact_details_section\", \"one_fm_email_id\", \"one_fm_country_code\", \"one_fm_contact_number\", \"one_fm_country_code_second\", \"one_fm_secondary_contact_number\", \"one_fm_contact_cb\", \"one_fm_language_section\", \"one_fm_languages\", \"country_and_nationality_section\", \"nationality_no\", \"nationality_subject\", \"nationality_cb\", \"date_of_naturalization\", \"one_fm_passport_section\", \"one_fm_passport_number\", \"one_fm_passport_holder_of\", \"one_fm_passport_issued\", \"one_fm_passport_expire\", \"one_fm_passport_cb\", \"one_fm_passport_type\", \"one_fm_visa_and_residency_section\", \"one_fm_have_a_valid_visa_in_kuwait\", \"one_fm_visa_type\", \"one_fm_cid_number\", \"one_fm_cid_expire\", \"one_fm_in_kuwait_at_present\", \"one_fm_visa_cb\", \"one_fm_current_employment_section_\", \"one_fm_current_employer\", \"one_fm_current_employer_website_link\", \"one_fm_employment_start_date\", \"one_fm_employment_end_date\", \"one_fm_current_employment_cb\", \"one_fm_current_job_title\", \"one_fm_current_salary\", \"one_fm_notice_period_in_days\", \"one_fm_educational_qualification_section\", \"one_fm_educational_qualification\", \"other_education\", \"one_fm_education_specialization\", \"one_fm_educational_qualification_cb\", \"one_fm_university\", \"one_fm_country_of_employment\", \"section_break_88\", \"one_fm_are_you_currently_studying\", \"one_fm_current_educational_institution\", \"column_break_91\", \"one_fm_place_of_study\", \"one_fm_entry_date_of_current_educational_institution\", \"section_break_66\", \"one_fm_applicant_is_overseas_or_local\", \"one_fm_country_of_overseas\", \"one_fm_is_transferable\", \"custom_transfer_reminder_date\", \"column_break_72\", \"one_fm_applicant_password\", \"authorized_signatory\", \"one_fm_old_number\", \"one_fm_old_designation\", \"one_fm_erf_pam_file_number\", \"one_fm_erf_pam_designation\", \"send_changes_to_supervisor\", \"accept_changes\", \"reject_changes\", \"suggestions\", \"save_me\", \"no_internal_issues\", \"one_fm_has_issue\", \"one_fm_type_of_issues\", \"column_break_149\", \"one_fm_change_pam_file_number\", \"pam_number_button\", \"pam_designation_button\", \"one_fm_change_pam_designation\", \"column_break_152\", \"one_fm_pam_file_number\", \"one_fm_pam_designation\", \"column_break_154\", \"one_fm_file_number\", \"one_fm_notify_recruiter\", \"previous_company_details\", \"one_fm_previous_company_trade_name_in_arabic\", \"one_fm__previous_company_authorized_signatory_name_arabic\", \"one_fm_previous_company_issuer_number\", \"column_break_142\", \"one_fm_previous_company_pam_file_number\", \"one_fm_government_project\", \"authorized_signatory_section\", \"one_fm_pam_authorized_signatory\", \"one_fm_signatory_name\", \"one_fm_grd_operator\", \"scans\", \"passport_data_page\", \"civil_id_front\", \"civil_id_back\", \"magic_link_details\", \"career_history_ml\", \"career_history_ml_url\", \"career_history_ml_expired\", \"column_break_avxor\", \"applicant_doc_ml\", \"applicant_doc_ml_url\", \"applicant_doc_ml_expired\"]"
},
{
"default_value": null,
"doc_type": "Attendance Request",
"docstatus": 0,
"doctype": "Property Setter",
- "doctype_or_field": "DocType",
- "field_name": null,
+ "doctype_or_field": "DocField",
+ "field_name": "shift_type",
"is_system_generated": 0,
"modified": "2024-08-05 15:07:06.238931",
"module": null,
diff --git a/one_fm/fixtures/workflow.json b/one_fm/fixtures/workflow.json
index dc5c3a9902..c8ef185a86 100755
--- a/one_fm/fixtures/workflow.json
+++ b/one_fm/fixtures/workflow.json
@@ -5662,7 +5662,7 @@
"parent": "Shift Permission",
"parentfield": "states",
"parenttype": "Workflow",
- "state": "Pending",
+ "state": "Draft",
"update_field": null,
"update_value": null,
"workflow_builder_id": null
@@ -5670,14 +5670,14 @@
{
"allow_edit": "Shift Supervisor",
"avoid_status_override": 0,
- "doc_status": "1",
+ "doc_status": "0",
"is_optional_state": 0,
"message": null,
"next_action_email_template": null,
"parent": "Shift Permission",
"parentfield": "states",
"parenttype": "Workflow",
- "state": "Approved",
+ "state": "Pending Approver",
"update_field": null,
"update_value": null,
"workflow_builder_id": null
@@ -5692,7 +5692,7 @@
"parent": "Shift Permission",
"parentfield": "states",
"parenttype": "Workflow",
- "state": "Rejected",
+ "state": "Approved",
"update_field": null,
"update_value": null,
"workflow_builder_id": null
@@ -5700,14 +5700,14 @@
{
"allow_edit": "Shift Supervisor",
"avoid_status_override": 0,
- "doc_status": "2",
+ "doc_status": "1",
"is_optional_state": 0,
"message": null,
"next_action_email_template": null,
"parent": "Shift Permission",
"parentfield": "states",
"parenttype": "Workflow",
- "state": "Cancelled",
+ "state": "Rejected",
"update_field": null,
"update_value": null,
"workflow_builder_id": null
@@ -5715,7 +5715,25 @@
],
"transitions": [
{
- "action": "Approve",
+ "action": "Submit for Review",
+ "allow_self_approval": 1,
+ "allowed": "Employee",
+ "allowed_user_field": "employee",
+ "allowed_user_id": null,
+ "condition": "",
+ "custom_confirm_message": null,
+ "custom_confirm_transition": 0,
+ "next_state": "Pending Approver",
+ "parent": "Shift Permission",
+ "parentfield": "transitions",
+ "parenttype": "Workflow",
+ "skip_creation_of_workflow_action": 0,
+ "skip_multiple_action": 0,
+ "state": "Draft",
+ "workflow_builder_id": null
+ },
+ {
+ "action": "Return To Draft",
"allow_self_approval": 1,
"allowed": "Shift Supervisor",
"allowed_user_field": "shift_supervisor",
@@ -5723,17 +5741,17 @@
"condition": "frappe.session.user in [doc.approver_user_id, 'abdullah@one-fm.com']",
"custom_confirm_message": null,
"custom_confirm_transition": 0,
- "next_state": "Approved",
+ "next_state": "Draft",
"parent": "Shift Permission",
"parentfield": "transitions",
"parenttype": "Workflow",
"skip_creation_of_workflow_action": 0,
"skip_multiple_action": 0,
- "state": "Pending",
+ "state": "Pending Approver",
"workflow_builder_id": null
},
{
- "action": "Reject",
+ "action": "Approve",
"allow_self_approval": 1,
"allowed": "Shift Supervisor",
"allowed_user_field": "shift_supervisor",
@@ -5741,17 +5759,17 @@
"condition": "frappe.session.user in [doc.approver_user_id, 'abdullah@one-fm.com']",
"custom_confirm_message": null,
"custom_confirm_transition": 0,
- "next_state": "Rejected",
+ "next_state": "Approved",
"parent": "Shift Permission",
"parentfield": "transitions",
"parenttype": "Workflow",
"skip_creation_of_workflow_action": 0,
"skip_multiple_action": 0,
- "state": "Pending",
+ "state": "Pending Approver",
"workflow_builder_id": null
},
{
- "action": "Cancel",
+ "action": "Reject",
"allow_self_approval": 1,
"allowed": "Shift Supervisor",
"allowed_user_field": "shift_supervisor",
@@ -5759,13 +5777,13 @@
"condition": "frappe.session.user in [doc.approver_user_id, 'abdullah@one-fm.com']",
"custom_confirm_message": null,
"custom_confirm_transition": 0,
- "next_state": "Cancelled",
+ "next_state": "Rejected",
"parent": "Shift Permission",
"parentfield": "transitions",
"parenttype": "Workflow",
"skip_creation_of_workflow_action": 0,
"skip_multiple_action": 0,
- "state": "Approved",
+ "state": "Pending Approver",
"workflow_builder_id": null
}
],
@@ -8037,7 +8055,7 @@
"parent": "Shift Request",
"parentfield": "states",
"parenttype": "Workflow",
- "state": "Pending Approval",
+ "state": "Pending Approver",
"update_field": null,
"update_value": null,
"workflow_builder_id": null
@@ -8071,41 +8089,11 @@
"update_field": "",
"update_value": "",
"workflow_builder_id": null
- },
- {
- "allow_edit": "Shift Supervisor",
- "avoid_status_override": 0,
- "doc_status": "2",
- "is_optional_state": 0,
- "message": null,
- "next_action_email_template": null,
- "parent": "Shift Request",
- "parentfield": "states",
- "parenttype": "Workflow",
- "state": "Cancelled",
- "update_field": "",
- "update_value": "",
- "workflow_builder_id": null
- },
- {
- "allow_edit": "Shift Supervisor",
- "avoid_status_override": 0,
- "doc_status": "1",
- "is_optional_state": 0,
- "message": null,
- "next_action_email_template": null,
- "parent": "Shift Request",
- "parentfield": "states",
- "parenttype": "Workflow",
- "state": "Update Request",
- "update_field": "",
- "update_value": "",
- "workflow_builder_id": null
}
],
"transitions": [
{
- "action": "Send to Supervisor",
+ "action": "Submit for Review",
"allow_self_approval": 1,
"allowed": "Employee",
"allowed_user_field": null,
@@ -8113,7 +8101,7 @@
"condition": "frappe.session.user not in [approver.user for approver in doc.custom_shift_approvers]",
"custom_confirm_message": null,
"custom_confirm_transition": 0,
- "next_state": "Pending Approval",
+ "next_state": "Pending Approver",
"parent": "Shift Request",
"parentfield": "transitions",
"parenttype": "Workflow",
@@ -8137,7 +8125,7 @@
"parenttype": "Workflow",
"skip_creation_of_workflow_action": 0,
"skip_multiple_action": 0,
- "state": "Pending Approval",
+ "state": "Pending Approver",
"workflow_builder_id": null
},
{
@@ -8155,47 +8143,11 @@
"parenttype": "Workflow",
"skip_creation_of_workflow_action": 0,
"skip_multiple_action": 0,
- "state": "Pending Approval",
- "workflow_builder_id": null
- },
- {
- "action": "Cancel",
- "allow_self_approval": 1,
- "allowed": "Shift Supervisor",
- "allowed_user_field": null,
- "allowed_user_id": null,
- "condition": "",
- "custom_confirm_message": null,
- "custom_confirm_transition": 0,
- "next_state": "Cancelled",
- "parent": "Shift Request",
- "parentfield": "transitions",
- "parenttype": "Workflow",
- "skip_creation_of_workflow_action": 0,
- "skip_multiple_action": 0,
- "state": "Approved",
- "workflow_builder_id": null
- },
- {
- "action": "Update Request",
- "allow_self_approval": 1,
- "allowed": "Shift Supervisor",
- "allowed_user_field": null,
- "allowed_user_id": null,
- "condition": "doc.update_request",
- "custom_confirm_message": null,
- "custom_confirm_transition": 0,
- "next_state": "Update Request",
- "parent": "Shift Request",
- "parentfield": "transitions",
- "parenttype": "Workflow",
- "skip_creation_of_workflow_action": 0,
- "skip_multiple_action": 0,
- "state": "Approved",
+ "state": "Pending Approver",
"workflow_builder_id": null
},
{
- "action": "Approve",
+ "action": "Return To Draft",
"allow_self_approval": 1,
"allowed": "Shift Supervisor",
"allowed_user_field": null,
@@ -8203,13 +8155,13 @@
"condition": null,
"custom_confirm_message": null,
"custom_confirm_transition": 0,
- "next_state": "Approved",
+ "next_state": "Draft",
"parent": "Shift Request",
"parentfield": "transitions",
"parenttype": "Workflow",
"skip_creation_of_workflow_action": 0,
"skip_multiple_action": 0,
- "state": "Update Request",
+ "state": "Pending Approver",
"workflow_builder_id": null
},
{
@@ -8258,7 +8210,7 @@
"doctype": "Workflow",
"document_type": "Attendance Request",
"is_active": 1,
- "modified": "2023-06-18 09:27:53.645303",
+ "modified": "2024-03-22 10:02:21.030159",
"name": "Attendance Request",
"override_status": 0,
"send_email_alert": 0,
@@ -8337,26 +8289,11 @@
"update_field": null,
"update_value": null,
"workflow_builder_id": null
- },
- {
- "allow_edit": "Shift Supervisor",
- "avoid_status_override": 0,
- "doc_status": "1",
- "is_optional_state": 0,
- "message": null,
- "next_action_email_template": null,
- "parent": "Attendance Request",
- "parentfield": "states",
- "parenttype": "Workflow",
- "state": "Update Request",
- "update_field": null,
- "update_value": null,
- "workflow_builder_id": null
}
],
"transitions": [
{
- "action": "Send to Supervisor",
+ "action": "Submit for Review",
"allow_self_approval": 1,
"allowed": "Employee",
"allowed_user_field": "owner",
@@ -8410,43 +8347,7 @@
"workflow_builder_id": null
},
{
- "action": "Cancel",
- "allow_self_approval": 1,
- "allowed": "Shift Supervisor",
- "allowed_user_field": null,
- "allowed_user_id": null,
- "condition": null,
- "custom_confirm_message": null,
- "custom_confirm_transition": 0,
- "next_state": "Cancelled",
- "parent": "Attendance Request",
- "parentfield": "transitions",
- "parenttype": "Workflow",
- "skip_creation_of_workflow_action": 0,
- "skip_multiple_action": 0,
- "state": "Approved",
- "workflow_builder_id": null
- },
- {
- "action": "Update Request",
- "allow_self_approval": 1,
- "allowed": "Shift Supervisor",
- "allowed_user_field": null,
- "allowed_user_id": null,
- "condition": "doc.update_request",
- "custom_confirm_message": null,
- "custom_confirm_transition": 0,
- "next_state": "Update Request",
- "parent": "Attendance Request",
- "parentfield": "transitions",
- "parenttype": "Workflow",
- "skip_creation_of_workflow_action": 0,
- "skip_multiple_action": 0,
- "state": "Approved",
- "workflow_builder_id": null
- },
- {
- "action": "Approve",
+ "action": "Return To Draft",
"allow_self_approval": 1,
"allowed": "Shift Supervisor",
"allowed_user_field": null,
@@ -8454,19 +8355,19 @@
"condition": null,
"custom_confirm_message": null,
"custom_confirm_transition": 0,
- "next_state": "Approved",
+ "next_state": "Draft",
"parent": "Attendance Request",
"parentfield": "transitions",
"parenttype": "Workflow",
"skip_creation_of_workflow_action": 0,
"skip_multiple_action": 0,
- "state": "Update Request",
+ "state": "Pending Approval",
"workflow_builder_id": null
},
{
- "action": "Reject",
+ "action": "Cancel",
"allow_self_approval": 1,
- "allowed": "Shift Supervisor",
+ "allowed": "System Manager",
"allowed_user_field": null,
"allowed_user_id": null,
"condition": null,
@@ -8478,7 +8379,7 @@
"parenttype": "Workflow",
"skip_creation_of_workflow_action": 0,
"skip_multiple_action": 0,
- "state": "Update Request",
+ "state": "Approved",
"workflow_builder_id": null
}
],
@@ -8640,7 +8541,7 @@
"doctype": "Workflow",
"document_type": "Timesheet",
"is_active": 1,
- "modified": "2023-12-28 23:31:07.237828",
+ "modified": "2024-03-26 09:02:55.302435",
"name": "Timesheet",
"override_status": 0,
"send_email_alert": 0,
@@ -8670,7 +8571,7 @@
"parent": "Timesheet",
"parentfield": "states",
"parenttype": "Workflow",
- "state": "Open",
+ "state": "Pending Approval",
"update_field": null,
"update_value": null,
"workflow_builder_id": null
@@ -8721,7 +8622,7 @@
"workflow_builder_id": null
},
{
- "allow_edit": "Employee",
+ "allow_edit": "System Manager",
"avoid_status_override": 0,
"doc_status": "2",
"is_optional_state": 0,
@@ -8738,7 +8639,7 @@
],
"transitions": [
{
- "action": "Open Request",
+ "action": "Submit for Review",
"allow_self_approval": 1,
"allowed": "Employee",
"allowed_user_field": "owner",
@@ -8746,7 +8647,7 @@
"condition": "doc.attendance_by_timesheet == 1",
"custom_confirm_message": null,
"custom_confirm_transition": 0,
- "next_state": "Open",
+ "next_state": "Pending Approval",
"parent": "Timesheet",
"parentfield": "transitions",
"parenttype": "Workflow",
@@ -8788,11 +8689,11 @@
"parenttype": "Workflow",
"skip_creation_of_workflow_action": 0,
"skip_multiple_action": 0,
- "state": "Open",
+ "state": "Pending Approval",
"workflow_builder_id": null
},
{
- "action": "Reject",
+ "action": "Return To Draft",
"allow_self_approval": 1,
"allowed": "Employee",
"allowed_user_field": "approver",
@@ -8800,22 +8701,22 @@
"condition": "doc.attendance_by_timesheet == 1",
"custom_confirm_message": null,
"custom_confirm_transition": 0,
- "next_state": "Rejected",
+ "next_state": "Draft",
"parent": "Timesheet",
"parentfield": "transitions",
"parenttype": "Workflow",
"skip_creation_of_workflow_action": 0,
"skip_multiple_action": 0,
- "state": "Open",
+ "state": "Pending Approval",
"workflow_builder_id": null
},
{
"action": "Cancel",
"allow_self_approval": 1,
- "allowed": "Employee",
- "allowed_user_field": "approver",
+ "allowed": "System Manager",
+ "allowed_user_field": "",
"allowed_user_id": null,
- "condition": "frappe.session.user == doc.approver and doc.attendance_by_timesheet == 1",
+ "condition": "doc.attendance_by_timesheet == 1",
"custom_confirm_message": null,
"custom_confirm_transition": 0,
"next_state": "Cancelled",
@@ -8830,26 +8731,8 @@
{
"action": "Cancel",
"allow_self_approval": 1,
- "allowed": "Employee",
- "allowed_user_field": "approver",
- "allowed_user_id": null,
- "condition": "frappe.session.user == doc.approver and doc.attendance_by_timesheet == 1",
- "custom_confirm_message": null,
- "custom_confirm_transition": 0,
- "next_state": "Cancelled",
- "parent": "Timesheet",
- "parentfield": "transitions",
- "parenttype": "Workflow",
- "skip_creation_of_workflow_action": 0,
- "skip_multiple_action": 0,
- "state": "Rejected",
- "workflow_builder_id": null
- },
- {
- "action": "Cancel",
- "allow_self_approval": 1,
- "allowed": "Employee",
- "allowed_user_field": "approver",
+ "allowed": "System Manager",
+ "allowed_user_field": "",
"allowed_user_id": null,
"condition": "doc.attendance_by_timesheet == 0",
"custom_confirm_message": null,
@@ -8873,7 +8756,7 @@
"doctype": "Workflow",
"document_type": "Employee Checkin Issue",
"is_active": 1,
- "modified": "2023-01-24 11:00:16.795074",
+ "modified": "2024-03-25 11:52:50.847003",
"name": "Employee Checkin Issue",
"override_status": 0,
"send_email_alert": 0,
@@ -8888,7 +8771,22 @@
"parent": "Employee Checkin Issue",
"parentfield": "states",
"parenttype": "Workflow",
- "state": "Pending",
+ "state": "Draft",
+ "update_field": null,
+ "update_value": null,
+ "workflow_builder_id": null
+ },
+ {
+ "allow_edit": "Employee",
+ "avoid_status_override": 0,
+ "doc_status": "0",
+ "is_optional_state": 0,
+ "message": null,
+ "next_action_email_template": null,
+ "parent": "Employee Checkin Issue",
+ "parentfield": "states",
+ "parenttype": "Workflow",
+ "state": "Pending Approval",
"update_field": null,
"update_value": null,
"workflow_builder_id": null
@@ -8940,13 +8838,31 @@
}
],
"transitions": [
+ {
+ "action": "Submit for Approval",
+ "allow_self_approval": 1,
+ "allowed": "Employee",
+ "allowed_user_field": null,
+ "allowed_user_id": null,
+ "condition": null,
+ "custom_confirm_message": null,
+ "custom_confirm_transition": 0,
+ "next_state": "Pending Approval",
+ "parent": "Employee Checkin Issue",
+ "parentfield": "transitions",
+ "parenttype": "Workflow",
+ "skip_creation_of_workflow_action": 0,
+ "skip_multiple_action": 0,
+ "state": "Draft",
+ "workflow_builder_id": null
+ },
{
"action": "Approve",
"allow_self_approval": 1,
"allowed": "Shift Supervisor",
- "allowed_user_field": null,
+ "allowed_user_field": "approver_user_id",
"allowed_user_id": null,
- "condition": "frappe.session.user in [doc.approver_user_id]",
+ "condition": "frappe.session.user in [doc.approver_user_id, 'Administrator']",
"custom_confirm_message": null,
"custom_confirm_transition": 0,
"next_state": "Approved",
@@ -8955,16 +8871,16 @@
"parenttype": "Workflow",
"skip_creation_of_workflow_action": 0,
"skip_multiple_action": 0,
- "state": "Pending",
+ "state": "Pending Approval",
"workflow_builder_id": null
},
{
"action": "Reject",
"allow_self_approval": 1,
"allowed": "Shift Supervisor",
- "allowed_user_field": null,
+ "allowed_user_field": "approver_user_id",
"allowed_user_id": null,
- "condition": "frappe.session.user in [doc.approver_user_id]",
+ "condition": "frappe.session.user in [doc.approver_user_id, 'Administrator']",
"custom_confirm_message": null,
"custom_confirm_transition": 0,
"next_state": "Rejected",
@@ -8973,7 +8889,7 @@
"parenttype": "Workflow",
"skip_creation_of_workflow_action": 0,
"skip_multiple_action": 0,
- "state": "Pending",
+ "state": "Pending Approval",
"workflow_builder_id": null
},
{
@@ -8982,7 +8898,7 @@
"allowed": "Shift Supervisor",
"allowed_user_field": null,
"allowed_user_id": null,
- "condition": "frappe.session.user in [doc.approver_user_id]",
+ "condition": "frappe.session.user in [doc.approver_user_id, 'Administrator']",
"custom_confirm_message": null,
"custom_confirm_transition": 0,
"next_state": "Cancelled",
diff --git a/one_fm/hooks.py b/one_fm/hooks.py
index e50675ba30..8e4f03e1af 100644
--- a/one_fm/hooks.py
+++ b/one_fm/hooks.py
@@ -394,6 +394,19 @@
"before_insert": "one_fm.api.doc_methods.help_article.before_insert",
# "on_update": "one_fm.api.doc_methods.help_article.on_update",
},
+ "Shift Request":{
+ "before_save":[
+ "one_fm.overrides.shift_request.fill_to_date",
+ "one_fm.overrides.shift_request.send_shift_request_mail",
+ "one_fm.overrides.shift_request.validate_from_date"
+ ],
+ "on_update": [
+ "one_fm.overrides.shift_request.on_update",
+ ],
+ "validate": [
+ "one_fm.overrides.shift_request.validate",
+ ]
+ },
"Customer": {
"on_update":"one_fm.tasks.erpnext.customer.on_update",
},
@@ -686,11 +699,8 @@
],
"15 13 * * *":[ # Attendance Check
'one_fm.one_fm.doctype.attendance_check.attendance_check.schedule_attendance_check',
- 'one_fm.one_fm.doctype.attendance_check.attendance_check.assign_attendance_manager_after_48_hours'
+ 'one_fm.one_fm.doctype.attendance_check.attendance_check.attendance_check_pending_approval_check'
],
- # "07 13 * * *":[ # Auto approve attendance check
- # 'one_fm.one_fm.doctype.attendance_check.attendance_check.approve_attendance_check'
- # ],
"15 12 * * *": [ # create shift assignment
'one_fm.api.tasks.assign_pm_shift'
],
@@ -783,9 +793,11 @@
"dt": "Assignment Rule",
"filters": [["name", "in",
[
- "RFM Approver", "Shift Permission Approver", "Attendance Check Reports To",
+ "RFM Approver", "Shift Permission Approver", "Attendance Check Reports To", "Shift Permission Approver",
"Attendance Check Site Supervisor", "Attendance Check Shift Supervisor", "Subcontract Staff Request",
- "Purchase Order Approver Action", "Purchase Order Finance Manager Action", "Purchase Order Purchase Manager Action"
+ "Purchase Order Approver Action", "Purchase Order Finance Manager Action", "Purchase Order Purchase Manager Action",
+ "Timesheet Return to Draft", "Timesheet Approval Assignment", "Shift Request Draft", "Shift Request Pending Approval",
+ "Attendance Request Return to Draft", "Attendance Request Approval", "Employee Checkin Issue Approval"
]
]]
},
diff --git a/one_fm/legal/doctype/penalty/penalty.py b/one_fm/legal/doctype/penalty/penalty.py
index d897d8560d..70b652f49c 100644
--- a/one_fm/legal/doctype/penalty/penalty.py
+++ b/one_fm/legal/doctype/penalty/penalty.py
@@ -16,13 +16,15 @@
import grpc
face_recognition_service_url = frappe.local.conf.face_recognition_service_url
-channels = [
- grpc.secure_channel(i, grpc.ssl_channel_credentials()) for i in face_recognition_service_url
-]
+# channels = [
+# grpc.secure_channel(i, grpc.ssl_channel_credentials()) for i in face_recognition_service_url
+# ]
# setup stub for face recognition
-stubs = [
- facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
-]
+# stubs = [
+# facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
+# ]
+
+stubs = list()
class Penalty(Document):
def after_insert(self):
diff --git a/one_fm/legal/doctype/penalty_issuance_details/penalty_issuance_details.json b/one_fm/legal/doctype/penalty_issuance_details/penalty_issuance_details.json
index 28cf33457d..16ec36e7c4 100644
--- a/one_fm/legal/doctype/penalty_issuance_details/penalty_issuance_details.json
+++ b/one_fm/legal/doctype/penalty_issuance_details/penalty_issuance_details.json
@@ -33,7 +33,7 @@
},
{
"fieldname": "exact_notes",
- "fieldtype": "Small Text",
+ "fieldtype": "Text Editor",
"label": "Exact Notes on Penalty"
},
{
@@ -113,7 +113,7 @@
],
"istable": 1,
"links": [],
- "modified": "2021-09-23 10:28:22.233449",
+ "modified": "2024-07-21 11:01:20.104225",
"modified_by": "Administrator",
"module": "Legal",
"name": "Penalty Issuance Details",
@@ -122,5 +122,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/one_fm/one_fm/custom/attendance.json b/one_fm/one_fm/custom/attendance.json
index 84a7d8c06a..ed6c81b526 100644
--- a/one_fm/one_fm/custom/attendance.json
+++ b/one_fm/one_fm/custom/attendance.json
@@ -11,7 +11,68 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-06 16:31:16.390600",
+ "creation": "2024-04-25 14:23:30.195015",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Attendance",
+ "fetch_from": "employee.employment_type",
+ "fetch_if_empty": 0,
+ "fieldname": "custom_employment_type",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 4,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "employee_name",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Employment Type",
+ "length": 0,
+ "mandatory_depends_on": null,
+ "modified": "2024-04-25 14:23:30.195015",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Attendance-custom_employment_type",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Employment Type",
+ "owner": "Administrator",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2023-06-07 22:54:35.910972",
"default": null,
"depends_on": null,
"description": null,
@@ -19,36 +80,33 @@
"dt": "Attendance",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "section_break_17",
- "fieldtype": "Section Break",
+ "fieldname": "column_break_nahps",
+ "fieldtype": "Column Break",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 21,
+ "idx": 39,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "early_exit",
- "is_system_generated": 1,
+ "insert_after": "reference_doctype",
+ "is_system_generated": 0,
"is_virtual": 0,
"label": null,
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-10-06 16:31:16.390600",
+ "modified": "2023-06-07 22:54:35.910972",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-section_break_17",
+ "name": "Attendance-column_break_nahps",
"no_copy": 0,
"non_negative": 0,
"options": null,
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -75,50 +133,47 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-06 16:31:17.654555",
+ "creation": "2023-06-07 22:49:37.281876",
"default": null,
- "depends_on": null,
+ "depends_on": "eval:doc.reference_doctype",
"description": null,
"docstatus": 0,
"dt": "Attendance",
- "fetch_from": "shift_assignment.site",
+ "fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "site",
- "fieldtype": "Link",
+ "fieldname": "reference_docname",
+ "fieldtype": "Dynamic Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 24,
+ "idx": 40,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "operations_shift",
- "is_system_generated": 1,
+ "insert_after": "column_break_nahps",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": "Site",
+ "label": "Reference Docname",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-10-06 16:31:17.654555",
+ "modified": "2023-06-07 22:49:37.281876",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-site",
+ "name": "Attendance-reference_docname",
"no_copy": 0,
"non_negative": 0,
- "options": "Operations Site",
+ "options": "reference_doctype",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 1,
+ "read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
@@ -139,50 +194,47 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-06 16:31:18.571753",
+ "creation": "2023-06-07 22:49:26.738481",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Attendance",
- "fetch_from": "shift_assignment.project",
+ "fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "project",
+ "fieldname": "reference_doctype",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 25,
+ "idx": 38,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "site",
- "is_system_generated": 1,
+ "insert_after": "references",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": "Project",
+ "label": "Reference Doctype",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-10-06 16:31:18.571753",
+ "modified": "2023-06-07 22:49:26.738481",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-project",
+ "name": "Attendance-reference_doctype",
"no_copy": 0,
"non_negative": 0,
- "options": "Project",
+ "options": "DocType",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 1,
+ "read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
@@ -203,7 +255,7 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-06 16:31:19.502006",
+ "creation": "2023-06-07 22:49:26.496439",
"default": null,
"depends_on": null,
"description": null,
@@ -211,36 +263,33 @@
"dt": "Attendance",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "column_break_21",
- "fieldtype": "Column Break",
+ "fieldname": "references",
+ "fieldtype": "Section Break",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 27,
+ "idx": 37,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "timesheet",
- "is_system_generated": 1,
+ "insert_after": "comment",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": null,
+ "label": "References",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-10-06 16:31:19.502006",
+ "modified": "2023-06-07 22:49:26.496439",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-column_break_21",
+ "name": "Attendance-references",
"no_copy": 0,
"non_negative": 0,
"options": null,
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -267,47 +316,44 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-06 16:31:20.181570",
+ "creation": "2023-05-28 11:45:33.517065",
"default": null,
- "depends_on": null,
+ "depends_on": "eval:doc.operations_role",
"description": null,
"docstatus": 0,
"dt": "Attendance",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "post_type",
- "fieldtype": "Link",
+ "fetch_from": "operations_role.sale_item",
+ "fetch_if_empty": 1,
+ "fieldname": "sale_item",
+ "fieldtype": "Data",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 31,
+ "idx": 33,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "roster_type",
- "is_system_generated": 1,
+ "insert_after": "post_type",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": "Post Type",
+ "label": "Sale Item",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-10-06 16:31:20.181570",
+ "modified": "2023-05-28 11:45:33.517065",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-post_type",
+ "name": "Attendance-sale_item",
"no_copy": 0,
"non_negative": 0,
- "options": "Operations Role",
+ "options": null,
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
- "print_hide": 0,
+ "print_hide": 1,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 1,
@@ -331,56 +377,53 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-06 16:31:20.181570",
+ "creation": "2023-01-22 12:33:39.207185",
"default": null,
- "depends_on": null,
+ "depends_on": "",
"description": null,
"docstatus": 0,
"dt": "Attendance",
- "fetch_from": "shift_assignment.operations_role",
+ "fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "operations_role",
- "fieldtype": "Link",
+ "fieldname": "comment",
+ "fieldtype": "Small Text",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 28,
+ "idx": 36,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "column_break_21",
- "is_system_generated": 1,
+ "insert_after": "attendance_comment",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": "Operations Role",
+ "label": "Comment",
"length": 0,
- "mandatory_depends_on": null,
- "modified": "2020-10-06 16:31:20.181570",
+ "mandatory_depends_on": "",
+ "modified": "2023-01-22 12:33:39.207185",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-operations_role",
+ "name": "Attendance-comment",
"no_copy": 0,
"non_negative": 0,
- "options": "Operations Role",
+ "options": null,
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 1,
+ "read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
- "translatable": 0,
+ "translatable": 1,
"unique": 0,
"width": null
},
@@ -395,7 +438,7 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-06 16:31:21.131695",
+ "creation": "2023-01-22 12:33:38.989680",
"default": null,
"depends_on": null,
"description": null,
@@ -403,42 +446,39 @@
"dt": "Attendance",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "post_abbrv",
- "fieldtype": "Data",
+ "fieldname": "attendance_comment",
+ "fieldtype": "Section Break",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 29,
+ "idx": 35,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "operations_role",
- "is_system_generated": 1,
+ "insert_after": "day_off_ot",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": "Post Abbrv",
+ "label": "Attendance Comment",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-10-06 16:31:21.131695",
+ "modified": "2023-01-22 12:33:38.989680",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-post_abbrv",
+ "name": "Attendance-attendance_comment",
"no_copy": 0,
"non_negative": 0,
"options": null,
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 1,
+ "read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
@@ -459,44 +499,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-16 13:38:23.800388",
+ "creation": "2023-01-03 01:00:44.771086",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Attendance",
- "fetch_from": "shift_assignment.shift",
- "fetch_if_empty": 1,
- "fieldname": "operations_shift",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "timesheet",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 23,
+ "idx": 27,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "shift_assignment",
- "is_system_generated": 1,
+ "insert_after": "project",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": "Operations Shift",
+ "label": "Timesheet",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-10-16 13:38:23.800388",
+ "modified": "2023-01-03 01:00:44.771086",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-operations_shift",
+ "name": "Attendance-timesheet",
"no_copy": 0,
"non_negative": 0,
- "options": "Operations Shift",
+ "options": "Timesheet",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -523,44 +560,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2021-11-10 12:20:53.433210",
- "default": "Basic",
+ "creation": "2022-11-06 10:49:34.555115",
+ "default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Attendance",
- "fetch_from": "shift_assignment.roster_type",
+ "fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "roster_type",
- "fieldtype": "Select",
+ "fieldname": "shift_assignment",
+ "fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 30,
+ "idx": 23,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
- "in_list_view": 1,
+ "in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "post_abbrv",
- "is_system_generated": 1,
+ "insert_after": "section_break_17",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": "Roster Type",
+ "label": "Shift Assignment",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2021-11-10 12:20:53.433210",
+ "modified": "2022-11-06 10:49:34.555115",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-roster_type",
+ "name": "Attendance-shift_assignment",
"no_copy": 0,
"non_negative": 0,
- "options": "Basic\nOver-Time",
+ "options": "Shift Assignment",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -572,7 +606,7 @@
"reqd": 0,
"search_index": 0,
"sort_options": 0,
- "translatable": 1,
+ "translatable": 0,
"unique": 0,
"width": null
},
@@ -622,9 +656,6 @@
"non_negative": 0,
"options": null,
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -651,44 +682,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-11-06 10:49:34.555115",
- "default": null,
+ "creation": "2021-11-10 12:20:53.433210",
+ "default": "Basic",
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Attendance",
- "fetch_from": null,
+ "fetch_from": "shift_assignment.roster_type",
"fetch_if_empty": 0,
- "fieldname": "shift_assignment",
- "fieldtype": "Link",
+ "fieldname": "roster_type",
+ "fieldtype": "Select",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 22,
+ "idx": 30,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "section_break_17",
- "is_system_generated": 0,
+ "insert_after": "post_abbrv",
+ "is_system_generated": 1,
"is_virtual": 0,
- "label": "Shift Assignment",
+ "label": "Roster Type",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2022-11-06 10:49:34.555115",
+ "modified": "2021-11-10 12:20:53.433210",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-shift_assignment",
+ "name": "Attendance-roster_type",
"no_copy": 0,
"non_negative": 0,
- "options": "Shift Assignment",
+ "options": "Basic\nOver-Time",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -700,7 +728,7 @@
"reqd": 0,
"search_index": 0,
"sort_options": 0,
- "translatable": 0,
+ "translatable": 1,
"unique": 0,
"width": null
},
@@ -715,44 +743,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-01-03 01:00:44.771086",
+ "creation": "2020-10-16 13:38:23.800388",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Attendance",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "timesheet",
+ "fetch_from": "shift_assignment.shift",
+ "fetch_if_empty": 1,
+ "fieldname": "operations_shift",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 26,
+ "idx": 23,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "project",
- "is_system_generated": 0,
+ "insert_after": "shift_assignment",
+ "is_system_generated": 1,
"is_virtual": 0,
- "label": "Timesheet",
+ "label": "Operations Shift",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-01-03 01:00:44.771086",
+ "modified": "2020-10-16 13:38:23.800388",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-timesheet",
+ "name": "Attendance-operations_shift",
"no_copy": 0,
"non_negative": 0,
- "options": "Timesheet",
+ "options": "Operations Shift",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -779,7 +804,7 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-01-22 12:33:38.989680",
+ "creation": "2020-10-06 16:31:21.131695",
"default": null,
"depends_on": null,
"description": null,
@@ -787,42 +812,39 @@
"dt": "Attendance",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "attendance_comment",
- "fieldtype": "Section Break",
+ "fieldname": "post_abbrv",
+ "fieldtype": "Data",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 34,
+ "idx": 29,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "day_off_ot",
- "is_system_generated": 0,
+ "insert_after": "operations_role",
+ "is_system_generated": 1,
"is_virtual": 0,
- "label": "Attendance Comment",
+ "label": "Post Abbrv",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-01-22 12:33:38.989680",
+ "modified": "2020-10-06 16:31:21.131695",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-attendance_comment",
+ "name": "Attendance-post_abbrv",
"no_copy": 0,
"non_negative": 0,
"options": null,
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
@@ -843,56 +865,53 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-01-22 12:33:39.207185",
+ "creation": "2020-10-06 16:31:20.181570",
"default": null,
- "depends_on": "",
+ "depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Attendance",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "comment",
- "fieldtype": "Small Text",
+ "fieldname": "post_type",
+ "fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 35,
+ "idx": 31,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "attendance_comment",
- "is_system_generated": 0,
+ "insert_after": "roster_type",
+ "is_system_generated": 1,
"is_virtual": 0,
- "label": "Comment",
+ "label": "Post Type",
"length": 0,
- "mandatory_depends_on": "",
- "modified": "2023-01-22 12:33:39.207185",
+ "mandatory_depends_on": null,
+ "modified": "2020-10-06 16:31:20.181570",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-comment",
+ "name": "Attendance-post_type",
"no_copy": 0,
"non_negative": 0,
- "options": null,
+ "options": "Operations Role",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
- "translatable": 1,
+ "translatable": 0,
"unique": 0,
"width": null
},
@@ -907,47 +926,44 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-05-28 11:45:33.517065",
+ "creation": "2020-10-06 16:31:20.181570",
"default": null,
- "depends_on": "eval:doc.operations_role",
+ "depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Attendance",
- "fetch_from": "operations_role.sale_item",
- "fetch_if_empty": 1,
- "fieldname": "sale_item",
- "fieldtype": "Data",
+ "fetch_from": "shift_assignment.operations_role",
+ "fetch_if_empty": 0,
+ "fieldname": "operations_role",
+ "fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 32,
+ "idx": 28,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "post_type",
- "is_system_generated": 0,
+ "insert_after": "column_break_21",
+ "is_system_generated": 1,
"is_virtual": 0,
- "label": "Sale Item",
+ "label": "Operations Role",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-05-28 11:45:33.517065",
+ "modified": "2020-10-06 16:31:20.181570",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-sale_item",
+ "name": "Attendance-operations_role",
"no_copy": 0,
"non_negative": 0,
- "options": null,
+ "options": "Operations Role",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
- "print_hide": 1,
+ "print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 1,
@@ -971,7 +987,7 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-06-07 22:49:26.496439",
+ "creation": "2020-10-06 16:31:19.502006",
"default": null,
"depends_on": null,
"description": null,
@@ -979,36 +995,33 @@
"dt": "Attendance",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "references",
- "fieldtype": "Section Break",
+ "fieldname": "column_break_21",
+ "fieldtype": "Column Break",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 36,
+ "idx": 27,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "comment",
- "is_system_generated": 0,
+ "insert_after": "timesheet",
+ "is_system_generated": 1,
"is_virtual": 0,
- "label": "References",
+ "label": null,
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-06-07 22:49:26.496439",
+ "modified": "2020-10-06 16:31:19.502006",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-references",
+ "name": "Attendance-column_break_21",
"no_copy": 0,
"non_negative": 0,
"options": null,
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -1035,50 +1048,47 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-06-07 22:49:26.738481",
+ "creation": "2020-10-06 16:31:18.571753",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Attendance",
- "fetch_from": null,
+ "fetch_from": "shift_assignment.project",
"fetch_if_empty": 0,
- "fieldname": "reference_doctype",
+ "fieldname": "project",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 37,
+ "idx": 25,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "references",
- "is_system_generated": 0,
+ "insert_after": "site",
+ "is_system_generated": 1,
"is_virtual": 0,
- "label": "Reference Doctype",
+ "label": "Project",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-06-07 22:49:26.738481",
+ "modified": "2020-10-06 16:31:18.571753",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-reference_doctype",
+ "name": "Attendance-project",
"no_copy": 0,
"non_negative": 0,
- "options": "DocType",
+ "options": "Project",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
@@ -1099,50 +1109,47 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-06-07 22:49:37.281876",
+ "creation": "2020-10-06 16:31:17.654555",
"default": null,
- "depends_on": "eval:doc.reference_doctype",
+ "depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Attendance",
- "fetch_from": null,
+ "fetch_from": "shift_assignment.site",
"fetch_if_empty": 0,
- "fieldname": "reference_docname",
- "fieldtype": "Dynamic Link",
+ "fieldname": "site",
+ "fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 39,
+ "idx": 24,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "column_break_nahps",
- "is_system_generated": 0,
+ "insert_after": "operations_shift",
+ "is_system_generated": 1,
"is_virtual": 0,
- "label": "Reference Docname",
+ "label": "Site",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-06-07 22:49:37.281876",
+ "modified": "2020-10-06 16:31:17.654555",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-reference_docname",
+ "name": "Attendance-site",
"no_copy": 0,
"non_negative": 0,
- "options": "reference_doctype",
+ "options": "Operations Site",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
@@ -1163,7 +1170,7 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-06-07 22:54:35.910972",
+ "creation": "2020-10-06 16:31:16.390600",
"default": null,
"depends_on": null,
"description": null,
@@ -1171,36 +1178,33 @@
"dt": "Attendance",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "column_break_nahps",
- "fieldtype": "Column Break",
+ "fieldname": "section_break_17",
+ "fieldtype": "Section Break",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 38,
+ "idx": 21,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "reference_doctype",
- "is_system_generated": 0,
+ "insert_after": "early_exit",
+ "is_system_generated": 1,
"is_virtual": 0,
"label": null,
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-06-07 22:54:35.910972",
+ "modified": "2020-10-06 16:31:16.390600",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-column_break_nahps",
+ "name": "Attendance-section_break_17",
"no_copy": 0,
"non_negative": 0,
"options": null,
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -1226,52 +1230,46 @@
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-12-24 15:24:16.904880",
+ "creation": "2024-04-25 14:23:30.068325",
"default_value": null,
"doc_type": "Attendance",
"docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "roster_type",
+ "doctype_or_field": "DocType",
+ "field_name": null,
"idx": 0,
"is_system_generated": 0,
- "modified": "2023-12-24 15:24:16.904880",
+ "modified": "2024-04-25 14:23:30.068325",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-roster_type-in_standard_filter",
+ "name": "Attendance-main-field_order",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "in_standard_filter",
- "property_type": "Check",
+ "property": "field_order",
+ "property_type": "Data",
"row_name": null,
- "value": "1"
+ "value": "[\"attendance_details\", \"naming_series\", \"employee\", \"employee_name\", \"employment_type\", \"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\", \"section_break_17\", \"shift_assignment\", \"operations_shift\", \"site\", \"project\", \"timesheet\", \"column_break_21\", \"operations_role\", \"post_abbrv\", \"roster_type\", \"post_type\", \"sale_item\", \"day_off_ot\", \"attendance_comment\", \"comment\", \"references\", \"reference_doctype\", \"column_break_nahps\", \"reference_docname\", \"amended_from\"]"
},
{
"_assign": null,
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-12-24 15:24:16.715079",
+ "creation": "2023-12-24 15:24:16.904880",
"default_value": null,
"doc_type": "Attendance",
"docstatus": 0,
- "doctype_or_field": "DocType",
- "field_name": null,
+ "doctype_or_field": "DocField",
+ "field_name": "roster_type",
"idx": 0,
"is_system_generated": 0,
- "modified": "2023-12-24 15:24:16.715079",
+ "modified": "2023-12-24 15:24:16.904880",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance-main-field_order",
+ "name": "Attendance-roster_type-in_standard_filter",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "field_order",
- "property_type": "Data",
+ "property": "in_standard_filter",
+ "property_type": "Check",
"row_name": null,
- "value": "[\"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\", \"section_break_17\", \"shift_assignment\", \"operations_shift\", \"site\", \"project\", \"timesheet\", \"column_break_21\", \"operations_role\", \"post_abbrv\", \"roster_type\", \"post_type\", \"sale_item\", \"day_off_ot\", \"attendance_comment\", \"comment\", \"references\", \"reference_doctype\", \"column_break_nahps\", \"reference_docname\", \"amended_from\"]"
+ "value": "1"
},
{
"_assign": null,
@@ -1291,9 +1289,6 @@
"module": null,
"name": "Attendance-working_hours-depends_on",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"property": "depends_on",
"property_type": "Data",
"row_name": null,
@@ -1317,9 +1312,6 @@
"module": null,
"name": "Attendance-shift-read_only",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"property": "read_only",
"property_type": "Check",
"row_name": null,
@@ -1343,9 +1335,6 @@
"module": null,
"name": "Attendance-shift-fetch_if_empty",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"property": "fetch_if_empty",
"property_type": "Check",
"row_name": null,
@@ -1369,9 +1358,6 @@
"module": null,
"name": "Attendance-shift-fetch_from",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"property": "fetch_from",
"property_type": "Small Text",
"row_name": null,
@@ -1395,9 +1381,6 @@
"module": null,
"name": "Attendance-attendance_date-in_standard_filter",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"property": "in_standard_filter",
"property_type": "Check",
"row_name": null,
@@ -1421,9 +1404,6 @@
"module": "One Fm",
"name": "Attendance-status-options",
"owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
"property": "options",
"property_type": "Select",
"row_name": null,
diff --git a/one_fm/one_fm/custom/attendance_request.json b/one_fm/one_fm/custom/attendance_request.json
index c53a6acce0..6958df5d31 100644
--- a/one_fm/one_fm/custom/attendance_request.json
+++ b/one_fm/one_fm/custom/attendance_request.json
@@ -1,5 +1,137 @@
{
"custom_fields": [
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2023-06-18 14:47:21.591893",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Attendance Request",
+ "fetch_from": "approver.user_id",
+ "fetch_if_empty": 1,
+ "fieldname": "approver_user",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 8,
+ "ignore_user_permissions": 1,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "approver_name",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Approver User",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2023-06-18 14:47:21.591893",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Attendance Request-approver_user",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "User",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2023-06-18 14:43:58.477374",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Attendance Request",
+ "fetch_from": "approver.employee_name",
+ "fetch_if_empty": 1,
+ "fieldname": "approver_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 7,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "approver",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Approver Name",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2023-06-18 14:43:58.477374",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Attendance Request-approver_name",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
{
"_assign": null,
"_comments": null,
@@ -21,7 +153,7 @@
"fetch_if_empty": 0,
"fieldname": "approver",
"fieldtype": "Link",
- "hidden": 0,
+ "hidden": 1,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
@@ -208,101 +340,101 @@
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2024-07-01 15:09:07.209381",
+ "creation": "2024-09-30 15:05:01.798681",
"default_value": null,
"doc_type": "Attendance Request",
"docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "employee",
+ "doctype_or_field": "DocType",
+ "field_name": null,
"idx": 0,
"is_system_generated": 0,
- "modified": "2024-07-01 15:09:07.209381",
+ "modified": "2024-09-30 15:05:01.798681",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance Request-employee-ignore_user_permissions",
+ "name": "Attendance Request-main-field_order",
"owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
- "property": "ignore_user_permissions",
- "property_type": "Check",
+ "property": "field_order",
+ "property_type": "Data",
"row_name": null,
- "value": "1"
+ "value": "[\"workflow_state\", \"employee\", \"employee_name\", \"department\", \"update_request\", \"approver\", \"approver_name\", \"approver_user\", \"column_break_5\", \"company\", \"from_date\", \"to_date\", \"half_day\", \"half_day_date\", \"include_holidays\", \"shift\", \"reason_section\", \"reason\", \"column_break_4\", \"explanation\", \"amended_from\"]"
},
{
"_assign": null,
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2024-07-01 15:09:06.992261",
+ "creation": "2023-11-13 10:52:21.502016",
"default_value": null,
"doc_type": "Attendance Request",
"docstatus": 0,
- "doctype_or_field": "DocType",
- "field_name": null,
+ "doctype_or_field": "DocField",
+ "field_name": "half_day",
"idx": 0,
"is_system_generated": 0,
- "modified": "2024-07-01 15:09:06.992261",
+ "modified": "2024-09-30 13:58:54.883633",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance Request-main-field_order",
+ "name": "Attendance Request-half_day-read_only",
"owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
- "property": "field_order",
- "property_type": "Data",
+ "property": "read_only",
+ "property_type": "Check",
"row_name": null,
- "value": "[\"workflow_state\", \"employee\", \"employee_name\", \"department\", \"update_request\", \"approver\", \"column_break_5\", \"company\", \"from_date\", \"to_date\", \"half_day\", \"half_day_date\", \"include_holidays\", \"shift\", \"reason_section\", \"reason\", \"column_break_4\", \"explanation\", \"amended_from\"]"
+ "value": "1"
},
{
"_assign": null,
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-02-08 11:38:39.429724",
+ "creation": "2023-11-13 10:52:21.451378",
"default_value": null,
"doc_type": "Attendance Request",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "reason",
+ "field_name": "half_day",
"idx": 0,
"is_system_generated": 0,
- "modified": "2024-06-25 13:56:31.109566",
+ "modified": "2024-09-30 13:58:54.872443",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance Request-reason-in_list_view",
+ "name": "Attendance Request-half_day-hidden",
"owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
- "property": "in_list_view",
+ "property": "hidden",
"property_type": "Check",
"row_name": null,
- "value": "0"
+ "value": "1"
},
{
"_assign": null,
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-02-08 11:38:39.442269",
+ "creation": "2023-02-11 23:31:05.168275",
"default_value": null,
"doc_type": "Attendance Request",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "employee_name",
+ "field_name": "explanation",
"idx": 0,
"is_system_generated": 0,
- "modified": "2024-06-25 13:56:31.087880",
+ "modified": "2024-09-30 13:58:54.858923",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance Request-employee_name-in_list_view",
+ "name": "Attendance Request-explanation-reqd",
"owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
- "property": "in_list_view",
+ "property": "reqd",
"property_type": "Check",
"row_name": null,
"value": "1"
@@ -312,18 +444,18 @@
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-02-08 11:38:39.455077",
+ "creation": "2023-02-08 11:38:39.492398",
"default_value": null,
"doc_type": "Attendance Request",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "employee",
+ "field_name": "workflow_state",
"idx": 0,
"is_system_generated": 0,
- "modified": "2024-06-25 13:56:31.067192",
+ "modified": "2024-09-30 13:58:54.848002",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance Request-employee-in_list_view",
+ "name": "Attendance Request-workflow_state-in_list_view",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -338,18 +470,18 @@
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-02-08 11:38:39.467485",
+ "creation": "2023-02-08 11:38:39.480029",
"default_value": null,
"doc_type": "Attendance Request",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "from_date",
+ "field_name": "to_date",
"idx": 0,
"is_system_generated": 0,
- "modified": "2024-06-25 13:56:31.046702",
+ "modified": "2024-09-30 13:58:54.835000",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance Request-from_date-in_list_view",
+ "name": "Attendance Request-to_date-in_list_view",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -364,18 +496,18 @@
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-02-08 11:38:39.480029",
+ "creation": "2023-02-08 11:38:39.467485",
"default_value": null,
"doc_type": "Attendance Request",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "to_date",
+ "field_name": "from_date",
"idx": 0,
"is_system_generated": 0,
- "modified": "2024-06-25 13:56:31.027948",
+ "modified": "2024-09-30 13:58:54.822943",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance Request-to_date-in_list_view",
+ "name": "Attendance Request-from_date-in_list_view",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -390,18 +522,18 @@
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-02-08 11:38:39.492398",
+ "creation": "2023-02-08 11:38:39.455077",
"default_value": null,
"doc_type": "Attendance Request",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "workflow_state",
+ "field_name": "employee",
"idx": 0,
"is_system_generated": 0,
- "modified": "2024-06-25 13:56:31.004431",
+ "modified": "2024-09-30 13:58:54.810560",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance Request-workflow_state-in_list_view",
+ "name": "Attendance Request-employee-in_list_view",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -416,23 +548,23 @@
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-02-11 23:31:05.168275",
+ "creation": "2023-02-08 11:38:39.442269",
"default_value": null,
"doc_type": "Attendance Request",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "explanation",
+ "field_name": "employee_name",
"idx": 0,
"is_system_generated": 0,
- "modified": "2024-06-25 13:56:30.979633",
+ "modified": "2024-09-30 13:58:54.796896",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance Request-explanation-reqd",
+ "name": "Attendance Request-employee_name-in_list_view",
"owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
- "property": "reqd",
+ "property": "in_list_view",
"property_type": "Check",
"row_name": null,
"value": "1"
@@ -442,49 +574,49 @@
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-11-13 10:52:21.451378",
+ "creation": "2023-02-08 11:38:39.429724",
"default_value": null,
"doc_type": "Attendance Request",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "half_day",
+ "field_name": "reason",
"idx": 0,
"is_system_generated": 0,
- "modified": "2024-06-25 13:56:30.937615",
+ "modified": "2024-09-30 13:58:54.783464",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance Request-half_day-hidden",
+ "name": "Attendance Request-reason-in_list_view",
"owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
- "property": "hidden",
+ "property": "in_list_view",
"property_type": "Check",
"row_name": null,
- "value": "1"
+ "value": "0"
},
{
"_assign": null,
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-11-13 10:52:21.502016",
+ "creation": "2024-07-01 15:09:07.209381",
"default_value": null,
"doc_type": "Attendance Request",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "half_day",
+ "field_name": "employee",
"idx": 0,
"is_system_generated": 0,
- "modified": "2024-06-25 13:56:30.918618",
+ "modified": "2024-09-30 13:58:54.749641",
"modified_by": "Administrator",
"module": null,
- "name": "Attendance Request-half_day-read_only",
+ "name": "Attendance Request-employee-ignore_user_permissions",
"owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
- "property": "read_only",
+ "property": "ignore_user_permissions",
"property_type": "Check",
"row_name": null,
"value": "1"
diff --git a/one_fm/one_fm/custom/employee_checkin.json b/one_fm/one_fm/custom/employee_checkin.json
index 70804859ed..d5917217eb 100644
--- a/one_fm/one_fm/custom/employee_checkin.json
+++ b/one_fm/one_fm/custom/employee_checkin.json
@@ -11,40 +11,40 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-01-26 16:05:02.103679",
+ "creation": "2024-03-25 01:31:19.979822",
"default": null,
- "depends_on": "eval:doc.shift_assignment",
+ "depends_on": "eval: doc.is_replaced;",
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": "shift_assignment.shift",
- "fetch_if_empty": 1,
- "fieldname": "operations_shift",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "replaced_employee_checkin",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 19,
- "ignore_user_permissions": 1,
+ "idx": 24,
+ "ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "shift_assignment",
+ "insert_after": "is_replaced",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Operations Shift",
+ "label": "Replaced Employee Checkin",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-05-26 19:06:49.143490",
+ "modified": "2024-03-25 01:31:19.979822",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-operations_shift",
+ "name": "Employee Checkin-replaced_employee_checkin",
"no_copy": 0,
"non_negative": 0,
- "options": "Operations Shift",
+ "options": "Shift Assignment",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -54,11 +54,12 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 1,
+ "read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -74,7 +75,7 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-01-26 16:05:01.508577",
+ "creation": "2024-03-11 22:56:55.324796",
"default": null,
"depends_on": null,
"description": null,
@@ -82,29 +83,29 @@
"dt": "Employee Checkin",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "shift_details",
- "fieldtype": "Section Break",
+ "fieldname": "is_replaced",
+ "fieldtype": "Check",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 17,
+ "idx": 23,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "shift_actual_end",
+ "insert_after": "company",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Shift Details",
+ "label": "Is Replaced",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-05-26 20:08:30.510474",
+ "modified": "2024-03-11 22:56:55.324796",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-shift_details",
+ "name": "Employee Checkin-is_replaced",
"no_copy": 0,
"non_negative": 0,
"options": null,
@@ -117,11 +118,12 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -137,40 +139,40 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-01-26 16:05:02.532340",
- "default": null,
- "depends_on": "eval:doc.shift_assignment",
+ "creation": "2023-08-02 09:00:58.483110",
+ "default": "Check-in Form",
+ "depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": "shift_assignment.shift_type",
- "fetch_if_empty": 1,
- "fieldname": "shift_type",
- "fieldtype": "Data",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "source",
+ "fieldtype": "Select",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 27,
- "ignore_user_permissions": 1,
+ "idx": 33,
+ "ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "roster_type",
+ "insert_after": "employee_checkin_issue",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Shift Type",
+ "label": "Source",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-05-26 20:09:19.795190",
+ "modified": "2023-08-02 09:00:58.483110",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-shift_type",
+ "name": "Employee Checkin-source",
"no_copy": 0,
"non_negative": 0,
- "options": null,
+ "options": "\nMobile App\nMobile Web\nCheck-in Form\nFrappe Page",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -185,6 +187,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 1,
"unique": 0,
"width": null
@@ -200,37 +203,37 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-01-26 16:05:02.397579",
+ "creation": "2023-06-09 05:21:24.630687",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "column_break_15",
- "fieldtype": "Column Break",
+ "fetch_from": "operations_role.post_abbrv",
+ "fetch_if_empty": 1,
+ "fieldname": "post_abbrv",
+ "fieldtype": "Data",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 23,
+ "idx": 27,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "company",
+ "insert_after": "operations_role",
"is_system_generated": 0,
"is_virtual": 0,
- "label": null,
+ "label": "Post Abbrv",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-05-26 20:09:43.875444",
+ "modified": "2023-06-09 05:21:24.630687",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-column_break_15",
+ "name": "Employee Checkin-post_abbrv",
"no_copy": 0,
"non_negative": 0,
"options": null,
@@ -243,11 +246,12 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -263,40 +267,40 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-01-26 16:05:02.669233",
+ "creation": "2023-06-09 05:20:54.998213",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "shift_permission",
+ "fetch_from": "shift_assignment.operations_role",
+ "fetch_if_empty": 1,
+ "fieldname": "operations_role",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 28,
+ "idx": 26,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "shift_type",
+ "insert_after": "column_break_15",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Shift Permission",
+ "label": "Operations Role",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-06-02 00:02:30.944886",
+ "modified": "2023-06-09 05:20:54.998213",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-shift_permission",
+ "name": "Employee Checkin-operations_role",
"no_copy": 0,
"non_negative": 0,
- "options": "Shift Permission",
+ "options": "Operations Role",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -311,6 +315,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -326,40 +331,40 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-01-26 16:05:02.797262",
- "default": "now",
+ "creation": "2023-06-09 03:37:57.748123",
+ "default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "actual_time",
- "fieldtype": "Datetime",
+ "fetch_from": "shift_assignment.company",
+ "fetch_if_empty": 1,
+ "fieldname": "company",
+ "fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 29,
+ "idx": 22,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "shift_permission",
+ "insert_after": "project",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Actual Time",
+ "label": "Company",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2020-06-02 21:47:57.757045",
+ "modified": "2023-06-09 03:37:57.748123",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-actual_time",
+ "name": "Employee Checkin-company",
"no_copy": 0,
"non_negative": 0,
- "options": null,
+ "options": "Company",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -374,6 +379,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -389,40 +395,40 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-05-10 11:21:36.399486",
+ "creation": "2023-06-09 03:34:51.630935",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "shift_assignment",
+ "fetch_from": "shift_assignment.project",
+ "fetch_if_empty": 1,
+ "fieldname": "project",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 18,
+ "idx": 21,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "shift_details",
- "is_system_generated": 1,
+ "insert_after": "operations_site",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": "Shift Assignment",
+ "label": "Project",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2022-05-10 11:21:36.399486",
+ "modified": "2023-06-09 03:34:51.630935",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-shift_assignment",
+ "name": "Employee Checkin-project",
"no_copy": 0,
"non_negative": 0,
- "options": "Shift Assignment",
+ "options": "Project",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -432,11 +438,12 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -452,41 +459,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-01-10 18:33:54.209569",
- "default": "0",
+ "creation": "2023-06-09 03:27:59.831109",
+ "default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "late_entry",
- "fieldtype": "Check",
+ "fetch_from": "shift_assignment.site",
+ "fetch_if_empty": 1,
+ "fieldname": "operations_site",
+ "fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 5,
+ "idx": 20,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "shift",
+ "insert_after": "operations_shift",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Late Entry",
+ "label": "Operations Site",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-01-10 18:33:54.209569",
- "modified_by": "e.anthony@one-fm.com",
+ "modified": "2023-06-09 03:27:59.831109",
+ "modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-late_entry",
+ "name": "Employee Checkin-operations_site",
"no_copy": 0,
"non_negative": 0,
- "options": null,
- "owner": "e.anthony@one-fm.com",
+ "options": "Operations Site",
+ "owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
@@ -500,6 +507,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -515,41 +523,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-01-10 19:21:18.836779",
+ "creation": "2023-03-04 16:23:07.886381",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "early_exit",
- "fieldtype": "Check",
+ "fetch_from": "shift_assignment.roster_type",
+ "fetch_if_empty": 1,
+ "fieldname": "roster_type",
+ "fieldtype": "Data",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 6,
+ "idx": 28,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "late_entry",
+ "insert_after": "post_abbrv",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Early Exit",
+ "label": "Roster Type",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-01-10 19:21:18.836779",
- "modified_by": "e.anthony@one-fm.com",
+ "modified": "2023-03-04 16:23:07.886381",
+ "modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-early_exit",
+ "name": "Employee Checkin-roster_type",
"no_copy": 0,
"non_negative": 0,
"options": null,
- "owner": "e.anthony@one-fm.com",
+ "owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
@@ -558,12 +566,13 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 1,
+ "read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
- "translatable": 0,
+ "sort_options": 0,
+ "translatable": 1,
"unique": 0,
"width": null
},
@@ -578,7 +587,7 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-01-24 11:06:17.669178",
+ "creation": "2023-02-02 11:01:34.236890",
"default": null,
"depends_on": null,
"description": null,
@@ -586,32 +595,32 @@
"dt": "Employee Checkin",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "employee_checkin_issue",
- "fieldtype": "Link",
+ "fieldname": "date",
+ "fieldtype": "Date",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 30,
+ "idx": 9,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "actual_time",
+ "in_standard_filter": 1,
+ "insert_after": "time",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Employee Checkin Issue",
+ "label": "Date",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-01-24 11:06:17.669178",
+ "modified": "2023-02-02 11:01:34.236890",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-employee_checkin_issue",
+ "name": "Employee Checkin-date",
"no_copy": 0,
"non_negative": 0,
- "options": "Employee Checkin Issue",
+ "options": null,
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -626,6 +635,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -641,7 +651,7 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-02-02 11:01:34.236890",
+ "creation": "2023-01-24 11:06:17.669178",
"default": null,
"depends_on": null,
"description": null,
@@ -649,32 +659,32 @@
"dt": "Employee Checkin",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "date",
- "fieldtype": "Date",
+ "fieldname": "employee_checkin_issue",
+ "fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 9,
+ "idx": 32,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
- "in_standard_filter": 1,
- "insert_after": "time",
+ "in_standard_filter": 0,
+ "insert_after": "actual_time",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Date",
+ "label": "Employee Checkin Issue",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-02-02 11:01:34.236890",
+ "modified": "2023-01-24 11:06:17.669178",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-date",
+ "name": "Employee Checkin-employee_checkin_issue",
"no_copy": 0,
"non_negative": 0,
- "options": null,
+ "options": "Employee Checkin Issue",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -689,6 +699,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -704,41 +715,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-03-04 16:23:07.886381",
+ "creation": "2023-01-10 19:21:18.836779",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": "shift_assignment.roster_type",
- "fetch_if_empty": 1,
- "fieldname": "roster_type",
- "fieldtype": "Data",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "early_exit",
+ "fieldtype": "Check",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 26,
+ "idx": 6,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "post_abbrv",
+ "insert_after": "late_entry",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Roster Type",
+ "label": "Early Exit",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-03-04 16:23:07.886381",
- "modified_by": "Administrator",
+ "modified": "2023-01-10 19:21:18.836779",
+ "modified_by": "e.anthony@one-fm.com",
"module": null,
- "name": "Employee Checkin-roster_type",
+ "name": "Employee Checkin-early_exit",
"no_copy": 0,
"non_negative": 0,
"options": null,
- "owner": "Administrator",
+ "owner": "e.anthony@one-fm.com",
"parent": null,
"parentfield": null,
"parenttype": null,
@@ -747,12 +758,13 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
- "translatable": 1,
+ "sort_options": 0,
+ "translatable": 0,
"unique": 0,
"width": null
},
@@ -767,41 +779,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-06-09 03:27:59.831109",
- "default": null,
+ "creation": "2023-01-10 18:33:54.209569",
+ "default": "0",
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": "shift_assignment.site",
- "fetch_if_empty": 1,
- "fieldname": "operations_site",
- "fieldtype": "Link",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "late_entry",
+ "fieldtype": "Check",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 20,
+ "idx": 5,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "operations_shift",
+ "insert_after": "shift",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Operations Site",
+ "label": "Late Entry",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-06-09 03:27:59.831109",
- "modified_by": "Administrator",
+ "modified": "2023-01-10 18:33:54.209569",
+ "modified_by": "e.anthony@one-fm.com",
"module": null,
- "name": "Employee Checkin-operations_site",
+ "name": "Employee Checkin-late_entry",
"no_copy": 0,
"non_negative": 0,
- "options": "Operations Site",
- "owner": "Administrator",
+ "options": null,
+ "owner": "e.anthony@one-fm.com",
"parent": null,
"parentfield": null,
"parenttype": null,
@@ -815,6 +827,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -830,40 +843,104 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-06-09 03:34:51.630935",
+ "creation": "2022-05-10 11:21:36.399486",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": "shift_assignment.project",
- "fetch_if_empty": 1,
- "fieldname": "project",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "shift_assignment",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 21,
+ "idx": 18,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "operations_site",
+ "insert_after": "shift_details",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Shift Assignment",
+ "length": 0,
+ "mandatory_depends_on": null,
+ "modified": "2022-05-10 11:21:36.399486",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Employee Checkin-shift_assignment",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Shift Assignment",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-01-26 16:05:02.797262",
+ "default": "now",
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Employee Checkin",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "actual_time",
+ "fieldtype": "Datetime",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 31,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "shift_permission",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Project",
+ "label": "Actual Time",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-06-09 03:34:51.630935",
+ "modified": "2020-06-02 21:47:57.757045",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-project",
+ "name": "Employee Checkin-actual_time",
"no_copy": 0,
"non_negative": 0,
- "options": "Project",
+ "options": null,
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -878,6 +955,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -893,40 +971,40 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-06-09 03:37:57.748123",
+ "creation": "2022-01-26 16:05:02.669233",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": "shift_assignment.company",
- "fetch_if_empty": 1,
- "fieldname": "company",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "shift_permission",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 22,
+ "idx": 30,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "project",
+ "insert_after": "shift_type",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Company",
+ "label": "Shift Permission",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-06-09 03:37:57.748123",
+ "modified": "2020-06-02 00:02:30.944886",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-company",
+ "name": "Employee Checkin-shift_permission",
"no_copy": 0,
"non_negative": 0,
- "options": "Company",
+ "options": "Shift Permission",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -941,6 +1019,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -956,40 +1035,40 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-06-09 05:20:54.998213",
+ "creation": "2022-01-26 16:05:02.397579",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": "shift_assignment.operations_role",
- "fetch_if_empty": 1,
- "fieldname": "operations_role",
- "fieldtype": "Link",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "column_break_15",
+ "fieldtype": "Column Break",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 24,
+ "idx": 25,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "column_break_15",
+ "insert_after": "replaced_employee_checkin",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Operations Role",
+ "label": null,
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-06-09 05:20:54.998213",
+ "modified": "2020-05-26 20:09:43.875444",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-operations_role",
+ "name": "Employee Checkin-column_break_15",
"no_copy": 0,
"non_negative": 0,
- "options": "Operations Role",
+ "options": null,
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -999,11 +1078,12 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 1,
+ "read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
@@ -1019,37 +1099,37 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-06-09 05:21:24.630687",
+ "creation": "2022-01-26 16:05:02.532340",
"default": null,
- "depends_on": null,
+ "depends_on": "eval:doc.shift_assignment",
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
- "fetch_from": "operations_role.post_abbrv",
+ "fetch_from": "shift_assignment.shift_type",
"fetch_if_empty": 1,
- "fieldname": "post_abbrv",
+ "fieldname": "shift_type",
"fieldtype": "Data",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 25,
- "ignore_user_permissions": 0,
+ "idx": 29,
+ "ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "operations_role",
+ "insert_after": "roster_type",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Post Abbrv",
+ "label": "Shift Type",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-06-09 05:21:24.630687",
+ "modified": "2020-05-26 20:09:19.795190",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-post_abbrv",
+ "name": "Employee Checkin-shift_type",
"no_copy": 0,
"non_negative": 0,
"options": null,
@@ -1067,7 +1147,8 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
- "translatable": 0,
+ "sort_options": 0,
+ "translatable": 1,
"unique": 0,
"width": null
},
@@ -1082,40 +1163,104 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2023-08-02 09:00:58.483110",
- "default": "Check-in Form",
+ "creation": "2022-01-26 16:05:01.508577",
+ "default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Employee Checkin",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "source",
- "fieldtype": "Select",
+ "fieldname": "shift_details",
+ "fieldtype": "Section Break",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 31,
+ "idx": 17,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "employee_checkin_issue",
+ "insert_after": "shift_actual_end",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Source",
+ "label": "Shift Details",
"length": 0,
"mandatory_depends_on": null,
- "modified": "2023-08-02 09:00:58.483110",
+ "modified": "2020-05-26 20:08:30.510474",
"modified_by": "Administrator",
"module": null,
- "name": "Employee Checkin-source",
+ "name": "Employee Checkin-shift_details",
"no_copy": 0,
"non_negative": 0,
- "options": "\nMobile App\nMobile Web\nCheck-in Form\nFrappe Page",
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-01-26 16:05:02.103679",
+ "default": null,
+ "depends_on": "eval:doc.shift_assignment",
+ "description": null,
+ "docstatus": 0,
+ "dt": "Employee Checkin",
+ "fetch_from": "shift_assignment.shift",
+ "fetch_if_empty": 1,
+ "fieldname": "operations_shift",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 19,
+ "ignore_user_permissions": 1,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "shift_assignment",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Operations Shift",
+ "length": 0,
+ "mandatory_depends_on": null,
+ "modified": "2020-05-26 19:06:49.143490",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Employee Checkin-operations_shift",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Operations Shift",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -1130,7 +1275,8 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
- "translatable": 1,
+ "sort_options": 0,
+ "translatable": 0,
"unique": 0,
"width": null
}
@@ -1139,6 +1285,32 @@
"doctype": "Employee Checkin",
"links": [],
"property_setters": [
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2024-03-25 01:53:05.703005",
+ "default_value": null,
+ "doc_type": "Employee Checkin",
+ "docstatus": 0,
+ "doctype_or_field": "DocType",
+ "field_name": null,
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-03-25 01:53:05.703005",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Employee Checkin-main-field_order",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "field_order",
+ "property_type": "Data",
+ "row_name": null,
+ "value": "[\"employee\", \"employee_name\", \"log_type\", \"shift\", \"late_entry\", \"early_exit\", \"column_break_4\", \"time\", \"date\", \"device_id\", \"skip_auto_attendance\", \"attendance\", \"shift_start\", \"shift_end\", \"shift_actual_start\", \"shift_actual_end\", \"shift_details\", \"shift_assignment\", \"operations_shift\", \"operations_site\", \"project\", \"company\", \"is_replaced\", \"replaced_employee_checkin\", \"column_break_15\", \"operations_role\", \"post_abbrv\", \"roster_type\", \"shift_type\", \"shift_permission\", \"actual_time\", \"employee_checkin_issue\", \"source\"]"
+ },
{
"_assign": null,
"_comments": null,
@@ -1635,4 +1807,4 @@
}
],
"sync_on_migrate": 1
-}
\ No newline at end of file
+}
diff --git a/one_fm/one_fm/custom/shift_assignment.json b/one_fm/one_fm/custom/shift_assignment.json
index 72dc5c66f8..43ba3c066d 100644
--- a/one_fm/one_fm/custom/shift_assignment.json
+++ b/one_fm/one_fm/custom/shift_assignment.json
@@ -11,7 +11,7 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-08 21:21:52.246455",
+ "creation": "2024-07-15 23:46:05.847965",
"default": null,
"depends_on": null,
"description": null,
@@ -19,32 +19,33 @@
"dt": "Shift Assignment",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "shift",
- "fieldtype": "Link",
+ "fieldname": "custom_day_off_ot",
+ "fieldtype": "Check",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 18,
+ "idx": 12,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "department",
+ "insert_after": "replaced_shift_assignment",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Shift",
+ "label": "Day Off OT",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2020-07-19 10:06:11.379683",
+ "modified": "2024-07-15 23:46:05.847965",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-shift",
+ "name": "Shift Assignment-custom_day_off_ot",
"no_copy": 0,
"non_negative": 0,
- "options": "Operations Shift",
+ "options": null,
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -59,6 +60,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
@@ -70,45 +72,46 @@
"_liked_by": null,
"_user_tags": null,
"allow_in_quick_entry": 0,
- "allow_on_submit": 1,
+ "allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-06 13:39:36.617647",
+ "creation": "2024-04-25 14:19:37.445452",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Shift Assignment",
- "fetch_from": "shift.site",
+ "fetch_from": "employee.employment_type",
"fetch_if_empty": 0,
- "fieldname": "site",
+ "fieldname": "custom_employment_type",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 4,
+ "idx": 3,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "shift_type",
- "is_system_generated": 1,
+ "insert_after": "employee_name",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": "Site",
+ "label": "Employment Type",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2020-10-06 13:39:36.617647",
+ "modified": "2024-04-25 14:19:37.445452",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-site",
+ "name": "Shift Assignment-custom_employment_type",
"no_copy": 0,
"non_negative": 0,
- "options": "Operations Site",
+ "options": "Employment Type",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -118,11 +121,12 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
@@ -139,40 +143,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-06 13:39:37.549249",
+ "creation": "2024-03-25 01:19:32.655200",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Shift Assignment",
- "fetch_from": "shift.project",
+ "fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "project",
+ "fieldname": "replaced_shift_assignment",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 5,
+ "idx": 12,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "site",
- "is_system_generated": 1,
+ "insert_after": "is_replaced",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": "Project",
+ "label": "Replaced Shift Assignment",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2020-10-06 13:39:37.549249",
- "modified_by": "Administrator",
+ "modified": "2024-03-25 01:19:32.655200",
+ "modified_by": "s.shaikh@one-fm.com",
"module": null,
- "name": "Shift Assignment-project",
+ "name": "Shift Assignment-replaced_shift_assignment",
"no_copy": 0,
"non_negative": 0,
- "options": "Project",
+ "options": "Shift Assignment",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -187,6 +192,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
@@ -203,40 +209,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-06 13:39:38.553796",
+ "creation": "2024-03-11 22:32:18.152808",
"default": null,
- "depends_on": null,
+ "depends_on": "eval: doc.is_replaced;",
"description": null,
"docstatus": 0,
"dt": "Shift Assignment",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "operations_role",
- "fieldtype": "Link",
+ "fieldname": "is_replaced",
+ "fieldtype": "Check",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 20,
+ "idx": 11,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "amended_from",
- "is_system_generated": 1,
+ "insert_after": "employee_schedule",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": "Operations Role",
+ "label": "Is Replaced",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2020-10-06 13:39:38.553796",
+ "modified": "2024-03-11 22:32:18.152808",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-operations_role",
+ "name": "Shift Assignment-is_replaced",
"no_copy": 0,
"non_negative": 0,
- "options": "Operations Role",
+ "options": null,
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -246,11 +253,12 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
@@ -267,40 +275,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2020-10-06 13:39:39.574629",
+ "creation": "2024-01-28 12:15:59.392296",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Shift Assignment",
- "fetch_from": "operations_role.post_abbrv",
+ "fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "post_abbrv",
- "fieldtype": "Data",
+ "fieldname": "employee_schedule",
+ "fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 21,
+ "idx": 10,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "operations_role",
- "is_system_generated": 1,
+ "insert_after": "site_location",
+ "is_system_generated": 0,
"is_virtual": 0,
- "label": "Post Abbreviation",
+ "label": "Employee Schedule",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2020-10-06 13:39:39.574629",
+ "modified": "2024-01-28 12:15:59.392296",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-post_abbrv",
+ "name": "Shift Assignment-employee_schedule",
"no_copy": 0,
"non_negative": 0,
- "options": null,
+ "options": "Employee Schedule",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -310,13 +319,14 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
- "translatable": 1,
+ "translatable": 0,
"unique": 0,
"width": null
},
@@ -331,40 +341,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2021-11-10 12:18:45.778534",
+ "creation": "2022-09-11 10:59:43.588068",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Shift Assignment",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "roster_type",
- "fieldtype": "Select",
+ "fetch_from": "site.site_location",
+ "fetch_if_empty": 1,
+ "fieldname": "site_location",
+ "fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 22,
+ "idx": 8,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "post_abbrv",
+ "insert_after": "shift_classification",
"is_system_generated": 1,
"is_virtual": 0,
- "label": "Roster Type",
+ "label": "Site Location",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2021-11-10 12:18:45.778534",
+ "modified": "2022-09-11 10:59:43.588068",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-roster_type",
+ "name": "Shift Assignment-site_location",
"no_copy": 0,
"non_negative": 0,
- "options": "Basic\nOver-Time",
+ "options": "Location",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -379,8 +390,9 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
- "translatable": 1,
+ "translatable": 0,
"unique": 0,
"width": null
},
@@ -393,39 +405,40 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "collapsible_depends_on": "",
+ "collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-06-09 12:10:56.437130",
+ "creation": "2022-07-05 09:57:27.270636",
"default": null,
- "depends_on": "eval: doc.shift_request;",
+ "depends_on": "shift",
"description": null,
"docstatus": 0,
"dt": "Shift Assignment",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "site_request",
- "fieldtype": "Section Break",
+ "fetch_from": "shift.shift_classification",
+ "fetch_if_empty": 1,
+ "fieldname": "shift_classification",
+ "fieldtype": "Data",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 23,
+ "idx": 7,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "roster_type",
+ "insert_after": "status",
"is_system_generated": 1,
"is_virtual": 0,
- "label": "Site Request",
+ "label": "Shift Classification",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2022-06-09 12:10:56.437130",
+ "modified": "2022-07-05 09:57:27.270636",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-site_request",
+ "name": "Shift Assignment-shift_classification",
"no_copy": 0,
"non_negative": 0,
"options": null,
@@ -438,13 +451,14 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
- "translatable": 0,
+ "translatable": 1,
"unique": 0,
"width": null
},
@@ -459,40 +473,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-06-09 12:10:56.624201",
+ "creation": "2022-07-05 09:43:25.363729",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Shift Assignment",
- "fetch_from": "shift_request.check_in_site",
+ "fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "check_in_site",
- "fieldtype": "Link",
+ "fieldname": "end_datetime",
+ "fieldtype": "Datetime",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 24,
+ "idx": 15,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "site_request",
+ "insert_after": "start_datetime",
"is_system_generated": 1,
"is_virtual": 0,
- "label": "Check In Site",
+ "label": "End Datetime",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2022-06-09 12:10:56.624201",
+ "modified": "2022-07-05 09:43:25.363729",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-check_in_site",
+ "name": "Shift Assignment-end_datetime",
"no_copy": 0,
"non_negative": 0,
- "options": "Location",
+ "options": null,
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -502,11 +517,12 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
@@ -523,7 +539,7 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-06-09 12:10:56.843708",
+ "creation": "2022-07-05 09:42:38.285022",
"default": null,
"depends_on": null,
"description": null,
@@ -531,29 +547,30 @@
"dt": "Shift Assignment",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "column_break_19",
- "fieldtype": "Column Break",
+ "fieldname": "start_datetime",
+ "fieldtype": "Datetime",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 25,
+ "idx": 14,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "check_in_site",
+ "insert_after": "end_date",
"is_system_generated": 1,
"is_virtual": 0,
- "label": null,
+ "label": "Start Datetime",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2022-06-09 12:10:56.843708",
+ "modified": "2022-07-05 09:42:38.285022",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-column_break_19",
+ "name": "Shift Assignment-start_datetime",
"no_copy": 0,
"non_negative": 0,
"options": null,
@@ -566,11 +583,12 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 0,
+ "read_only": 1,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
@@ -613,6 +631,7 @@
"is_virtual": 0,
"label": "Check Out Site",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
"modified": "2022-06-09 12:10:57.024639",
"modified_by": "Administrator",
@@ -635,6 +654,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
@@ -651,7 +671,7 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-07-05 09:42:38.285022",
+ "creation": "2022-06-09 12:10:56.843708",
"default": null,
"depends_on": null,
"description": null,
@@ -659,29 +679,30 @@
"dt": "Shift Assignment",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "start_datetime",
- "fieldtype": "Datetime",
+ "fieldname": "column_break_19",
+ "fieldtype": "Column Break",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 14,
+ "idx": 25,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "end_date",
+ "insert_after": "check_in_site",
"is_system_generated": 1,
"is_virtual": 0,
- "label": "Start Datetime",
+ "label": null,
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2022-07-05 09:42:38.285022",
+ "modified": "2022-06-09 12:10:56.843708",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-start_datetime",
+ "name": "Shift Assignment-column_break_19",
"no_copy": 0,
"non_negative": 0,
"options": null,
@@ -694,11 +715,12 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 1,
+ "read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
@@ -715,40 +737,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-07-05 09:43:25.363729",
+ "creation": "2022-06-09 12:10:56.624201",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Shift Assignment",
- "fetch_from": null,
+ "fetch_from": "shift_request.check_in_site",
"fetch_if_empty": 0,
- "fieldname": "end_datetime",
- "fieldtype": "Datetime",
+ "fieldname": "check_in_site",
+ "fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 15,
+ "idx": 24,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "start_datetime",
+ "insert_after": "site_request",
"is_system_generated": 1,
"is_virtual": 0,
- "label": "End Datetime",
+ "label": "Check In Site",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2022-07-05 09:43:25.363729",
+ "modified": "2022-06-09 12:10:56.624201",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-end_datetime",
+ "name": "Shift Assignment-check_in_site",
"no_copy": 0,
"non_negative": 0,
- "options": null,
+ "options": "Location",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -758,11 +781,12 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 1,
+ "read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
@@ -777,39 +801,40 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "collapsible_depends_on": null,
+ "collapsible_depends_on": "",
"columns": 0,
- "creation": "2022-07-05 09:57:27.270636",
+ "creation": "2022-06-09 12:10:56.437130",
"default": null,
- "depends_on": "shift",
+ "depends_on": "eval: doc.shift_request;",
"description": null,
"docstatus": 0,
"dt": "Shift Assignment",
- "fetch_from": "shift.shift_classification",
- "fetch_if_empty": 1,
- "fieldname": "shift_classification",
- "fieldtype": "Data",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "site_request",
+ "fieldtype": "Section Break",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 7,
+ "idx": 23,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "status",
+ "insert_after": "roster_type",
"is_system_generated": 1,
"is_virtual": 0,
- "label": "Shift Classification",
+ "label": "Site Request",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2022-07-05 09:57:27.270636",
+ "modified": "2022-06-09 12:10:56.437130",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-shift_classification",
+ "name": "Shift Assignment-site_request",
"no_copy": 0,
"non_negative": 0,
"options": null,
@@ -822,13 +847,14 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 1,
+ "read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
- "translatable": 1,
+ "translatable": 0,
"unique": 0,
"width": null
},
@@ -843,40 +869,41 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2022-09-11 10:59:43.588068",
+ "creation": "2021-11-10 12:18:45.778534",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Shift Assignment",
- "fetch_from": "site.site_location",
- "fetch_if_empty": 1,
- "fieldname": "site_location",
- "fieldtype": "Link",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "roster_type",
+ "fieldtype": "Select",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 8,
+ "idx": 22,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "shift_classification",
+ "insert_after": "post_abbrv",
"is_system_generated": 1,
"is_virtual": 0,
- "label": "Site Location",
+ "label": "Roster Type",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2022-09-11 10:59:43.588068",
+ "modified": "2021-11-10 12:18:45.778534",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-site_location",
+ "name": "Shift Assignment-roster_type",
"no_copy": 0,
"non_negative": 0,
- "options": "Location",
+ "options": "Basic\nOver-Time",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -891,6 +918,139 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2020-10-06 13:39:39.574629",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Assignment",
+ "fetch_from": "operations_role.post_abbrv",
+ "fetch_if_empty": 0,
+ "fieldname": "post_abbrv",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 21,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "operations_role",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Post Abbreviation",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2020-10-06 13:39:39.574629",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Assignment-post_abbrv",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2023-05-16 21:50:39.040758",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Assignment",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "post_type",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 26,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "check_out_site",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Post Type",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2020-10-06 13:39:38.553796",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Assignment-post_type",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Operations Role",
+ "owner": null,
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
@@ -907,7 +1067,7 @@
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
- "creation": "2024-01-28 12:15:59.392296",
+ "creation": "2020-10-06 13:39:38.553796",
"default": null,
"depends_on": null,
"description": null,
@@ -915,32 +1075,231 @@
"dt": "Shift Assignment",
"fetch_from": null,
"fetch_if_empty": 0,
- "fieldname": "employee_schedule",
+ "fieldname": "operations_role",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
- "idx": 9,
+ "idx": 20,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
- "insert_after": "site_location",
+ "insert_after": "amended_from",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Operations Role",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2020-10-06 13:39:38.553796",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Assignment-operations_role",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Operations Role",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2020-10-06 13:39:37.549249",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Assignment",
+ "fetch_from": "shift.project",
+ "fetch_if_empty": 0,
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 5,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "site",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Project",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2020-10-06 13:39:37.549249",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Assignment-project",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Project",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2020-10-06 13:39:36.617647",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Assignment",
+ "fetch_from": "shift.site",
+ "fetch_if_empty": 0,
+ "fieldname": "site",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 4,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "shift_type",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Site",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2020-10-06 13:39:36.617647",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Assignment-site",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Operations Site",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2020-10-08 21:21:52.246455",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Assignment",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "shift",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 22,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "department",
"is_system_generated": 0,
"is_virtual": 0,
- "label": "Employee Schedule",
+ "label": "Shift",
"length": 0,
+ "link_filters": null,
"mandatory_depends_on": null,
- "modified": "2024-01-28 12:15:59.392296",
+ "modified": "2020-07-19 10:06:11.379683",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-employee_schedule",
+ "name": "Shift Assignment-shift",
"no_copy": 0,
"non_negative": 0,
- "options": "Employee Schedule",
+ "options": "Operations Shift",
"owner": "Administrator",
"parent": null,
"parentfield": null,
@@ -950,11 +1309,12 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
- "read_only": 1,
+ "read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
+ "show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
@@ -970,44 +1330,70 @@
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-02-01 18:47:40.479659",
+ "creation": "2024-07-15 23:46:05.530118",
+ "default_value": null,
+ "doc_type": "Shift Assignment",
+ "docstatus": 0,
+ "doctype_or_field": "DocType",
+ "field_name": null,
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:46:05.530118",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Assignment-main-field_order",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "field_order",
+ "property_type": "Data",
+ "row_name": null,
+ "value": "[\"employee\", \"employee_name\", \"custom_employment_type\", \"shift_type\", \"site\", \"project\", \"status\", \"shift_classification\", \"site_location\", \"employee_schedule\", \"is_replaced\", \"replaced_shift_assignment\", \"day_off_ot\", \"column_break_3\", \"company\", \"start_date\", \"end_date\", \"start_datetime\", \"end_datetime\", \"shift_request\", \"department\", \"shift\", \"amended_from\", \"operations_role\", \"post_abbrv\", \"roster_type\", \"site_request\", \"check_in_site\", \"column_break_19\", \"check_out_site\", \"post_type\"]"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2020-10-08 21:25:24.944941",
"default_value": null,
"doc_type": "Shift Assignment",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "employee",
+ "field_name": "shift_type",
"idx": 0,
"is_system_generated": 0,
- "modified": "2023-02-01 18:47:40.479659",
- "modified_by": "e.anthony@one-fm.com",
+ "modified": "2024-07-15 23:39:03.480409",
+ "modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-employee-in_standard_filter",
- "owner": "e.anthony@one-fm.com",
+ "name": "Shift Assignment-shift_type-fetch_from",
+ "owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
- "property": "in_standard_filter",
- "property_type": "Check",
+ "property": "fetch_from",
+ "property_type": "Small Text",
"row_name": null,
- "value": "1"
+ "value": "shift.shift_type"
},
{
"_assign": null,
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-02-01 18:46:59.050508",
+ "creation": "2023-02-01 18:46:58.929151",
"default_value": null,
"doc_type": "Shift Assignment",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "end_date",
+ "field_name": "start_date",
"idx": 0,
"is_system_generated": 0,
- "modified": "2023-02-01 18:46:59.050508",
- "modified_by": "e.anthony@one-fm.com",
+ "modified": "2024-07-15 23:39:03.454811",
+ "modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-end_date-in_standard_filter",
+ "name": "Shift Assignment-start_date-in_standard_filter",
"owner": "e.anthony@one-fm.com",
"parent": null,
"parentfield": null,
@@ -1030,8 +1416,8 @@
"field_name": "end_date",
"idx": 0,
"is_system_generated": 0,
- "modified": "2023-02-01 18:46:59.001473",
- "modified_by": "e.anthony@one-fm.com",
+ "modified": "2024-07-15 23:39:03.427065",
+ "modified_by": "Administrator",
"module": null,
"name": "Shift Assignment-end_date-in_list_view",
"owner": "e.anthony@one-fm.com",
@@ -1048,18 +1434,18 @@
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2023-02-01 18:46:58.929151",
+ "creation": "2023-02-01 18:46:59.050508",
"default_value": null,
"doc_type": "Shift Assignment",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "start_date",
+ "field_name": "end_date",
"idx": 0,
"is_system_generated": 0,
- "modified": "2023-02-01 18:46:58.929151",
- "modified_by": "e.anthony@one-fm.com",
+ "modified": "2024-07-15 23:39:03.399366",
+ "modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-start_date-in_standard_filter",
+ "name": "Shift Assignment-end_date-in_standard_filter",
"owner": "e.anthony@one-fm.com",
"parent": null,
"parentfield": null,
@@ -1074,26 +1460,26 @@
"_comments": null,
"_liked_by": null,
"_user_tags": null,
- "creation": "2020-10-08 21:25:24.944941",
+ "creation": "2023-02-01 18:47:40.479659",
"default_value": null,
"doc_type": "Shift Assignment",
"docstatus": 0,
"doctype_or_field": "DocField",
- "field_name": "shift_type",
+ "field_name": "employee",
"idx": 0,
"is_system_generated": 0,
- "modified": "2020-05-31 22:44:02.659032",
+ "modified": "2024-07-15 23:39:03.373559",
"modified_by": "Administrator",
"module": null,
- "name": "Shift Assignment-shift_type-fetch_from",
- "owner": "Administrator",
+ "name": "Shift Assignment-employee-in_standard_filter",
+ "owner": "e.anthony@one-fm.com",
"parent": null,
"parentfield": null,
"parenttype": null,
- "property": "fetch_from",
- "property_type": "Small Text",
+ "property": "in_standard_filter",
+ "property_type": "Check",
"row_name": null,
- "value": "shift.shift_type"
+ "value": "1"
}
],
"sync_on_migrate": 1
diff --git a/one_fm/one_fm/custom/shift_request.json b/one_fm/one_fm/custom/shift_request.json
index 423942d7c0..6d4964b4dc 100644
--- a/one_fm/one_fm/custom/shift_request.json
+++ b/one_fm/one_fm/custom/shift_request.json
@@ -1,2182 +1,2503 @@
{
- "custom_fields": [
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2024-06-10 10:54:45.691280",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "custom_shift_approvers",
- "fieldtype": "Table MultiSelect",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 21,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "company_name",
- "is_system_generated": 0,
- "is_virtual": 0,
- "label": "Shift Approvers",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2024-06-10 10:54:45.691280",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-custom_shift_approvers",
- "no_copy": 0,
- "non_negative": 0,
- "options": "Shift Request Approvers",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2023-09-07 09:46:46.735944",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "site.project",
- "fetch_if_empty": 1,
- "fieldname": "project",
- "fieldtype": "Link",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 10,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "site",
- "is_system_generated": 0,
- "is_virtual": 0,
- "label": "Project",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2023-09-07 09:46:46.735944",
- "modified_by": "administrator",
- "module": null,
- "name": "Shift Request-project",
- "no_copy": 0,
- "non_negative": 0,
- "options": "Project",
- "owner": "administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2023-08-27 12:29:23.451324",
- "default": "0",
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "assign_day_off",
- "fieldtype": "Check",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 6,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "employee_name",
- "is_system_generated": 0,
- "is_virtual": 0,
- "label": "Assign Day Off",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2023-08-27 12:29:23.451324",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-assign_day_off",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2023-08-24 14:42:29.507724",
- "default": "",
- "depends_on": "eval:doc.assign_day_off == 0;",
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "operations_shift.site",
- "fetch_if_empty": 0,
- "fieldname": "site",
- "fieldtype": "Link",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 9,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "shift",
- "is_system_generated": 0,
- "is_virtual": 0,
- "label": "Site",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2023-08-24 14:42:29.507724",
- "modified_by": "saoud@one-fm.com",
- "module": null,
- "name": "Shift Request-site",
- "no_copy": 0,
- "non_negative": 0,
- "options": "Operations Site",
- "owner": "saoud@one-fm.com",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2023-08-17 09:01:43.226555",
- "default": null,
- "depends_on": "eval:doc.assign_day_off == 0;",
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "roster_type",
- "fieldtype": "Select",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 19,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "operations_role",
- "is_system_generated": 0,
- "is_virtual": 0,
- "label": "Roster Type",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2023-08-17 09:01:43.226555",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-roster_type",
- "no_copy": 0,
- "non_negative": 0,
- "options": "Basic\nOver-Time",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 1,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2023-08-13 11:13:58.647338",
- "default": null,
- "depends_on": "eval:doc.assign_day_off == 0;",
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "operations_role",
- "fieldtype": "Link",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 18,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "to_date",
- "is_system_generated": 0,
- "is_virtual": 0,
- "label": "Operations Role",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": "eval:doc.assign_day_off == 0;",
- "modified": "2023-08-13 11:13:58.647338",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-operations_role",
- "no_copy": 0,
- "non_negative": 0,
- "options": "Operations Role",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-10-25 19:22:07.712273",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "employee.company",
- "fetch_if_empty": 0,
- "fieldname": "company_name",
- "fieldtype": "Link",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 20,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "roster_type",
- "is_system_generated": 0,
- "is_virtual": 0,
- "label": "Company Name",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-10-25 19:22:07.712273",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-company_name",
- "no_copy": 0,
- "non_negative": 0,
- "options": "Company",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-10-25 19:22:07.480541",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "approver",
- "fetch_if_empty": 0,
- "fieldname": "shift_approver",
- "fieldtype": "Link",
- "hidden": 1,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 22,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "custom_shift_approvers",
- "is_system_generated": 0,
- "is_virtual": 0,
- "label": "Shift Approver",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-10-25 19:22:07.480541",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-shift_approver",
- "no_copy": 0,
- "non_negative": 0,
- "options": "User",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-10-25 19:17:33.755555",
- "default": null,
- "depends_on": "eval:doc.assign_day_off == 0;",
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "operations_shift.shift_type",
- "fetch_if_empty": 0,
- "fieldname": "shift",
- "fieldtype": "Link",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 8,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "operations_shift",
- "is_system_generated": 0,
- "is_virtual": 0,
- "label": "Shift",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-10-25 19:17:33.755555",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-shift",
- "no_copy": 0,
- "non_negative": 0,
- "options": "Shift Type",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-09-15 14:47:57.229223",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "check_in_site.geofence_radius",
- "fetch_if_empty": 0,
- "fieldname": "checkout_radius",
- "fieldtype": "Data",
- "hidden": 1,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 34,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "checkout_latitude",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "CheckOut Radius",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-09-15 14:47:57.229223",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-checkout_radius",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 1,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-09-15 14:47:28.051018",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "check_in_site.geofence_radius",
- "fetch_if_empty": 0,
- "fieldname": "checkin_radius",
- "fieldtype": "Data",
- "hidden": 1,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 28,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "checkin_latitude",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "Checkin Radius",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-09-15 14:47:28.051018",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-checkin_radius",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 1,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-09-15 14:32:22.655683",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "check_out_site.longitude",
- "fetch_if_empty": 0,
- "fieldname": "checkout_longitude",
- "fieldtype": "Data",
- "hidden": 1,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 32,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "check_out_site",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "CheckOut Longitude",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-09-15 14:32:22.655683",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-checkout_longitude",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 1,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-09-15 14:32:22.431809",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "check_out_site.latitude",
- "fetch_if_empty": 0,
- "fieldname": "checkout_latitude",
- "fieldtype": "Data",
- "hidden": 1,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 33,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "checkout_longitude",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "CheckOut Latitude",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-09-15 14:32:22.431809",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-checkout_latitude",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 1,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-09-15 14:32:22.211146",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "check_in_site.latitude",
- "fetch_if_empty": 0,
- "fieldname": "checkin_latitude",
- "fieldtype": "Data",
- "hidden": 1,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 27,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "checkin_longitude",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "Checkin Latitude",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-09-15 14:32:22.211146",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-checkin_latitude",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 1,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-09-15 14:32:21.865468",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "check_in_site.longitude",
- "fetch_if_empty": 0,
- "fieldname": "checkin_longitude",
- "fieldtype": "Data",
- "hidden": 1,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 26,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "check_in_site",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "Checkin Longitude",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-09-15 14:32:21.865468",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-checkin_longitude",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 1,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-09-14 08:29:37.811938",
- "default": null,
- "depends_on": "eval: doc.check_out_site;",
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "checkout_map_html",
- "fieldtype": "HTML",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 35,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "checkout_radius",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "Checkout Map HTML",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-09-14 08:29:37.811938",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-checkout_map_html",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-09-14 08:29:37.538698",
- "default": null,
- "depends_on": "eval: doc.check_in_site;",
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "checkin_map_html",
- "fieldtype": "HTML",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 29,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "checkin_radius",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "Checkin Map HTML",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-09-14 08:29:37.538698",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-checkin_map_html",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-09-14 08:27:55.575742",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "checkout_map",
- "fieldtype": "Geolocation",
- "hidden": 1,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 36,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "checkout_map_html",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "Checkout Map",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-09-14 08:27:55.575742",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-checkout_map",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-09-14 08:27:55.168412",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "checkin_map",
- "fieldtype": "Geolocation",
- "hidden": 1,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 37,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "checkout_map",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "Checkin Map",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-09-14 08:27:55.168412",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-checkin_map",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-09-12 12:03:55.326660",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "employee.employee_name",
- "fetch_if_empty": 1,
- "fieldname": "title",
- "fieldtype": "Data",
- "hidden": 1,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 3,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "workflow_state",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "title",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-09-12 12:03:55.326660",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-title",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-07-16 00:47:33.304148",
- "default": null,
- "depends_on": "eval:doc.docstatus > 0",
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "update_request",
- "fieldtype": "Check",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 22,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "shift_approver",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "Update Request",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-07-16 00:47:33.304148",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-update_request",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-06-09 11:57:59.882895",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "column_break_15",
- "fieldtype": "Column Break",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 30,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "checkin_map_html",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": null,
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-06-09 11:57:59.882895",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-column_break_15",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-06-09 11:51:59.922003",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "site.site_location",
- "fetch_if_empty": 1,
- "fieldname": "check_out_site",
- "fieldtype": "Link",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 31,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "column_break_15",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "Check Out Site",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-06-09 11:51:59.922003",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-check_out_site",
- "no_copy": 0,
- "non_negative": 0,
- "options": "Location",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-06-09 11:51:59.742040",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "site.site_location",
- "fetch_if_empty": 1,
- "fieldname": "check_in_site",
- "fieldtype": "Link",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 25,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "site_request",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "Check In Site",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-06-09 11:51:59.742040",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-check_in_site",
- "no_copy": 0,
- "non_negative": 0,
- "options": "Location",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-06-09 11:51:59.517107",
- "default": null,
- "depends_on": "eval:doc.assign_day_off == 0;",
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "site_request",
- "fieldtype": "Section Break",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 24,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "amended_from",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "Site Request",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-06-09 11:51:59.517107",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-site_request",
- "no_copy": 0,
- "non_negative": 0,
- "options": null,
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-06-05 09:26:05.254538",
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": null,
- "fetch_if_empty": 0,
- "fieldname": "workflow_state",
- "fieldtype": "Link",
- "hidden": 1,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 2,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "shift_type",
- "is_system_generated": 1,
- "is_virtual": 0,
- "label": "Workflow State",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": null,
- "modified": "2022-06-05 09:26:05.254538",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-workflow_state",
- "no_copy": 1,
- "non_negative": 0,
- "options": "Workflow State",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "columns": 0,
- "creation": "2022-01-26 16:04:58.243003",
- "default": null,
- "depends_on": "eval:doc.assign_day_off == 0;",
- "description": null,
- "docstatus": 0,
- "dt": "Shift Request",
- "fetch_from": "employee.shift",
- "fetch_if_empty": 1,
- "fieldname": "operations_shift",
- "fieldtype": "Link",
- "hidden": 0,
- "hide_border": 0,
- "hide_days": 0,
- "hide_seconds": 0,
- "idx": 7,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_preview": 0,
- "in_standard_filter": 0,
- "insert_after": "assign_day_off",
- "is_system_generated": 0,
- "is_virtual": 0,
- "label": "Operations Shift",
- "length": 0,
- "link_filters": null,
- "mandatory_depends_on": "eval:doc.assign_day_off == 0;",
- "modified": "2020-06-09 11:39:10.240577",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-operations_shift",
- "no_copy": 0,
- "non_negative": 0,
- "options": "Operations Shift",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "read_only_depends_on": null,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "show_dashboard": 0,
- "sort_options": 0,
- "translatable": 0,
- "unique": 0,
- "width": null
- }
- ],
- "custom_perms": [],
- "doctype": "Shift Request",
- "links": [],
- "property_setters": [
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:30.413468",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "approver",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.526227",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-approver-ignore_user_permissions",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "ignore_user_permissions",
- "property_type": "Check",
- "row_name": null,
- "value": "1"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:30.434921",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "status",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.514474",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-status-read_only",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "read_only",
- "property_type": "Check",
- "row_name": null,
- "value": "1"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:30.458234",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "company",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.501617",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-company-fetch_from",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "fetch_from",
- "property_type": "Small Text",
- "row_name": null,
- "value": "employee.company"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:30.478227",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "company",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.482857",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-company-read_only",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "read_only",
- "property_type": "Check",
- "row_name": null,
- "value": "1"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:30.499959",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "approver",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.460702",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-approver-fetch_from",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "fetch_from",
- "property_type": "Small Text",
- "row_name": null,
- "value": ""
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:30.519702",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "approver",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.447888",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-approver-fetch_if_empty",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "fetch_if_empty",
- "property_type": "Check",
- "row_name": null,
- "value": "0"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:30.542631",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "shift_type",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.434958",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-shift_type-Hidden",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "Hidden",
- "property_type": null,
- "row_name": null,
- "value": "1"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:30.587785",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "company",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.422074",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-company-Hidden",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "Hidden",
- "property_type": "Check",
- "row_name": null,
- "value": "1"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:30.563579",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "approver",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.409950",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-approver-Hidden",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "Hidden",
- "property_type": "check",
- "row_name": null,
- "value": "1"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:30.608274",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "approver",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.397840",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-approver-read_only",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "read_only",
- "property_type": "Check",
- "row_name": null,
- "value": "1"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:31.406572",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "shift_type",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.385128",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-shift_type-fetch_from",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "fetch_from",
- "property_type": "Small Text",
- "row_name": null,
- "value": "operations_shift.shift_type"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:31.430007",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "shift_type",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.372817",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-shift_type-depends_on",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "depends_on",
- "property_type": "Data",
- "row_name": null,
- "value": "eval:doc.assign_day_off == 0;"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:31.449267",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "shift_type",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.361228",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-shift_type-read_only",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "read_only",
- "property_type": "Check",
- "row_name": null,
- "value": "1"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:31.610534",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocType",
- "field_name": null,
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.347720",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-main-field_order",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "field_order",
- "property_type": "Data",
- "row_name": null,
- "value": "[\"shift_type\", \"workflow_state\", \"title\", \"employee\", \"employee_name\", \"assign_day_off\", \"operations_shift\", \"shift\", \"site\", \"project\", \"department\", \"status\", \"column_break_4\", \"company\", \"approver\", \"from_date\", \"to_date\", \"operations_role\", \"roster_type\", \"company_name\", \"shift_approver\", \"update_request\", \"amended_from\", \"site_request\", \"check_in_site\", \"checkin_longitude\", \"checkin_latitude\", \"checkin_radius\", \"checkin_map_html\", \"column_break_15\", \"check_out_site\", \"checkout_longitude\", \"checkout_latitude\", \"checkout_radius\", \"checkout_map_html\", \"checkout_map\", \"checkin_map\"]"
- },
- {
- "_assign": null,
- "_comments": null,
- "_liked_by": null,
- "_user_tags": null,
- "creation": "2024-06-12 10:53:31.630157",
- "default_value": null,
- "doc_type": "Shift Request",
- "docstatus": 0,
- "doctype_or_field": "DocField",
- "field_name": "employee",
- "idx": 0,
- "is_system_generated": 0,
- "modified": "2024-06-11 10:59:17.335897",
- "modified_by": "Administrator",
- "module": null,
- "name": "Shift Request-employee-ignore_user_permissions",
- "owner": "Administrator",
- "parent": null,
- "parentfield": null,
- "parenttype": null,
- "property": "ignore_user_permissions",
- "property_type": "Check",
- "row_name": null,
- "value": "1"
- }
- ],
- "sync_on_migrate": 1
-}
\ No newline at end of file
+ "custom_fields": [
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2024-06-19 17:55:38.138857",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "replaced_employee.employee_name",
+ "fetch_if_empty": 0,
+ "fieldname": "custom_replaced_employee_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 9,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "replaced_employee",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Replaced Employee Name",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2024-06-19 17:55:38.138857",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-custom_replaced_employee_name",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2024-03-25 00:34:36.720161",
+ "default": null,
+ "depends_on": "eval:doc.purpose == 'Replace Existing Assignment';",
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "replaced_employee",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 8,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "purpose",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Replaced Employee",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": "eval:doc.purpose == 'Replace Existing Assignment';",
+ "modified": "2024-03-25 00:34:36.720161",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-replaced_employee",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Employee",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2024-03-12 11:43:09.115655",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "purpose",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 6,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "employee_name",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Purpose",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2024-03-12 11:43:09.115655",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-purpose",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": " \nAssign Day Off\nAssign Unrostered Employee\nReplace Existing Assignment\nUpdate Existing Assignment\nDay Off Overtime",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2023-09-07 09:46:46.735944",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "site.project",
+ "fetch_if_empty": 1,
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 13,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "site",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Project",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2023-09-07 09:46:46.735944",
+ "modified_by": "administrator",
+ "module": null,
+ "name": "Shift Request-project",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Project",
+ "owner": "administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2023-08-24 14:42:29.507724",
+ "default": "",
+ "depends_on": "eval:doc.purpose != 'Assign Day Off'",
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "operations_shift.site",
+ "fetch_if_empty": 0,
+ "fieldname": "site",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 12,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "shift",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Site",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2023-08-24 14:42:29.507724",
+ "modified_by": "saoud@one-fm.com",
+ "module": null,
+ "name": "Shift Request-site",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Operations Site",
+ "owner": "saoud@one-fm.com",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2023-08-17 09:01:43.226555",
+ "default": null,
+ "depends_on": "eval:doc.purpose != 'Assign Day Off';",
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "roster_type",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 21,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "operations_role",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Roster Type",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2023-08-17 09:01:43.226555",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-roster_type",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Basic\nOver-Time",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2023-08-13 11:13:58.647338",
+ "default": null,
+ "depends_on": "eval:doc.purpose != 'Assign Day Off';",
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 1,
+ "fieldname": "operations_role",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 20,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "to_date",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Operations Role",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": "eval:doc.purpose != 'Assign Day Off';",
+ "modified": "2023-08-13 11:13:58.647338",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-operations_role",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Operations Role",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-10-25 19:22:07.712273",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "employee.company",
+ "fetch_if_empty": 0,
+ "fieldname": "company_name",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 22,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "roster_type",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Company Name",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-10-25 19:22:07.712273",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-company_name",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Company",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-10-25 19:22:07.480541",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "approver",
+ "fetch_if_empty": 0,
+ "fieldname": "shift_approver",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 23,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "company_name",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Shift Approver",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-10-25 19:22:07.480541",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-shift_approver",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "User",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-10-25 19:17:33.755555",
+ "default": null,
+ "depends_on": "eval:doc.purpose != 'Assign Day Off'",
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "operations_shift.shift_type",
+ "fetch_if_empty": 0,
+ "fieldname": "shift",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 11,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "operations_shift",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Shift",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-10-25 19:17:33.755555",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-shift",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Shift Type",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-09-15 14:47:57.229223",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "check_in_site.geofence_radius",
+ "fetch_if_empty": 0,
+ "fieldname": "checkout_radius",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 34,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "checkout_latitude",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "CheckOut Radius",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-09-15 14:47:57.229223",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-checkout_radius",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-09-15 14:47:28.051018",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "check_in_site.geofence_radius",
+ "fetch_if_empty": 0,
+ "fieldname": "checkin_radius",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 28,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "checkin_latitude",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Checkin Radius",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-09-15 14:47:28.051018",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-checkin_radius",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-09-15 14:32:22.655683",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "check_out_site.longitude",
+ "fetch_if_empty": 0,
+ "fieldname": "checkout_longitude",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 32,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "check_out_site",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "CheckOut Longitude",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-09-15 14:32:22.655683",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-checkout_longitude",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-09-15 14:32:22.431809",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "check_out_site.latitude",
+ "fetch_if_empty": 0,
+ "fieldname": "checkout_latitude",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 33,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "checkout_longitude",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "CheckOut Latitude",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-09-15 14:32:22.431809",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-checkout_latitude",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-09-15 14:32:22.211146",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "check_in_site.latitude",
+ "fetch_if_empty": 0,
+ "fieldname": "checkin_latitude",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 27,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "checkin_longitude",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Checkin Latitude",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-09-15 14:32:22.211146",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-checkin_latitude",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-09-15 14:32:21.865468",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "check_in_site.longitude",
+ "fetch_if_empty": 0,
+ "fieldname": "checkin_longitude",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 26,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "check_in_site",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Checkin Longitude",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-09-15 14:32:21.865468",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-checkin_longitude",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-09-14 08:29:37.811938",
+ "default": null,
+ "depends_on": "eval: doc.check_out_site;",
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "checkout_map_html",
+ "fieldtype": "HTML",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 35,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "checkout_radius",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Checkout Map HTML",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-09-14 08:29:37.811938",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-checkout_map_html",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-09-14 08:29:37.538698",
+ "default": null,
+ "depends_on": "eval: doc.check_in_site;",
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "checkin_map_html",
+ "fieldtype": "HTML",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 29,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "checkin_radius",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Checkin Map HTML",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-09-14 08:29:37.538698",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-checkin_map_html",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-09-14 08:27:55.575742",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "checkout_map",
+ "fieldtype": "Geolocation",
+ "hidden": 1,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 36,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "checkout_map_html",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Checkout Map",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-09-14 08:27:55.575742",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-checkout_map",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-09-14 08:27:55.168412",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "checkin_map",
+ "fieldtype": "Geolocation",
+ "hidden": 1,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 37,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "checkout_map",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Checkin Map",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-09-14 08:27:55.168412",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-checkin_map",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-09-12 12:03:55.326660",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "employee.employee_name",
+ "fetch_if_empty": 1,
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 3,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "workflow_state",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "title",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-09-12 12:03:55.326660",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-title",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-07-16 00:47:33.304148",
+ "default": null,
+ "depends_on": "eval:doc.docstatus > 0",
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "update_request",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 22,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "shift_approver",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Update Request",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-07-16 00:47:33.304148",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-update_request",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-06-09 11:57:59.882895",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "column_break_15",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 30,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "checkin_map_html",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": null,
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-06-09 11:57:59.882895",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-column_break_15",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-06-09 11:51:59.922003",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "site.site_location",
+ "fetch_if_empty": 1,
+ "fieldname": "check_out_site",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 31,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "column_break_15",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Check Out Site",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-06-09 11:51:59.922003",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-check_out_site",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Location",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-06-09 11:51:59.742040",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "site.site_location",
+ "fetch_if_empty": 1,
+ "fieldname": "check_in_site",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 25,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "site_request",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Check In Site",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-06-09 11:51:59.742040",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-check_in_site",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Location",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-06-09 11:51:59.517107",
+ "default": null,
+ "depends_on": "eval:doc.purpose != 'Assign Day Off'",
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "site_request",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 24,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "amended_from",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Site Request",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-06-09 11:51:59.517107",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-site_request",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-06-05 09:26:05.254538",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "workflow_state",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 2,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "shift_type",
+ "is_system_generated": 1,
+ "is_virtual": 0,
+ "label": "Workflow State",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2022-06-05 09:26:05.254538",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-workflow_state",
+ "no_copy": 1,
+ "non_negative": 0,
+ "options": "Workflow State",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2022-01-26 16:04:58.243003",
+ "default": null,
+ "depends_on": "eval:doc.purpose != 'Assign Day Off';",
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": "employee.shift",
+ "fetch_if_empty": 1,
+ "fieldname": "operations_shift",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 10,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "custom_replaced_employee_name",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Operations Shift",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": "eval:doc.purpose != 'Assign Day Off';",
+ "modified": "2020-06-09 11:39:10.240577",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-operations_shift",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Operations Shift",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "creation": "2024-06-10 10:54:45.691280",
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "dt": "Shift Request",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "custom_shift_approvers",
+ "fieldtype": "Table MultiSelect",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "idx": 22,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "shift_approver",
+ "is_system_generated": 0,
+ "is_virtual": 0,
+ "label": "Shift Approvers",
+ "length": 0,
+ "link_filters": null,
+ "mandatory_depends_on": null,
+ "modified": "2024-06-10 10:54:45.691280",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-custom_shift_approvers",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": "Shift Request Approvers",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 1,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "show_dashboard": 0,
+ "sort_options": 0,
+ "translatable": 0,
+ "unique": 0,
+ "width": null
+ }
+ ],
+ "custom_perms": [
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "amend": 0,
+ "cancel": 0,
+ "create": 1,
+ "creation": "2024-07-15 12:14:46.536113",
+ "delete": 0,
+ "docstatus": 0,
+ "email": 1,
+ "export": 1,
+ "idx": 0,
+ "if_owner": 0,
+ "import": 0,
+ "modified": "2022-06-21 08:29:12.248404",
+ "modified_by": "Administrator",
+ "name": "88b8d0b4bb",
+ "owner": "Administrator",
+ "parent": "Shift Request",
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR User",
+ "select": 0,
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "creation": "2024-07-15 12:14:44.268206",
+ "delete": 1,
+ "docstatus": 0,
+ "email": 1,
+ "export": 1,
+ "idx": 0,
+ "if_owner": 0,
+ "import": 0,
+ "modified": "2022-06-21 08:29:12.325404",
+ "modified_by": "Administrator",
+ "name": "95852459e8",
+ "owner": "Administrator",
+ "parent": "Shift Request",
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR Manager",
+ "select": 0,
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "amend": 0,
+ "cancel": 0,
+ "create": 1,
+ "creation": "2024-07-15 12:14:40.430392",
+ "delete": 0,
+ "docstatus": 0,
+ "email": 1,
+ "export": 1,
+ "idx": 0,
+ "if_owner": 0,
+ "import": 0,
+ "modified": "2022-06-21 08:29:12.331484",
+ "modified_by": "Administrator",
+ "name": "2fbdd67c34",
+ "owner": "Administrator",
+ "parent": "Shift Request",
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Employee",
+ "select": 0,
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "creation": "2024-07-15 12:14:35.753245",
+ "delete": 1,
+ "docstatus": 0,
+ "email": 1,
+ "export": 1,
+ "idx": 0,
+ "if_owner": 0,
+ "import": 0,
+ "modified": "2023-10-01 10:40:24.375344",
+ "modified_by": "Administrator",
+ "name": "b12f17194f",
+ "owner": "Administrator",
+ "parent": "Shift Request",
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 0,
+ "role": "Employee Self Service",
+ "select": 0,
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "doctype": "Shift Request",
+ "links": [],
+ "property_setters": [
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2024-07-15 23:42:37.881380",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "shift_type",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:42:37.881380",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-shift_type-mandatory_depends_on",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "mandatory_depends_on",
+ "property_type": "Data",
+ "row_name": null,
+ "value": "eval:doc.purpose != 'Assign Day Off';"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2024-01-04 11:32:05.704330",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "shift_type",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:02.223831",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-shift_type-mandatory_depends_on",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "mandatory_depends_on",
+ "property_type": "Data",
+ "row_name": null,
+ "value": "eval:doc.purpose != 'Assign Day Off';"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2024-01-04 11:32:05.704330",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "employee",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:02.196761",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-employee-ignore_user_permissions",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "ignore_user_permissions",
+ "property_type": "Check",
+ "row_name": null,
+ "value": "1"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-12-03 13:28:16.705746",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "shift_type",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:02.170543",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-shift_type-read_only",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "read_only",
+ "property_type": "Check",
+ "row_name": null,
+ "value": "1"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-12-03 13:28:16.557588",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "shift_type",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:02.143369",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-shift_type-depends_on",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "depends_on",
+ "property_type": "Data",
+ "row_name": null,
+ "value": "eval:doc.purpose != 'Assign Day Off';"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-12-03 13:24:51.627067",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "shift_type",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:02.116838",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-shift_type-fetch_from",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "fetch_from",
+ "property_type": "Small Text",
+ "row_name": null,
+ "value": "operations_shift.shift_type"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-08-15 08:25:24.328373",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "approver",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:02.090167",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-approver-read_only",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "read_only",
+ "property_type": "Check",
+ "row_name": null,
+ "value": "1"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-08-15 08:25:24.296662",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "approver",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:02.064144",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-approver-Hidden",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "Hidden",
+ "property_type": "check",
+ "row_name": null,
+ "value": "1"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-08-15 08:25:24.311426",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "company",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:02.037445",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-company-Hidden",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "Hidden",
+ "property_type": "Check",
+ "row_name": null,
+ "value": "1"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-08-15 08:25:24.282157",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "shift_type",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:02.005395",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-shift_type-Hidden",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "Hidden",
+ "property_type": null,
+ "row_name": null,
+ "value": "1"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-08-15 08:25:24.267657",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "approver",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:01.979549",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-approver-fetch_if_empty",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "fetch_if_empty",
+ "property_type": "Check",
+ "row_name": null,
+ "value": "0"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-08-15 08:25:24.252811",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "approver",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:01.951776",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-approver-fetch_from",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "fetch_from",
+ "property_type": "Small Text",
+ "row_name": null,
+ "value": ""
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-08-15 08:25:24.222399",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "company",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:01.924818",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-company-read_only",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "read_only",
+ "property_type": "Check",
+ "row_name": null,
+ "value": "1"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-08-15 08:25:24.207824",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "company",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:01.897645",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-company-fetch_from",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "fetch_from",
+ "property_type": "Small Text",
+ "row_name": null,
+ "value": "employee.company"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-08-15 08:25:24.193261",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "status",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:01.870953",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-status-read_only",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "read_only",
+ "property_type": "Check",
+ "row_name": null,
+ "value": "1"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2023-08-15 08:25:24.163911",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocField",
+ "field_name": "approver",
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 23:39:01.844511",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-approver-ignore_user_permissions",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "ignore_user_permissions",
+ "property_type": "Check",
+ "row_name": null,
+ "value": "1"
+ },
+ {
+ "_assign": null,
+ "_comments": null,
+ "_liked_by": null,
+ "_user_tags": null,
+ "creation": "2024-06-19 17:55:42.044477",
+ "default_value": null,
+ "doc_type": "Shift Request",
+ "docstatus": 0,
+ "doctype_or_field": "DocType",
+ "field_name": null,
+ "idx": 0,
+ "is_system_generated": 0,
+ "modified": "2024-07-15 12:15:40.541256",
+ "modified_by": "Administrator",
+ "module": null,
+ "name": "Shift Request-main-field_order",
+ "owner": "Administrator",
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "property": "field_order",
+ "property_type": "Data",
+ "row_name": null,
+ "value": "[\"shift_type\", \"workflow_state\", \"title\", \"employee\", \"employee_name\", \"purpose\", \"replaced_employee\", \"custom_replaced_employee_name\", \"operations_shift\", \"shift\", \"site\", \"project\", \"department\", \"column_break_4\", \"company\", \"approver\", \"from_date\", \"to_date\", \"operations_role\", \"roster_type\", \"company_name\", \"shift_approver\", \"custom_shift_approvers\", \"update_request\", \"status\", \"amended_from\", \"site_request\", \"check_in_site\", \"checkin_longitude\", \"checkin_latitude\", \"checkin_radius\", \"checkin_map_html\", \"column_break_15\", \"check_out_site\", \"checkout_longitude\", \"checkout_latitude\", \"checkout_radius\", \"checkout_map_html\", \"checkout_map\", \"checkin_map\"]"
+ }
+ ],
+ "sync_on_migrate": 1
+ }
diff --git a/one_fm/one_fm/doctype/attendance_check/attendance_check.js b/one_fm/one_fm/doctype/attendance_check/attendance_check.js
index d77dc099a0..766fc1093e 100644
--- a/one_fm/one_fm/doctype/attendance_check/attendance_check.js
+++ b/one_fm/one_fm/doctype/attendance_check/attendance_check.js
@@ -18,10 +18,17 @@ frappe.ui.form.on('Attendance Check', {
}
}
},
-
+ attendance_status: function(frm){
+ if (frm.doc.attendance_status != "Present"){
+ frm.doc.justification = ""
+ }
+ },
});
+
+
+
var allow_only_attendance_manager = (frm) => {
const time_difference_check = calculate_time_difference(frm.doc.creation, 48)
if (time_difference_check && frm.doc.docstatus != 1){
diff --git a/one_fm/one_fm/doctype/attendance_check/attendance_check.json b/one_fm/one_fm/doctype/attendance_check/attendance_check.json
index adbb9a9542..e551561997 100644
--- a/one_fm/one_fm/doctype/attendance_check/attendance_check.json
+++ b/one_fm/one_fm/doctype/attendance_check/attendance_check.json
@@ -343,12 +343,12 @@
"mandatory_depends_on": "eval:doc.justification == \"Mobile isn't supporting the app\""
},
{
- "depends_on": "justification",
+ "depends_on": "eval:[\"Invalid media content\", \"Mobile isn't supporting the app\", \"Employees insist that he/she did check in or out\", \"Other\"].includes(doc.justification)",
"description": "Upload screenshot with the same issue!",
"fieldname": "screenshot",
"fieldtype": "Attach",
"label": "Screenshot",
- "mandatory_depends_on": "justification"
+ "mandatory_depends_on": "eval:[\"Invalid media content\", \"Mobile isn't supporting the app\", \"Employees insist that he/she did check in or out\", \"Other\"].includes(doc.justification)"
},
{
"depends_on": "eval:doc.justification == \"Other\"",
@@ -370,6 +370,7 @@
},
{
"default": "0",
+ "fetch_from": "employee.attendance_by_timesheet",
"fieldname": "attendance_by_timesheet",
"fieldtype": "Check",
"label": "Attendance By Timesheet",
@@ -384,7 +385,7 @@
"link_fieldname": "reference_docname"
}
],
- "modified": "2024-01-24 13:03:12.127598",
+ "modified": "2024-07-21 18:09:03.081135",
"modified_by": "Administrator",
"module": "One Fm",
"name": "Attendance Check",
diff --git a/one_fm/one_fm/doctype/attendance_check/attendance_check.py b/one_fm/one_fm/doctype/attendance_check/attendance_check.py
index 533808e23d..b9c8e55d37 100644
--- a/one_fm/one_fm/doctype/attendance_check/attendance_check.py
+++ b/one_fm/one_fm/doctype/attendance_check/attendance_check.py
@@ -1,83 +1,129 @@
# Copyright (c) 2023, omar jaber and contributors
# For license information, please see license.txt
from datetime import datetime, timedelta
-from itertools import chain
from frappe.model.document import Document
+from one_fm.processor import sendemail
import frappe,json
from frappe import _
from frappe.desk.form.assign_to import add as add_assignment
-from frappe.utils import nowdate, add_to_date, cstr, add_days, today, format_date, now, get_url_to_form
+from frappe.utils import add_days, today, now, get_url_to_form, getdate
from one_fm.utils import (
production_domain,
- fetch_attendance_manager_user_obj,
+ fetch_attendance_manager_user,
get_approver
)
from one_fm.operations.doctype.operations_shift.operations_shift import get_shift_supervisor
class AttendanceCheck(Document):
def before_insert(self):
- attendance_exist = frappe.db.get_value(self.doctype, {
- 'employee':self.employee, 'date':self.date, 'roster_type':self.roster_type
- }, ["name", "workflow_state"], as_dict=1)
- if attendance_exist:
- frappe.throw(f"""{self.doctype} already exist for {self.employee} on {self.date} with name {attendance_exist.name}""")
- employee = frappe.get_doc("Employee", self.employee)
- # set shift assignment
- shift_assignment = frappe.db.get_value("Shift Assignment", {
- "employee":self.employee, "start_date":self.date, "roster_type":self.roster_type, "status":"Active", "docstatus":1},
- ["name", "start_date", "start_datetime", "end_datetime"],
- as_dict=1
- )
+ self.validate_duplicate()
+ # Get shift assignment for the date and roster type
+ shift_assignment = self.get_shift_assignment()
+ # Set shift assignment to the attendance check
if shift_assignment:
self.shift_assignment = shift_assignment.name
self.start_time = shift_assignment.start_datetime
self.end_time = shift_assignment.end_datetime
-
- # check checkin logs
- checkins = frappe.db.sql(f"""
- SELECT ec.name, ec.owner, ec.creation, ec.modified, ec.modified_by,
- ec.docstatus, ec.idx, ec.employee, ec.employee_name, ec.log_type,
- ec.late_entry, ec.early_exit, ec.time, ec.date, ec.skip_auto_attendance,
- ec.shift_actual_start, ec.shift_actual_end, ec.shift_assignment,
- ec.operations_shift, ec.shift_type, ec.roster_type, ec.operations_site,
- ec.project, ec.company, ec.operations_role, ec.post_abbrv,ec.shift_permission,
- ec.actual_time,
- MIN(CASE WHEN ec.log_type = 'IN' THEN ec.time END) AS earliest_time,
- MAX(CASE WHEN ec.log_type = 'OUT' THEN ec.time END) AS latest_time,
- MIN(CASE WHEN ec.log_type = 'IN' THEN ec.name END) AS in_name,
- MAX(CASE WHEN ec.log_type = 'OUT' THEN ec.name END) AS out_name
- FROM
- `tabEmployee Checkin` ec
- WHERE
- ec.shift_assignment="{self.shift_assignment}"
- GROUP BY
- ec.shift_assignment;
- """, as_dict=1)
- if checkins:
- if checkins[0].in_name:
- self.checkin_record=checkins[0].in_name
- if checkins[0].out_name:
- self.checkout_record=checkins[0].out_name
-
- attendance_request = frappe.db.sql(f"""
- SELECT * FROM `tabAttendance Request`
- WHERE '{self.date}' BETWEEN from_date AND to_date
- AND docstatus=1
- """, as_dict=1)
- if attendance_request:
+ # Get checkin records for the
+ checkins = self.get_checkins_details()
+ # Set check in recods to attendance check
+ if checkins and len(checkins)>0:
+ self.checkin_record=checkins[0].in_name if checkins[0].in_name else ""
+ self.checkout_record=checkins[0].out_name if checkins[0].out_name else ""
+
+ attendance_request = self.get_attendance_request()
+ if attendance_request and len(attendance_request)>0:
self.attendance_request=attendance_request[0].name
- # check shift permission
- shift_permission = frappe.db.get_value("Shift Permission", {
- "employee":self.employee, "date":self.date, "roster_type":self.roster_type, "docstatus":["!=", 0]},
- ["name"]
- )
+ # Get shift permission
+ shift_permission = self.get_shift_permission()
+ # Set shift permission details
if shift_permission:
- self.shift_permission = shift_permission
+ self.shift_permission = shift_permission.name
self.has_shift_permissions = 1
- # get approver
+ # Set approver
+ self.set_attedance_check_approver()
+
+ def validate_duplicate(self):
+ attendance_exist = frappe.db.get_value(
+ self.doctype,
+ {
+ 'employee':self.employee,
+ 'date':self.date,
+ 'roster_type':self.roster_type
+ },
+ ["name", "workflow_state"],
+ as_dict=1
+ )
+ if attendance_exist:
+ msg = f"""Attendance Check already exist for {self.employee} on {self.date} with name {attendance_exist.name}"""
+ frappe.throw(msg)
+
+ def get_shift_assignment(self):
+ return frappe.db.get_value(
+ "Shift Assignment",
+ {
+ "employee":self.employee,
+ "start_date":self.date,
+ "roster_type":self.roster_type,
+ "status":"Active",
+ "docstatus":1
+ },
+ ["name", "start_date", "start_datetime", "end_datetime"],
+ as_dict=1
+ )
+
+ def get_checkins_details(self):
+ return frappe.db.sql(f"""
+ SELECT
+ MIN(CASE WHEN ec.log_type = 'IN' THEN ec.name END) AS in_name,
+ MAX(CASE WHEN ec.log_type = 'OUT' THEN ec.name END) AS out_name
+ FROM
+ `tabEmployee Checkin` ec
+ WHERE
+ ec.shift_assignment="{self.shift_assignment}"
+ GROUP BY
+ ec.shift_assignment;
+ """, as_dict=1)
+
+ def get_attendance_request(self):
+ return frappe.db.sql(f"""
+ select
+ name
+ from
+ `tabAttendance Request`
+ where
+ '{self.date}'
+ between
+ from_date
+ and
+ to_date
+ and
+ docstatus=1
+ """, as_dict=1)
+
+ def get_shift_permission(self):
+ return frappe.db.get_value(
+ "Shift Permission",
+ {
+ "employee":self.employee,
+ "date":self.date,
+ "roster_type":self.roster_type,
+ "docstatus":["!=", 0]
+ },
+ ["name"],
+ as_dict=1
+ )
+
+ def set_attedance_check_approver(self):
+ employee = frappe.db.get_value(
+ "Employee",
+ {"name": self.employee},
+ ["reports_to", "shift", "site"],
+ as_dict=1
+ )
if employee.reports_to:
self.reports_to = employee.reports_to
if employee.shift:
@@ -93,486 +139,509 @@ def after_insert(self):
def validate(self):
+ self.validate_is_replaced_shift_assignment()
self.validate_justification()
+ def validate_is_replaced_shift_assignment(self):
+ if self.attendance_status and self.attendance_status != "Absent" and self.shift_assignment:
+ if frappe.db.get_value("Shift Assignment", self.shift_assignment, "is_replaced") == 1:
+ frappe.throw(_(f"{self.employee_name} was replaced for this shift and cannot be marked present."))
+
def validate_justification(self):
- '''
- The method is used to validate the justification and its dependend fields
- '''
+ # The method is used to validate the justification and its dependend fields
if self.attendance_status == 'Present':
if not self.justification:
frappe.throw("Please select Justification")
- if self.justification != "Other":
- self.other_reason = ""
-
if self.justification == "Other":
if not self.other_reason:
frappe.throw("Please write the other Reason")
-
- if self.justification != "Mobile isn't supporting the app":
- self.mobile_brand = ""
- self.mobile_model = ""
-
+ else:
+ self.other_reason = ""
if self.justification == "Mobile isn't supporting the app":
if not self.mobile_brand:
frappe.throw("Please select mobile brand")
if not self.mobile_model:
frappe.throw("Please Select Mobile Model")
-
- """if self.justification not in ["Invalid media content","Out-of-site location", "User not assigned to shift", "Other"]:
- self.screenshot = ""
+ else:
+ self.mobile_brand = ""
+ self.mobile_model = ""
if self.justification in ["Invalid media content","Out-of-site location", "User not assigned to shift"]:
if not self.screenshot:
frappe.throw("Please Attach ScreenShot")
- """
+ else:
+ self.screenshot = ""
else:
self.justification = ""
- """if self.justification == "Approved by Administrator":
- if not check_attendance_manager(email=frappe.session.user):
- frappe.throw("Only the Attendance manager can select 'Approved by Administrator' ")
- """
-
-
- def validate_unscheduled_check(self):
- #If Check is unscheduled,confirmed user roles before submitting
- if self.is_unscheduled:
- if check_attendance_manager(frappe.session.user):
- return
- elif "Shift Supervisor" in frappe.get_roles(frappe.session.user) or "Site Supervisor" in frappe.get_roles(frappe.session.user):
- shift_assignments = frappe.db.get_list("Shift Assignment", filters={'docstatus':1,'start_date':self.date,'employee':self.employee}, fields="*")
- if not shift_assignments:
- frappe.throw(f"No Shift Assignments found for {self.employee_name} on {self.date} Please create one")
- else:
- frappe.throw("Only Shift/Site Supervisors and Attendance Managers have permission to submit unscheduled Attendance Checks")
+ if self.justification == "Approved by Administrator" and not check_attendance_manager(email=frappe.session.user):
+ frappe.throw("Only the Attendance manager can select 'Approved by Administrator' ")
def on_submit(self):
+ if not self.attendance_status:
+ frappe.throw(_('To Approve the record set Attendance Status'))
shift_working = frappe.db.get_value("Employee", self.employee, "shift_working")
- if self.attendance_status == "Present" and shift_working:
- self.validate_unscheduled_check()
if self.attendance_status == "On Leave":
self.check_on_leave_record()
if self.attendance_status == "Day Off" and shift_working:
- validate_day_off(self,convert=0)
- self.validate_justification_and_attendance_status()
- self.mark_attendance()
-
- def mark_attendance(self):
- if self.workflow_state == 'Approved':
- comment = ""
- logs = []
- check_attendance = frappe.db.get_value('Attendance',
- {'attendance_date': self.date, 'employee': self.employee, 'docstatus': ['<', 2],
- 'roster_type':self.roster_type
- }, ["status", "name"], as_dict=1)
- comment = "Created from Attendance Check"
- if check_attendance:
- if check_attendance.status == "Absent":
- if self.attendance_status == "Absent":
- pass
- elif self.attendance_status in ["Present", "Day Off", "On Leave"]:
- working_hours=0
- if self.shift_assignment:
- working_hours = frappe.db.get_value("Operations Shift",
- frappe.db.get_value("Shift Assignment", self.shift_assignment, 'shift'), 'duration')
- else:
- working_hours = 8 if self.attendance_status == 'Present' else 0
- frappe.db.sql(f"""UPDATE `tabAttendance`
- SET status = '{self.attendance_status}',
- reference_doctype="{self.doctype}",
- reference_docname="{self.name}",
- modified="{str(now())}",
- working_hours={working_hours},
- modified_by="{frappe.session.user}",
- comment="{comment}"
- WHERE name = "{check_attendance.name}"
- """
- )
- frappe.db.commit()
- else:
- att = frappe.new_doc("Attendance")
- att.employee = self.employee
- att.employee_name = self.employee_name
- att.attendance_date = self.date
- att.status = self.attendance_status
- att.roster_type = self.roster_type
- att.reference_doctype = "Attendance Check"
- att.reference_docname = self.name
- att.comment = comment
- if not frappe.db.get_value("Employee", self.employee, "attendance_by_timesheet"):
- if self.shift_assignment:
- att.shift_assignment = self.shift_assignment
- else:
- shift_assignment = frappe.db.exists("Shift Assignment", {
- 'employee':self.employee, 'start_date':self.date, 'roster_type':self.roster_type
- })
- if shift_assignment:
- att.shift_assignment = shift_assignment
- if att.shift_assignment and att.status=='Present':
- att.working_hours = frappe.db.get_value("Operations Shift",
- frappe.db.get_value("Shift Assignment", att.shift_assignment, 'shift'), 'duration')
- if att.status != 'Day Off':
- if not att.working_hours:
- att.working_hours = 8 if self.attendance_status == 'Present' else 0
- att.insert(ignore_permissions=True)
- att.submit()
- for i in logs:
- frappe.db.set_value("Employee Checkin", i.name, "attendance", att.name)
-
-
- def validate_justification_and_attendance_status(self):
- if not self.attendance_status:
- frappe.throw(_('To Approve the record set Attendance Status'))
+ self.validate_day_off()
+ if self.attendance_status != "On Leave":
+ self.mark_attendance()
def check_on_leave_record(self):
- submited_leave_record = frappe.db.sql(f"""SELECT leave_type
- FROM `tabLeave Application`
- WHERE employee = '{self.employee}'
- AND '{self.date}' >= from_date AND '{self.date}' <= to_date
- AND workflow_state = 'Approved'
- AND docstatus = 1
- """,as_dict=True)
-
- if submited_leave_record:
- return
- else:
- draft_leave_records = frappe.db.sql(f"""select employee_name,leave_approver_name,name
- FROM `tabLeave Application`
- WHERE employee = '{self.employee}'
- AND '{self.date}' >= from_date AND '{self.date}' <= to_date
- AND docstatus = 0
- """,as_dict=True)
- if draft_leave_records:
+ if self.attendance_status == "On Leave":
+ draft_leave_records = self.get_draft_leave_records()
+ if draft_leave_records and len(draft_leave_records) > 0:
doc_url = get_url_to_form('Leave Application',draft_leave_records[0].get('name'))
- error_template = frappe.render_template('one_fm/templates/emails/attendance_check_alert.html',context={'doctype':'Leave Application','current_user':frappe.session.user,'date':self.date,'approver':draft_leave_records[0].get('leave_approver_name'),'page_link':doc_url,'employee_name':self.employee_name})
+ error_template = frappe.render_template(
+ 'one_fm/templates/emails/attendance_check_alert.html',
+ context={
+ 'doctype':'Leave Application',
+ 'current_user':frappe.session.user,
+ 'date':self.date,
+ 'approver':draft_leave_records[0].get('leave_approver_name'),
+ 'page_link':doc_url,
+ 'employee_name':self.employee_name
+ }
+ )
frappe.throw(error_template)
else:
+ link_to_new_leave = frappe.utils.get_url('/app/leave-application/new-leave-application-1')
frappe.throw(f"""
- Please note that a Leave Application has not been created for {self.employee_name}.
-
- To create a leave application Click Here
+ Please note that a Leave Application has not been created for {self.employee_name}.
+
+ To create a leave application
+
+ Click Here
+
""")
+ def get_draft_leave_records(self):
+ return frappe.db.sql(f"""
+ select
+ employee_name, leave_approver_name, name
+ from
+ `tabLeave Application`
+ where
+ employee = '{self.employee}'
+ and
+ '{self.date}' >= from_date
+ and
+ '{self.date}' <= to_date
+ and
+ docstatus = 0
+ """,
+ as_dict=True
+ )
+ def validate_day_off(self):
+ if self.attendance_status == "Day Off":
+ # Check if shift request for that day exists
+ shift_request = self.get_shift_request()
+ if shift_request:
+ workflow_state = shift_request[0].get("workflow_state")
+ if workflow_state in {"Draft", "Pending Approval"}:
+ doc_url = get_url_to_form('Shift Request',shift_request[0].get('name'))
+ approver_full_name = frappe.db.get_value("User", shift_request[0].get('approver'), 'full_name')
+ error_template = frappe.render_template(
+ "one_fm/templates/emails/attendance_check_alert.html",
+ context={
+ "doctype":"Shift Request",
+ "current_user":frappe.session.user,
+ "date":self.date,
+ "approver":approver_full_name,
+ "page_link":doc_url,
+ "employee_name":self.employee_name
+ }
+ )
+ frappe.throw(error_template)
+ elif workflow_state == "Approved":
+ return
+
+ # Cancelled or shift request not created at all
+ link_to_new_shift_request = frappe.utils.get_url('/app/shift-request/new-shift-request-1')
+ frappe.throw(f"""
+
+ Please note that a shift request has not been created for
+ {self.employee_name} on {self.date}
+
+
+ To create a Shift Request
+
+ Click Here
+
+ """)
+
+ def get_shift_request(self):
+ return frappe.db.sql(f"""
+ select
+ name, approver, workflow_state
+ from
+ `tabShift Request`
+ where
+ employee = '{self.employee}'
+ and
+ from_date <= '{self.date}'
+ and
+ to_date >='{self.date}'
+ """,
+ as_dict=1
+ )
-def get_attendance_by_timesheet_employees(employees,attendance_date):
- #Get the applicable employees if the current date falls on a public holiday
- try:
- default_company = frappe.defaults.get_defaults().company
- cond = ''
- if not default_company:
- default_company = frappe.get_last_doc("Company").name
- holiday_lists = frappe.db.sql(f"""SELECT h.parent from `tabHoliday` h
- WHERE h.holiday_date = '{attendance_date}'
- """, as_dict=1)
- if holiday_lists:
- default_holiday_list = frappe.get_value("Company",default_company,'default_holiday_list')
- holiday_lists =[i.parent for i in holiday_lists]
- if default_holiday_list in holiday_lists:
- if len(holiday_lists)>1:
- cond += f" and holiday_list not in {tuple(holiday_lists)}"
- else:
- cond += f" and holiday_list !='{holiday_lists[0]}'"
+ def mark_attendance(self):
+ if self.workflow_state == 'Approved':
+ attendance = self.get_existing_attendance()
+ if attendance and len(attendance) > 0:
+ self.update_existing_attendance_record(attendance)
else:
- if len(holiday_lists)>1:
- cond+=f" and holiday_list is Not NULL and holiday_list not in {tuple(holiday_lists)}"
- else:
- cond+=f" and holiday_list is Not NULL and holiday_list != '{holiday_lists[0]}'"
- if employees:
- if len(employees)==1:
- cond+=f" and name !='{employees[0]}'"
- else:
- cond+= f" and name not in {tuple(employees)}"
-
- ts_employees = frappe.db.sql(f"""SELECT name from `tabEmployee` where status = "Active" AND date_of_joining <= '{attendance_date}' and attendance_by_timesheet = 1 {cond}""",as_dict=1)
- return [i.name for i in ts_employees]
- return [i.name for i in frappe.get_all("Employee",{"status":"Active",'date_of_joining':['<=',attendance_date],'attendance_by_timesheet':1,'name':['NOT IN',employees]},['name'])]
- except:
- frappe.log_error(title = "Attendance Check Error for Timesheet",message = frappe.get_traceback())
- #Ensure that a value is returned regardless of what happens here
- return []
+ self.create_new_attendance_record()
+
+ def get_existing_attendance(self):
+ return frappe.db.get_value(
+ "Attendance",
+ {
+ "attendance_date": self.date,
+ "employee": self.employee,
+ "docstatus": ["<", 2],
+ "roster_type": self.roster_type
+ },
+ ["status", "name"],
+ as_dict=1
+ )
+ def update_existing_attendance_record(self, attendance):
+ if attendance.status != self.attendance_status:
+ working_hours = self.get_shift_working_hours(self.shift_assignment)
+ frappe.db.sql(f"""
+ update
+ `tabAttendance`
+ set
+ status = '{self.attendance_status}',
+ reference_doctype='{self.doctype}',
+ reference_docname='{self.name}',
+ modified='{str(now())}',
+ working_hours={working_hours},
+ modified_by='{frappe.session.user}',
+ comment="Created from Attendance Check"
+ where
+ name = '{attendance.name}'
+ """)
+ frappe.db.commit()
+
+ def create_new_attendance_record(self):
+ attendance = frappe.new_doc("Attendance")
+ attendance.employee = self.employee
+ attendance.employee_name = self.employee_name
+ attendance.attendance_date = self.date
+ attendance.status = self.attendance_status
+ attendance.roster_type = self.roster_type
+ attendance.reference_doctype = self.doctype
+ attendance.reference_docname = self.name
+ attendance.comment = "Created from Attendance Check"
+
+ if not frappe.db.get_value("Employee", self.employee, "attendance_by_timesheet"):
+ # Set shift assignmet to attendance recod
+ if self.shift_assignment:
+ attendance.shift_assignment = self.shift_assignment
+ else:
+ shift_assignment = frappe.db.exists("Shift Assignment", {
+ 'employee':self.employee, 'start_date':self.date, 'roster_type':self.roster_type
+ })
+ if shift_assignment:
+ attendance.shift_assignment = shift_assignment
+
+ if attendance.shift_assignment and attendance.status=='Present':
+ attendance.working_hours = self.get_shift_working_hours(attendance.shift_assignment)
+ if not attendance.working_hours and attendance.status != 'Day Off':
+ attendance.working_hours = 8 if self.attendance_status == 'Present' else 0
+ attendance.insert(ignore_permissions=True)
+ attendance.submit()
+
+ def get_shift_working_hours(self, shift_assignment=False):
+ working_hours=0
+ if shift_assignment:
+ shift = frappe.db.get_value("Shift Assignment", shift_assignment, 'shift')
+ if shift:
+ working_hours = frappe.db.get_value("Operations Shift", shift, 'duration')
+ else:
+ working_hours = 8 if self.attendance_status == 'Present' else 0
+ return working_hours
def create_attendance_check(attendance_date=None):
if production_domain():
if not attendance_date:
attendance_date = add_days(today(), -1)
+ attendance_date = getdate(attendance_date)
+
+ # Create attendance check for absentees for the date
+ absentees = get_absentees_on_date(attendance_date)
+ insert_attendance_check_records(absentees, attendance_date)
+
+ # Create attendance check for employee who is shift working but no attendance marked on the date
+ attendance_not_marked_shift_employees = get_attendance_not_marked_shift_employees(attendance_date)
+ if attendance_not_marked_shift_employees:
+ insert_attendance_check_records(attendance_not_marked_shift_employees, attendance_date, True)
+
+def get_absentees_on_date(attendance_date):
+ return frappe.get_all("Attendance",
+ filters={
+ 'docstatus': 1,
+ 'status': 'Absent',
+ 'attendance_date': attendance_date,
+ "employee": ["not in", frappe.db.get_list('Shift Permission', filters={'date': attendance_date}, pluck='employee')]
+ },
+ fields=[
+ "employee",
+ "roster_type",
+ "name as attendance",
+ "comment as attendance_comment",
+ "shift_assignment",
+ "status as attendance_status"
+ ]
+ )
- absentees = frappe.get_all("Attendance", filters={
- 'docstatus':1,
- 'status':'Absent',
- 'attendance_date':attendance_date},
- fields="*"
- )
-
- attendance_by_timesheet = 0
-
- for count, i in enumerate(absentees):
- try:
-
- doc = frappe.get_doc({
- "doctype":"Attendance Check",
- "employee":i.employee,
- "roster_type":i.roster_type,
- "date":i.attendance_date,
- "attendance_by_timesheet": attendance_by_timesheet,
- "attendance":i.name,
- "attendance_comment":i.comment,
- "shift_assignment":i.shift_assignment,
- "attendance_marked":1,
- "marked_attendance_status":i.status
- }).insert(ignore_permissions=1)
- except Exception as e:
- if not "Attendance Check already exist for" in str(e):
- frappe.log_error(message=frappe.get_traceback(), title="Attendance Check Creation")
- if count%10==0:
- frappe.db.commit()
- frappe.db.commit()
-
- # create for no shift but active shift based employees
- attendance_list = [i.employee for i in frappe.db.get_list("Attendance", {
- "attendance_date":attendance_date})]
+def get_attendance_not_marked_shift_employees(attendance_date):
+ # Fetch the list of employees, attendance marked for the date and basic roster
+ attendance_list = frappe.db.get_list("Attendance",
+ filters={
+ "attendance_date": attendance_date,
+ "roster_type": "Basic",
+ "status": ["not in", ["Absent"]],
+ "docstatus": 1
+ },
+ fields=["employee"]
+ )
+ employees = [attendance.employee for attendance in attendance_list]
- no_shifts = frappe.db.get_list("Employee", {
+ # Fetch all the employees who is shift working but no attendance marked
+ return frappe.db.get_list("Employee",
+ filters={
"shift_working":1,
"status":"Active",
- "name": ["NOT IN", attendance_list],
+ "name": ["not in", employees],
"date_of_joining": ["<=", attendance_date]
- }
- )
- if no_shifts:
- for count, i in enumerate(no_shifts):
- try:
- if not frappe.db.exists("Attendance", {
- 'attendance_date':attendance_date,
- 'employee':i.name}
- ):
- doc = frappe.get_doc({
- "doctype":"Attendance Check",
- "employee":i.name,
- "roster_type":"Basic",
- 'is_unscheduled':1,
- "date":attendance_date
- }).insert(ignore_permissions=1)
- except Exception as e:
- if not "Attendance Check already exist for" in str(e):
- frappe.log_error(message=frappe.get_traceback(), title="Attendance Check Creation")
- if count%10==0:
- frappe.db.commit()
- frappe.db.commit()
-
-
- no_timesheet = frappe.db.sql(f"""SELECT emp.employee FROM `tabEmployee` emp
- WHERE emp.attendance_by_timesheet = 1
- AND emp.status ='Active'
- AND emp.employee_name != 'Test Employee'
- AND emp.date_of_joining <= '{attendance_date}'
- AND emp.name NOT IN (SELECT employee from `tabTimesheet` WHERE start_date='{attendance_date}')
- AND '{attendance_date}' NOT IN (SELECT holiday_date from `tabHoliday` h WHERE h.parent = emp.holiday_list AND h.holiday_date = '{attendance_date}')
- AND emp.employee NOT IN (SELECT employee
- FROM `tabLeave Application`
- Where '{attendance_date}' >= from_date AND '{attendance_date}' <= to_date
- AND workflow_state = 'Approved'
- AND docstatus = 1)
- """, as_dict=1)
-
- if no_timesheet:
- for count, i in enumerate(no_timesheet):
- try:
- doc = frappe.get_doc({
- "doctype":"Attendance Check",
- "employee":i.employee,
- "roster_type":'Basic',
- "date":attendance_date,
- "attendance_by_timesheet": 1,
- "attendance_marked":0,
- "comment":"No Timesheet Created."
- }).insert(ignore_permissions=1)
- except Exception as e:
- if not "Attendance Check already exist for" in str(e):
- frappe.log_error(message=frappe.get_traceback(), title="Attendance Check Creation")
- if count%10==0:
- frappe.db.commit()
-
-def approve_attendance_check():
- attendance_checks = frappe.get_all("Attendance Check", filters={
- "date":["<", today()], "workflow_state":"Pending Approval"}
+ },
+ fields=["name as employee"]
)
- for i in attendance_checks:
- doc = frappe.get_doc("Attendance Check", i.name)
- if not doc.justification:
- doc.justification = "Approved by Administrator"
- if not doc.attendance_status:
- doc.attendance_status = "Absent"
- doc.workflow_state = "Approved"
- try:
- doc.submit()
- except Exception as e:
- if str(e)=="To date can not greater than employee's relieving date":
- doc.db_set("Comment", f"Employee exited company on {frappe.db.get_value('Employee', doc.employee, 'relieving_date')}\n{doc.comment or ''}")
-
-def mark_missing_attendance(attendance_checkin_found):
- for i in attendance_checkin_found:
+def insert_attendance_check_records(details, attendance_date, is_unscheduled=False):
+ for count, data in enumerate(details):
try:
- checkin_record = ""
- checkout_record = ""
- shift_assignment = ""
- att = ""
- working_hours = 0
- in_time = ''
- out_time = ''
- comment = ''
- day_off_ot = 0
-
- if i.attendance:
- att = frappe.get_doc("Attendance", i.attendance)
- if i.shift_assignment:
- shift_assignment = frappe.get_doc("Shift Assignment", i.shift_assignment)
- day_off_ot = frappe.get_value("Employee Schedule", {
- 'employee':i.employee,
- 'date':i.date,
- 'roster_type':i.roster_type
- }, 'day_off_ot')
- if i.checkin_record:
- checkin_record = frappe.get_doc("Employee Checkin", i.checkin_record)
- if i.checkout_record:
- checkout_record = frappe.get_doc("Employee Checkin", i.checkout_record)
-
- # calculate working hours
- if checkin_record and not checkout_record and shift_assignment:
- working_hours = (shift_assignment.end_datetime - checkin_record.time).total_seconds() / (60 * 60)
- in_time = checkin_record.time
- out_time = shift_assignment.end_datetime
- comment = "No checkout record found"
- elif checkin_record and checkout_record and shift_assignment:
- working_hours = (checkout_record.time - checkin_record.time).total_seconds() / (60 * 60)
- in_time = checkin_record.time
- out_time = checkout_record.time
- if att:
- # set values based on existing attendance
- if att.status=='Absent':
- att.db_set({
- 'working_hours':working_hours,
- 'in_time':in_time,
- 'out_time':out_time,
- 'comment':comment,
- 'day_off_ot':day_off_ot,
- 'status':'Present'
- })
- else:
- att = frappe.new_doc("Attendance")
- att.employee = i.employee
- att.employee_name = i.employee_name
- att.attendance_date = i.date
- att.status = 'Present'
- att.roster_type = i.roster_type
- att.shift_assignment = i.shift_assignment
- att.in_time = in_time
- att.out_time = out_time
- att.working_hours = working_hours
- att.comment = comment
- att.day_off_ot = day_off_ot
- att.insert(ignore_permissions=True)
- att.submit()
- frappe.db.set_value("Employee Checkin", i.checkin_record, "attendance", att.name)
- if checkout_record:
- frappe.db.set_value("Employee Checkin", i.checkout_record, "attendance", att.name)
+ attendance_by_timesheet = False
+ if not is_unscheduled:
+ attendance_by_timesheet = frappe.db.get_value("Employee", data["employee"], "attendance_by_timesheet")
+ filters = {
+ "doctype": "Attendance Check",
+ "employee": data["employee"],
+ "date": attendance_date,
+ "attendance": data["attendance"] if "attendance" in data else "",
+ "roster_type": data["roster_type"] if "roster_type" in data else "Basic",
+ 'is_unscheduled': is_unscheduled,
+ "attendance_by_timesheet": attendance_by_timesheet,
+ "marked_attendance_status": data["attendance_status"] if "attendance_status" in data else "",
+ "shift_assignment": data["shift_assignment"] if "shift_assignment" in data else "",
+ "attendance_marked": 1 if "attendance" in data else 0,
+ "comment": data["attendance_comment"] if "attendance_comment" in data else ""
+ }
+
+ doc = frappe.get_doc(filters)
+ doc.flags.ignore_mandatory = 1
+ doc.insert(ignore_permissions=1)
except Exception as e:
- frappe.log_error(frappe.get_traceback(), 'Attendance Remark')
+ if not "Attendance Check already exist for" in str(e):
+ frappe.log_error(message=frappe.get_traceback(), title="Attendance Check Creation")
+ if count%10==0:
+ frappe.db.commit()
+ frappe.db.commit()
@frappe.whitelist()
def check_attendance_manager(email: str) -> bool:
return frappe.db.get_value("Employee", {"user_id": email}) == frappe.db.get_single_value("ONEFM General Setting", "attendance_manager")
+def attendance_check_pending_approval_check():
+ pending_approval_attendance_checks = get_pending_approval_attendance_check(48)
+ if pending_approval_attendance_checks and len(pending_approval_attendance_checks) > 0:
+ # Issue Penalty to the assigned approver
+ issue_penalty_to_the_assigned_approver(pending_approval_attendance_checks)
+ # Assign the attendance checks to attendance manager for approval
+ assign_attendance_manager(pending_approval_attendance_checks)
+
+ frappe.db.commit()
-@frappe.whitelist()
-def validate_day_off(form,convert=1):
- # Validates the existence of a shift request when the attendance status of the attendance
- doc = json.loads(form) if convert else form
- if doc.get('attendance_status') == "Day Off":
- #check if shift request for that day exists
- query = f"Select name from `tabShift Request` where docstatus = 1 and assign_day_off = 1 and employee = '{doc.get('employee')}' and from_date <= '{doc.get('date')}' and to_date >='{doc.get('date')}'"
- submited_result_set = frappe.db.sql(query,as_dict=1)
- if submited_result_set:
- return
+def get_pending_approval_attendance_check(hours):
+ # Method to get list of attendance check, which is in panding approval state after a given hours
+ date_time = datetime.strptime(now(), '%Y-%m-%d %H:%M:%S.%f') - timedelta(hours=hours)
+ return frappe.db.sql("""
+ select
+ name, _assign as assign_to
+ from
+ `tabAttendance Check`
+ where
+ creation <= %s
+ and
+ docstatus = 0
+
+ """, (date_time), as_dict=1)
+
+
+def issue_penalty_to_the_assigned_approver(pending_approval_attendance_checks):
+ try:
+ approvers = {}
+ for pending_approval_attendance_check in pending_approval_attendance_checks:
+
+ if pending_approval_attendance_check.get('assign_to'):
+ assign_to = frappe.parse_json(pending_approval_attendance_check.assign_to)
+ if assign_to and len(assign_to) > 0:
+ if assign_to[0] in approvers:
+ approvers[assign_to[0]] += ", "+pending_approval_attendance_check.name
+ else:
+ approvers[assign_to[0]] = pending_approval_attendance_check.name
+
+ penalty_type = frappe.db.get_single_value("ONEFM General Setting", "att_check_approver_penalty_type")
+ for approver in approvers:
+ note = "There are attendance check not approved "+approvers[approver]
+ approver_employee = frappe.db.get_values(
+ "Employee",
+ {"user_id": approver},
+ ['name', 'employee_name', 'designation'],
+ as_dict=True
+ )
+ if approver_employee and len(approver_employee)>0:
+ penalty = frappe.get_doc({
+ "doctype": "Penalty",
+ "penalty_issuance_time": now(),
+ "recipient_employee": approver_employee[0].name,
+ "recipient_name": approver_employee[0].employee_name,
+ "recipient_designation": approver_employee[0].designation,
+ "recipient_user": approver,
+ })
+ penalty_details = penalty.append("penalty_details")
+ penalty_details.penalty_type = penalty_type
+ penalty_details.exact_notes = note
+ penalty.save(ignore_permissions=True)
+ except:
+ frappe.log_error(title = "Error Creating Penalty Documents",message = frappe.get_traceback())
+
+def fetch_existing_todos(manager):
+ """Fetch the existing todos for attendance checks assigned to the attendance manager
+ Args:
+ manager (Str): User
+ """
+ existing_todos = frappe.get_all("ToDo",{'allocated_to':manager,'status':'Open','reference_type':'Attendance Check'},['reference_name'])
+ return [i.reference_name for i in existing_todos]
+
+
+def create_split_query(todos,limit,manager,today,today_datetime):
+ """
+ This is to mitigate max_allowed_packet errors when the query size is too large
+ Args:
+ todos (_type_): all todos
+ limit (int): the number of todos per string
+ """
+ def create_sql_query(sublist):
+ values = []
+ for d in sublist:
+ vals = f"""
+ '{manager}_{d.name}',
+ '{manager}',
+ 'Attendance Check',
+ '{d.name}',
+ "Assignment for Attendance Check {d.name}",
+ 'Medium',
+ 'Open',
+ '{today}',
+ "Administrator",
+ "Action",
+ "Administrator",
+ '{today_datetime}',
+ '{today_datetime}'
+ """
+ values.append(f"({vals})")
+ query = """ INSERT INTO `tabToDo`
+ (`name`,`allocated_to`, `reference_type`, `reference_name`,
+ `description`, `priority`,`status`, `date`,`owner`,`type`,`assigned_by`,`creation`, `modified`)
+ VALUES """ + ', '.join(values)
+ return query
+
+ split_lists = [todos[i:i + limit] for i in range(0, len(todos), limit)]
+
+ # Creating SQL query strings for each sublist
+ sql_queries = [create_sql_query(sublist) for sublist in split_lists]
+
+ return sql_queries
+
+def create_todos(manager,todos):
+ """Create todos for the attendance manager
+ Using this approach because there a potential for over 50k entries and timeout
+
+ Args:
+ manager (str): attenance manager user
+ todos (list): a list of dicts with todo details
+ """
+ try:
+ today = frappe.utils.getdate()
+ today_datetime = frappe.utils.get_datetime()
+
+ if len(todos)>10000:
+ #Query needs to be split to avoid max query package error
+ split_query =create_split_query(todos,10000,manager,today,today_datetime)
+ for each in split_query:
+ frappe.db.sql(each,values=[])
else:
- draft_query = f"Select name,approver from `tabShift Request` where docstatus = 0 and assign_day_off = 1 and employee = '{doc.get('employee')}' and from_date <= '{doc.get('date')}' and to_date >='{doc.get('date')}'"
- drafts_result_set = frappe.db.sql(draft_query,as_dict=1)
- if drafts_result_set:
-
-
- doc_url = get_url_to_form('Shift Request',drafts_result_set[0].get('name'))
- approver_full_name = frappe.db.get_value("User",drafts_result_set[0].get('approver'),'full_name')
- error_template = frappe.render_template('one_fm/templates/emails/attendance_check_alert.html',context={'doctype':'Shift Request','current_user':frappe.session.user,'date':doc.date,'approver':approver_full_name,'page_link':doc_url,'employee_name':doc.employee_name})
- frappe.throw(error_template)
- else:
- #cancelled or shift request not created at all
- frappe.throw(f"""
- Please note that a shift request has not been created for {doc.employee_name} on {doc.date}..
-
- To create a Shift Request Click Here
- """)
-
+ query = """
+ INSERT INTO
+ `tabToDo`
+ (
+ `name`,`allocated_to`, `reference_type`, `reference_name`,`description`, `priority`,
+ `status`, `date`, `assigned_by`,`creation`, `modified`,`type`,`owner`
+ )
+ VALUES
+ """
+ query_body = """"""
+ for each in todos:
+ query_body+= f"""
+ (
+ "{'_'.join([manager,each.name])}", "{manager}", "{'Attendance Check'}", "{each.name}",
+ "Assignment for Attendance Check {each.name}", "{'Medium'}", "{'Open'}", '{today}',
+ "Administrator",'{today_datetime}','{today_datetime}',"Action","Administrator"
+ ),"""
+ if query_body:
+ query += query_body[:-1]
+ frappe.db.sql(query,values=[])
+ frappe.db.commit()
+ except:
+ frappe.log_error(title = "Error Assigning to Attendance Manager",message = frappe.get_traceback())
-def assign_attendance_manager_after_48_hours():
- attendance_manager_user = fetch_attendance_manager_user_obj()
- if attendance_manager_user:
- date_time = datetime.strptime(now(), '%Y-%m-%d %H:%M:%S.%f') - timedelta(hours=48)
- attendance_check = attendance_check = frappe.db.sql("""
- SELECT name
- FROM `tabAttendance Check`
- WHERE creation <= %s
- AND docstatus = 0
- AND TIME(creation) <= %s
- """, (date_time, date_time.time()), as_list=1)
-
- if attendance_check:
- list_of_names = tuple(chain.from_iterable(attendance_check))
- if list_of_names:
- for obj in list_of_names:
- add_assignment({
- 'doctype': "Attendance Check",
- 'name': obj,
- 'assign_to': [attendance_manager_user],
- })
+def notify_manager(manager):
+ """Notify the manager that new todos have been created for them
+ Args:
+ manager (str): attendance manager
+ """
+ try:
+ page_link = frappe.utils.get_url()+f'/app/todo?date={frappe.utils.get_date_str(frappe.utils.getdate())}&allocated_to={manager}'
+ msg = frappe.render_template('one_fm/templates/emails/attendance_manager_todo_assignment.html', context={"manager": manager,'page_link':page_link})
+ sendemail(recipients= [manager], content=msg, subject="Pending Attendance Checks", delayed=False)
+ except:
+ frappe.log_error(title = "Error Notifying Attendance Manager",message = frappe.get_traceback())
+
+
-def check_for_missed(date,schedules,shift_assignments,attendance_requests,all_attendance,checks_to_create):
- all_leaves = frappe.db.sql(f"""SELECT name,employee
- FROM `tabLeave Application`
- Where '{date}' >= from_date AND '{date}' <= to_date
- AND workflow_state = 'Approved'
- AND docstatus = 1
- """,as_dict=True)
- all_shifts_query = f"""Select employee from `tabShift Request` where docstatus = 1 and
- from_date <= '{date}' and to_date >='{date}'"""
- all_leave_employees = [i.employee for i in all_leaves]
- all_shifts = frappe.db.sql(all_shifts_query,as_dict = 1)
- shift_request_employees = [i.employee for i in all_shifts]
- schedule_employees = [i.employee for i in schedules]
- shift_assignments_employees = [i.employee for i in shift_assignments]
- attendance_requests_employees = [i.employee for i in attendance_requests]
- all_attendance_employees = [i.employee for i in all_attendance]
- merged = shift_request_employees+schedule_employees+shift_assignments_employees+\
- all_attendance_employees+attendance_requests_employees+checks_to_create+all_leave_employees
- merged_set = set(merged) #Remove Duplicates
- merged_tuple = tuple(merged_set)
- if len(merged_tuple) == 1:
- single_employee = merged_tuple[0]
- employees_with_no_docs_query = f""" SELECT name from `tabEmployee` where status = 'Active' AND date_of_joining <= '{date}' AND shift_working = 1 and name = {single_employee}
- """
-
- #Employees with no shift,schedule,leaves etc
- else:
- employees_with_no_docs_query = f""" SELECT name from `tabEmployee` where status = 'Active' AND date_of_joining <= '{date}' and shift_working = 1 and name not in {merged_tuple}
- """
- result_set = frappe.db.sql(employees_with_no_docs_query,as_dict=1)
- return [i.name for i in result_set] if result_set else []
+def assign_attendance_manager(pending_approval_attendance_checks):
+ attendance_manager_user = fetch_attendance_manager_user()
+ if attendance_manager_user:
+ existing_todos = fetch_existing_todos(attendance_manager_user)
+ filtered_pending_approval_attendance_check = [i for i in pending_approval_attendance_checks if i.name not in existing_todos ]
+ create_todos(attendance_manager_user,filtered_pending_approval_attendance_check)
+ notify_manager(attendance_manager_user)
+
def schedule_attendance_check():
frappe.enqueue(create_attendance_check, queue='long', timeout=7000)
diff --git a/one_fm/one_fm/doctype/onefm_general_setting/onefm_general_setting.json b/one_fm/one_fm/doctype/onefm_general_setting/onefm_general_setting.json
index f84bd4a69d..231781eef0 100644
--- a/one_fm/one_fm/doctype/onefm_general_setting/onefm_general_setting.json
+++ b/one_fm/one_fm/doctype/onefm_general_setting/onefm_general_setting.json
@@ -24,6 +24,8 @@
"attendance_manager",
"attendance_manager_name",
"attendance_check_exclusion",
+ "column_break_xudp",
+ "att_check_approver_penalty_type",
"employee_section",
"employee_master_role",
"wiki_page_access",
@@ -73,6 +75,7 @@
"options": "ONEFM Document Access Roles Detail"
},
{
+ "collapsible": 1,
"fieldname": "section_break_7yvzt",
"fieldtype": "Section Break",
"label": "Attendance"
@@ -136,6 +139,16 @@
"fieldtype": "Check",
"label": "Extend User Permissions"
},
+ {
+ "fieldname": "column_break_xudp",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "att_check_approver_penalty_type",
+ "fieldtype": "Link",
+ "label": "Attendance Check Approver Penalty Type",
+ "options": "Penalty Type"
+ },
{
"description": "Table of roles allowed to view employee info",
"fieldname": "employee_info_access",
@@ -178,4 +191,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
-}
\ No newline at end of file
+}
diff --git a/one_fm/one_fm/page/face_recognition/face_recognition.py b/one_fm/one_fm/page/face_recognition/face_recognition.py
index d2d3ba817e..6ab594b456 100755
--- a/one_fm/one_fm/page/face_recognition/face_recognition.py
+++ b/one_fm/one_fm/page/face_recognition/face_recognition.py
@@ -20,14 +20,15 @@
# setup channel for face recognition
face_recognition_service_url = frappe.local.conf.face_recognition_service_url
-channels = [
- grpc.secure_channel(i, grpc.ssl_channel_credentials()) for i in face_recognition_service_url
-]
+# channels = [
+# grpc.secure_channel(i, grpc.ssl_channel_credentials()) for i in face_recognition_service_url
+# ]
# setup stub for face recognition
-stubs = [
- facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
-]
+# stubs = [
+# facial_recognition_pb2_grpc.FaceRecognitionServiceStub(i) for i in channels
+# ]
+stubs = list()
class NumpyArrayEncoder(JSONEncoder):
@@ -151,7 +152,17 @@ def verify():
@frappe.whitelist()
def user_within_site_geofence(employee, log_type, user_latitude, user_longitude):
""" This method checks if user's given coordinates fall within the geofence radius of the user's assigned site in Shift Assigment. """
- shift = get_current_shift(employee)
+ shift_exists = get_current_shift(employee)
+ if shift_exists['type'] == "Early":
+ # check if user can checkin with the correct time
+ return response("Resource Not Found", 404, None, f"You are checking in too early, checkin is allowed in {shift_exists['data']} minutes ")
+ elif shift_exists['type'] == "Late":
+ return response("Resource Not Found", 404, None, f"You are checking out too late, checkout was allowed {shift_exists['data']} minutes ago ")
+ elif shift_exists['type'] == "On Time":
+ shift = shift_exists['data']
+ else:
+ shift = None
+
date = cstr(getdate())
if shift:
if frappe.db.exists("Shift Request", {"employee":employee, 'from_date':['<=',date],'to_date':['>=',date]}):
@@ -216,6 +227,7 @@ def update_onboarding_employee(employee):
onboard_employee = frappe.get_doc('Onboard Employee', onboard_employee_exist.name)
onboard_employee.enrolled = True
onboard_employee.enrolled_on = now_datetime()
+ onboard_employee.flags.ignore_mandatory = True
onboard_employee.save(ignore_permissions=True)
frappe.db.commit()
@@ -251,6 +263,7 @@ def update_onboarding_employee(employee):
onboard_employee = frappe.get_doc('Onboard Employee', onboard_employee_exist)
onboard_employee.enrolled = True
onboard_employee.enrolled_on = now_datetime()
+ onboard_employee.flags.ignore_mandatory = True
onboard_employee.save(ignore_permissions=True)
frappe.db.commit()
@@ -328,7 +341,16 @@ def check_existing():
if not employee:
frappe.throw(_("Please link an employee to the logged in user to proceed further."))
- shift = get_current_shift(employee)
+ shift_exists = get_current_shift(employee)
+ if shift_exists['type'] == "Early":
+ # check if user can checkin with the correct time
+ return response("Resource Not Found", 404, None, f"You are checking in too early, checkin is allowed in {shift_exists['data']} minutes ")
+ elif shift_exists['type'] == "Late":
+ return response("Resource Not Found", 404, None, f"You are checking out too late, checkout was allowed {shift_exists['data']} minutes ago ")
+ elif shift_exists['type'] == "On Time":
+ shift = shift_exists['data']
+ else:
+ shift = None
#if employee schedule is linked with the previous Checkin doc
if shift:
diff --git a/one_fm/operations/doctype/employee_checkin_issue/employee_checkin_issue.js b/one_fm/operations/doctype/employee_checkin_issue/employee_checkin_issue.js
index 6eaea7899f..c0665c9963 100644
--- a/one_fm/operations/doctype/employee_checkin_issue/employee_checkin_issue.js
+++ b/one_fm/operations/doctype/employee_checkin_issue/employee_checkin_issue.js
@@ -3,9 +3,9 @@
frappe.ui.form.on('Employee Checkin Issue', {
refresh: function(frm) {
- if(!frm.doc.issue && frm.doc.workflow_state == 'Approved'){
- frm.add_custom_button(__('Create Issue'), function() {
- create_issue(frm);
+ if(!frm.doc.ticket && frm.doc.workflow_state == 'Approved'){
+ frm.add_custom_button(__('Create Ticket'), function() {
+ create_ticket(frm);
}).addClass('btn-primary');
}
},
@@ -14,9 +14,24 @@ frappe.ui.form.on('Employee Checkin Issue', {
},
employee: function(frm) {
get_shift_assignment(frm);
+ },
+ validate: function(frm){
+ validate_date(frm);
+ },
+ date: function(frm){
+ validate_date(frm);
}
+
});
+
+function validate_date(frm){
+ if(frm.doc.date < frappe.datetime.now_date()){
+ frappe.throw("Date can not be set to a past date")
+ }
+
+}
+
function set_employee_from_the_session_user(frm) {
if(frappe.session.user != 'Administrator' && frm.is_new()){
frappe.db.get_value('Employee', {'user_id': frappe.session.user} , 'name', function(r) {
@@ -33,12 +48,12 @@ function set_employee_from_the_session_user(frm) {
}
};
-function create_issue(frm) {
+function create_ticket(frm) {
frappe.call({
- method: 'create_issue',
+ method: 'create_hd_ticket',
doc: frm.doc,
freeze: true,
- freeze_message: __("Creating the Issue...!")
+ freeze_message: __("Creating the Ticket!")
})
};
diff --git a/one_fm/operations/doctype/employee_checkin_issue/employee_checkin_issue.json b/one_fm/operations/doctype/employee_checkin_issue/employee_checkin_issue.json
index a95f78132d..3d0425e48e 100644
--- a/one_fm/operations/doctype/employee_checkin_issue/employee_checkin_issue.json
+++ b/one_fm/operations/doctype/employee_checkin_issue/employee_checkin_issue.json
@@ -23,9 +23,10 @@
"column_break_9",
"shift",
"shift_type",
+ "roster_type",
"section_break_17",
"amended_from",
- "issue"
+ "ticket"
],
"fields": [
{
@@ -160,18 +161,25 @@
"label": "Issue Details",
"mandatory_depends_on": "eval:doc.issue_type==\"Other\""
},
+ {
+ "fetch_from": "assigned_shift.roster_type",
+ "fieldname": "roster_type",
+ "fieldtype": "Select",
+ "label": "Roster Type",
+ "options": "Basic\nOver-Time"
+ },
{
"allow_on_submit": 1,
- "fieldname": "issue",
+ "fieldname": "ticket",
"fieldtype": "Link",
- "label": "Issue",
- "options": "Issue",
+ "label": "Ticket",
+ "options": "HD Ticket",
"read_only": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2023-01-24 12:30:38.577875",
+ "modified": "2024-04-23 09:04:39.452102",
"modified_by": "Administrator",
"module": "Operations",
"name": "Employee Checkin Issue",
diff --git a/one_fm/operations/doctype/employee_checkin_issue/employee_checkin_issue.py b/one_fm/operations/doctype/employee_checkin_issue/employee_checkin_issue.py
index a580c1af3f..aa539b13fd 100644
--- a/one_fm/operations/doctype/employee_checkin_issue/employee_checkin_issue.py
+++ b/one_fm/operations/doctype/employee_checkin_issue/employee_checkin_issue.py
@@ -4,10 +4,11 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
-from frappe.utils import getdate, get_datetime, add_to_date, format_date, cstr
+from frappe.utils import getdate, get_datetime, add_to_date, format_date
from frappe import _
from one_fm.api.notification import get_employee_user_id
from hrms.hr.doctype.shift_assignment.shift_assignment import get_shift_details
+from one_fm.api.v1.face_recognition import create_checkin_log
from one_fm.api.v1.utils import response
from one_fm.utils import (
workflow_approve_reject, send_workflow_action_email, get_approver
@@ -25,107 +26,114 @@ class ShiftDetailsMissing(frappe.ValidationError):
class EmployeeCheckinIssue(Document):
def validate(self):
self.check_shift_details_value()
- self.validate_date()
+ self.validate_attendance()
+ self.validate_employee_checkin()
self.validate_duplicate_record()
- if self.workflow_state in ['Pending', 'Approved']:
- self.validate_attendance()
- self.validate_employee_checkin()
+ self.validate_date()
+
+ # This method validates the shift details availability for employee
+ def check_shift_details_value(self):
+ if not self.assigned_shift or not self.shift or not self.shift_supervisor or not self.shift_type:
+ frappe.throw(_("Shift details are missing. Please make sure that the correct date is set."), exc=ShiftDetailsMissing)
def validate_attendance(self):
- attendance = frappe.db.exists('Attendance',{'attendance_date': self.date, 'employee': self.employee, 'docstatus': 1})
+ attendance = frappe.db.exists(
+ "Attendance",
+ {
+ "attendance_date": self.date,
+ "employee": self.employee,
+ "docstatus": 1,
+ "roster_type": self.roster_type
+ }
+ )
if attendance:
- frappe.throw(_('There is an Attendance {0} exists for the \
- Employee {1} on {2}'.format(attendance, self.employee_name, format_date(self.date))), exc=ExistAttendance)
+ frappe.throw(
+ _("Attendance record {0} already exists for {1} having Roster Type {2} on {3}.".format
+ (
+ attendance, self.employee_name, self.roster_type, format_date(self.date)
+ )
+ ),
+ exc=ExistAttendance
+ )
def validate_employee_checkin(self):
start_date = get_datetime(self.date)
end_date = add_to_date(start_date, hours=23.9998)
- employee_checkin = frappe.db.exists('Employee Checkin',
- {'log_type': self.log_type, 'time': ["between", [start_date, end_date]], 'employee': self.employee}
+ employee_checkin = frappe.db.exists(
+ "Employee Checkin",
+ {
+ "log_type": self.log_type,
+ "time": ["between", [start_date, end_date]],
+ "shift_assignment": self.assigned_shift,
+ "employee": self.employee
+ }
)
if employee_checkin:
- frappe.throw(_('There is an Employee Checkin {0} exists for the \
- Employee {1} on {2}'.format(employee_checkin, self.employee_name, format_date(self.date))), exc=ExistCheckin)
+ frappe.throw(
+ _("Employee Checkin {0} of type {1} already exists for the shift assignment {2}".format(
+ employee_checkin, self.log_type, self.assigned_shift
+ )
+ ),
+ exc=ExistCheckin
+ )
- # This method validates the shift details availability for employee
- def check_shift_details_value(self):
- if not self.assigned_shift or not self.shift or not self.shift_supervisor or not self.shift_type:
- frappe.throw(_("Shift details are missing. Please make sure date is correct."), exc=ShiftDetailsMissing)
+ # This method validates any dublicate ECI for the employee on same day
+ def validate_duplicate_record(self):
+ if frappe.db.exists(
+ "Employee Checkin Issue",
+ {
+ "employee": self.employee,
+ "assigned_shift": self.assigned_shift,
+ "log_type": self.log_type,
+ "name": ["not in", [self.name]],
+ "date": self.date
+ }
+ ):
+ msg = _(
+ "{employee} has already created Employee Checkin Issue for {log_type} against the same Shift Assignment".format(
+ employee=self.employee_name, log_type=self.log_type
+ )
+ )
+ frappe.throw(msg)
# This method validates the ECI date and avoid creating ECI for previous days
def validate_date(self):
- if self.docstatus==0 and getdate(self.date) < getdate() and self.is_new():
- frappe.throw(_("Oops! You cannot create a Employee Checkin Issue for a previous date."))
+ if self.docstatus == 0 and getdate(self.date) < getdate() and self.is_new():
+ frappe.throw(_("Oops! You cannot create Employee Checkin Issue for a previous date."))
- # This method validates any dublicate ECI for the employee on same day
- def validate_duplicate_record(self):
- date = getdate(self.date).strftime('%d-%m-%Y')
- if frappe.db.exists("Employee Checkin Issue", {"employee": self.employee, "date":self.date,
- "assigned_shift": self.assigned_shift, "log_type": self.log_type, "name": ["not in", [self.name]]}):
- frappe.throw(_("{employee} has already created a Employee Checkin Issue for {log_type} on {date}.".format(employee=self.employee_name, log_type=self.log_type, date=date)))
+ def on_update(self):
+ if self.workflow_state == "Approved":
+ create_checkin_log(self.employee, self.log_type, 0, self.latitude, self.longitude, "Mobile App")
+ self.create_hd_ticket()
- @frappe.whitelist()
- def create_issue(self):
- if not self.issue:
- issue_type = False
- if frappe.db.exists('Issue Type', {'name': 'Checkin Issue'}):
- issue_type = 'Checkin Issue'
- else:
- issue_type_doc = frappe.get_doc({"doctype": "Issue Type", "__newname": "Checkin Issue"}).insert()
- issue_type = issue_type_doc.name
- if issue_type:
- issue = frappe.new_doc('Issue')
- issue.subject = "Employee Checkin Issue - {0}".format(self.issue_type)
- issue.raised_by = frappe.session.user
- doc_link = frappe.utils.get_url(self.get_url())
- description = issue.subject + "
The user found an issue in the app \
- and recorded in Employee Checkin Issue.
".format(doc_link)
- issue.description = description
- issue.issue_type = issue_type
- issue.save(ignore_permissions=True)
- self.issue = issue.name
- self.save(ignore_permissions=True)
- self.reload()
- def on_update(self):
- if self.workflow_state == 'Approved':
- create_employee_checkin_for_employee_checkin_issue(self)
- workflow_approve_reject(self, [get_employee_user_id(self.employee)])
-
- if self.workflow_state == 'Pending':
- send_workflow_action_email(self, recipients=[get_employee_user_id(self.shift_supervisor)])
-
- if self.workflow_state in ['Rejected']:
- workflow_approve_reject(self, [get_employee_user_id(self.employee)])
-
-def create_employee_checkin_for_employee_checkin_issue(employee_checkin_issue, from_api=False):
- """
- Method to create Employee Checkin from the Employee Checkin Issue
- args:
- employee_checkin_issue: Object of Employee Checkin Issue
- """
- # Get shift details for the employee
- shift_assignment = frappe.get_doc("Shift Assignment", employee_checkin_issue.assigned_shift)
-
- employee_checkin = frappe.new_doc('Employee Checkin')
- employee_checkin.employee = employee_checkin_issue.employee
- employee_checkin.log_type = employee_checkin_issue.log_type
- employee_checkin.time = shift_assignment.start_datetime if employee_checkin_issue.log_type == "IN" else shift_assignment.end_datetime
- employee_checkin.date = shift_assignment.start_date if employee_checkin_issue.log_type=='IN' else shift_assignment.end_datetime
- employee_checkin.skip_auto_attendance = False
- employee_checkin.employee_checkin_issue = employee_checkin_issue.name
- # The field shift in shift assignment is operations shift
- employee_checkin.operations_shift = shift_assignment.shift
- employee_checkin.shift_type = shift_assignment.shift_type
- # The field shift_type in shift assignment is shift type and in employee checkin the field shift is shift type
- employee_checkin.shift = shift_assignment.shift_type
- employee_checkin.shift_assignment = employee_checkin_issue.assigned_shift
- if employee_checkin_issue.latitude and employee_checkin_issue.longitude:
- employee_checkin.device_id = cstr(employee_checkin_issue.latitude)+","+cstr(employee_checkin_issue.longitude)
- if from_api:
- employee_checkin.flags.ignore_validate = True
- employee_checkin.save(ignore_permissions=True)
- frappe.db.commit()
+ @frappe.whitelist()
+ def create_hd_ticket(self):
+ if not self.ticket:
+ ticket_type = self.get_ticket_type()
+ ticket = frappe.new_doc('HD Ticket')
+ ticket.subject = "Employee Checkin Issue - {0}".format(self.issue_type)
+ ticket.raised_by = frappe.session.user
+ doc_link = frappe.utils.get_url(self.get_url())
+ description = ticket.subject + "
The user found an issue in the app \
+ and recorded in Employee Checkin Issue.
".format(doc_link)
+ ticket.description = description
+ ticket.ticket_type = ticket_type
+ ticket.save(ignore_permissions=True)
+ self.ticket = ticket.name
+ self.save(ignore_permissions=True)
+ self.reload()
+
+ def get_ticket_type(self):
+ if not frappe.db.exists('HD Ticket Type', {'name': 'Checkin Issue'}):
+ ticket_type_doc = frappe.get_doc(
+ {
+ "doctype": "HD Ticket Type",
+ "__newname": "Checkin Issue"
+ }
+ ).insert()
+ return ticket_type_doc.name
+ return 'Checkin Issue'
@frappe.whitelist()
def fetch_approver(employee, date=None):
@@ -154,32 +162,6 @@ def fetch_approver(employee, date=None):
tail_end = "on {0}".format(date)
frappe.throw("No shift assignment found for {employee} {tail_end}".format(employee=employee, tail_end=tail_end))
-# Approve pemding employee checkin issue before marking attendance
-def approve_open_employee_checkin_issue(start_date, end_date):
- try:
- employee_checkin_issue_list = frappe.db.sql(f"""
- SELECT eci.name FROM `tabEmployee Checkin Issue` eci JOIN `tabShift Assignment` sa
- ON sa.name=eci.assigned_shift
- WHERE sa.start_date='{start_date}' and sa.end_date='{end_date}'
- AND eci.workflow_state='Pending' AND eci.docstatus=0
- """, as_dict=1)
- error_list = """"""
- for employee_checkin_issue in employee_checkin_issue_list:
- try:
- # Apply workflow
- employee_checkin_issue_doc = frappe.get_doc("Employee Checkin Issue", employee_checkin_issue.name)
- employee_checkin_issue_doc.db_set('workflow_state', 'Approved')
- employee_checkin_issue_doc.db_set('docstatus', 1)
- employee_checkin_issue_doc.add_comment("Info", "This record is System Aprroved")
- employee_checkin_issue_doc.reload()
- # Create checkin from employee checkin issue
- create_employee_checkin_for_employee_checkin_issue(employee_checkin_issue_doc, True)
- except Exception as e:
- error_list += str(e)+'\n\n'
- if error_list:frappe.log_error(error_list, 'Employee Checkin Issue')
- except Exception as e:
- frappe.log_error(frappe.get_traceback(), 'Employee Checkin Issue')
-
@frappe.whitelist()
def create_checkin_issue(employee, issue_type, log_type, latitude, longitude, reason):
try:
diff --git a/one_fm/operations/doctype/employee_schedule/employee_schedule.json b/one_fm/operations/doctype/employee_schedule/employee_schedule.json
index 9342de65b9..51afaea114 100755
--- a/one_fm/operations/doctype/employee_schedule/employee_schedule.json
+++ b/one_fm/operations/doctype/employee_schedule/employee_schedule.json
@@ -8,6 +8,7 @@
"field_order": [
"employee",
"employee_name",
+ "employment_type",
"department",
"column_break_3",
"date",
@@ -19,6 +20,8 @@
"shift_type",
"start_datetime",
"end_datetime",
+ "is_replaced",
+ "replaced_employee_schedule",
"column_break_6",
"site",
"project",
@@ -192,6 +195,27 @@
{
"fieldname": "column_break_d2pic",
"fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "fieldname": "is_replaced",
+ "fieldtype": "Check",
+ "label": "Is Replaced"
+ },
+ {
+ "depends_on": "eval:doc.is_replaced;",
+ "fieldname": "replaced_employee_schedule",
+ "fieldtype": "Link",
+ "label": "Replaced Employee Schedule",
+ "options": "Employee Schedule"
+ },
+ {
+ "fetch_from": "employee.employment_type",
+ "fieldname": "employment_type",
+ "fieldtype": "Link",
+ "label": "Employment Type",
+ "options": "Employment Type",
+ "read_only": 1
}
],
"links": [],
diff --git a/one_fm/operations/doctype/shift_permission/shift_permission.js b/one_fm/operations/doctype/shift_permission/shift_permission.js
index 10993bc563..20e4237d0d 100755
--- a/one_fm/operations/doctype/shift_permission/shift_permission.js
+++ b/one_fm/operations/doctype/shift_permission/shift_permission.js
@@ -15,8 +15,15 @@ frappe.ui.form.on('Shift Permission', {
get_shift_assignment(frm);
},
date: function(frm) {
+ validate_date(frm);
get_shift_assignment(frm);
- }
+ },
+ validate: function(frm){
+ validate_date(frm)
+ },
+ permission_type: function(frm) {
+ get_shift_assignment(frm);
+ },
});
function set_employee_from_the_session_user(frm) {
@@ -38,10 +45,10 @@ function set_employee_from_the_session_user(frm) {
function set_options_for_permission_type(frm) {
if(frm.doc.log_type){
if(frm.doc.log_type == 'IN'){
- frm.set_df_property('permission_type', 'options', ['', 'Arrive Late', 'Forget to Checkin', 'Checkin Issue']);
+ frm.set_df_property('permission_type', 'options', ['', 'Arrive Late', ]);
}
else{
- frm.set_df_property('permission_type', 'options', ['', 'Leave Early', 'Forget to Checkout', 'Checkout Issue']);
+ frm.set_df_property('permission_type', 'options', ['', 'Leave Early', ]);
}
}
else{
@@ -81,3 +88,17 @@ function set_shift_details(frm, name, supervisor, shift, shift_type){
frappe.model.set_value(frm.doctype, frm.docname, "shift", shift);
frappe.model.set_value(frm.doctype, frm.docname, "shift_type", shift_type);
}
+
+
+var validate_date = (frm) => {
+ if (frm.doc.date){
+ if (frm.doc.date < frappe.datetime.now_date()){
+ if (frm.is_new()){
+ frappe.throw("Please note that Shift permission cannot be created for a past date")
+
+ } else {
+ frappe.throw("Please note that Shift permission cannot be updated to a past date")
+ }
+ }
+ }
+}
diff --git a/one_fm/operations/doctype/shift_permission/shift_permission.json b/one_fm/operations/doctype/shift_permission/shift_permission.json
index 22c2554411..fec2b045fc 100755
--- a/one_fm/operations/doctype/shift_permission/shift_permission.json
+++ b/one_fm/operations/doctype/shift_permission/shift_permission.json
@@ -12,7 +12,6 @@
"date",
"column_break_3",
"log_type",
- "permission_type",
"section_break_5",
"assigned_shift",
"roster_type",
@@ -25,9 +24,6 @@
"section_break_12",
"arrival_time",
"leaving_time",
- "latitude",
- "column_break_20",
- "longitude",
"section_break_17",
"reason",
"amended_from"
@@ -85,15 +81,6 @@
"options": "Shift Assignment",
"read_only": 1
},
- {
- "fieldname": "permission_type",
- "fieldtype": "Select",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Permission Type",
- "options": "\nArrive Late\nLeave Early\nForget to Checkin\nForget to Checkout\nCheckin Issue\nCheckout Issue",
- "reqd": 1
- },
{
"fieldname": "shift_supervisor",
"fieldtype": "Link",
@@ -119,21 +106,21 @@
},
{
"default": "Now",
- "depends_on": "eval:doc.permission_type == \"Arrive Late\"",
+ "depends_on": "eval:doc.log_type == \"IN\"",
"fieldname": "arrival_time",
"fieldtype": "Time",
"in_list_view": 1,
"label": "Arrival Time",
- "mandatory_depends_on": "eval:doc.permission_type == \"Arrive Late\""
+ "mandatory_depends_on": "eval:doc.log_type == \"IN\""
},
{
"default": "Now",
- "depends_on": "eval:doc.permission_type== \"Leave Early\"",
+ "depends_on": "eval:doc.log_type == \"OUT\"",
"fieldname": "leaving_time",
"fieldtype": "Time",
"in_list_view": 1,
"label": "Leaving Time",
- "mandatory_depends_on": "eval:doc.permission_type== \"Leave Early\""
+ "mandatory_depends_on": "eval:doc.log_type == \"OUT\""
},
{
"fieldname": "amended_from",
@@ -153,18 +140,6 @@
"label": "Employee Name",
"read_only": 1
},
- {
- "depends_on": "eval:doc.permission_type == \"Checkin Issue\" || doc.permission_type == \"Checkout Issue\"",
- "fieldname": "latitude",
- "fieldtype": "Float",
- "label": "Latitude"
- },
- {
- "depends_on": "eval:doc.permission_type == \"Checkin Issue\" || doc.permission_type == \"Checkout Issue\"",
- "fieldname": "longitude",
- "fieldtype": "Float",
- "label": "Longitude"
- },
{
"fieldname": "section_break_17",
"fieldtype": "Section Break"
@@ -201,10 +176,6 @@
"options": "\nIN\nOUT",
"reqd": 1
},
- {
- "fieldname": "column_break_20",
- "fieldtype": "Column Break"
- },
{
"fetch_from": "assigned_shift.roster_type",
"fetch_if_empty": 1,
diff --git a/one_fm/operations/doctype/shift_permission/shift_permission.py b/one_fm/operations/doctype/shift_permission/shift_permission.py
index a0f729b6d7..99c14d88b4 100755
--- a/one_fm/operations/doctype/shift_permission/shift_permission.py
+++ b/one_fm/operations/doctype/shift_permission/shift_permission.py
@@ -2,13 +2,14 @@
# Copyright (c) 2020, omar jaber and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
+from datetime import datetime
import frappe
from frappe.model.document import Document
-from frappe.utils import getdate, get_datetime, add_to_date, format_date, cstr, now
+from frappe.utils import getdate, format_date
+from frappe.workflow.doctype.workflow_action.workflow_action import apply_workflow
from frappe import _
+from frappe.desk.form.assign_to import add, remove
from one_fm.api.notification import create_notification_log, get_employee_user_id
-from hrms.hr.doctype.shift_assignment.shift_assignment import get_shift_details
-from one_fm.processor import sendemail
from one_fm.utils import has_super_user_role, get_approver
class PermissionTypeandLogTypeError(frappe.ValidationError):
@@ -25,12 +26,11 @@ class ShiftDetailsMissing(frappe.ValidationError):
class ShiftPermission(Document):
def validate(self):
- self.validate_permission_type()
self.check_shift_details_value()
self.validate_date()
self.validate_record()
self.validate_approver()
- if self.workflow_state in ['Pending', 'Approved']:
+ if self.workflow_state in ['Pending Approver', 'Approved']:
self.validate_attendance()
if not self.title:
self.title = self.emp_name
@@ -39,25 +39,10 @@ def validate_attendance(self):
attendance = frappe.db.exists('Attendance',{'attendance_date': self.date, 'employee': self.employee, 'docstatus': 1})
if attendance:
frappe.throw(_('There is an Attendance {0} exists for the Employee {1} on {2}'.format(attendance, self.emp_name, format_date(self.date))), exc=ExistAttendance)
-
- def validate_permission_type(self):
- if self.log_type == 'IN' and self.permission_type not in ['Arrive Late', 'Forget to Checkin', 'Checkin Issue']:
- frappe.throw(_('Permission Type cannot be {0}. It should be one of "Arrive Late", "Forget to Checkin", "Checkin Issue" for Log Type "IN"'.format(self.permission_type)),
- exc = PermissionTypeandLogTypeError)
- if self.log_type == 'OUT' and self.permission_type not in ['Leave Early', 'Forget to Checkout', 'Checkout Issue']:
- frappe.throw(_('Permission Type cannot be {0}. It should be one of "Leave Early", "Forget to Checkout", "Checkout Issue" for Log Type "OUT"'.format(self.permission_type)),
- exc = PermissionTypeandLogTypeError)
- if self.permission_type == "Arrive Late":
- field_list = [{'Arrival Time':'arrival_time'}]
- self.leaving_time = ''
- self.set_mandatory_fields(field_list)
- if self.permission_type == "Leave Early":
- field_list = [{'Leaving Time':'leaving_time'}]
- self.arrival_time = ''
- self.set_mandatory_fields(field_list)
- if self.permission_type not in ['Arrive Late', 'Leave Early']:
- self.arrival_time = ''
- self.leaving_time = ''
+
+ def on_update(self):
+ self.update_shift_assignment_checkin()
+ self.assign_to_owner()
# This method validates the shift details availability for employee
def check_shift_details_value(self):
@@ -66,17 +51,17 @@ def check_shift_details_value(self):
# This method validates the permission date and avoid creating permission for previous days
def validate_date(self):
- if self.docstatus==0 and getdate(self.date) < getdate() and self.is_new():
- frappe.throw(_("Oops! You cannot apply for permission for a previous date."))
+ if getdate(self.date) < getdate():
+ frappe.throw(_("Please note that shift permission can not be created for past date")) if self.is_new() else frappe.throw("Please note that shift permission can not be updated to a past date")
# This method validates any dublicate permission for the employee on same day
def validate_record(self):
date = getdate(self.date).strftime('%d-%m-%Y')
if self.docstatus==0 and frappe.db.exists("Shift Permission", {
"employee": self.employee, "date":self.date, "assigned_shift": self.assigned_shift,
- "permission_type": self.permission_type, "workflow_state":"Pending", 'name':['!=', self.name]
+ "workflow_state":"Pending Approver", 'name':['!=', self.name]
}):
- frappe.throw(_("{employee} has already applied for permission to {type} on {date}.".format(employee=self.emp_name, type=self.permission_type.lower(), date=date)))
+ frappe.throw(_("{employee} has already applied for permission on {date}.".format(employee=self.emp_name,date=date)))
# This method will display the mandatory fields for the user
def set_mandatory_fields(self,field_list):
@@ -101,8 +86,8 @@ def after_insert(self):
def send_notification(self):
date = getdate(self.date).strftime('%d-%m-%Y')
user = get_employee_user_id(self.shift_supervisor)
- subject = _("{employee} has applied for permission to {type} on {date}.".format(employee=self.emp_name, type=self.permission_type.lower(), date=date))
- message = _("{employee} has applied for permission to {type} on {date}.".format(employee=self.emp_name, type=self.permission_type.lower(), date=date))
+ subject = _("{employee} has applied for permission on {date}.".format(employee=self.emp_name, date=date))
+ message = _("{employee} has applied for permission on {date}.".format(employee=self.emp_name, date=date))
create_notification_log(subject, message, [user], self)
def validate_approver(self):
@@ -114,45 +99,80 @@ def validate_approver(self):
def on_submit(self):
employee_user = frappe.get_value('Employee', self.employee, 'user_id')
- subject = _('Your shift request {0} has been {1} by {2}'.format(self.name, self.workflow_state, self.approver_name))
+ subject = _('Your shift permission {0} has been {1} by {2}'.format(self.name, self.workflow_state, self.approver_name))
if self.workflow_state == 'Approved':
- create_employee_checkin_for_shift_permission(self)
if employee_user:
create_notification_log(subject, subject, [employee_user], self)
if self.workflow_state == 'Rejected':
message = False
if self.log_type == 'IN':
- message = _('Your shift request has been rejected, Please checkin once you arrive to the site before [half way mark] or your attendance will be marked absent!')
+ message = _('Your shift permission has been rejected, Please checkin once you arrive to the site before [half way mark] or your attendance will be marked absent!')
if self.log_type == 'OUT':
- message = _('Your shift request has been rejected, Please make sure to checkout!')
+ message = _('Your shift permission has been rejected, Please make sure to checkout!')
if message and employee_user:
create_notification_log(subject, message, [employee_user], self)
def on_cancel(self):
pass
-def create_employee_checkin_for_shift_permission(shift_permission):
- """
- Method to create Employee Checkin from the Shift Permission
- args:
- shift_permission: Object of Shift Permission
- """
- try:
- if frappe.db.get_single_value("HR and Payroll Additional Settings", 'validate_shift_permission_on_employee_checkin')\
- and not frappe.db.exists('Employee Checkin', {'shift_permission': shift_permission.name, 'docstatus': 1}):
- if shift_permission.permission_type in ["Arrive Late", "Forget to Checkin", "Checkin Issue"] and not shift_permission.log_type:
- shift_permission.db_set('log_type', "IN")
- elif shift_permission.permission_type in ["Leave Early", "Forget to Checkout", "Checkout Issue"] and not shift_permission.log_type:
- shift_permission.db_set('log_type', "OUT")
- shift_permission.reload()
- if not shift_permission.log_type:
- return False
- # Get shift details for the employee
- create_checkin(shift_permission)
- except Exception as e:
- frappe.log_error(frappe.get_traceback(), "Shift Permission")
+ def update_shift_assignment_checkin(self) -> None:
+ if self.workflow_state == "Approved" and self.get_doc_before_save().workflow_state != "Approved":
+ if self.assigned_shift:
+ if self.log_type == "IN":
+ if self.arrival_time:
+ date_time = datetime.strptime(self.date + " " + self.arrival_time, '%Y-%m-%d %H:%M:%S')
+ frappe.db.sql("""
+ UPDATE `tabShift Assignment`
+ SET start_datetime = %s
+ WHERE name = %s
+ """, (date_time, self.assigned_shift))
+
+ frappe.db.sql("""
+ UPDATE `tabEmployee Checkin`
+ SET shift_actual_start = %s, late_entry = 0
+ WHERE shift_assignment = %s
+ AND log_type = %s
+ """, (date_time, self.assigned_shift, self.log_type))
+
+ else:
+ if self.leaving_time:
+ date_time = datetime.strptime(self.date + " " + self.leaving_time, '%Y-%m-%d %H:%M:%S')
+ frappe.db.sql("""
+ UPDATE `tabShift Assignment`
+ SET end_datetime = %s
+ WHERE name = %s
+ """, (date_time, self.assigned_shift))
+
+ frappe.db.sql("""
+ UPDATE `tabEmployee Checkin`
+ SET shift_actual_end = %s, early_exit = 0
+ WHERE shift_assignment = %s
+ AND log_type = %s
+ """, (date_time, self.assigned_shift, self.log_type))
+
+ frappe.db.commit()
+
+ def assign_to_owner(self):
+ # Assign back to owner if Shift permission is Returned to Draft state from Pending Approver
+ if not self.get("__unsaved"):
+ if self.workflow_state == "Draft" and self.get_doc_before_save().workflow_state == "Pending Approver":
+ # Remove approver's assignment
+ remove(self.doctype, self.name, frappe.session.user, ignore_permissions=False)
+
+ # Assign back to document owner
+ add({
+ 'doctype': self.doctype,
+ 'name': self.name,
+ 'assign_to': [self.owner],
+ 'description': (_(f"Shift Permission: {self.name} has been returned to Draft. Please check and review."))
+ })
+
+ if self.workflow_state == "Pending Approver" and self.get_doc_before_save().workflow_state == "Draft":
+ # Remove doc owner's assignment
+ remove(self.doctype, self.name, self.owner, ignore_permissions=False)
+
@frappe.whitelist()
def fetch_approver(employee, date=None):
if employee:
@@ -167,7 +187,7 @@ def fetch_approver(employee, date=None):
limit_page_length=1
)
if employee_shift and len(employee_shift) > 0:
- approver = get_approver(employee, date)
+ approver = get_approver(employee)
return {
'shift_assignment':employee_shift[0].name,
'approver':approver,
@@ -184,8 +204,9 @@ def approve_open_shift_permission(start_date, end_date):
shift_permissions = frappe.db.sql(f"""
SELECT sp.name FROM `tabShift Permission` sp JOIN `tabShift Assignment` sa
ON sa.name=sp.assigned_shift
- WHERE sa.start_date='{start_date}' and sa.end_date='{end_date}'
- AND sp.workflow_state='Pending' AND sp.docstatus=0
+ WHERE sa.start_date ='{start_date}' and sa.end_date <='{end_date}'
+ AND sa.is_replaced = 0
+ AND sp.workflow_state='Pending Approver' AND sp.docstatus=0
""", as_dict=1)
# apply workflow
error_list = """"""
@@ -193,6 +214,7 @@ def approve_open_shift_permission(start_date, end_date):
try:
shift_permission = frappe.get_doc("Shift Permission", i.name)
create_checkin(shift_permission)
+ apply_workflow(shift_permission, 'Approve')
except Exception as e:
error_list += str(e)+'\n\n'
if error_list:frappe.log_error(error_list, 'Shift Permission')
@@ -231,6 +253,7 @@ def create_checkin(shift_permission):
}):
if not shift_permission.workflow_state == 'Approved':
shift_permission.db_set('workflow_state', "Approved")
+ shift_permission.db_set("docstatus", 1)
shift_permission.reload()
frappe.db.commit()
# Get shift details for the employee shift_assignment = frappe.get_doc("Shift Assignment", shift_permission.assigned_shift)
diff --git a/one_fm/overrides/assignment_rule.py b/one_fm/overrides/assignment_rule.py
index 818afdac2a..367c188a55 100644
--- a/one_fm/overrides/assignment_rule.py
+++ b/one_fm/overrides/assignment_rule.py
@@ -1,10 +1,12 @@
from one_fm.utils import get_doctype_mandatory_fields
from frappe.workflow.doctype.workflow_action.workflow_action import (
- get_next_possible_transitions,
+
get_workflow_name,
get_workflow_action_url,
get_doc_workflow_state
)
+
+from one_fm.overrides.workflow import get_next_possible_transitions
from frappe.model.workflow import (
apply_workflow,
get_workflow_state_field
diff --git a/one_fm/overrides/attendance.py b/one_fm/overrides/attendance.py
index e6d46e305e..ebed53e874 100644
--- a/one_fm/overrides/attendance.py
+++ b/one_fm/overrides/attendance.py
@@ -8,9 +8,8 @@
from datetime import timedelta, datetime as p_datetime
from hrms.hr.doctype.attendance.attendance import *
from hrms.hr.utils import validate_active_employee, get_holidays_for_employee
-from one_fm.utils import get_holiday_today
+from one_fm.utils import get_holiday_today, is_holiday
from one_fm.operations.doctype.shift_permission.shift_permission import create_checkin as approve_shift_permission
-from one_fm.operations.doctype.employee_checkin_issue.employee_checkin_issue import approve_open_employee_checkin_issue
from frappe.model import table_fields
from frappe.workflow.doctype.workflow_action.workflow_action import apply_workflow
@@ -197,7 +196,7 @@ def update_shift_details_in_attendance(doc):
def mark_single_attendance(emp, att_date, roster_type="Basic"):
# check if attendance exists
# get holiday, employee schedule, shift assignment, employee checkins
-
+
if not frappe.db.exists("Attendance", {
'employee': emp,
'attendance_date':att_date,
@@ -251,6 +250,7 @@ def mark_single_attendance(emp, att_date, roster_type="Basic"):
else:
mark_for_shift_assignment(employee.name, att_date)
+
def mark_for_shift_assignment(employee, att_date, roster_type='Basic'):
shift_assignment = frappe.db.get_value("Shift Assignment", {
'employee':employee,
@@ -287,12 +287,18 @@ def mark_for_shift_assignment(employee, att_date, roster_type='Basic'):
fields="name, owner, creation, modified, modified_by, docstatus, idx, employee, employee_name, log_type, late_entry, early_exit, time, date, skip_auto_attendance, shift_actual_start, shift_actual_end, shift_assignment, operations_shift, shift_type, shift_permission, actual_time, MAX(time) as time",
order_by="time DESC", group_by="shift_assignment"
)
+
out_checkins = _out_checkins[0] if _out_checkins else frappe._dict({})
+
+ early_exit = 0
# check if checkin and out exists
if (out_checkins and in_checkins):
if (out_checkins.time < in_checkins.time):
out_checkins = False # The employee checked in, out, in but not out
-
+
+ early_exit = not frappe.db.exists("Employee Checkin", {"log_type": "IN", "time": [">", out_checkins.time], "shift_assignment": shift_assignment.name}) if out_checkins.early_exit else 0
+
+
# start checkin
if in_checkins:
if ((in_checkins.time - shift_assignment.start_datetime).total_seconds() / (60*60)) > 4:
@@ -309,7 +315,7 @@ def mark_for_shift_assignment(employee, att_date, roster_type='Basic'):
comment = ""
checkin = in_checkins
checkout = out_checkins
-
+
create_single_attendance_record(frappe._dict({
'status': status,
'comment': comment,
@@ -319,12 +325,16 @@ def mark_for_shift_assignment(employee, att_date, roster_type='Basic'):
'checkout': checkout,
'shift_assignment':shift_assignment,
'employee':frappe.db.get_value("Employee", employee, ["name", "employee_name", "holiday_list"], as_dict=1),
- 'roster_type': roster_type
+ 'roster_type': roster_type,
+ "early_exit": early_exit
})
)
# working_hours = (out_time - in_time).total_seconds() / (60 * 60)
+
+
+
def create_single_attendance_record(record):
if not frappe.db.exists("Attendance", {
'employee':record.employee.name,
@@ -355,8 +365,6 @@ def create_single_attendance_record(record):
doc.site = record.shift_assignment.site
if record.checkin:
if record.checkin.late_entry:doc.late_entry=1
- if record.checkout:
- if record.checkout.early_exit:doc.early_exit=1
# check if worked less
work_duration = 0
if record.checkin and record.checkout and record.shift_assignment:
@@ -421,14 +429,14 @@ def remark_for_active_employees(from_date=None):
if i.shift_assignment:
shift_assignment = frappe.get_doc("Shift Assignment", i.shift_assignment)
checkins = frappe.get_list(
- "Employee Checkin",
- {"shift_assignment":i.shift_assignment},
+ "Employee Checkin",
+ {"shift_assignment":i.shift_assignment},
"*",
order_by="time ASC")
if checkins:
ins = [d for d in checkins if d.log_type=="IN"]#.sort(key = lambda x:x.time)
outs = [d for d in checkins if d.log_type=="OUT"]#.sort(key = lambda x:x.time)
-
+
if ins:ins = ins[0]
if outs:
outs = outs[-1]
@@ -463,7 +471,7 @@ def remark_for_active_employees(from_date=None):
'roster_type': shift_assignment.roster_type
})
)
-
+
def remark_absent_for_employees(employees, date):
@@ -485,28 +493,29 @@ def mark_overtime_attendance(from_date, to_date):
mark_for_shift_assignment(employee.name, from_date, roster_type='Over-Time')
+
+
def mark_all_attendance():
- from one_fm.operations.doctype.shift_permission.shift_permission import approve_open_shift_permission
- start_date = add_days(getdate(), -1)
- end_date = getdate()
- approve_open_shift_permission(str(start_date), str(end_date))
- approve_open_employee_checkin_issue(str(start_date), str(end_date))
- frappe.enqueue(mark_open_timesheet_and_create_attendance)
- frappe.enqueue(mark_leave_attendance)
- frappe.enqueue(mark_daily_attendance, start_date=start_date, end_date=end_date, timeout=4000, queue='long')
+ from one_fm.operations.doctype.shift_permission.shift_permission import approve_open_shift_permission
+ start_date = add_days(getdate(), -1)
+ end_date = getdate()
+ frappe.enqueue(approve_open_shift_permission, start_date=str(start_date), end_date=str(end_date))
+ frappe.enqueue(approve_pending_employee_checkin_issue)
+ frappe.enqueue(mark_open_timesheet_and_create_attendance)
+ frappe.enqueue(mark_daily_attendance, start_date=start_date, end_date=end_date, timeout=4000, queue='long')
def mark_daily_attendance(start_date, end_date):
try:
creation = now()
owner = frappe.session.user
naming_series = 'HR-ATT-.YYYY.-'
- existing_attendance = [i.employee for i in frappe.get_list("Attendance", {
+ existing_attendance = frappe.get_list("Attendance", {
'attendance_date':start_date,
- 'roster_type':'Basic', 'status':['IN', ['Present', 'Holiday', 'On Leave',
- 'Work From Home', 'On Hold', 'Day Off']]
+ 'roster_type':'Basic',
+ 'status':['IN', ['Present', 'Holiday', 'On Leave','Work From Home', 'On Hold', 'Day Off']]
},
- "employee"
- )]
+ pluck="employee"
+ )
query = """
INSERT INTO `tabAttendance` (`name`, `naming_series`,`employee`, `employee_name`, `working_hours`, `status`, `shift`, `in_time`, `out_time`,
@@ -521,24 +530,24 @@ def mark_daily_attendance(start_date, end_date):
# Mark Holiday Attendance
holiday_attendance_employee = frappe.db.sql(f"""
SELECT e.name, e.employee_name, e.company, e.department,
- h.description from `tabEmployee` e ,`tabHoliday List` hl
+ h.description from `tabEmployee` e ,`tabHoliday List` hl
INNER JOIN `tabHoliday` h ON h.parent = hl.name
- WHERE e.holiday_list = hl.name
+ WHERE e.holiday_list = hl.name
AND h.holiday_date = '{start_date}'
AND h.weekly_off=0
AND '{start_date}' BETWEEN hl.from_date AND hl.to_date
AND e.status='Active'
""",
as_dict=1)
-
-
+
+
if holiday_attendance_employee:
for i in holiday_attendance_employee:
if not i.name in existing_attendance:
try:
frappe.db.sql(f"""
DELETE FROM `tabAttendance` WHERE employee="{i.name}" AND
- attendance_date="{start_date}"
+ attendance_date="{start_date}"
AND roster_type="Basic"
AND status="Absent"
""")
@@ -552,15 +561,16 @@ def mark_daily_attendance(start_date, end_date):
"{i.department}", 0, 0, "", "", "Basic", {1}, "{owner}",
"{owner}", "{creation}", "{creation}", "{i.description}"
),"""
+
today = getdate()
# Mark DayOff Attendance
#Find Employees with no schedule but have Day Off in the company holiday. Mainly for head office employees
day_off_no_schedule = frappe.db.sql(f"""
SELECT e.name, e.employee_name, e.company, e.department,
- h.description from `tabEmployee` e ,`tabHoliday List` hl
+ h.description from `tabEmployee` e ,`tabHoliday List` hl
INNER JOIN `tabHoliday` h ON h.parent = hl.name
- WHERE e.holiday_list = hl.name
+ WHERE e.holiday_list = hl.name
AND h.holiday_date = '{start_date}'
AND h.weekly_off=1
AND e.attendance_by_timesheet = 0
@@ -570,16 +580,16 @@ def mark_daily_attendance(start_date, end_date):
AND e.date_of_joining < '{today}'
""",
as_dict=1)
-
-
+
+
day_off_employee = frappe.db.sql(f"""
- SELECT e.name, e.employee_name, e.company, e.department, es.name as es_name from `tabEmployee` e
+ SELECT e.name, e.employee_name, e.company, e.department, es.name as es_name from `tabEmployee` e
INNER JOIN `tabEmployee Schedule` es ON es.employee =e.name
- WHERE date='{start_date}'
+ WHERE date='{start_date}'
AND es.employee_availability='Day Off'
AND e.attendance_by_timesheet = 0
AND e.status='Active'
-
+
""", as_dict=1
)
@@ -591,7 +601,7 @@ def mark_daily_attendance(start_date, end_date):
try:
frappe.db.sql(f"""
DELETE FROM `tabAttendance` WHERE employee="{i.name}" AND
- attendance_date="{start_date}"
+ attendance_date="{start_date}"
AND roster_type="Basic"
AND status="Absent"
""")
@@ -607,7 +617,7 @@ def mark_daily_attendance(start_date, end_date):
),"""
attendance_request = frappe.db.sql(f"""
- SELECT e.name, e.employee_name, e.company, e.department, ar.name as ar_name from `tabEmployee` e
+ SELECT e.name, e.employee_name, e.company, e.department, ar.name as ar_name, ar.reason as ar_reason from `tabEmployee` e
INNER JOIN `tabAttendance Request` ar ON ar.employee =e.name
WHERE '{start_date}' BETWEEN ar.from_date AND ar.to_date
AND ar.workflow_state='Approved'
@@ -617,13 +627,14 @@ def mark_daily_attendance(start_date, end_date):
# create BASIC DAY OFF
for i in attendance_request:
+ status = "Work From Home" if i.ar_reason == "Work From Home" else "Present"
if not i.get('ar_name'):
i.ar_name = i.description
if not i.name in existing_attendance:
try:
frappe.db.sql(f"""
DELETE FROM `tabAttendance` WHERE employee="{i.name}" AND
- attendance_date="{start_date}"
+ attendance_date="{start_date}"
AND roster_type="Basic"
AND status="Absent"
""")
@@ -632,12 +643,18 @@ def mark_daily_attendance(start_date, end_date):
continue
query_body+= f"""
(
- "HR-ATT_{start_date}_{i.name}_Basic", "{naming_series}" , "{i.name}", "{i.employee_name}", 0, "Work From Home", '', NULL,
+ "HR-ATT_{start_date}_{i.name}_Basic", "{naming_series}" , "{i.name}", "{i.employee_name}", 0, "{status}", '', NULL,
NULL, "", "", "", "", "{start_date}", "{i.company}",
"{i.department}", 0, 0, "", "", "Basic", 1, "{owner}",
"{owner}", "{creation}", "{creation}", "Attendance Request - {i.ar_name}"
),"""
+ else:
+ att_name = frappe.db.get_value("Attendance", {"employee": i.name, "attendance_date": start_date}, "name")
+ if att_name:
+ frappe.db.set_value("Attendance", att_name, "status", status)
+
+
# UPDATE QUERY
if query_body:
query += query_body[:-1]
@@ -673,7 +690,7 @@ def mark_daily_attendance(start_date, end_date):
end_date = add_days(start_date, 1)
attendance_marking = AttendanceMarking()
attendance_marking.get_datetime(
- start=p_datetime.strptime(f'{start_date} 00:00:00', '%Y-%m-%d %H:%M:%S'),
+ start=p_datetime.strptime(f'{start_date} 00:00:00', '%Y-%m-%d %H:%M:%S'),
end=p_datetime.strptime(f'{end_date} 00:00:00', '%Y-%m-%d %H:%M:%S'),
attendance_type=True,
)
@@ -688,7 +705,7 @@ def mark_daily_attendance(start_date, end_date):
timeout=7000
)
except Exception as e:
- frappe.log_error(frappe.get_traceback(), "Attendance Marking")
+ frappe.log_error(frappe.get_traceback(), "Attendance Marking")
def remark_attendance(start_date, end_date):
try:
@@ -701,7 +718,7 @@ def remark_attendance(start_date, end_date):
'docstatus':1},
fields='name, employee'
)
- missing_attendances = [i.employee for i in shift_assignments if not i.employee in [x.employee for x in attendances]]
+ missing_attendances = [i.employee for i in shift_assignments if not i.employee in [x.employee for x in attendances]]
for employee in missing_attendances:
mark_bulk_attendance(employee, start_date, start_date)
except:
@@ -726,23 +743,55 @@ def update_day_off_ot(attendances):
if day_off_ot:
att.db_set("day_off_ot", day_off_ot)
except:
- frappe.log_error(frappe.get_traceback(), "Attendance Marking OT")
+ frappe.log_error(frappe.get_traceback(), "Attendance Marking OT")
def mark_open_timesheet_and_create_attendance():
- timesheets = frappe.get_list("Timesheet", {'workflow_state':'Open',
- 'start_date':add_days(getdate(), -1)})
+ the_date = add_days(getdate(), -1)
+ employee_list = frappe.db.get_list("Employee", filters={"status": "Active", "attendance_by_timesheet": 1}, pluck="name")
+ timesheets = frappe.db.get_list("Timesheet", {'workflow_state':'Pending Approval',
+ 'start_date': the_date},)
+
for i in timesheets:
try:
apply_workflow(frappe.get_doc("Timesheet", i.name), 'Approve')
except Exception as e:
- pass
-
+ frappe.log_error(frappe.get_traceback(), "Timesheet Marking")
+
+ present_employees = frappe.db.get_list("Timesheet", filters={"start_date": the_date, "workflow_state": "Approved"}, pluck="employee")
+ for obj in employee_list:
+ status, message = is_holiday(employee=obj)
+ if obj not in present_employees:
+ att = frappe.new_doc("Attendance")
+ att.employee = obj
+ att.attendance_date = the_date
+ att.status = "Absent" if not status else "Day Off"
+ att.working_hours = 0
+ att.reference_doctype = "Timesheet"
+ try:
+ att.insert(ignore_permissions=True)
+ att.submit()
+ except Exception as e:
+ frappe.log_error(frappe.get_traceback(), "TImesheet Attendance Marking")
+ frappe.db.commit()
+
+
+def approve_pending_employee_checkin_issue():
+ pending_eci = frappe.db.get_list("Employee Checkin Issue", {"workflow_state": "Pending Approval", "date": add_days(getdate(), -1)}, pluck="name")
+ if pending_eci:
+ for obj in pending_eci:
+ try:
+ apply_workflow(frappe.get_doc("Employee Checkin Issue", obj), "Approve")
+ except Exception as e:
+ frappe.log_error(frappe.get_traceback(), "Error while approving employee checkin issue")
+ frappe.db.commit()
+
+
def mark_leave_attendance():
try:
date = add_days(getdate(), -1)
creation = now()
-
+
owner = frappe.session.user
naming_series = 'HR-ATT-.YYYY.-'
e_list = []
@@ -772,12 +821,20 @@ def mark_leave_attendance():
AND l.status = 'Approved'
""", as_dict=1)
on_leave_employees = [i for i in on_leave_employees if not i.employee in basic_attendance_employees]
-
- # create On Hold Attendance
+
+ # create On Hold Attendance
if on_leave_employees:
for i in on_leave_employees:
- basic_attendance_employees.append(i.employee)
-
+ name = f"HR-ATT_{date}_{i.employee}_Basic"
+ emp = employees_dict.get(i.employee)
+ query_body+= f"""
+ (
+ "{name}", "{naming_series}","{i.employee}", "{i.employee_name}", "On Leave", '{i.leave_type}', '{i.name}',
+ "{date}", "{i.company}", "{i.department}","Basic", {1}, "{owner}",
+ "{owner}", "{creation}", "{creation}", "{i.leave_type}"
+ ),"""
+ basic_attendance_employees.append(i.employee)
+
if query_body:
query += query_body[:-1]
query += f"""
@@ -806,18 +863,21 @@ def mark_timesheet_daily_attendance(timesheet_employees,start_date):
Mark all the employees included in the daily attendance schedule
"""
try:
-
+
query = """
- INSERT INTO `tabAttendance` (`name`, `naming_series`,`employee`, `employee_name`, `working_hours`, `status`, `shift`, `in_time`, `out_time`,
- `shift_assignment`, `operations_shift`, `site`, `project`, `attendance_date`, `company`,
- `department`, `late_entry`, `early_exit`, `operations_role`, `post_abbrv`, `roster_type`, `docstatus`, `modified_by`, `owner`,
- `creation`, `modified`, `comment`)
+ INSERT INTO
+ `tabAttendance`
+ (
+ `name`, `naming_series`,`employee`, `employee_name`, `employment_type`, `working_hours`, `status`,
+ `shift`, `in_time`, `out_time`, `shift_assignment`, `operations_shift`, `site`, `project`,
+ `attendance_date`, `company`, `department`, `late_entry`, `early_exit`, `operations_role`,
+ `post_abbrv`, `roster_type`, `docstatus`, `modified_by`, `owner`, `creation`, `modified`, `comment`
+ )
VALUES
-
"""
employees = frappe.get_all("Employee",filters={"name":["IN",timesheet_employees]},fields="*")
employees_dict = frappe._dict()
-
+
for i in employees:
employees_dict[i.name] = i
owner = frappe.session.user
@@ -829,7 +889,7 @@ def mark_timesheet_daily_attendance(timesheet_employees,start_date):
name = f"HR-ATT_{start_date}_{i}_Basic"
query_body+= f"""
(
- "{name}", "{naming_series}", "{i}", "{emp.employee_name}", 0, "Absent", '{emp.shift}', NULL,
+ "{name}", "{naming_series}", "{i}", "{emp.employee_name}", "{emp.employment_type}", 0, "Absent", '{emp.shift}', NULL,
NULL, "NULL", "NULL", "{emp.site}", "{emp.project}", "{start_date}", "{emp.company}",
"{emp.department}", 0, 0, "NULL", "NULL", "Basic", {1}, "{owner}",
"{owner}", "{creation}", "{creation}", "No Timesheet record found"
@@ -841,6 +901,7 @@ def mark_timesheet_daily_attendance(timesheet_employees,start_date):
naming_series = VALUES(naming_series),
employee = VALUES(employee),
employee_name = VALUES(employee_name),
+ employment_type = VALUES(employment_type),
working_hours = VALUES(working_hours),
status = VALUES(status),
shift = VALUES(shift),
@@ -872,7 +933,7 @@ class AttendanceMarking():
This class will be used to mark attendance
"""
- def __ini__(self):
+ def __init__(self):
self.attendance_type = None
self.start = None
self.end = None
@@ -893,9 +954,9 @@ def mark_shift_attendance(self):
if self.attendance_type:
client_shifts = frappe.db.sql(f"""
SELECT sa.* FROM `tabShift Assignment` sa
- JOIN `tabOperations Role` op ON sa.operations_role=op.name
- WHERE
- sa.start_date='{self.start.date()}'
+ JOIN `tabOperations Role` op ON sa.operations_role=op.name
+ WHERE sa.is_replaced = 0
+ AND sa.start_date='{self.start.date()}'
AND op.attendance_by_client=1 AND op.docstatus=1
;
""", as_dict=1)
@@ -905,24 +966,24 @@ def mark_shift_attendance(self):
shifts = frappe.db.sql(f"""
SELECT sa.* FROM `tabShift Assignment` sa
- JOIN `tabOperations Role` op ON sa.operations_role=op.name
- WHERE
- sa.start_date='{self.start.date()}'
+ JOIN `tabOperations Role` op ON sa.operations_role=op.name
+ WHERE sa.is_replaced = 0
+ AND sa.start_date='{self.start.date()}'
AND op.attendance_by_client=0 AND op.status='Active'
""", as_dict=1)
non_shifts = frappe.db.sql(f"""
SELECT sa.* FROM `tabShift Assignment` sa
- JOIN `tabEmployee` e ON sa.employee=e.name
- WHERE
- sa.end_datetime BETWEEN '{self.start}' AND '{self.end}'
+ JOIN `tabEmployee` e ON sa.employee=e.name
+ WHERE sa.is_replaced = 0
+ AND sa.end_datetime BETWEEN '{self.start}' AND '{self.end}'
AND e.shift_working=0""", as_dict=1)
shifts.extend(non_shifts)
else:
client_shifts = frappe.db.sql(f"""
SELECT sa.* FROM `tabShift Assignment` sa
- JOIN `tabOperations Role` op ON sa.operations_role=op.name
- WHERE
- sa.end_datetime BETWEEN '{self.start}' AND '{self.end}'
+ JOIN `tabOperations Role` op ON sa.operations_role=op.name
+ WHERE sa.is_replaced = 0
+ AND sa.end_datetime BETWEEN '{self.start}' AND '{self.end}'
AND op.attendance_by_client=1 AND op.status='Active'
;
""", as_dict=1)
@@ -932,33 +993,44 @@ def mark_shift_attendance(self):
shifts = frappe.db.sql(f"""
SELECT sa.* FROM `tabShift Assignment` sa
- JOIN `tabOperations Role` op ON sa.operations_role=op.name
- WHERE
- sa.end_datetime BETWEEN '{self.start}' AND '{self.end}'
+ JOIN `tabOperations Role` op ON sa.operations_role=op.name
+ WHERE sa.is_replaced = 0
+ AND sa.end_datetime BETWEEN '{self.start}' AND '{self.end}'
AND op.attendance_by_client=0 AND op.status='Active'
""", as_dict=1)
non_shifts = frappe.db.sql(f"""
SELECT sa.* FROM `tabShift Assignment` sa
- JOIN `tabEmployee` e ON sa.employee=e.name
- WHERE
- sa.end_datetime BETWEEN '{self.start}' AND '{self.end}'
+ JOIN `tabEmployee` e ON sa.employee=e.name
+ WHERE sa.is_replaced = 0
+ AND sa.end_datetime BETWEEN '{self.start}' AND '{self.end}'
AND e.shift_working=0""", as_dict=1)
shifts.extend(non_shifts)
+
+
+
if shifts:
- checkins = self.get_checkins(tuple([i.name for i in shifts]) if len(shifts)>1 else (shifts[0].name))
+ existing_attendance = frappe.get_list("Attendance", {
+ 'attendance_date':getdate(self.start),
+ 'shift_assignment':['in',[i.name for i in shifts]],
+ 'status':['IN', ['Present', 'Holiday', 'On Leave','Work From Home', 'On Hold', 'Day Off']]
+ },
+ pluck="employee"
+ )
+ checkins = self.get_checkins(tuple([i.name for i in shifts]) if len(shifts) > 1 else (shifts[0].name,""))
if checkins:
# employees = [i.employee for i in shifts]
checked_in_employees = [i.employee for i in checkins]
no_checkins = [i for i in shifts if not i.employee in checked_in_employees]
- if no_checkins: #create absent
+ if no_checkins: #create absent if no attendance already exists
for i in no_checkins:
- try:
- record = frappe._dict({**dict(i), **{
- "status":"Absent", "comment":"No checkin record found", "working_hours":0,
- "dt":"Shift Assignment"}})
- self.create_attendance(record)
- except:
- pass
+ if i.employee not in existing_attendance:
+ try:
+ record = frappe._dict({**dict(i), **{
+ "status":"Absent", "comment":"No checkin record found", "working_hours":0,
+ "dt":"Shift Assignment"}})
+ self.create_attendance(record)
+ except:
+ frappe.log_error(message=frappe.get_traceback(), title=f"Error Marking Attendance for {i.employee}")
if checkins: # check for work hours
# start checkin
for i in checkins:
@@ -967,11 +1039,12 @@ def mark_shift_attendance(self):
'employee':i.employee,
'attendance_date':i.shift_actual_start.date(),
'roster_type':i.roster_type,
- 'status': ["IN", ["Present", "On Leave", "Holiday", "Day Off",
+ 'status': ["IN", ["Present", "On Leave", "Holiday", "Day Off",
"On Hold", "Work From Home"]]
}):
total_hours = (i.shift_actual_end - i.shift_actual_start).total_seconds() / (60*60)
- half_hour = total_hours/2
+ i.early_exit = self.check_early_exit(i)
+ half_hour = total_hours / 2
working_hours = 0
status = "Absent"
comment = ""
@@ -1003,53 +1076,93 @@ def mark_shift_attendance(self):
except Exception as e:
pass
+
+ def check_early_exit(self, checkin: dict) -> bool:
+ """
+ Validates the presence of a checkin record created after the last checkout record if
+ the last checkout record was set as a early exit
+
+ """
+ if checkin:
+ in_name = checkin.get("in_name")
+ out_name = checkin.get("out_name")
+ if in_name and out_name:
+ early_exit_doc = frappe.db.get_value(
+ "Employee Checkin",
+ {"name": out_name, "early_exit": 1, "log_type": "OUT"},
+ ["time"],
+ as_dict=1
+ )
+
+ if early_exit_doc:
+ check = frappe.db.sql(
+ """
+ SELECT name FROM `tabEmployee Checkin`
+ WHERE shift_assignment = %s
+ AND log_type = %s
+ AND time > %s
+ """,
+ (checkin.shift_assignment,"IN",early_exit_doc.time),
+ as_dict=1
+ )
+ return not bool(check)
+ return False
+
+
+
+
+
+
def get_checkins(self, shift_assignments):
query = f"""
- SELECT
- ec.name,
- ec.owner,
- ec.creation,
- ec.modified,
- ec.modified_by,
- ec.docstatus,
- ec.idx,
- ec.employee,
- ec.employee_name,
- ec.log_type,
+ SELECT
+ ec.name,
+ ec.owner,
+ ec.creation,
+ ec.modified,
+ ec.modified_by,
+ ec.docstatus,
+ ec.idx,
+ ec.employee,
+ ec.employee_name,
+ ec.log_type,
ec.late_entry,
ec.early_exit,
- ec.time,
- ec.date,
- ec.skip_auto_attendance,
- ec.shift_actual_start,
- ec.shift_actual_end,
- ec.shift_assignment,
- ec.operations_shift,
+ ec.time,
+ ec.date,
+ ec.skip_auto_attendance,
+ ec.shift_actual_start,
+ ec.shift_actual_end,
+ ec.shift_assignment,
+ ec.operations_shift,
ec.shift_type,
- ec.roster_type,
- ec.operations_site,
- ec.project,
- ec.company,
- ec.operations_role,
+ ec.roster_type,
+ ec.operations_site,
+ ec.project,
+ ec.company,
+ ec.operations_role,
ec.post_abbrv,
- ec.shift_permission,
- ec.actual_time,
+ ec.shift_permission,
+ ec.actual_time,
+ ec.is_replaced,
MIN(CASE WHEN ec.log_type = 'IN' THEN ec.time END) AS earliest_time,
MAX(CASE WHEN ec.log_type = 'OUT' THEN ec.time END) AS latest_time,
- MIN(CASE WHEN ec.log_type = 'IN' THEN ec.name END) AS in_name,
+ MIN(CASE WHEN ec.log_type = 'IN' THEN ec.name END) AS in_name,
MAX(CASE WHEN ec.log_type = 'OUT' THEN ec.name END) AS out_name
- FROM
+ FROM
`tabEmployee Checkin` ec
- WHERE
+ WHERE
ec.shift_assignment in {shift_assignments}
- GROUP BY
+ AND ec.is_replaced = 0
+
+ GROUP BY
ec.shift_assignment;
"""
return frappe.db.sql(query, as_dict=1)
def mark_day_off(self):
days_off = frappe.get_list("Employee Schedule", {
- 'date':self.start.date(), 'employee_availability':'Day Off'
+ 'date':self.start.date(), 'employee_availability':'Day Off', "is_replaced": 0
}, "*")
for i in days_off:
try:
@@ -1057,18 +1170,18 @@ def mark_day_off(self):
'employee':i.employee,
'attendance_date':i.date,
'roster_type':i.roster_type,
- 'status': ["IN", ["Present", "On Leave", "Holiday", "Day Off",
+ 'status': ["IN", ["Present", "On Leave", "Holiday", "Day Off",
"On Hold", "Work From Home"]]
}):
record = frappe._dict({**dict(i), **{
"status":"Day Off", "comment":f"Employee Schedule - {i.name}",
"dt":"Employee Schedule"}})
self.create_attendance(record)
-
+
except Exception as e:
pass
-
+
def create_attendance(self, record, attendace_type=None):
try:
# clear absent
@@ -1082,19 +1195,18 @@ def create_attendance(self, record, attendace_type=None):
try:
frappe.db.sql(f"""
DELETE FROM `tabAttendance` WHERE employee="{record.employee}" AND
- attendance_date="{_date}"
+ attendance_date="{_date}"
AND roster_type="{record.roster_type}"
""")
except:
pass
frappe.db.commit()
-
doc = frappe._dict({})
doc.doctype = "Attendance"
doc.employee = record.employee
doc.status = record.status
-
+
doc.attendance_date = _date
if record.shift_assignment:
doc.shift_assignment = record.shift_assignment
@@ -1106,6 +1218,11 @@ def create_attendance(self, record, attendace_type=None):
doc.shift = record.shift_type
doc.operations_shift = record.shift
doc.site = record.site
+ doc.custom_employment_type = record.custom_employment_type
+ if record.dt=="Employee Schedule":
+ doc.custom_employment_type = record.employment_type
+ if record.dt=="Employee Checkin":
+ doc.custom_employment_type = frappe.db.get_value("Employee", doc.employee, "employment_type")
if record.late_entry:
doc.late_entry= record.late_entry
if record.early_exit:
@@ -1130,7 +1247,7 @@ def create_attendance(self, record, attendace_type=None):
frappe.db.set_value("Employee Checkin", record.out_name, 'attendance', doc.name)
frappe.db.commit()
except Exception as e:
- frappe.log_error(message=str(e), title="Hourly Attendance Marking")
+ frappe.log_error(message=frappe.get_traceback(), title=f"Hourly Attendance Marking - {str(e)}")
def run_attendance_marking_hourly():
@@ -1144,8 +1261,8 @@ def mark_day_off_for_yesterday():
start_date = add_days(getdate(), -1)
attendance_marking = AttendanceMarking()
attendance_marking.get_datetime(
- start=p_datetime.strptime(f'{start_date} 00:00:00', '%Y-%m-%d %H:%M:%S'),
+ start=p_datetime.strptime(f'{start_date} 00:00:00', '%Y-%m-%d %H:%M:%S'),
end=p_datetime.strptime(f'{getdate()} 00:00:00', '%Y-%m-%d %H:%M:%S'),
attendance_type=True,
)
- frappe.enqueue(attendance_marking.mark_day_off, queue="long", timeout=6000)
\ No newline at end of file
+ frappe.enqueue(attendance_marking.mark_day_off, queue="long", timeout=6000)
diff --git a/one_fm/overrides/attendance_request.py b/one_fm/overrides/attendance_request.py
index 436adbb4cc..678789af91 100644
--- a/one_fm/overrides/attendance_request.py
+++ b/one_fm/overrides/attendance_request.py
@@ -1,20 +1,20 @@
import frappe, pandas as pd
from frappe import _
-from frappe.model.document import Document
-from frappe.utils import add_days, date_diff, getdate, nowdate, get_link_to_form, format_date
+from frappe.utils import getdate, get_link_to_form, format_date
from erpnext.setup.doctype.employee.employee import is_holiday
-from hrms.hr.utils import validate_active_employee, validate_dates
+from hrms.hr.utils import validate_active_employee
from hrms.hr.doctype.attendance_request.attendance_request import AttendanceRequest
-from frappe.model.workflow import apply_workflow
+
+from frappe.desk.form.assign_to import add, remove
from one_fm.utils import (
- send_workflow_action_email, get_holiday_today, workflow_approve_reject, get_approver, has_super_user_role
+ send_workflow_action_email, workflow_approve_reject, get_approver, has_super_user_role
)
class AttendanceRequestOverride(AttendanceRequest):
def validate(self):
validate_active_employee(self.employee)
- validate_future_dates(self, self.from_date, self.to_date)
+ 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"))
@@ -24,7 +24,7 @@ def before_insert(self):
check_for_attendance(self)
def set_approver(self):
- if not self.approver and self.employee:
+ if self.employee:
self.approver = get_approver(self.employee)
if self.approver:
approver = frappe.db.get_value(
@@ -33,7 +33,8 @@ def set_approver(self):
['user_id', 'employee_name'],
as_dict=1
)
- pass
+ self.approver_name = approver.employee_name
+ self.approver_user = approver.user_id
def on_submit(self):
if not self.reports_to():
@@ -54,14 +55,13 @@ def cancel_requested_attendance(self):
def on_update(self):
self.send_notification()
+ self.assign_to_owner()
def on_update_after_submit(self):
self.send_notification()
if self.update_request:
if self.workflow_state == 'Approved':
self.create_attendance()
- if self.workflow_state == 'Update Request':
- self.cancel_requested_attendance()
def get_shift_assignment(self, attendance_date):
"""
@@ -73,7 +73,7 @@ def get_shift_assignment(self, attendance_date):
def create_attendance(self):
date_range = pd.date_range(self.from_date, self.to_date)
for d in date_range:
- if d.date()<= getdate():
+ if d.date() <= getdate():
self.mark_attendance(str(d.date()))
def get_employee(self):
@@ -86,7 +86,7 @@ def mark_attendance(self, attendance_date):
working_hours = frappe.db.get_value('Shift Type', shift_assignment.shift_type, 'duration') if shift_assignment else 8
# check if attendance exists
attendance_name = super(AttendanceRequestOverride, self).get_attendance_record(attendance_date)
- status = "Work From Home" if self.reason == "Work From Home" else "Present"
+ status = "Present" if self.reason == "On Duty" else "Work From Home"
if attendance_name:
# update existing attendance, change the status
doc = frappe.get_doc("Attendance", attendance_name)
@@ -157,8 +157,8 @@ def mark_attendance(self, attendance_date):
def send_notification(self):
if self.workflow_state in ['Pending Approval']:
- send_workflow_action_email(self, [self.approver])
- if self.workflow_state in ['Rejected', 'Approved', 'Update Request', 'Cancelled']:
+ send_workflow_action_email(self, [self.approver_user])
+ if self.workflow_state in ['Rejected', 'Approved', 'Cancelled']:
workflow_approve_reject(self, recipients=None)
def validate_if_attendance_not_applicable(self, attendance_date):
@@ -189,56 +189,65 @@ def validate_if_attendance_not_applicable(self, attendance_date):
@frappe.whitelist()
def reports_to(self):
employee_user = frappe.get_value("Employee", {"name": self.employee}, "user_id")
- if frappe.session.user == self.approver or has_super_user_role(employee_user) or (
+ if frappe.session.user == self.approver_user or has_super_user_role(employee_user) or (
frappe.session.user == "administrator"
):
return True
- frappe.msgprint('This Attendance Request can only be approved by the employee supervisor')
return False
+ def assign_to_owner(self):
+ # Assign back to owner if Attendance Request is Returned to Draft state from Pending Approval
+ if not self.get("__unsaved"):
+ if self.workflow_state == "Draft" and self.get_doc_before_save().workflow_state == "Pending Approval":
+ # Remove approver's assignment
+ remove(self.doctype, self.name, frappe.session.user, ignore_permissions=False)
+
+ # Assign back to document owner
+ add({
+ 'doctype': self.doctype,
+ 'name': self.name,
+ 'assign_to': [self.owner],
+ 'description': (_(f"Attendance Request: {self.name} has been returned to Draft. Please check and review."))
+ })
+ if self.workflow_state == "Pending Approval" and self.get_doc_before_save().workflow_state == "Draft":
+ # Remove doc owner's assignment
+ remove(self.doctype, self.name, self.owner, ignore_permissions=False)
+
def check_for_attendance(doc):
att = frappe.get_list("Attendance", {"employee": doc.employee, "attendance_date":["between", [doc.from_date, doc.to_date]]}, ['status'])
if att:
frappe.msgprint("Your attendance is marked for today as "+ att[0].status )
-def validate_future_dates(doc, from_date, to_date):
+def validate_dates(doc, from_date, to_date):
date_of_joining, relieving_date = frappe.db.get_value(
"Employee", doc.employee, ["date_of_joining", "relieving_date"]
)
if getdate(from_date) > getdate(to_date):
- frappe.throw(_("To date can not be less than from date"))
+ frappe.throw(_("To date can not be less than from date"), title="Invalid From Date")
elif date_of_joining and getdate(from_date) < getdate(date_of_joining):
- frappe.throw(_("From date can not be less than employee's joining date"))
+ frappe.throw(_("From date can not be less than employee's joining date"), title="Invalid From Date")
elif relieving_date and getdate(to_date) > getdate(relieving_date):
- frappe.throw(_("To date can not greater than employee's relieving date"))
-
-
-
-@frappe.whitelist()
-def update_request(attendance_request, from_date, to_date):
- from_date = getdate(from_date)
- to_date = getdate(to_date)
- attendance_request_obj = frappe.get_doc('Attendance Request', attendance_request)
- validate_future_dates(attendance_request_obj, from_date, to_date)
- attendance_request_obj.db_set("from_date", from_date)
- attendance_request_obj.db_set("to_date", to_date)
- attendance_request_obj.db_set("update_request", True)
- apply_workflow(attendance_request_obj, "Update Request")
-
+ frappe.throw(_("To date can not greater than employee's relieving date"), title="Invalid From Date")
+ if getdate(from_date) < getdate():
+ if doc.is_new():
+ msg = _("Please note that Attendance Request cannot be created for a past date")
+ else:
+ msg = _("Please note that Attendance Request cannot be updated for a past date")
+ frappe.throw(msg, title="Invalid From Date")
def mark_future_attendance_request():
- """
- GET attendance request for the future where date is today
- """
- attendance_requests = frappe.db.sql(f"""
- SELECT name FROM `tabAttendance Request`
- WHERE '{getdate()}' BETWEEN from_date AND to_date
- AND docstatus=1
- """, as_dict=1)
- for row in attendance_requests:
- try:
- frappe.get_doc("Attendance Request", row.name).mark_attendance(str(getdate()))
- except Exception as e:
- frappe.log_error(str(e), 'Attendance Request')
+ """
+ GET attendance request for the future where date is today
+ """
+ attendance_requests = frappe.db.sql(f"""
+ SELECT name FROM `tabAttendance Request`
+ WHERE '{getdate()}' BETWEEN from_date AND to_date
+ AND docstatus=1
+ """, as_dict=1)
+ for row in attendance_requests:
+ try:
+ frappe.get_doc("Attendance Request", row.name).mark_attendance(str(getdate()))
+ except Exception as e:
+ frappe.log_error(str(e), 'Attendance Request')
diff --git a/one_fm/overrides/employee.py b/one_fm/overrides/employee.py
index d2a8996253..a79a54b348 100644
--- a/one_fm/overrides/employee.py
+++ b/one_fm/overrides/employee.py
@@ -493,6 +493,48 @@ def message(self):
return self._message
+def has_day_off(employee, date):
+ """
+ Checks if an employee has a scheduled day off on a specific date.
+ Args:
+ employee (str): The name of the employee to check for a day off.
+ date (str): The date for which to check if the employee has a scheduled day off.
+ Format: "YYYY-MM-DD"
+ Returns:
+ bool: True if the employee has a scheduled day off on the given date, False otherwise.
+ """
+ if frappe.db.exists(
+ "Employee Schedule",
+ {
+ "employee":employee,
+ "date":date,
+ "employee_availability":"Day Off"
+ }
+ ):
+ return True
+ return False
+
+def is_employee_on_leave(employee, date):
+ """
+ Checks if an employee is on leave on a specific date.
+ Args:
+ employee (str): The name of the employee to check for leave.
+ date (str): The date for which to check if the employee is on leave.
+ Format: "YYYY-MM-DD"
+ Returns:
+ bool: True if the employee is on leave on the given date, False otherwise.
+ """
+ if frappe.db.exists(
+ "Attendance",
+ {
+ "status": "On Leave",
+ "docstatus": 1,
+ "employee": employee,
+ "attendance_date": date
+ }
+ ):
+ return True
+ return False
diff --git a/one_fm/overrides/employee_checkin.py b/one_fm/overrides/employee_checkin.py
index b60f02ffb1..092a4f24a6 100644
--- a/one_fm/overrides/employee_checkin.py
+++ b/one_fm/overrides/employee_checkin.py
@@ -28,15 +28,17 @@ def fetch_shift(self):
pass
def validate(self):
- validate_active_employee(self.employee)
- self.validate_duplicate_log()
- if frappe.db.get_single_value("HR and Payroll Additional Settings", 'validate_shift_permission_on_employee_checkin'):
- try:
+ try:
+ validate_active_employee(self.employee)
+ self.validate_duplicate_log()
+ if frappe.db.get_single_value("HR and Payroll Additional Settings",
+ 'validate_shift_permission_on_employee_checkin'):
existing_perm = None
checkin_time = get_datetime(self.time)
curr_shift = get_current_shift(self.employee)
if curr_shift:
- curr_shift = curr_shift.as_dict()
+ current_shift_obj = curr_shift['data']
+ curr_shift = current_shift_obj.as_dict()
start_date = curr_shift["start_date"].strftime("%Y-%m-%d")
existing_perm = frappe.db.sql(f""" select name from `tabShift Permission` where date = '{start_date}' and employee = '{self.employee}' and permission_type = '{perm_map[self.log_type]}' and workflow_state = 'Approved' """, as_dict=1)
self.shift_assignment = curr_shift["name"]
@@ -57,8 +59,8 @@ def validate(self):
self.time = curr_shift["end_datetime"]
self.skip_auto_attendance = 0
self.shift_permission = existing_perm[0]["name"]
- except Exception as e:
- frappe.throw(frappe.get_traceback())
+ except Exception as e:
+ frappe.throw(frappe.get_traceback())
def validate_duplicate_log(self):
doc = frappe.db.sql(f""" select name from `tabEmployee Checkin` where employee = '{self.employee}' and time = '{self.time}' and (NOT name = '{self.name}')""", as_dict=1)
@@ -79,13 +81,16 @@ def before_insert(self):
self.shift_actual_end = sa.end_datetime
def after_insert(self):
- frappe.db.commit()
- self.reload()
- if not (self.shift_assignment and self.shift_type and self.operations_shift and self.shift_actual_start and self.shift_actual_end):
+ try:
+ frappe.db.commit()
+ self.reload()
+ # if not (self.shift_assignment and self.shift_type and self.operations_shift and self.shift_actual_start and self.shift_actual_end):
frappe.enqueue(after_insert_background, employee_checkin=self.name)
- if self.log_type == "IN":
- frappe.enqueue(notify_supervisor_about_late_entry, checkin=self)
+ if self.log_type == "IN":
+ frappe.enqueue(notify_supervisor_about_late_entry, checkin=self)
+ except Exception as e:
+ frappe.log_error(frappe.get_traceback(), 'Employee Checkin')
def exists_checkin(current_shift_assignment, checkin_name, log_type="IN"):
'''
@@ -108,23 +113,24 @@ def exists_checkin(current_shift_assignment, checkin_name, log_type="IN"):
return False
def after_insert_background(employee_checkin):
- checkin_doc = frappe.get_doc("Employee Checkin", employee_checkin)
+ self = frappe.get_doc("Employee Checkin", employee_checkin)
try:
# update shift if not exists
- curr_shift = get_current_shift(checkin_doc.employee)
+ curr_shift = get_current_shift(self.employee)
if curr_shift:
+ curr_shift = curr_shift['data']
shift_type = frappe.db.sql(f"""SELECT * FROM `tabShift Type` WHERE name='{curr_shift.shift_type}' """, as_dict=1)[0]
# calculate entry
early_exit = 0
late_entry = 0
- actual_time = str(checkin_doc.time)
+ actual_time = str(self.time)
if not '.' in actual_time:
actual_time += '.000000'
-
- if checkin_doc.log_type=='IN':
+
+ if self.log_type=='IN':
if (datetime.strptime(actual_time, '%Y-%m-%d %H:%M:%S.%f') - timedelta(minutes=shift_type.late_entry_grace_period)) > curr_shift.start_datetime:
late_entry = 1
- if checkin_doc.log_type=='OUT':
+ if self.log_type=='OUT':
if (datetime.strptime(actual_time, '%Y-%m-%d %H:%M:%S.%f') + timedelta(minutes=shift_type.early_exit_grace_period)) < curr_shift.end_datetime:
early_exit = 1
@@ -132,12 +138,12 @@ def after_insert_background(employee_checkin):
UPDATE `tabEmployee Checkin` SET
shift_assignment="{curr_shift.name}", operations_shift="{curr_shift.shift}", shift_type='{curr_shift.shift_type}',
shift='{curr_shift.shift_type}', shift_actual_start="{curr_shift.start_datetime}", shift_actual_end="{curr_shift.end_datetime}",
- shift_start="{curr_shift.start_datetime.date()}", shift_end="{curr_shift.end_datetime.date()}", early_exit={early_exit},
- late_entry={late_entry}, date='{curr_shift.start_date if checkin_doc.log_type=='IN' else curr_shift.end_datetime}',
+ shift_start="{curr_shift.start_datetime}", shift_end="{curr_shift.end_datetime}", early_exit={early_exit},
+ late_entry={late_entry}, date='{curr_shift.start_date if self.log_type=='IN' else curr_shift.end_datetime}',
operations_site="{curr_shift.site}", post_abbrv="{curr_shift.post_abbrv}", project="{curr_shift.project}",
company="{curr_shift.company}", operations_role="{curr_shift.operations_role}",
roster_type='{curr_shift.roster_type}'
- WHERE name="{checkin_doc.name}"
+ WHERE name="{self.name}"
"""
frappe.db.sql(query, values=[], as_dict=1)
frappe.db.commit()
@@ -151,76 +157,72 @@ def after_insert_background(employee_checkin):
start_time = get_datetime(cstr(getdate()) + " 00:00:00")
end_time = get_datetime(cstr(getdate()) + " 23:59:59")
- log_exist = frappe.db.exists("Employee Checkin", {"log_type": checkin_doc.log_type, "time": [ "between", (start_time, end_time)], "skip_auto_attendance": 0 ,"shift_type": checkin_doc.shift_type, "name": ["!=", checkin_doc.name]})
+ log_exist = frappe.db.exists("Employee Checkin", {"log_type": self.log_type, "time": [ "between", (start_time, end_time)], "skip_auto_attendance": 0 ,"shift_type": self.shift_type, "name": ["!=", self.name]})
if not log_exist:
# In case of back to back shift
- if checkin_doc.shift_type:
- shift_type = frappe.get_doc("Shift Type", checkin_doc.shift_type)
+ if self.shift_type:
+ shift_type = frappe.get_doc("Shift Type", self.shift_type)
curr_shift = frappe._dict({
- 'actual_start': checkin_doc.shift_actual_start,
- 'actual_end': checkin_doc.shift_actual_end,
- 'end_datetime': checkin_doc.shift_end,
- 'start_datetime': checkin_doc.shift_start,
+ 'actual_start': self.shift_actual_start,
+ 'actual_end': self.shift_actual_end,
+ 'end_datetime': self.shift_end,
+ 'start_datetime': self.shift_start,
'shift_type': shift_type
})
-
-
if curr_shift:
- supervisor_user = get_notification_user(checkin_doc, checkin_doc.employee)
- distance, radius = validate_location(checkin_doc)
+ supervisor_user = get_notification_user(self, self.employee)
+ distance, radius = validate_location(self)
message_suffix = _("Location logged is inside the site.") if distance <= radius else _("Location logged is {location}m outside the site location.").format(location=cstr(cint(distance)- radius))
-
- if checkin_doc.log_type == "IN" and checkin_doc.skip_auto_attendance == 0:
+ if self.log_type == "IN" and self.skip_auto_attendance == 0:
# LATE: Checkin time is after [Shift Start + Late Grace Entry period]
- if shift_type.enable_entry_grace_period == 1 and get_datetime(checkin_doc.time) > (get_datetime(checkin_doc.shift_actual_start) + timedelta(minutes=shift_type.late_entry_grace_period)):
- time_diff = get_datetime(checkin_doc.time) - get_datetime(checkin_doc.shift_actual_start)
+ if shift_type.enable_entry_grace_period == 1 and get_datetime(self.time) > (get_datetime(self.shift_actual_start) + timedelta(minutes=shift_type.late_entry_grace_period)):
+ time_diff = get_datetime(self.time) - get_datetime(self.shift_actual_start)
hrs, mins, secs = cstr(time_diff).split(":")
delay = "{hrs} hrs {mins} mins".format(hrs=hrs, mins=mins) if cint(hrs) > 0 else "{mins} mins".format(mins=mins)
- subject = _("{employee} has checked in late by {delay}. {location}".format(employee=checkin_doc.employee_name, delay=delay, location=message_suffix))
- message = _("{employee_name} has checked in late by {delay}. {location}
Issue Penalty
".format(employee_name=checkin_doc.employee_name,shift=checkin_doc.operations_shift, date=cstr(checkin_doc.time), employee=checkin_doc.employee, delay=delay, location=message_suffix))
+ subject = _("{employee} has checked in late by {delay}. {location}".format(employee=self.employee_name, delay=delay, location=message_suffix))
+ message = _("{employee_name} has checked in late by {delay}. {location}
Issue Penalty
".format(employee_name=self.employee_name,shift=self.operations_shift, date=cstr(self.time), employee=self.employee, delay=delay, location=message_suffix))
for_users = [supervisor_user]
- create_notification_log(subject, message, for_users, checkin_doc)
+ create_notification_log(subject, message, for_users, self)
- elif checkin_doc.log_type == "IN" and checkin_doc.skip_auto_attendance == 1:
- subject = _("Hourly Report: {employee} checked in at {time}. {location}".format(employee=checkin_doc.employee_name, time=checkin_doc.time, location=message_suffix))
- message = _("Hourly Report: {employee} checked in at {time}. {location}".format(employee=checkin_doc.employee_name, time=checkin_doc.time, location=message_suffix))
+ elif self.log_type == "IN" and self.skip_auto_attendance == 1:
+ subject = _("Hourly Report: {employee} checked in at {time}. {location}".format(employee=self.employee_name, time=self.time, location=message_suffix))
+ message = _("Hourly Report: {employee} checked in at {time}. {location}".format(employee=self.employee_name, time=self.time, location=message_suffix))
for_users = [supervisor_user]
- create_notification_log(subject, message, for_users, checkin_doc)
+ create_notification_log(subject, message, for_users, self)
- elif checkin_doc.log_type == "OUT":
+ elif self.log_type == "OUT":
# Automatic checkout
- if not checkin_doc.device_id:
+ if not self.device_id:
title = "Checkin Report"
category = "Attendance"
- subject = _("Automated Checkout: {employee} forgot to checkout.".format(employee=checkin_doc.employee_name))
- message = _('Review check out '.format(name=checkin_doc.name))
+ subject = _("Automated Checkout: {employee} forgot to checkout.".format(employee=self.employee_name))
+ message = _('Review check out '.format(name=self.name))
for_users = [supervisor_user]
send_notification(title, subject, message, category, for_users)
#EARLY: Checkout time is before [Shift End - Early grace exit time]
- elif shift_type.enable_exit_grace_period == 1 and checkin_doc.device_id and get_datetime(checkin_doc.time) < (get_datetime(checkin_doc.shift_actual_end) - timedelta(minutes=shift_type.early_exit_grace_period)):
- time_diff = get_datetime(checkin_doc.shift_actual_end) - get_datetime(checkin_doc.time)
+ elif shift_type.enable_exit_grace_period == 1 and self.device_id and get_datetime(self.time) < (get_datetime(self.shift_actual_end) - timedelta(minutes=shift_type.early_exit_grace_period)):
+ time_diff = get_datetime(self.shift_actual_end) - get_datetime(self.time)
hrs, mins, secs = cstr(time_diff).split(":")
early = "{hrs} hrs {mins} mins".format(hrs=hrs, mins=mins) if cint(hrs) > 0 else "{mins} mins".format(mins=mins)
- subject = _("{employee} has checked out early by {early}. {location}".format(employee=checkin_doc.employee_name, early=early, location=message_suffix))
- message = _("{employee_name} has checked out early by {early}. {location}
Issue Penalty
".format(employee_name=checkin_doc.employee_name, shift=checkin_doc.operations_shift, date=cstr(checkin_doc.time), employee=checkin_doc.employee_name, early=early, location=message_suffix))
+ subject = _("{employee} has checked out early by {early}. {location}".format(employee=self.employee_name, early=early, location=message_suffix))
+ message = _("{employee_name} has checked out early by {early}. {location}
Issue Penalty
".format(employee_name=self.employee_name, shift=self.operations_shift, date=cstr(self.time), employee=self.employee_name, early=early, location=message_suffix))
for_users = [supervisor_user]
- create_notification_log(subject, message, for_users, checkin_doc)
+ create_notification_log(subject, message, for_users, self)
else:
# When no shift assigned, supervisor of active shift of the nearest site is sent a notification about unassigned checkin.
- location = checkin_doc.device_id
- # supervisor = get_closest_location(checkin_doc.time, location)
- reporting_manager = frappe.get_value("Employee", {"user_id": checkin_doc.owner}, "reports_to")
+ location = self.device_id
+ # supervisor = get_closest_location(self.time, location)
+ reporting_manager = frappe.get_value("Employee", {"user_id": self.owner}, "reports_to")
supervisor = get_employee_user_id(reporting_manager)
if supervisor:
- subject = _("{employee} has checked in on an unassigned shift".format(employee=checkin_doc.employee_name))
- message = _("{employee} has checked in on an unassigned shift".format(employee=checkin_doc.employee_name))
+ subject = _("{employee} has checked in on an unassigned shift".format(employee=self.employee_name))
+ message = _("{employee} has checked in on an unassigned shift".format(employee=self.employee_name))
for_users = [supervisor]
- create_notification_log(subject, message, for_users, checkin_doc)
-
-
+ create_notification_log(subject, message, for_users, self)
+
@frappe.whitelist()
diff --git a/one_fm/overrides/leave_application.py b/one_fm/overrides/leave_application.py
index 5319ab1e58..2effff8a0d 100644
--- a/one_fm/overrides/leave_application.py
+++ b/one_fm/overrides/leave_application.py
@@ -5,8 +5,8 @@
from frappe.utils import get_fullname, nowdate, add_to_date
from hrms.hr.doctype.leave_application.leave_application import *
from one_fm.processor import sendemail
-
-from frappe.desk.form.assign_to import add
+from frappe.desk.form.assign_to import add,remove
+from erpnext.crm.utils import get_open_todos
from one_fm.api.notification import create_notification_log
from frappe.utils import getdate, date_diff
import pandas as pd
@@ -486,6 +486,14 @@ def update_attendance_recods(self):
frappe.msgprint(_("Attendance are created for the leave Appication {0}!".format(self.name)), alert=True)
+
+def remove_assignment(attendance_check):
+ open_todo = get_open_todos("Attendance Check",attendance_check)
+ if open_todo:
+ for each in open_todo:
+ remove("Attendance Check",attendance_check,each.allocated_to,ignore_permissions=1)
+
+
@frappe.whitelist()
def get_leave_details(employee, date):
allocation_records = get_leave_allocation_records(employee, date)
diff --git a/one_fm/overrides/shift_assignment.py b/one_fm/overrides/shift_assignment.py
index 3192705c60..d8768e4ecb 100644
--- a/one_fm/overrides/shift_assignment.py
+++ b/one_fm/overrides/shift_assignment.py
@@ -5,7 +5,7 @@
class ShiftAssignmentOverride(ShiftAssignment):
-
+
def validate(self):
self.set_datetime()
super(ShiftAssignmentOverride, self).validate()
@@ -25,7 +25,7 @@ def before_insert(self):
self.set_datetime()
if not frappe.db.exists("Employee", {'name':self.employee, 'status':'Active'}):
frappe.throw(f"{self.employee} - {self.employee_name} is not active and cannot be assigned to a shift")
-
+
def set_datetime(self):
if self.shift_type:
shift = frappe.get_doc("Shift Type", self.shift_type)
@@ -48,7 +48,7 @@ def get_cut_off(self):
'start':start_cutoff,
'end':end_cutoff,
})
-
+
def can_checkin_out(self):
"""
Check if user can checkin or our based on earliest and latest in or out.
@@ -57,7 +57,7 @@ def can_checkin_out(self):
if ((now_datetime() < cutoff.start) or (now_datetime() > cutoff.end)):
return False
return True
-
+
def after_4hrs(self):
"""
Check if checkin time has exceeded 4hrs, which mean employee is late.
@@ -68,41 +68,54 @@ def after_4hrs(self):
return True
return False
- def check_existing_checking(self):
- """API to determine the applicable Log type.
- The api checks employee's last lcheckin log type. and determine what next log type needs to be
- Returns:
- True: The log in was "IN", so his next Log Type should be "OUT".
- False: either no log type or last log type is "OUT", so his next Ltg Type should be "IN".
+ def get_last_checkin_log_type(self):
+ """
+ The method checks employee's last checkin log type
+ Returns:
+ The last log_type if a checkin recod exist for the shift assignment
+ Else return False
"""
- checkin = frappe.db.get_list("Employee Checkin", filters={
- 'employee':self.employee, 'shift_assignment':self.name,
- 'roster_type':self.roster_type
- },
- fields='log_type',
+ checkin = frappe.db.get_list(
+ "Employee Checkin",
+ filters={
+ "employee":self.employee,
+ "shift_assignment":self.name,
+ "shift_actual_start":self.start_datetime,
+ "shift_actual_end":self.end_datetime,
+ "roster_type":self.roster_type
+ },
+ fields="log_type",
order_by="actual_time DESC"
)
- if checkin:
- # #For Check IN
- if checkin[0].log_type=='OUT':
- return "IN"
- #For Check OUT
- else:
- return "OUT"
+ if checkin and len(checkin) > 0:
+ return checkin[0].log_type
+ return False
+
+ def get_next_checkin_log_type(self):
+ """
+ Method to determine the applicable Log type.
+ The method checks employee's last lcheckin log type. and determine what next log type needs to be
+ Returns:
+ The last log_type if a checkin recod exist for the shift assignment
+ Else return IN
+ """
+ last_log_type = self.get_last_checkin_log_type()
+ if last_log_type and last_log_type == "IN":
+ return "OUT"
return "IN"
def has_overlapping_timings(self) -> bool:
"""
Accepts two shift types and checks whether their timings are overlapping
"""
- if datetime.strptime(str(self.start_datetime), '%Y-%m-%d %H:%M:%S').date()>datetime.strptime(today(), '%Y-%m-%d').date():
+ if datetime.strptime(str(self.start_datetime), '%Y-%m-%d %H:%M:%S').date() > datetime.strptime(today(), '%Y-%m-%d').date():
frappe.throw(f"Shift cannot be created for date greater than today. Today is {today()}, you requested {self.start_date}")
existing_shift = frappe.db.sql(f"""
SELECT * FROM `tabShift Assignment` WHERE
employee="{self.employee}" AND status='Active' AND docstatus=1 AND (
(start_datetime BETWEEN '{self.start_datetime}' AND '{self.end_datetime}')
- OR
+ OR
(end_datetime BETWEEN '{self.start_datetime}' AND '{self.end_datetime}')
)
@@ -115,4 +128,3 @@ def has_overlapping_timings(self) -> bool:
""")
return True
return False
-
\ No newline at end of file
diff --git a/one_fm/overrides/shift_request.py b/one_fm/overrides/shift_request.py
index 0f6972a07c..a8706e2388 100644
--- a/one_fm/overrides/shift_request.py
+++ b/one_fm/overrides/shift_request.py
@@ -4,13 +4,14 @@
from frappe import _
from frappe.utils import getdate, today, cstr, add_to_date, add_days, get_url_to_form
from frappe.model.workflow import apply_workflow
+from frappe.desk.form.assign_to import add as add_assignment, DuplicateToDoError
from frappe.utils import getdate, cstr
from hrms.hr.utils import share_doc_with_approver
from hrms.hr.doctype.shift_request.shift_request import *
from one_fm.utils import (
workflow_approve_reject, send_workflow_action_email, get_approver
)
-from one_fm.api.notification import get_employee_user_id
+from one_fm.api.notification import create_notification_log, get_employee_user_id
from one_fm.operations.doctype.operations_shift.operations_shift import get_supervisor_operations_shifts
from one_fm.processor import sendemail
@@ -19,41 +20,15 @@ class OverlappingShiftError(frappe.ValidationError):
pass
class ShiftRequestOverride(ShiftRequest):
- def validate(self):
- # ensure status is not pending
- if self.is_new():
- self.status='Draft'
- if self.status=='Pending Approval':
- self.status == 'Draft'
-
- process_shift_assignemnt(self) # set shift assignment and employee schedule
-
def on_submit(self):
if self.workflow_state != 'Update Request':
- self.db_set("status", self.workflow_state) if self.workflow_state!='Pending Approval' else self.db_set("status", 'Draft')
+ self.db_set("status", self.workflow_state) if self.workflow_state!='Pending Approver' else self.db_set("status", 'Draft')
- def before_save(self):
- # Fill 'To' date if not set
- if not self.to_date:
- self.to_date = self.from_date
-
- # Validate 'From' date
- if not self.assign_day_off:
- if getdate(today()) > getdate(self.from_date):
- frappe.throw('From Date cannot be before today.')
-
- send_shift_request_mail(self)
def on_update(self):
for approver in self.custom_shift_approvers:
share_doc_with_approver(self, approver.user)
- if self.workflow_state in ['Approved', 'Rejected']:
- workflow_approve_reject(self, [get_employee_user_id(self.employee)])
-
- if self.workflow_state == 'Draft':
- send_workflow_action_email(self,[approver.user for approver in self.custom_shift_approvers])
- validate_shift_overlap(self)
def validate_approver(self):
if not self.is_new() and self.workflow_state == "Approved":
@@ -95,7 +70,7 @@ def cancel_shift_assignment_of_request(self):
shift_assignment_doc = frappe.get_doc("Shift Assignment", shift["name"])
shift_assignment_doc.cancel()
shift_assignment_doc.delete()
- if self.from_date <= cstr(getdate()) <= self.to_date and schedule_exists:
+ if self.from_date <= getdate() <= self.to_date and schedule_exists:
schedule = frappe.get_doc("Employee Schedule",{"employee":self.employee, "date":cstr(getdate())})
if schedule:
sa = frappe.get_doc(dict(
@@ -113,17 +88,78 @@ def cancel_shift_assignment_of_request(self):
)).insert()
sa.submit()
+def validate(doc, event=None):
+ # ensure status is not pending
+ if doc.is_new():
+ doc.status = 'Draft'
+ if doc.status == 'Pending Approver':
+ doc.status == 'Draft'
+ if doc.status == 'Draft' and doc.purpose == 'Assign Unrostered Employee':
+ if check_for_roster(doc):
+ frappe.throw("Employee Already has been rostered for the given dates")
+ if doc.status == 'Draft' and doc.purpose == 'Replace Existing Assignment':
+ if doc.replaced_employee:
+ shift_assignemnt_exists = frappe.get_list("Shift Assignment",
+ filters=[['employee', '=', doc.replaced_employee],
+ ['start_date', 'between', [doc.from_date, doc.to_date]],
+ ['roster_type', '=', "Basic"]], fields=['name'])
+ if not shift_assignemnt_exists:
+ frappe.throw("Employee being replaced does not have existing shift.")
+ process_shift_assignment(doc) # set shift assignment and employee schedule
+
+
+def shift_request_submit(self):
+ if self.workflow_state != 'Update Request':
+ self.db_set("status", self.workflow_state) if self.workflow_state != 'Pending Approver' else self.db_set(
+ "status", 'Draft')
+
+
+def validate_default_shift(self):
+ default_shift = frappe.get_value("Employee", self.employee, "default_shift")
+ if self.shift_type == default_shift:
+ pass
+
+
+def on_update(doc, event):
+ if doc.workflow_state in ['Approved', 'Rejected']:
+ workflow_approve_reject(doc, [get_employee_user_id(doc.employee)])
+
+ if doc.workflow_state == 'Draft':
+ send_workflow_action_email(doc,[approver.user for approver in doc.custom_shift_approvers])
+ validate_shift_overlap(doc)
+
+ if doc.workflow_state == 'Pending Approver':
+ assign_approver(doc, doc.approver)
+
+
+def assign_approver(doc, approver_id):
+ add_assignment({
+ 'doctype': doc.doctype,
+ 'name': doc.name,
+ 'assign_to': [approver_id],
+ 'description':
+ _(
+ f"""
+ Please Note that a Shift Request {doc.name} has been submitted.
+ Please review and take necessary actions
+ """
+ )
+ })
+
def validate_shift_overlap(doc):
curr_date = getdate()
- shift_assignment = frappe.db.get_list("Shift Assignment", {'employee':doc.employee, 'start_date': doc.from_date, "roster_type": "Basic", 'status':'Active'}, ['shift','start_datetime', 'end_datetime'])
- shift_type = frappe.db.get_list("Shift Type",{'name':doc.shift_type}, ['start_time', 'end_time'])
+ shift_assignment = frappe.db.get_list("Shift Assignment", {'employee': doc.employee, 'start_date': doc.from_date,
+ "roster_type": "Basic", 'status': 'Active'},
+ ['shift', 'start_datetime', 'end_datetime'])
+ shift_type = frappe.db.get_list("Shift Type", {'name': doc.shift_type}, ['start_time', 'end_time'])
- shift_start_time = datetime.datetime.combine(curr_date , (datetime.datetime.min + shift_type[0].start_time).time())
+ shift_start_time = datetime.datetime.combine(curr_date, (datetime.datetime.min + shift_type[0].start_time).time())
if shift_type[0].start_time > shift_type[0].end_time:
- shift_end_time = datetime.datetime.combine(add_to_date(curr_date, days=1) , (datetime.datetime.min + shift_type[0].end_time).time())
+ shift_end_time = datetime.datetime.combine(add_to_date(curr_date, days=1),
+ (datetime.datetime.min + shift_type[0].end_time).time())
else:
- shift_end_time = datetime.datetime.combine(curr_date , (datetime.datetime.min + shift_type[0].end_time).time())
+ shift_end_time = datetime.datetime.combine(curr_date, (datetime.datetime.min + shift_type[0].end_time).time())
if doc.roster_type == "Over-Time" and shift_assignment:
if shift_start_time < shift_assignment[0].end_datetime:
@@ -136,35 +172,173 @@ def validate_shift_overlap(doc):
frappe.throw(msg, title=_("Overlapping Shifts"), exc=OverlappingShiftError)
+def shift_request_cancel(self):
+ '''
+ Method used to override Shift Request on_cancel
+ '''
+ cancel_shift_assignment_of_request(self)
+
+
def on_update_after_submit(doc, method):
if doc.update_request:
if doc.workflow_state == 'Update Request':
doc.db_set("status", 'Draft')
doc.cancel_shift_assignment_of_request()
-def process_shift_assignemnt(doc, event=None):
- role_abbr = frappe.db.get_value("Operations Role",doc.operations_role,'post_abbrv')
- if doc.workflow_state=='Approved' and doc.docstatus==1:
- if doc.assign_day_off == 1:
+
+def process_shift_assignment(doc, event=None):
+ day_off_ot = 1 if doc.purpose == "Day Off Overtime" else 0
+ role_abbr = frappe.db.get_value("Operations Role", doc.operations_role, 'post_abbrv')
+ shift_worker = frappe.db.get_value("Employee", doc.employee, 'shift_working')
+ if doc.workflow_state == 'Approved' and doc.docstatus == 1:
+ if doc.purpose == "Assign Day Off":
assign_day_off(doc)
- else:
- if doc.roster_type == "Basic" and cstr(doc.from_date) == cstr(getdate()):
- shift_assignemnt = frappe.get_value("Shift Assignment", {'employee':doc.employee, 'start_date': doc.from_date, 'roster_type':"Basic"}, ['name'])
- if shift_assignemnt:
- update_shift_assignment(shift_assignemnt, doc )
- else:
- create_shift_assignment_from_request(doc)
-
- # check for existing schedule
+ elif doc.purpose == 'Replace Existing Assignment':
+ if doc.roster_type == "Basic" and cstr(doc.from_date) <= cstr(getdate()) <= cstr(doc.to_date):
+ shift_assignemnt = frappe.get_list("Shift Assignment", filters=[['employee', '=', doc.employee],
+ ['start_date', 'between',
+ [doc.from_date, doc.to_date]],
+ ['roster_type', '=', "Basic"]],
+ fields=['name'])
+ replace_shift_assignment(shift_assignemnt, doc)
+ if shift_worker == 1:
+ # check for existing schedule
+ schedule_date_range = [str(i.date()) for i in pd.date_range(start=doc.from_date, end=doc.to_date)]
+ existing_schedules = frappe.db.sql(f""" SELECT name, date FROM `tabEmployee Schedule`
+ WHERE employee="{doc.employee}" AND roster_type="{doc.roster_type}"
+ AND date BETWEEN '{doc.from_date}' AND '{doc.to_date}' """, as_dict=1)
+ if existing_schedules:
+ replace_employee_schedule(doc, existing_schedules, schedule_date_range)
+ elif doc.purpose == 'Assign Unrostered Employee':
schedule_date_range = [str(i.date()) for i in pd.date_range(start=doc.from_date, end=doc.to_date)]
+ new_date_range = [i for i in schedule_date_range]
+
+ for date in new_date_range:
+ create_employee_schedule_from_request(doc, date)
+ if date == today():
+ shift_assignment = frappe._dict({
+ "company": doc.company,
+ "operations_shift": doc.operations_shift,
+ "roster_type": doc.roster_type,
+ "shift_type": doc.shift_type,
+ "employee": doc.employee,
+ "from_date": date,
+ "name": doc.name,
+ "check_in_site": doc.check_in_site,
+ "check_out_site": doc.check_out_site,
+ "operations_role": doc.operations_role
+ })
+ create_shift_assignment_from_request(shift_assignment)
+
+ elif day_off_ot:
+ validate_day_of_ot(doc)
+ shift_assignment = frappe.get_list("Shift Assignment", filters=[['employee', '=', doc.employee],
+ ['start_date', 'between',
+ [doc.from_date, doc.to_date]],
+ ['roster_type', '=', "Basic"]],
+ fields=['name'])
+ if shift_assignment:
+ update_shift_assignment(shift_assignment[0].name, doc,day_off_ot=day_off_ot)
+ else:
+ create_shift_assignment_from_request(doc,day_off_ot=day_off_ot)
+
+ elif doc.purpose == 'Update Existing Assignment':
+ if check_for_roster(doc):
+ if doc.roster_type == "Basic" and cstr(doc.from_date) <= cstr(getdate()) <= cstr(doc.to_date):
+ shift_assignemnt = frappe.get_list("Shift Assignment", filters=[['employee', '=', doc.employee],
+ ['start_date', 'between',
+ [doc.from_date, doc.to_date]],
+ ['roster_type', '=', "Basic"]],
+ fields=['name'])
+ if shift_assignemnt:
+ update_shift_assignment(shift_assignemnt[0].name, doc)
+ if shift_worker == 1:
+ # check for existing schedule
+ schedule_date_range = [str(i.date()) for i in pd.date_range(start=doc.from_date, end=doc.to_date)]
+ found_schedules_date = []
+ existing_schedules = frappe.db.sql(f""" SELECT name, date FROM `tabEmployee Schedule`
+ WHERE employee="{doc.employee}" AND roster_type="{doc.roster_type}"
+ AND date BETWEEN '{doc.from_date}' AND '{doc.to_date}' """, as_dict=1)
+ if existing_schedules:
+ # update existing schedule
+ for es in existing_schedules:
+ start_time, end_time = frappe.db.get_value("Shift Type", doc.shift_type,
+ ['start_time', 'end_time'])
+ end_date = es.date
+ if start_time > end_time:
+ end_date = add_days(end_date, 1)
+
+ frappe.db.set_value('Employee Schedule', es.name, {
+ 'shift': doc.operations_shift,
+ 'shift_type': doc.shift_type,
+ 'start_datetime': f"{es.date} {start_time}",
+ 'end_datetime': f"{end_date} {end_time}",
+ 'operations_role': doc.operations_role,
+ 'post_abbrv': role_abbr,
+ 'employee_availability': 'Working',
+ 'roster_type': doc.roster_type,
+ 'department': doc.department,
+ 'site': doc.site,
+ 'reference_doctype': doc.doctype,
+ 'reference_docname': doc.name,
+ })
+ found_schedules_date.append(str(es.date))
+ # create new schedule
+ new_date_range = [i for i in schedule_date_range if not i in found_schedules_date]
+ if new_date_range:
+ for date in new_date_range:
+ if frappe.db.exists("Employee Schedule", {'date': date, 'employee': doc.employee,
+ 'employee_availability': 'Day Off'}):
+ es = frappe.get_doc("Employee Schedule", {'date': date, 'employee': doc.employee,
+ 'employee_availability': 'Day Off'})
+ start_time, end_time = frappe.db.get_value("Shift Type", doc.shift_type,
+ ['start_time', 'end_time'])
+ end_date = es.date
+ if start_time > end_time:
+ end_date = add_days(end_date, 1)
+
+ frappe.db.set_value('Employee Schedule', es.name, {
+ 'shift': doc.operations_shift,
+ 'shift_type': doc.shift_type,
+ 'start_datetime': f"{es.date} {start_time}",
+ 'end_datetime': f"{end_date} {end_time}",
+ 'operations_role': doc.operations_role,
+ 'post_abbrv': role_abbr,
+ 'employee_availability': 'Working',
+ 'roster_type': doc.roster_type,
+ 'department': doc.department,
+ 'site': doc.site,
+ 'reference_doctype': doc.doctype,
+ 'reference_docname': doc.name
+ }
+ )
+ else:
+ create_employee_schedule_from_request(doc, d)
+
+
+def validate_day_of_ot(shift_request):
+ """Setup all the documents that need to be created and modified because of the
+ approval of a shift request"""
+ role_abbr = frappe.db.get_value("Operations Role",shift_request.operations_role,'post_abbrv')
+ total_shift_request_duration = frappe.utils.date_diff(shift_request.to_date,shift_request.from_date)+1
+ existing_schedule = frappe.db.sql(f"""SELECT name,date from `tabEmployee Schedule` where employee = '{shift_request.employee}' and date between '{shift_request.from_date}' and '{shift_request.to_date}' and employee_availability = 'Day Off' """,as_dict=1)
+ if existing_schedule:
+ if int(total_shift_request_duration)!=int(len(existing_schedule)):
+ existing_dates = [frappe.utils.get_date_str(i.date) for i in existing_schedule]
+ schedule_date_range = [str(i.date()) for i in pd.date_range(start=shift_request.from_date, end=shift_request.to_date)]
+ missing_dates = [i for i in schedule_date_range if i not in existing_dates ]
+ frappe.throw(f"""Please ensure that an employee schedule for Day Off exists for ALL days in this shift request,
+ Please create a Day Off schedule for {', '.join(missing_dates)} """)
+ else:
+ schedule_date_range = [str(i.date()) for i in pd.date_range(start=shift_request.from_date, end=shift_request.to_date)]
found_schedules_date = []
existing_schedules = frappe.db.sql(f""" SELECT name, date FROM `tabEmployee Schedule`
- WHERE employee="{doc.employee}" AND roster_type="{doc.roster_type}"
- AND date BETWEEN '{doc.from_date}' AND '{doc.to_date}' """, as_dict=1)
+ WHERE employee="{shift_request.employee}" AND roster_type="{shift_request.roster_type}"
+ AND date BETWEEN '{shift_request.from_date}' AND '{shift_request.to_date}' """, as_dict=1)
if existing_schedules:
# update existing schedule
for es in existing_schedules:
- start_time, end_time = frappe.db.get_value("Shift Type", doc.shift_type, ['start_time', 'end_time'])
+ start_time, end_time = frappe.db.get_value("Shift Type", shift_request.shift_type, ['start_time', 'end_time'])
end_date = es.date
if start_time > end_time:
end_date = add_days(end_date, 1)
@@ -172,94 +346,169 @@ def process_shift_assignemnt(doc, event=None):
# validate_operations_post_overfill({es.date: 1}, doc.operations_shift)
frappe.db.set_value('Employee Schedule', es.name, {
- 'shift':doc.operations_shift,
- 'shift_type':doc.shift_type,
+ 'shift':shift_request.operations_shift,
+ 'shift_type':shift_request.shift_type,
'start_datetime': f"{es.date} {start_time}",
'end_datetime': f"{end_date} {end_time}",
- 'operations_role':doc.operations_role,
+ 'operations_role':shift_request.operations_role,
'post_abbrv': role_abbr,
'employee_availability':'Working',
- 'roster_type':doc.roster_type,
- 'department':doc.department,
- 'site':doc.site,
- 'reference_doctype': doc.doctype,
- 'reference_docname': doc.name
+ 'roster_type':shift_request.roster_type,
+ 'department':shift_request.department,
+ 'site':shift_request.site,
+ 'day_off_ot':1,
+ 'reference_doctype': shift_request.doctype,
+ 'reference_docname': shift_request.name
})
found_schedules_date.append(str(es.date))
- # create new schedule
- new_date_range = [i for i in schedule_date_range if not i in found_schedules_date]
- if new_date_range:
- for d in new_date_range:
- if frappe.db.exists("Employee Schedule", {'date':d, 'employee':doc.employee, 'employee_availability':'Day Off'}):
- es = frappe.get_doc("Employee Schedule", {'date':d, 'employee':doc.employee, 'employee_availability':'Day Off'})
- start_time, end_time = frappe.db.get_value("Shift Type", doc.shift_type, ['start_time', 'end_time'])
- end_date = es.date
- if start_time > end_time:
- end_date = add_days(end_date, 1)
-
- # validate_operations_post_overfill({es.date: 1}, doc.operations_shift)
-
- frappe.db.set_value('Employee Schedule', es.name, {
- 'shift':doc.operations_shift,
- 'shift_type':doc.shift_type,
- 'start_datetime': f"{es.date} {start_time}",
- 'end_datetime': f"{end_date} {end_time}",
- 'operations_role':doc.operations_role,
- 'post_abbrv': role_abbr,
- 'employee_availability':'Working',
- 'roster_type':doc.roster_type,
- 'department':doc.department,
- 'site':doc.site,
- 'reference_doctype': doc.doctype,
- 'reference_docname': doc.name
- }
- )
-
- else:
- schedule = frappe.new_doc("Employee Schedule")
- schedule.employee = doc.employee
- schedule.date = d
- schedule.shift = doc.operations_shift
- schedule.shift_type = doc.shift_type
- schedule.operations_role = doc.operations_role
- schedule.post_abbrv = role_abbr
- schedule.employee_availability = 'Working'
- schedule.roster_type = doc.roster_type
- schedule.department = doc.department
- schedule.site = doc.site
- schedule.reference_doctype = doc.doctype
- schedule.reference_docname = doc.name
- schedule.save(ignore_permissions=True)
-
-def update_shift_assignment(shift_assignemnt,shift_request):
+ else:
+ frappe.throw(f"No Day Off set for {shift_request.employee_name} between {shift_request.from_date} and {shift_request.to_date}")
+
+def update_shift_assignment(shift_assignemnt, shift_request,day_off_ot = False):
assignment_doc = frappe.get_doc('Shift Assignment', shift_assignemnt)
- project = frappe.db.get_value("Operations Shift", {'name':shift_request.operations_shift}, ['project'])
- assignment_doc.db_set("company" , shift_request.company)
- assignment_doc.db_set("shift" , shift_request.operations_shift)
- assignment_doc.db_set("roster_type" , shift_request.roster_type)
- assignment_doc.db_set("shift_type" , shift_request.shift_type)
- assignment_doc.db_set("site" , shift_request.site)
- assignment_doc.db_set("project" , project)
- assignment_doc.db_set("site_location" , shift_request.check_in_site)
- assignment_doc.db_set("employee" , shift_request.employee)
- assignment_doc.db_set("start_date" , shift_request.from_date)
- assignment_doc.db_set("shift_request" , shift_request.name)
- assignment_doc.db_set("check_in_site" , shift_request.check_in_site)
- assignment_doc.db_set("check_out_site" , shift_request.check_out_site)
- shift_type_data = frappe.get_doc("Shift Type",shift_request.shift_type)
+ project = frappe.db.get_value("Operations Shift", {'name': shift_request.operations_shift}, ['project'])
+ assignment_doc.db_set("company", shift_request.company)
+ assignment_doc.db_set("shift", shift_request.operations_shift)
+ assignment_doc.db_set("roster_type", shift_request.roster_type)
+ assignment_doc.db_set("shift_type", shift_request.shift_type)
+ assignment_doc.db_set("site", shift_request.site)
+ assignment_doc.db_set("project", project)
+ assignment_doc.db_set("site_location", shift_request.check_in_site)
+ assignment_doc.db_set("employee", shift_request.employee)
+ assignment_doc.db_set("start_date", shift_request.from_date)
+ assignment_doc.db_set("shift_request", shift_request.name)
+ assignment_doc.db_set("check_in_site", shift_request.check_in_site)
+ assignment_doc.db_set("check_out_site", shift_request.check_out_site)
+ shift_type_data = frappe.get_doc("Shift Type", shift_request.shift_type)
if shift_type_data:
- start_datetime = datetime.datetime.strptime(f"{shift_request.from_date} {(datetime.datetime.min + shift_type_data.start_time).time()}", '%Y-%m-%d %H:%M:%S')
+ start_datetime = datetime.datetime.strptime(
+ f"{shift_request.from_date} {(datetime.datetime.min + shift_type_data.start_time).time()}",
+ '%Y-%m-%d %H:%M:%S')
if shift_type_data.end_time.total_seconds() < shift_type_data.start_time.total_seconds():
- end_datetime = datetime.datetime.strptime(f"{add_days(assignment_doc.start_date, 1)} {(datetime.datetime.min + shift_type_data.end_time).time()}", '%Y-%m-%d %H:%M:%S')
+ end_datetime = datetime.datetime.strptime(
+ f"{add_days(assignment_doc.start_date, 1)} {(datetime.datetime.min + shift_type_data.end_time).time()}",
+ '%Y-%m-%d %H:%M:%S')
else:
- end_datetime = datetime.datetime.strptime(f"{assignment_doc.start_date} {(datetime.datetime.min + shift_type_data.end_time).time()}", '%Y-%m-%d %H:%M:%S')
+ end_datetime = datetime.datetime.strptime(
+ f"{assignment_doc.start_date} {(datetime.datetime.min + shift_type_data.end_time).time()}",
+ '%Y-%m-%d %H:%M:%S')
- assignment_doc.db_set("start_datetime" ,start_datetime)
- assignment_doc.db_set("end_datetime" , end_datetime)
+ assignment_doc.db_set("start_datetime", start_datetime)
+ assignment_doc.db_set("end_datetime", end_datetime)
if shift_request.operations_role:
- assignment_doc.db_set("operations_role" , shift_request.operations_role)
+ assignment_doc.db_set("operations_role", shift_request.operations_role)
+ if day_off_ot:
+ assignment_doc.db_set("custom_day_off_ot" , 1)
-def create_shift_assignment_from_request(shift_request, submit=True):
+
+def replace_shift_assignment(shift_assignemnt, shift_request):
+ '''
+ Method used to create Shift Assignment from Shift Request
+ args:
+ shift_request: Object of shift request
+ submit: Boolean
+ '''
+ project = frappe.db.get_value("Operations Shift", {'name': shift_request.operations_shift}, ['project'])
+
+ #Replace Existing Assignment
+ if shift_assignemnt:
+ assignment_doc = frappe.get_doc('Shift Assignment', shift_assignemnt[0].name)
+ assignment_doc.db_set("company", shift_request.company)
+ assignment_doc.db_set("shift", shift_request.operations_shift)
+ assignment_doc.db_set("roster_type", shift_request.roster_type)
+ assignment_doc.db_set("shift_type", shift_request.shift_type)
+ assignment_doc.db_set("site", shift_request.site)
+ assignment_doc.db_set("project", project)
+ assignment_doc.db_set("site_location", shift_request.check_in_site)
+ assignment_doc.db_set("employee", shift_request.employee)
+ assignment_doc.db_set("start_date", shift_request.from_date)
+ assignment_doc.db_set("shift_request", shift_request.name)
+ assignment_doc.db_set("check_in_site", shift_request.check_in_site)
+ assignment_doc.db_set("check_out_site", shift_request.check_out_site)
+ else:
+ assignment_doc = create_shift_assignment_from_request(shift_request)
+
+ replaced_shift_assignment = frappe.get_doc("Shift Assignment", {'employee': shift_request.replaced_employee,
+ 'start_date': shift_request.from_date,
+ 'roster_type': "Basic"})
+ replaced_shift_assignment.db_set("is_replaced", 1)
+ replaced_shift_assignment.db_set("replaced_shift_assignment", assignment_doc.name)
+
+ frappe.db.sql("""
+ UPDATE `tabEmployee Checkin`
+ SET is_replaced = 1
+ WHERE shift_assignment = %s
+ """, (replaced_shift_assignment.name,))
+
+ shift_type_data = frappe.get_doc("Shift Type", shift_request.shift_type)
+ if shift_type_data:
+ start_datetime = datetime.datetime.strptime(
+ f"{shift_request.from_date} {(datetime.datetime.min + shift_type_data.start_time).time()}",
+ '%Y-%m-%d %H:%M:%S')
+ if shift_type_data.end_time.total_seconds() < shift_type_data.start_time.total_seconds():
+ end_datetime = datetime.datetime.strptime(
+ f"{add_days(assignment_doc.start_date, 1)} {(datetime.datetime.min + shift_type_data.end_time).time()}",
+ '%Y-%m-%d %H:%M:%S')
+ else:
+ end_datetime = datetime.datetime.strptime(
+ f"{assignment_doc.start_date} {(datetime.datetime.min + shift_type_data.end_time).time()}",
+ '%Y-%m-%d %H:%M:%S')
+
+ assignment_doc.db_set("start_datetime", start_datetime)
+ assignment_doc.db_set("end_datetime", end_datetime)
+ if shift_request.operations_role:
+ assignment_doc.db_set("operations_role", shift_request.operations_role)
+
+ #Update Employee Checkin
+ frappe.db.sql(f"""UPDATE `tabEmployee Checkin`
+ SET is_replaced = 1,
+ replaced_employee_checkin = (
+ SELECT name
+ FROM `tabEmployee Checkin`
+ WHERE employee = '{shift_request.replaced_employee}'
+ AND shift_assignment = '{replaced_shift_assignment}'
+ LIMIT 1
+ )
+ WHERE shift_assignment = '{replaced_shift_assignment}'
+ AND employee = '{shift_request.replaced_employee}';
+ """)
+
+
+def replace_employee_schedule(doc, existing_schedules, schedule_date_range):
+ try:
+ # replace existing schedule
+ for es in existing_schedules:
+ role_abbr = frappe.db.get_value("Operations Role", doc.operations_role, 'post_abbrv')
+ start_time, end_time = frappe.db.get_value("Shift Type", doc.shift_type, ['start_time', 'end_time'])
+ end_date = es.date
+ if start_time > end_time:
+ end_date = add_days(end_date, 1)
+ frappe.db.set_value('Employee Schedule', es.name, {
+ 'shift': doc.operations_shift,
+ 'shift_type': doc.shift_type,
+ 'start_datetime': f"{es.date} {start_time}",
+ 'end_datetime': f"{end_date} {end_time}",
+ 'operations_role': doc.operations_role,
+ 'post_abbrv': role_abbr,
+ 'employee_availability': 'Working',
+ 'roster_type': doc.roster_type,
+ 'department': doc.department,
+ 'site': doc.site,
+ 'reference_doctype': doc.doctype,
+ 'reference_docname': doc.name,
+ })
+
+ #Update Existing Employee Schedule
+ replaced_employee_schedule = frappe.get_doc("Employee Schedule",
+ {'employee': doc.replaced_employee, 'date': es.date}, ['name'])
+ replaced_employee_schedule.db_set("is_replaced", 1)
+ replaced_employee_schedule.db_set("replaced_employee_schedule", es.name)
+ except Exception as e:
+ frappe.throw(_("Error Replacing Employee Schedule"))
+ frappe.log_error(frappe.get_traceback(), "Error Replacing Employee Schedule")
+
+
+def create_shift_assignment_from_request(shift_request, submit=True,day_off_ot = False):
'''
Method used to create Shift Assignment from Shift Request
args:
@@ -276,23 +525,49 @@ def create_shift_assignment_from_request(shift_request, submit=True):
assignment_doc.shift_request = shift_request.name
assignment_doc.check_in_site = shift_request.check_in_site
assignment_doc.check_out_site = shift_request.check_out_site
+ assignment_doc.custom_day_off_ot = 1 if day_off_ot else 0
+ if day_off_ot:
+ assignment_doc.flags.ignore_validate = True
if shift_request.operations_role:
assignment_doc.operations_role = shift_request.operations_role
+
assignment_doc.insert()
if submit:
assignment_doc.submit()
frappe.db.commit()
+ return assignment_doc
+
+
+
+def create_employee_schedule_from_request(doc, date):
+ schedule = frappe.new_doc("Employee Schedule")
+ schedule.employee = doc.employee
+ schedule.date = date
+ schedule.shift = doc.operations_shift
+ schedule.shift_type = doc.shift_type
+ schedule.operations_role = doc.operations_role
+ # schedule.post_abbrv = frappe.db.get_value("Operations Role", doc.operations_role, 'post_abbrv')
+ schedule.employee_availability = 'Working'
+ schedule.roster_type = doc.roster_type
+ schedule.department = doc.department
+ schedule.site = doc.site
+ schedule.reference_doctype = doc.doctype
+ schedule.reference_docname = doc.name
+ schedule.save(ignore_permissions=True)
+
def assign_day_off(shift_request):
- shift_assignment = frappe.get_list('Shift Assignment' ,{'employee':shift_request.employee, 'start_date': shift_request.from_date}, ['name'])
+ shift_assignment = frappe.get_list('Shift Assignment',
+ {'employee': shift_request.employee, 'start_date': shift_request.from_date},
+ ['name', "start_date"])
if shift_assignment:
for s in shift_assignment:
- shift = frappe.get_doc("Shift Assignment", s.name)
- if shift.start_date >= getdate():
- shift.cancel()
- shift.delete()
+ frappe.db.sql(f"""DELETE from `tabEmployee Checkin` WHERE employee='{shift_request.employee}' AND shift_assignment='{s.name}'""")
+ if s.start_date >= getdate():
+ frappe.db.sql(f"""DELETE from `tabShift Assignment` WHERE name='{s.name}'""")
- employee_schedule = frappe.get_list('Employee Schedule' ,{'employee':shift_request.employee, 'date': ["between", (shift_request.from_date, shift_request.to_date)]}, ['name'])
+ employee_schedule = frappe.get_list('Employee Schedule', {'employee': shift_request.employee, 'date': ["between", (
+ shift_request.from_date, shift_request.to_date)]}, ['name'])
if employee_schedule:
for es in employee_schedule:
schedule = frappe.get_doc("Employee Schedule", es.name)
@@ -312,6 +587,71 @@ def assign_day_off(shift_request):
frappe.db.commit()
+def cancel_shift_assignment_of_request(shift_request):
+ '''
+ Method used to cancel Shift Assignment of a Shift Request
+ args:
+ shift_request: Object of shift request
+ submit: Boolean
+ '''
+ schedule_exists = frappe.db.exists("Employee Schedule",
+ {"employee": shift_request.employee, "date": cstr(getdate()),
+ "employee_availability": "Working"})
+
+ shift_assignment_list = frappe.get_list(
+ "Shift Assignment",
+ {
+ "employee": shift_request.employee,
+ "shift_request": shift_request.name,
+ "docstatus": 1
+ }
+ )
+ if shift_assignment_list:
+ for shift in shift_assignment_list:
+ shift_assignment_doc = frappe.get_doc("Shift Assignment", shift["name"])
+ shift_assignment_doc.cancel()
+ shift_assignment_doc.delete()
+ if shift_request.from_date <= getdate() <= shift_request.to_date and schedule_exists:
+ schedule = frappe.get_doc("Employee Schedule", {"employee": shift_request.employee, "date": cstr(getdate())})
+ if schedule:
+ sa = frappe.get_doc(dict(
+ doctype='Shift Assignment',
+ start_date=cstr(getdate()),
+ employee=schedule.employee,
+ employee_name=schedule.employee_name,
+ department=schedule.department,
+ operations_role=schedule.operations_role,
+ shift=schedule.shift,
+ site=schedule.site,
+ project=schedule.project,
+ shift_type=schedule.shift_type,
+ roster_type=schedule.roster_type,
+ )).insert()
+ sa.submit()
+
+
+def validate_approver(self):
+ shift, department = frappe.get_value("Employee", self.employee, ["shift", "department"])
+
+ approvers = frappe.db.sql(
+ """select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""",
+ (department),
+ )
+
+ approvers = [approver[0] for approver in approvers]
+
+ if frappe.db.exists("Employee", self.employee, ["reports_to"]):
+ report_to = frappe.get_value("Employee", self.employee, ["reports_to"])
+ approvers.append(frappe.get_value("Employee", report_to, "user_id"))
+
+ if shift:
+ shift_supervisor = frappe.get_value("Operations Shift", shift, "supervisor")
+ approvers.append(frappe.get_value("Employee", shift_supervisor, "user_id"))
+
+ if self.approver not in approvers:
+ frappe.throw(_("Only Approvers can Approve this Request."))
+
+
@frappe.whitelist()
def fetch_approver(doc):
doc = frappe._dict(json.loads(doc))
@@ -325,12 +665,36 @@ def fetch_approver(doc):
if approver_user_id:
return [approver_user_id] + other_approvers
+
+
+def fill_to_date(doc, method):
+ if not doc.to_date:
+ doc.to_date = doc.from_date
+
+
+def validate_from_date(doc, method):
+ if getdate(today()) > getdate(doc.from_date):
+ attendance_manager = get_employee_user_id(frappe.db.get_single_value("ONEFM General Setting", "attendance_manager"))
+ if frappe.session.user == attendance_manager:
+ return
+
+ if doc.purpose != 'Assign Day Off':
+ message = "Please note that Shift Requests cannot be created for a past date." if doc.is_new() else "Please note that Shift Requests cannot be updated to a past date."
+ frappe.throw(
+ _(message),
+ title=_("Invalid Start Date"),
+ )
+
+
@frappe.whitelist()
def update_request(shift_request, from_date, to_date):
from_date = getdate(from_date)
to_date = getdate(to_date)
if getdate(today()) > from_date:
- frappe.throw('From Date cannot be before today.')
+ frappe.throw(
+ _("Please note that Shift Requests cannot be updated to a past date."),
+ title=_("Invalid Start Date"),
+ )
if from_date > to_date:
frappe.throw('To Date cannot be before From Date.')
shift_request_obj = frappe.get_doc('Shift Request', shift_request)
@@ -340,6 +704,7 @@ def update_request(shift_request, from_date, to_date):
shift_request_obj.db_set("status", 'Draft')
apply_workflow(shift_request_obj, "Update Request")
+
def _get_employee_from_user(user):
employee_docname = frappe.db.get_value("Employee", {"user_id": user})
return employee_docname if employee_docname else None
@@ -366,12 +731,13 @@ def get_manager(doctype, employee):
return [i.name for i in values]
-
@frappe.whitelist()
def fetch_employee_details(employee):
- emp_data = frappe.get_all("Employee",{'name':employee},['employee_name','company','shift','site','project','default_shift','department'])
+ emp_data = frappe.get_all("Employee", {'name': employee},
+ ['employee_name', 'company', 'shift', 'site', 'project', 'default_shift', 'department'])
return emp_data[0] if emp_data else []
+
@frappe.whitelist()
def get_employees(doctype, txt, searchfield, start, page_len, filters):
"""
@@ -380,28 +746,32 @@ def get_employees(doctype, txt, searchfield, start, page_len, filters):
"""
- is_master= False
+ is_master = False
default_user_roles = None
- employee_master_roles = frappe.get_all("ONEFM Document Access Roles Detail",{'parent':"ONEFM General Setting",'parentfield':"employee_master_role"},['role'])
+ employee_master_roles = frappe.get_all("ONEFM Document Access Roles Detail",
+ {'parent': "ONEFM General Setting", 'parentfield': "employee_master_role"},
+ ['role'])
user_roles = frappe.get_roles(frappe.session.user) or []
- default_user_roles = [i.role for i in employee_master_roles] if employee_master_roles else ['HR Manager',"HR User"]
+ default_user_roles = [i.role for i in employee_master_roles] if employee_master_roles else ['HR Manager', "HR User"]
#if user has any default hr role
- if [role for role in user_roles if role in default_user_roles ]:
- is_master = True
- if is_master:
+ if [role for role in user_roles if role in default_user_roles]:
+ is_master = True
+ if is_master:
#If the user has not typed anything on the employee field
if not txt:
- employees = frappe.db.sql("Select name,employee_name,employee_id from `tabEmployee` where status = 'Active' ")
+ employees = frappe.db.sql(
+ "Select name,employee_name,employee_id from `tabEmployee` where status = 'Active' ")
return employees
else:
#If the user has typed anything on the employee field
- employees = frappe.db.sql(f"Select name,employee_name,employee_id from `tabEmployee` where status = 'Active' and name like '%{txt}%' or employee_name like '%{txt}%' or employee_id like '%{txt}%' ")
+ employees = frappe.db.sql(
+ f"Select name,employee_name,employee_id from `tabEmployee` where status = 'Active' and name like '%{txt}%' or employee_name like '%{txt}%' or employee_id like '%{txt}%' ")
return employees
else:
allowed_employees = []
user = frappe.session.user
- if user!="Administrator":
+ if user != "Administrator":
employee_id = _get_employee_from_user(user)
if employee_id:
employee_base_query = f"""
@@ -411,32 +781,34 @@ def get_employees(doctype, txt, searchfield, start, page_len, filters):
query = None
allowed_employees.append(employee_id)
#get all reports to
- reports_to = frappe.get_all("Employee",{'reports_to':employee_id,'status':"Active"},'name')
+ reports_to = frappe.get_all("Employee", {'reports_to': employee_id, 'status': "Active"}, 'name')
if reports_to:
- allowed_employees+=[i.name for i in reports_to]
+ allowed_employees += [i.name for i in reports_to]
#get all employees in project,shift and site
if allowed_employees:
- cond_str = f" and name in {tuple(allowed_employees)}" if len(allowed_employees)>1 else f" and name = '{allowed_employees[0]}'"
+ cond_str = f" and name in {tuple(allowed_employees)}" if len(
+ allowed_employees) > 1 else f" and name = '{allowed_employees[0]}'"
if txt:
- cond_str+=f" and (name like '%{txt}%' or employee_name like '%{txt}%' or employee_id like '%{txt}%') "
- query=employee_base_query+cond_str
- shifts = get_manager('Operations Shift',employee_id)
+ cond_str += f" and (name like '%{txt}%' or employee_name like '%{txt}%' or employee_id like '%{txt}%') "
+ query = employee_base_query + cond_str
+ shifts = get_manager('Operations Shift', employee_id)
if shifts:
- cond_str = f" and shift in {tuple(shifts)}" if len(shifts)>1 else f" and shift = '{shifts[0]}'"
+ cond_str = f" and shift in {tuple(shifts)}" if len(shifts) > 1 else f" and shift = '{shifts[0]}'"
if txt:
- cond_str+=f" and (name like '%{txt}%' or employee_name like '%{txt}%' or employee_id like '%{txt}%') "
+ cond_str += f" and (name like '%{txt}%' or employee_name like '%{txt}%' or employee_id like '%{txt}%') "
query += f""" UNION SELECT name,employee_name,employee_id from `tabEmployee` where status = "Active" {cond_str} """
- sites = get_manager('Operations Site',employee_id)
+ sites = get_manager('Operations Site', employee_id)
if sites:
- cond_str = f" and site in {tuple(sites)}" if len(sites)>1 else f" and site = '{sites[0]}'"
+ cond_str = f" and site in {tuple(sites)}" if len(sites) > 1 else f" and site = '{sites[0]}'"
if txt:
- cond_str+=f" and (name like '%{txt}%' or employee_name like '%{txt}%' or employee_id like '%{txt}%') "
+ cond_str += f" and (name like '%{txt}%' or employee_name like '%{txt}%' or employee_id like '%{txt}%') "
query += f""" UNION SELECT name,employee_name,employee_id from `tabEmployee` where status = "Active" {cond_str} """
- project = get_manager('Project',employee_id)
+ project = get_manager('Project', employee_id)
if project:
- cond_str = f" and project in {tuple(project)}" if len(project)>1 else f" and project = '{project[0]}'"
+ cond_str = f" and project in {tuple(project)}" if len(
+ project) > 1 else f" and project = '{project[0]}'"
if txt:
- cond_str+=f" and (name like '%{txt}%' or employee_name like '%{txt}%' or employee_id like '%{txt}%') "
+ cond_str += f" and (name like '%{txt}%' or employee_name like '%{txt}%' or employee_id like '%{txt}%') "
query += f""" UNION SELECT name,employee_name,employee_id from `tabEmployee` where status = "Active" {cond_str} """
#Check if employee is set in operations shift, operations site or project
@@ -447,12 +819,26 @@ def get_employees(doctype, txt, searchfield, start, page_len, filters):
return ()
else:
if not txt:
- return frappe.db.sql("Select name,employee_name,employee_id from `tabEmployee` where status = 'Active' ")
+ return frappe.db.sql(
+ "Select name,employee_name,employee_id from `tabEmployee` where status = 'Active' ")
else:
return frappe.db.sql("Select name,employee_name,employee_id from `tabEmployee` where status = 'Active' and name \
like '%{txt}%' or employee_name like '%{txt}%' \or employee_id like '%{txt}%' ")
+def check_for_roster(doc):
+ schedule_date_range = [str(i.date()) for i in pd.date_range(start=doc.from_date, end=doc.to_date)]
+ new_date_range = [i for i in schedule_date_range]
+ if new_date_range:
+ for date in new_date_range:
+ if frappe.db.exists("Employee Schedule", {'date': date, 'employee': doc.employee}):
+ return True
+ elif frappe.db.exists("Shift Assignment", {"docstatus": 1, "start_date": date, "employee": doc.employee}):
+ return True
+ else:
+ return False
+
+
@frappe.whitelist()
def get_operations_role(doctype, txt, searchfield, start, page_len, filters):
shift = filters.get('operations_shift')
@@ -463,13 +849,15 @@ def get_operations_role(doctype, txt, searchfield, start, page_len, filters):
""".format(shift=shift))
return operations_roles
+
def has_overlap(shift1, shift2):
shift1 = frappe.get_doc("Shift Type", shift1)
shift2 = frappe.get_doc("Shift Type", shift2)
if shift1.end_time <= shift2.start_time or shift1.start_time >= shift2.end_time:
- return True #No Overlap
+ return True #No Overlap
else:
- return False #Overlap
+ return False #Overlap
+
def daterange(start_date, end_date):
start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d')
@@ -478,8 +866,8 @@ def daterange(start_date, end_date):
yield start_date + datetime.timedelta(n)
-def send_shift_request_mail(doc):
- if doc.workflow_state == 'Pending Approval':
+def send_shift_request_mail(doc, method):
+ if doc.workflow_state == 'Pending Approver':
try:
title = f"Urgent Notification: {doc.doctype} Requires Your Immediate Review"
context = dict(
diff --git a/one_fm/overrides/timesheet.py b/one_fm/overrides/timesheet.py
index 4a305b19b3..f9b4c5ed1f 100644
--- a/one_fm/overrides/timesheet.py
+++ b/one_fm/overrides/timesheet.py
@@ -1,11 +1,12 @@
import frappe
import itertools
+from frappe.desk.form.assign_to import add as add_assignment
from frappe.utils import cstr, flt, add_days, time_diff_in_hours, getdate, get_datetime_in_timezone
from calendar import monthrange
from hrms.overrides.employee_timesheet import *
from frappe import _
from one_fm.processor import sendemail
-from one_fm.utils import send_workflow_action_email, get_approver_user
+from one_fm.utils import send_workflow_action_email, get_approver_user, get_approver
class TimesheetOveride(Timesheet):
@@ -21,15 +22,16 @@ def validate(self):
self.validate_start_date()
def validate_start_date(self):
- start_date = getdate(self.start_date)
- if start_date > getdate():
- frappe.throw(_("Please note that timesheets cannot be created for a date in the future"))
-
- def before_insert(self):
- self.set_dates()
- start_date = getdate(self.start_date)
- if start_date < get_datetime_in_timezone("Asia/Kuwait").date():
- frappe.throw(_("Please note that timesheets cannot be created for a previous date."))
+ if frappe.session.user != "Administrator":
+ start_date = getdate(self.start_date)
+ if start_date > getdate():
+ frappe.throw(_("Please note that Timesheets cannot be created for a future date"), title="Invalid Start Date")
+ if start_date < getdate():
+ if self.is_new():
+ msg = _("Please note that Timesheets cannot be created for a past date")
+ else:
+ msg = _("Please note that Timesheets cannot be updated for a past date")
+ frappe.throw(msg, title="Invalid Start Date")
def before_save(self):
if not self.is_new():
@@ -39,7 +41,8 @@ def before_save(self):
# 2. Check if value of end_date is changed and it is before current date according to AST.
# If any of the above criteria is fulfilled then throw an error.
if (self.has_value_changed("start_date") and date_in_ast > self.get('start_date')) or (self.has_value_changed("end_date") and date_in_ast > self.get('end_date')):
- frappe.throw(_("Please note that timesheets cannot be updated to a previous date."))
+ frappe.throw(_("Please note that timesheets cannot be updated to a previous date."), title="Invalid Start Date")
+ self.assign_unassign()
def set_approver(self):
if self.attendance_by_timesheet:
@@ -55,10 +58,11 @@ def before_submit(self):
frappe.throw("Total Hours cannot be 0 or less.")
def on_update(self):
- if self.workflow_state == 'Open':
+ if self.workflow_state == 'Pending Approval':
send_workflow_action_email(self, [self.approver])
message = "The timesheet {0} of {1}, Open for your Approval".format(self.name, self.employee_name)
create_notification_log("Pending - Workflow Action on Timesheet", message, [self.approver], self)
+
def on_submit(self):
self.validate_mandatory_fields()
@@ -66,9 +70,10 @@ def on_submit(self):
if self.workflow_state == "Approved":
self.check_approver()
self.create_attendance()
- elif self.workflow_state == "Rejected":
+ elif self.workflow_state == "Canceled":
self.check_approver()
self.notify_the_employee()
+ self.delete_todo()
def notify_the_employee(self):
timesheet_url = '{1}'.format(frappe.utils.get_link_to_form("Timesheet", self.name), self.name)
@@ -124,6 +129,57 @@ def check_approver(self):
if frappe.session.user not in [self.approver, "Administrator"]:
frappe.throw(_("Only Approver can Approve/Reject the timesheet"))
+
+
+ def assign_unassign(self) -> None:
+ previous_doc = self.get_doc_before_save() if not self.is_new() else self
+ if previous_doc.workflow_state != self.workflow_state:
+ self.delete_todo()
+ if self.workflow_state in {"Draft", "Pending Approval"}:
+ add_assignment({
+ 'doctype': self.doctype,
+ 'name': self.name,
+ 'assign_to': [self.owner if self.workflow_state == "Draft" else self.approver],
+ 'description': (_(self.fetch_description()))
+ })
+
+
+ def delete_todo(self):
+ return frappe.db.sql("""
+ DELETE FROM `tabToDo`
+ WHERE reference_type = %s AND reference_name = %s
+ """, (self.doctype, self.name))
+
+
+ def fetch_description(self):
+ return f"""
+ Here is to inform you that the following { self.doctype }({ self.name }) requires your attention/action.
+
+ The details of the request are as follows:
+
+
+
+
+ Label |
+ Value |
+
+
+
+
+
+ Employee |
+ { self.employee } |
+
+
+
+ """
+
+@frappe.whitelist()
+def fetch_approver(employee):
+ approver = get_approver(employee)
+ if approver:
+ return frappe.get_value("Employee", approver, ["user_id"])
+
def timesheet_automation(start_date=None,end_date=None,project=None):
filters = {
'attendance_date': ['between', (start_date, end_date)],
@@ -280,3 +336,8 @@ def create_notification_log(subject, message, for_users, reference_doc):
# If notification log type is Alert then it will not send email for the log
doc.type = 'Alert'
doc.insert(ignore_permissions=True)
+
+
+
+
+
diff --git a/one_fm/patches.txt b/one_fm/patches.txt
index 6d602a2334..2b28d9673f 100644
--- a/one_fm/patches.txt
+++ b/one_fm/patches.txt
@@ -82,6 +82,8 @@ one_fm.patches.v15_0.replicate_issue_types_to_hd_ticket_types
# one_fm.patches.v15_0.update_timesheet_workflow_sate
one_fm.patches.v15_0.delete_duplicate_leave_attendance
one_fm.patches.v15_0.update_subcontract_doc
+one_fm.patches.v15_0.update_shift_permission_workflow
+one_fm.patches.v15_0.remove_assign_day_off_field
one_fm.patches.v15_0.update_employee_status
one_fm.patches.v15_0.update_employee_id_qr_code_generator
one_fm.patches.v15_0.update_religion_field_options
@@ -92,4 +94,5 @@ one_fm.patches.v15_0.submit_shift_permissions
one_fm.patches.v15_0.update_shift_request
one_fm.patches.v15_0.add_task_assignments_to_form_field
one_fm.patches.v15_0.disable_wrong_shift_request
-one_fm.patches.v15_0.update_job_offer_00057_erf
\ No newline at end of file
+one_fm.patches.v15_0.update_shift_request_day_off
+one_fm.patches.v15_0.update_job_offer_00057_erf
diff --git a/one_fm/patches/v15_0/delete_duplicate_leave_attendance.py b/one_fm/patches/v15_0/delete_duplicate_leave_attendance.py
index b196f7c8e5..95659cd735 100644
--- a/one_fm/patches/v15_0/delete_duplicate_leave_attendance.py
+++ b/one_fm/patches/v15_0/delete_duplicate_leave_attendance.py
@@ -24,4 +24,4 @@ def execute() :
delete_list.append (atts[1].name)
delete_list = str(tuple(delete_list)).replace(',)', ')')
- frappe.db.sql(f"""DELETE FROM `tabAttendance` where name IN {delete_list}""")
\ No newline at end of file
+ frappe.db.sql(f"""DELETE FROM `tabAttendance` where name IN {delete_list}""")
diff --git a/one_fm/patches/v15_0/delete_field_assign_day_off.py b/one_fm/patches/v15_0/delete_field_assign_day_off.py
new file mode 100644
index 0000000000..80c97f2d6b
--- /dev/null
+++ b/one_fm/patches/v15_0/delete_field_assign_day_off.py
@@ -0,0 +1,7 @@
+import frappe
+
+def execute():
+ column = 'assign_day_off'
+ if column in frappe.db.get_table_columns("Shift Request"):
+ frappe.db.sql("ALTER TABLE `tabShift Request` drop column {0}".format(column))
+ frappe.db.commit()
\ No newline at end of file
diff --git a/one_fm/patches/v15_0/remove_assign_day_off_field.py b/one_fm/patches/v15_0/remove_assign_day_off_field.py
new file mode 100644
index 0000000000..995d9e21df
--- /dev/null
+++ b/one_fm/patches/v15_0/remove_assign_day_off_field.py
@@ -0,0 +1,4 @@
+import frappe
+
+def execute():
+ frappe.db.sql("DELETE from `tabCustom Field` where name = 'Shift Request-assign_day_off'")
\ No newline at end of file
diff --git a/one_fm/patches/v15_0/update_shift_permission_workflow.py b/one_fm/patches/v15_0/update_shift_permission_workflow.py
new file mode 100644
index 0000000000..2ca513b8c9
--- /dev/null
+++ b/one_fm/patches/v15_0/update_shift_permission_workflow.py
@@ -0,0 +1,9 @@
+import frappe
+
+
+def execute():
+ frappe.db.sql("""
+ UPDATE `tabShift Permission`
+ SET workflow_state = "Pending Approver"
+ WHERE workflow_state = "Pending"
+ """)
\ No newline at end of file
diff --git a/one_fm/patches/v15_0/update_shift_request_day_off.py b/one_fm/patches/v15_0/update_shift_request_day_off.py
new file mode 100644
index 0000000000..e605ad231c
--- /dev/null
+++ b/one_fm/patches/v15_0/update_shift_request_day_off.py
@@ -0,0 +1,9 @@
+import frappe
+
+
+def execute():
+ frappe.db.sql("""
+ UPDATE `tabShift Request`
+ SET purpose = "Assign Day Off"
+ WHERE assign_day_off = 1
+ """)
\ No newline at end of file
diff --git a/one_fm/patches/v15_0/update_timesheet_workflow_sate.py b/one_fm/patches/v15_0/update_timesheet_workflow_sate.py
new file mode 100644
index 0000000000..98b4e12c09
--- /dev/null
+++ b/one_fm/patches/v15_0/update_timesheet_workflow_sate.py
@@ -0,0 +1,12 @@
+import frappe
+
+
+def execute():
+ frappe.db.sql("""
+ UPDATE
+ `tabTimesheet`
+ SET
+ workflow_state = 'Pending Approval'
+ WHERE
+ workflow_state = 'Open'
+ """)
diff --git a/one_fm/permissions.py b/one_fm/permissions.py
index a7e0087884..83b4884926 100644
--- a/one_fm/permissions.py
+++ b/one_fm/permissions.py
@@ -60,9 +60,8 @@ def get_custom_user_permissions(user=None):
if not user or user in ("Administrator", "Guest"):
return {}
- cached_user_permissions = frappe.cache.hget("user_permissions", user)
-
-
+ cached_user_permissions = frappe.cache().hget("user_permissions", user)
+
if cached_user_permissions is not None:
return cached_user_permissions
@@ -104,7 +103,7 @@ def add_doc_to_perm(perm, doc_name, is_default):
if data:
for each in data:
out['Employee'].append({'doc':each.name,'applicable_for':None,'is_default':0})
- frappe.cache.hset("user_permissions", user, out)
+ frappe.cache().hset("user_permissions", user, out)
except frappe.db.SQLError as e:
if frappe.db.is_table_missing(e):
diff --git a/one_fm/public/js/doctype_js/attendance_request.js b/one_fm/public/js/doctype_js/attendance_request.js
index 649283aa0f..5f9819058a 100644
--- a/one_fm/public/js/doctype_js/attendance_request.js
+++ b/one_fm/public/js/doctype_js/attendance_request.js
@@ -1,7 +1,6 @@
frappe.ui.form.on('Attendance Request', {
refresh: (frm)=>{
frm.trigger('check_workflow');
- set_update_request_btn(frm);
},
validate: (frm) => {
validate_from_date(frm);
@@ -17,89 +16,17 @@ frappe.ui.form.on('Attendance Request', {
})
}
},
- employee: (frm)=>{
- // Set approver
- frm.events.set_approver(frm);
- },
from_date: (frm) =>{
validate_from_date(frm);
},
- set_approver: (frm) =>{
- if(frm.doc.employee){
- frappe.call({
- method: 'one_fm.utils.get_approver_user',
- args:{
- 'employee':frm.doc.employee
- },
- callback: function(r) {
- let approver = "";
- if(r.message){
- approver = r.message;
- }
- frm.set_value("approver", approver);
- frm.refresh_field("approver");
- }
- });
- }
- else{
- frm.set_value("approver", "");
- frm.refresh_field("approver");
- }
- }
});
-function set_update_request_btn(frm) {
- if(frm.doc.docstatus == 1 && frm.doc.workflow_state == 'Approved' && !frm.doc.update_request){
- if(frappe.user.has_role('Shift Supervisor')){
- frm.add_custom_button(__('Update Request'), function() {
- update_request(frm);
- });
- }
- }
-};
-
-function update_request(frm) {
- var dialog = new frappe.ui.Dialog({
- title: 'Update Request',
- fields: [
- {fieldtype: "Date", label: "From Date", fieldname: "from_date", reqd: true},
- {fieldtype: "Date", label: "To Date", fieldname: "to_date", reqd: true},
- ],
- primary_action_label: __("Update"),
- primary_action : function(){
- frappe.confirm(
- __('Are you sure to proceed?'),
- function(){
- // Yes
- frappe.call({
- method: 'one_fm.overrides.attendance_request.update_request',
- args: {
- attendance_request: frm.doc.name,
- from_date: dialog.get_value('from_date'),
- to_date: dialog.get_value('to_date'),
- },
- callback: function(r) {
- if(!r.exc) {
- frm.reload_doc();
- }
- },
- freaze: true,
- freaze_message: __("Update Request ..")
- });
- dialog.hide();
- },
- function(){
- // No
- dialog.hide();
- }
- );
- }
- });
- dialog.show();
-};
-
validate_from_date = (frm) => {
if (frm.doc.from_date < frappe.datetime.now_date()){
- frappe.throw("Atendance Request can not be created for past dates.")
+ if (frm.is_new()){
+ frappe.throw("Atendance Request can not be created for past dates.")
+ }else {
+ frappe.throw("Atendance Request can not be updated to a past date.")
+ }
}
-}
+}
\ No newline at end of file
diff --git a/one_fm/public/js/doctype_js/employee_checkin.js b/one_fm/public/js/doctype_js/employee_checkin.js
index 4a8eb6f444..b0ab8bde4d 100644
--- a/one_fm/public/js/doctype_js/employee_checkin.js
+++ b/one_fm/public/js/doctype_js/employee_checkin.js
@@ -20,12 +20,10 @@ frappe.ui.form.on('Employee Checkin', {
});
-validate_source_of_checkin = (frm) => {
- allowed_sources = ['Mobile App', 'Mobile Web']
- if (frappe.session.user!='Administrator'){
- if(!allowed_sources.includes(frm.doc.source)){
- frappe.throw("Employee Checkin can only be via the Mobile App or Mobile Web App")
- }
+var validate_source_of_checkin = (frm) => {
+ var allowed_sources = ['Mobile App', 'Mobile Web']
+ if(!allowed_sources.includes(frm.doc.source)){
+ frappe.throw("Employee Checkin can only be via the Mobile App or Mobile Web App")
}
}
diff --git a/one_fm/public/js/doctype_js/shift_request.js b/one_fm/public/js/doctype_js/shift_request.js
index e9d4df3a72..20bbbeffb0 100644
--- a/one_fm/public/js/doctype_js/shift_request.js
+++ b/one_fm/public/js/doctype_js/shift_request.js
@@ -16,6 +16,7 @@ frappe.ui.form.on('Shift Request', {
onload: function(frm){
prefillForm(frm);
set_employee_filters(frm)
+
},
refresh: function(frm) {
@@ -44,6 +45,14 @@ frappe.ui.form.on('Shift Request', {
});
}
+ },
+ purpose: function(frm){
+ update_shift_role(frm);
+
+ },
+ replaced_employee: function (frm){
+ update_shift_role(frm);
+
}
});
@@ -234,4 +243,33 @@ var prefillForm = frm =>{
}
});
}
-}
\ No newline at end of file
+}
+
+const update_shift_role = (frm) => {
+ if (frm.doc.purpose == "Replace Existing Assignment" && frm.doc.replaced_employee){
+ frappe.call({
+ method: "frappe.client.get_list",
+ args: {
+ doctype: "Shift Assignment",
+ fields: ["operations_role", "shift"],
+ order_by: "modified desc",
+ filters: {
+ "employee": frm.doc.replaced_employee
+ },
+ limit_page_length: 1
+ },
+ callback: function(r) {
+ if (r.message && r.message.length > 0) {
+ var last_doc = r.message[0];
+ frm.set_value({
+ operations_role: last_doc.operations_role,
+ operations_shift: last_doc.shift
+ })
+
+ }
+ }
+ }
+
+ )
+ }
+};
\ No newline at end of file
diff --git a/one_fm/templates/emails/attendance_manager_todo_assignment.html b/one_fm/templates/emails/attendance_manager_todo_assignment.html
new file mode 100644
index 0000000000..dbffc3e3e7
--- /dev/null
+++ b/one_fm/templates/emails/attendance_manager_todo_assignment.html
@@ -0,0 +1,18 @@
+
+
+ Dear {{manager}},
+
+ Please note that there are pending Attendance Checks from that require your attention
+
+ You can view the documents by clicking here .
+
+
+
+ Best regards.
+
+ |
+
+
+
+
+
diff --git a/one_fm/utils.py b/one_fm/utils.py
index 0c342d7c0d..a174be6ad4 100755
--- a/one_fm/utils.py
+++ b/one_fm/utils.py
@@ -3073,6 +3073,36 @@ def get_approver_user(employee):
return frappe.db.get_value("Employee", approver, "user_id")
return None
+
+@frappe.whitelist()
+def has_super_user_role(user=None):
+ '''
+ A method to check the user is having super user role
+ Default it will be the role 'Director'
+ User having this role can be self approve configured documents like Shift Permission.
+ The user having this role no need reports to, since it will be the same employee linked to the user.
+
+ args:
+ user: user ID, eg: employee123@one_fm.com
+
+ return boolean(True if super user role exists in the given user's role list)
+ '''
+ if not user:
+ user = frappe.session.user
+ if user:
+ # get the user roles
+ user_roles = frappe.get_roles(user)
+ # Check if the default super user role in the user role list
+ if "Director" in user_roles:
+ return True
+ else:
+ # Get configured super user in ONEFM General Setting
+ super_user_role = frappe.db.get_single_value("ONEFM General Setting", "super_user_role")
+ # Check if the super user role exists in the user role list
+ if super_user_role and super_user_role in user_roles:
+ return True
+ return False
+
@frappe.whitelist()
def get_approver(employee, date=False):
'''
@@ -3124,34 +3154,6 @@ def get_approver(employee, date=False):
return line_manager
-@frappe.whitelist()
-def has_super_user_role(user=None):
- '''
- A method to check the user is having super user role
- Default it will be the role 'Director'
- User having this role can be self approve configured documents like Shift Permission.
- The user having this role no need reports to, since it will be the same employee linked to the user.
-
- args:
- user: user ID, eg: employee123@one_fm.com
-
- return boolean(True if super user role exists in the given user's role list)
- '''
- if not user:
- user = frappe.session.user
- if user:
- # get the user roles
- user_roles = frappe.get_roles(user)
- # Check if the default super user role in the user role list
- if "Director" in user_roles:
- return True
- else:
- # Get configured super user in ONEFM General Setting
- super_user_role = frappe.db.get_single_value("ONEFM General Setting", "super_user_role")
- # Check if the super user role exists in the user role list
- if super_user_role and super_user_role in user_roles:
- return True
- return False
def get_approver_for_many_employees(supervisor=None):
"""
@@ -3409,64 +3411,44 @@ def custom_validate_interviewer(self):
def get_current_shift(employee):
"""
Get current shift employee should be logged into
+ This Method Checks if Employee has a shift and is within the checkin Range.
+ args:
+ employee: Employee ID
+ return dict (type, data)
"""
- sql = f"""
- SELECT * FROM `tabShift Assignment`
- WHERE employee="{employee}" AND status="Active" AND docstatus=1 AND
- ('{now()}' BETWEEN start_datetime AND end_datetime)
- """
- shift = frappe.db.sql(sql, as_dict=1)
- if shift: # shift was checked in between start and end time
- return frappe.get_doc("Shift Assignment", shift[0])
- else: # we look right and left (right for next shift)
- dt = datetime.strptime(now(), '%Y-%m-%d %H:%M:%S.%f')
- curtime_plus_1 = dt + timedelta(hours=1)
+ try:
+ nowtime = now_datetime()
sql = f"""
- SELECT * FROM `tabShift Assignment`
- WHERE employee="{employee}" AND status="Active" AND docstatus=1 AND
- ('{curtime_plus_1}' BETWEEN start_datetime AND end_datetime)
- """
- shift = frappe.db.sql(sql, as_dict=1)
- if shift: # shift was checked 1hr ahead
- return frappe.get_doc("Shift Assignment", shift[0])
- else:
- curtime_plus_1 = dt + timedelta(hours=-1)
- sql = f"""
- SELECT * FROM `tabShift Assignment`
- WHERE employee="{employee}" AND status="Active" AND docstatus=1 AND
- ('{curtime_plus_1}' BETWEEN start_datetime AND end_datetime)
+ SELECT sa.*,
+ DATE_SUB(sa.start_datetime, INTERVAL st.begin_check_in_before_shift_start_time MINUTE) as checkin_time,
+ DATE_ADD(sa.end_datetime, INTERVAL st.allow_check_out_after_shift_end_time MINUTE) as checkout_time
+ FROM `tabShift Assignment` sa
+ JOIN `tabShift Type` st ON sa.shift_type = st.name
+ WHERE sa.employee="{employee}"
+ AND sa.status="Active"
+ AND sa.docstatus=1
+ AND (DATE('{nowtime}') = sa.start_date
+ OR DATE_ADD(DATE('{nowtime}'), INTERVAL 1 DAY) = sa.start_date
+ OR DATE('{nowtime}') = sa.end_date)
"""
- shift = frappe.db.sql(sql, as_dict=1)
- if shift: # shift was checked 1hr in the past
- return frappe.get_doc("Shift Assignment", shift[0])
- return False
-
-@frappe.whitelist()
-def check_existing_checking(shift):
- """API to determine the applicable Log type.
- The api checks employee's last lcheckin log type. and determine what next log type needs to be
- Returns:
- True: The log in was "IN", so his next Log Type should be "OUT".
- False: either no log type or last log type is "OUT", so his next Ltg Type should be "IN".
- """
- checkin = frappe.db.get_list("Employee Checkin", filters={
- 'employee':shift.employee, 'shift_assignment':shift.name,
- 'shift_actual_start':shift.start_datetime,
- 'shift_actual_end':shift.end_datetime,
- 'roster_type':shift.roster_type
- },
- fields='log_type',
- order_by="actual_time DESC"
- )
- if checkin:
- # #For Check IN
- if checkin[0].log_type=='OUT':
- return "IN"
- #For Check OUT
- else:
- return "OUT"
- return "IN"
+ shift = frappe.db.sql(sql, as_dict=1)
+ if shift: # shift was checked in between start and end time
+ data = frappe.get_doc("Shift Assignment", shift[0].name)
+ if shift[0].checkin_time > nowtime:
+ minutes = int((shift[0].checkin_time - nowtime).total_seconds() / 60)
+ return {"type":"Early", "data": data, "time": minutes}
+ elif shift[0].checkout_time < nowtime:
+ minutes = int((nowtime - shift[0].checkout_time).total_seconds() / 60)
+ return {"type":"Late", "data": data, "time": minutes}
+ elif shift[0].checkin_time <= nowtime <= shift[0].checkout_time:
+ return {"type":"On Time", "data": data, "time": 0}
+ else:
+ return False
+ return False
+ except Exception as e:
+ frappe.log_error(frappe.get_traceback(), "Error while getting current shift")
+ return False
@frappe.whitelist()
def check_existing():
@@ -3479,22 +3461,24 @@ def check_existing():
employee = frappe.get_value("Employee", {"user_id": frappe.session.user})
if not employee:
return response("Employee not found", 404, None, "Employee not found")
- curr_shift = get_current_shift(employee)
+ shift_exists = get_current_shift(employee)
+ if shift_exists['type'] == "On Time":
+ curr_shift = shift_exists['data']
if not curr_shift:
return response("Employee not found", 404, None, "Employee not found")
- log_type = check_existing_checking(curr_shift)
+ log_type = curr_shift.get_next_checkin_log_type()
if log_type=='IN':
return response("success", 200, True, "")
return response("success", 200, False, "")
-def fetch_attendance_manager_user_obj() -> str:
- attendance_manager = frappe.get_doc("ONEFM General Setting").get("attendance_manager")
+def fetch_attendance_manager_user() -> str:
+ attendance_manager = frappe.db.get_single_value("ONEFM General Setting", "attendance_manager")
if attendance_manager:
attendance_manager_user = frappe.db.get_value("Employee", {"name": attendance_manager}, "user_id")
- return attendance_manager_user
+ if attendance_manager_user:
+ return attendance_manager_user
return ""
-
def custom_toggle_notifications(user: str, enable: bool = False):
try:
settings = frappe.get_doc("Notification Settings", user)
@@ -3640,4 +3624,20 @@ def send_work_anniversary_reminders():
others = [d for d in anniversary_persons if d != person]
reminder_text = get_work_anniversary_reminder_text(others)
send_work_anniversary_reminder(person_email, reminder_text, others, message, sender)
-
\ No newline at end of file
+
+
+
+def is_holiday(employee, date=None, raise_exception=True):
+ try:
+ date = today() if not date else date
+ holiday_list = get_holiday_list_for_employee(employee.name, raise_exception)
+ if not holiday_list:
+ return False, ""
+ holidays = frappe.db.get_value("Holiday", {"parent": holiday_list, "holiday_date": date}, ["weekly_off"], as_dict=1)
+ if holidays:
+ return (True, f"Dear {employee.employee_name}, Today is your day off. Happy Recharging!.") if holidays.weekly_off else (True, "Today is your holiday, have fun")
+ return False, ""
+ except Exception as e:
+ frappe.log_error(frappe.get_traceback(), "Error while validating Holiday")
+ return False, ""
+