def set_resolution_time(doc): start_date_time = get_datetime( doc.get("service_level_agreement_creation") or doc.creation) if doc.meta.has_field("resolution_time"): doc.resolution_time = time_diff_in_seconds(doc.resolution_date, start_date_time) # total time taken by a user to close the issue apart from wait_time if not doc.meta.has_field("user_resolution_time"): return communications = frappe.get_all( "Communication", filters={ "reference_doctype": doc.doctype, "reference_name": doc.name }, fields=["sent_or_received", "name", "creation"], order_by="creation", ) pending_time = [] for i in range(len(communications)): if (communications[i].sent_or_received == "Received" and communications[i - 1].sent_or_received == "Sent"): wait_time = time_diff_in_seconds(communications[i].creation, communications[i - 1].creation) if wait_time > 0: pending_time.append(wait_time) total_pending_time = sum(pending_time) resolution_time_in_secs = time_diff_in_seconds(doc.resolution_date, start_date_time) doc.user_resolution_time = resolution_time_in_secs - total_pending_time
def total_working_hours(doc, method): if doc.in_time and doc.out_time: in_time_f = datetime.strptime(doc.in_time, '%H:%M:%S') out_time_f = datetime.strptime(doc.out_time, '%H:%M:%S') frappe.errprint(type(doc.attendance_date)) frappe.errprint(type(doc.out_date)) actual_working_hours = frappe.db.get_value("Employee", doc.employee, "working_hours") td = (out_time_f - in_time_f) - actual_working_hours if actual_working_hours > (out_time_f - in_time_f): td = (out_time_f - in_time_f) if doc.out_date > doc.attendance_date: next_day = timedelta(hours=24) worked_hrs = time_diff_in_seconds(out_time_f + next_day, in_time_f) else: worked_hrs = time_diff_in_seconds(out_time_f, in_time_f) total_working_hours = (worked_hrs / 3600.00) if td.seconds >= 2700: total_working_hours = math.ceil(total_working_hours) else: total_working_hours = math.floor(total_working_hours) att = frappe.get_doc("Attendance", doc.name) att.update({"total_working_hours": total_working_hours}) att.db_update() frappe.db.commit()
def set_user_resolution_time(issue): # total time taken by a user to close the issue apart from wait_time communications = frappe.get_list( "Communication", filters={ "reference_doctype": issue.doctype, "reference_name": issue.name }, fields=["sent_or_received", "name", "creation"], order_by="creation") pending_time = [] for i in range(len(communications)): if communications[i].sent_or_received == "Received" and communications[ i - 1].sent_or_received == "Sent": wait_time = time_diff_in_seconds(communications[i].creation, communications[i - 1].creation) if wait_time > 0: pending_time.append(wait_time) total_pending_time = sum(pending_time) resolution_time_in_secs = time_diff_in_seconds(issue.resolution_date, issue.creation) user_resolution_time = resolution_time_in_secs - total_pending_time issue.db_set("user_resolution_time", user_resolution_time)
def set_response_and_resolution_time(self, priority=None, service_level_agreement=None): service_level_agreement = get_active_service_level_agreement_for(self) if not service_level_agreement: if frappe.db.get_value("Issue", self.name, "service_level_agreement"): frappe.throw(_("Couldn't Set Service Level Agreement {0}.").format(self.service_level_agreement)) return if (service_level_agreement.customer and self.customer) and not (service_level_agreement.customer == self.customer): frappe.throw(_("This Service Level Agreement is specific to Customer {0}").format(service_level_agreement.customer)) self.service_level_agreement = service_level_agreement.name if not self.priority: self.priority = service_level_agreement.default_priority priority = get_priority(self) if not self.creation: self.creation = now_datetime() self.service_level_agreement_creation = now_datetime() start_date_time = get_datetime(self.service_level_agreement_creation) self.response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) self.resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) self.response_by_variance = round(time_diff_in_seconds(self.response_by, now_datetime())) self.resolution_by_variance = round(time_diff_in_seconds(self.resolution_by, now_datetime()))
def execute(): att = frappe.db.sql("""SELECT at.name, at.att_date, at.employee, at.employee_name, at.overtime, at.shift FROM `tabAttendance` at, `tabShift Type` sh WHERE at.docstatus = 1 AND sh.in_out_required = 1 AND at.shift = sh.name ORDER BY at.name""", as_list=1) for i in att: att = frappe.get_doc("Attendance", i[0]) shft = frappe.get_doc("Shift Type", i[5]) pu_data = [] overtime = 0 tt_in = 0 tt_out = 0 shft_hrs = shft.hours_required_per_day.seconds shft_marg = shft.time_margin.seconds shft_rounding = shft.time_rounding.seconds for d in att.attendance_time: pu_data.append([d.idx, d.time_type, d.date_time]) #only calculate the ot if there are any IN and OUT entries for j in range(len(pu_data)-1): if pu_data[j][1] == 'In Time': tt_in += time_diff_in_seconds(pu_data[j+1][2], pu_data[j][2]) else: tt_out += time_diff_in_seconds(pu_data[j+1][2], pu_data[j][2]) overtime = ((tt_in - shft_hrs + shft_marg)- \ ((tt_in + shft_marg - shft_hrs)%shft_rounding))/3600 frappe.db.set_value("Attendance", i[0], 'overtime', overtime) print ("Attendance Updated " + i[0] + " for date:" + i[1] + " employee: " + i[3] + " New OT=" + overtime)
def set_service_level_agreement_variance(issue=None): current_time = frappe.flags.current_time or now_datetime() filters = {"status": "Open", "agreement_status": "Ongoing"} if issue: filters = {"name": issue} for issue in frappe.get_list("Issue", filters=filters): doc = frappe.get_doc("Issue", issue.name) if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer variance = round(time_diff_in_seconds(doc.response_by, current_time), 2) frappe.db.set_value( dt="Issue", dn=doc.name, field="response_by_variance", val=variance, update_modified=False ) if variance < 0: frappe.db.set_value( dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False ) if not doc.resolution_date: # resolution_date set when issue has been closed variance = round(time_diff_in_seconds(doc.resolution_by, current_time), 2) frappe.db.set_value( dt="Issue", dn=doc.name, field="resolution_by_variance", val=variance, update_modified=False ) if variance < 0: frappe.db.set_value( dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False )
def update_agreement_status_on_custom_status(self): """ Update Agreement Fulfilled status using Custom Scripts for Custom Issue Status """ if not self.first_responded_on: # first_responded_on set when first reply is sent to customer self.response_by_variance = round(time_diff_in_seconds(self.response_by, now_datetime()), 2) if not self.resolution_date: # resolution_date set when issue has been closed self.resolution_by_variance = round(time_diff_in_seconds(self.resolution_by, now_datetime()), 2) self.agreement_status = "Fulfilled" if self.response_by_variance > 0 and self.resolution_by_variance > 0 else "Failed"
def handle_hold_time(self, status): if self.service_level_agreement: # set response and resolution variance as None as the issue is on Hold pause_sla_on = frappe.db.get_all( "Pause SLA On Status", fields=["status"], filters={"parent": self.service_level_agreement} ) hold_statuses = [entry.status for entry in pause_sla_on] update_values = {} if hold_statuses: if self.status in hold_statuses and status not in hold_statuses: update_values["on_hold_since"] = frappe.flags.current_time or now_datetime() if not self.first_responded_on: update_values["response_by"] = None update_values["response_by_variance"] = 0 update_values["resolution_by"] = None update_values["resolution_by_variance"] = 0 # calculate hold time when status is changed from any hold status to any non-hold status if self.status not in hold_statuses and status in hold_statuses: hold_time = self.total_hold_time if self.total_hold_time else 0 now_time = frappe.flags.current_time or now_datetime() last_hold_time = 0 if self.on_hold_since: # last_hold_time will be added to the sla variables last_hold_time = time_diff_in_seconds(now_time, self.on_hold_since) update_values["total_hold_time"] = hold_time + last_hold_time # re-calculate SLA variables after issue changes from any hold status to any non-hold status # add hold time to SLA variables start_date_time = get_datetime(self.service_level_agreement_creation) priority = get_priority(self) now_time = frappe.flags.current_time or now_datetime() if not self.first_responded_on: response_by = get_expected_time_for( parameter="response", service_level=priority, start_date_time=start_date_time ) response_by = add_to_date(response_by, seconds=round(last_hold_time)) response_by_variance = round(time_diff_in_seconds(response_by, now_time)) update_values["response_by"] = response_by update_values["response_by_variance"] = response_by_variance + last_hold_time resolution_by = get_expected_time_for( parameter="resolution", service_level=priority, start_date_time=start_date_time ) resolution_by = add_to_date(resolution_by, seconds=round(last_hold_time)) resolution_by_variance = round(time_diff_in_seconds(resolution_by, now_time)) update_values["resolution_by"] = resolution_by update_values["resolution_by_variance"] = resolution_by_variance + last_hold_time update_values["on_hold_since"] = None self.db_set(update_values)
def update_first_response_time(parent, communication): if parent.meta.has_field("first_response_time") and not parent.get("first_response_time"): if is_system_user(communication.sender): first_responded_on = communication.creation if parent.meta.has_field("first_responded_on") and communication.sent_or_received == "Sent": parent.db_set("first_responded_on", first_responded_on) parent.db_set("first_response_time", round(time_diff_in_seconds(first_responded_on, parent.creation), 2))
def set_avg_response_time(parent, communication): if parent.meta.has_field( "avg_response_time") and communication.sent_or_received == "Sent": # avg response time for all the responses communications = frappe.get_list( "Communication", filters={ "reference_doctype": parent.doctype, "reference_name": parent.name }, fields=["sent_or_received", "name", "creation"], order_by="creation") if len(communications): response_times = [] for i in range(len(communications)): if communications[ i].sent_or_received == "Sent" and communications[ i - 1].sent_or_received == "Received": response_time = round( time_diff_in_seconds(communications[i].creation, communications[i - 1].creation), 2) if response_time > 0: response_times.append(response_time) if response_times: avg_response_time = sum(response_times) / len(response_times) parent.db_set("avg_response_time", avg_response_time)
def get_role_permission(self, username): from cloud.cloud.doctype.cloud_company_group.cloud_company_group import list_users if not self.owner_id: return None if self.owner_type == 'User': if self.owner_id == username: return 'Admin' else: for user in list_users(self.owner_id): if user.name == username: return user.role share_role = None for d in frappe.db.get_values("IOT ShareGroupDevice", { "device": self.name, "parenttype": 'IOT Share Group' }, "parent"): if frappe.get_value("IOT ShareGroupUser", { "parent": d[0], "user": username }, "parent") == d[0]: if share_role != 'Admin': share_role = frappe.get_value("IOT Share Group", d[0], 'role') for d in frappe.db.get_values("IOT Device Share", { "device": self.name, "share_to": username }, "name"): end_time = frappe.get_value("IOT Device Share", d[0], "end_time") if time_diff_in_seconds(end_time, get_datetime()) > 0: share_role = 'Admin' return share_role
def get_expected_time_for(parameter, service_level, start_date_time): current_date_time = start_date_time expected_time = current_date_time start_time = None end_time = None if parameter == "response": allotted_seconds = service_level.get("response_time") elif parameter == "resolution": allotted_seconds = service_level.get("resolution_time") else: frappe.throw(_("{0} parameter is invalid").format(parameter)) expected_time_is_set = 0 support_days = {} for service in service_level.get("support_and_resolution"): support_days[service.workday] = frappe._dict({ "start_time": service.start_time, "end_time": service.end_time, }) holidays = get_holidays(service_level.get("holiday_list")) weekdays = get_weekdays() while not expected_time_is_set: current_weekday = weekdays[current_date_time.weekday()] if not is_holiday(current_date_time, holidays) and current_weekday in support_days: start_time = current_date_time - datetime(current_date_time.year, current_date_time.month, current_date_time.day) \ if getdate(current_date_time) == getdate(start_date_time) and get_time_in_timedelta(current_date_time.time()) > support_days[current_weekday].start_time \ else support_days[current_weekday].start_time end_time = support_days[current_weekday].end_time time_left_today = time_diff_in_seconds(end_time, start_time) # no time left for support today if time_left_today <= 0: pass elif allotted_seconds: if time_left_today >= allotted_seconds: expected_time = datetime.combine( getdate(current_date_time), get_time(start_time)) expected_time = add_to_date(expected_time, seconds=allotted_seconds) expected_time_is_set = 1 else: allotted_seconds = allotted_seconds - time_left_today if not expected_time_is_set: current_date_time = add_to_date(current_date_time, days=1) if end_time and allotted_seconds >= 86400: current_date_time = datetime.combine(getdate(current_date_time), get_time(end_time)) else: current_date_time = expected_time return current_date_time
def should_remove_barcode_image(barcode): '''Check if it's time to delete barcode image from server. ''' if isinstance(barcode, string_types): barcode = frappe.get_doc('File', barcode) lifespan = frappe.db.get_value('System Settings', 'System Settings', 'lifespan_qrcode_image') if time_diff_in_seconds(get_datetime(), barcode.creation) > int(lifespan): return True return False
def update_mins_to_first_communication(parent, communication): if parent.meta.has_field('mins_to_first_response') and not parent.get('mins_to_first_response'): if frappe.db.get_all('User', filters={'email': communication.sender, 'user_type': 'System User', 'enabled': 1}, limit=1): first_responded_on = communication.creation if parent.meta.has_field('first_responded_on'): parent.db_set('first_responded_on', first_responded_on) parent.db_set('mins_to_first_response', round(time_diff_in_seconds(first_responded_on, parent.creation) / 60), 2)
def calculate_total_hours(self): if self.arrival_time == "#--:--" or self.arrival_time == "00:00" or self.arrival_time == "0:00:00": self.arrival_time = "00:00:00" if self.departure_time == "#--:--" or self.departure_time == "00:00" or self.departure_time == "0:00:00": self.departure_time = "00:00:00" try: totalworkhours = flt(time_diff_in_seconds(self.departure_time,self.arrival_time))/3600 except: try: time = time_diff(self.departure_time,self.arrival_time) totalworkhours = flt(time.hour) + flt(time.minute)/60 + flt(time.second)/3600 except: frappe.throw(_("Possible error in arrival time {0} or departure time {1} for employee {2}").format(self.arrival_time,self.departure_time,self.employee)) self.working_time = totalworkhours weekday = get_datetime(self.att_date).weekday() working_hours = frappe.db.sql("""select working_hours from `tabWorking Hours` where %s between from_date and to_date and docstatus < 2""", (self.att_date)) if working_hours: self.normal_time = flt(working_hours[0][0]) else: self.normal_time = flt(frappe.db.get_single_value("Regulations", "working_hours")) self.overtime = 0 self.overtime_fridays = 0 self.overtime_holidays = 0 self.status = 'Present' if len(self.get_holidays_for_employee(self.att_date,self.att_date)): self.normal_time = 0 self.overtime_holidays = flt(totalworkhours) - flt(self.normal_time) elif weekday == 4: self.normal_time = 0 self.overtime_fridays = flt(totalworkhours) - flt(self.normal_time) else: if totalworkhours > self.normal_time: self.overtime = flt(totalworkhours) - flt(self.normal_time) elif totalworkhours > 2: self.normal_time = totalworkhours elif totalworkhours > 0: frappe.throw(_("Work Hours under 2. Please check the time for employee {0}, date {1}").format(self.employee,self.att_date)) elif totalworkhours < 0: frappe.throw(_("Work Hours negative. Please check the time for employee {0}, date {1}").format(self.employee,self.att_date)) else: if self.arrival_time == "00:00:00" and self.departure_time == "00:00:00": self.normal_time = 0 self.status = 'Absent' else: frappe.throw(_("Please check the time for employee {0}, date {1}").format(self.employee,self.att_date))
def should_remove_barcode_image(barcode): """Check if it's time to delete barcode image from server.""" if isinstance(barcode, string_types): barcode = frappe.get_doc("File", barcode) lifespan = (frappe.db.get_value("System Settings", "System Settings", "lifespan_qrcode_image") or 240) if time_diff_in_seconds(get_datetime(), barcode.creation) > int(lifespan): return True return False
def calculate_hold_hours(): # In case issue was closed and after few days it has been opened # The hold time should be calculated from resolution_date on_hold_since = doc.resolution_date or doc.on_hold_since if on_hold_since: current_hold_hours = time_diff_in_seconds(now_time, on_hold_since) doc.total_hold_time = (doc.total_hold_time or 0) + current_hold_hours doc.on_hold_since = None
def is_within_operating_hours(workstation, operation, from_datetime, to_datetime): operation_length = time_diff_in_seconds(to_datetime, from_datetime) workstation = frappe.get_doc("Workstation", workstation) for working_hour in workstation.working_hours: slot_length = (get_datetime(working_hour.end_time) - get_datetime(working_hour.start_time)).total_seconds() if slot_length >= operation_length: return frappe.throw(_("Operation {0} longer than any available working hours in workstation {1}, break down the operation into multiple operations").format(operation, workstation.name), NotInWorkingHoursError)
def calculate_hours(in_date, out_date, in_time, out_time, employee): working_hrs = frappe.db.get_value("Employee", employee, "working_hours") if working_hrs: shift_hrs = working_hrs.seconds in_time_f = datetime.strptime(in_time, '%H:%M:%S') out_time_f = datetime.strptime(out_time, '%H:%M:%S') if in_date < out_date: next_day = timedelta(hours=24) worked_hrs = time_diff_in_seconds(out_time_f + next_day, in_time_f) else: worked_hrs = time_diff_in_seconds(out_time_f, in_time_f) overtime = cint(worked_hrs - shift_hrs) # overtime_f = datetime.strptime( # overtime, '%H:%M:%S') min_hr = timedelta(seconds=3600) ot_f = timedelta(seconds=overtime) if ot_f > min_hr: return ot_f.seconds
def list(): try: valid_auth_code() # frappe.logger(__name__).debug("List Devices for user {0}").format(user) # Get Enteprise Devices ent_devices = [] user = frappe.session.user groups = _list_user_groups(user) companies = list_user_companies(user) for g in groups: dev_list = [d[0] for d in frappe.db.get_values("IOT Device", { "owner_id": g.name, "owner_type": "Cloud Company Group" }, "name")] ent_devices.append({"group": g.name, "devices": dev_list, "role": g.role}) # Get Shared Devices shd_devices = [] for shared_group in [ d[0] for d in frappe.db.get_values("IOT ShareGroupUser", {"user": user}, "parent")]: # Make sure we will not having shared device from your company if frappe.get_value("IOT Share Group", shared_group, "company") in companies: continue role = frappe.get_value("IOT Share Group", shared_group, "role") dev_list = [] for dev in [d[0] for d in frappe.db.get_values("IOT ShareGroupDevice", {"parent": shared_group}, "device")]: dev_list.append(dev) shd_devices.append({"group": shared_group, "devices": dev_list, "role": role}) device_share_list = [] for d in frappe.db.get_values("IOT Device Share", {"share_to": user}, "name"): end_time = frappe.get_value("IOT Device Share", d[0], "end_time") if time_diff_in_seconds(end_time, get_datetime()) > 0: device_share_list.append(frappe.get_value("IOT Device Share", d[0], "device")) shd_devices.append({"group": "IOT Device Share", "devices": device_share_list, "role": 'Admin'}) # Get Private Devices pri_devices = [d[0] for d in frappe.db.get_values("IOT Device", {"owner_id": user, "owner_type": "User"}, "name")] devices = { "company_devices": ent_devices, "private_devices": pri_devices, "shared_devices": shd_devices, } frappe.response.update({ "ok": True, "data": devices }) except Exception as ex: frappe.response.update({ "ok": False, "error": str(ex) })
def valide_date(data): obj = json.loads(data) from frappe.utils import time_diff_in_seconds from_date_time = datetime.datetime.strptime(obj.get('from_date_time'), '%d/%m/%Y %H:%M').strftime('%Y-%m-%d %H:%M:%S') curr_date_time = datetime.datetime.strptime(obj.get('curr_date_time'), '%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d %H:%M:%S') if time_diff_in_seconds(from_date_time, curr_date_time) < 0: return False return True
def validate_duration(doc): if not doc.duration or doc.duration <= 0: frappe.throw(_("Duration must be geater than zero")) else: # time diff in minutes = in seconds / 60 total_time_diff = time_diff_in_seconds(doc.to_time, doc.from_time)/60 if total_time_diff <= 0: frappe.throw(_("From Time should not be greater than or equal to To Time")) if total_time_diff < doc.duration: frappe.throw(_("Duration between from time and to time must be greater than or equal to duration given")) elif total_time_diff % doc.duration != 0: frappe.throw(_("Duration between from time and to time must be multiple of duration given"))
def validate_duration(doc): if doc.duration <= 0: frappe.throw(_("Duration must be geater than zero")) else: # time diff in minutes = in seconds / 60 total_time_diff = time_diff_in_seconds(doc.to_time, doc.from_time)/60 if total_time_diff <= 0: frappe.throw(_("From Time should not be greater than or equal to To Time")) if total_time_diff < doc.duration: frappe.throw(_("Duration between from time and to time must be greater than or equal to duration given")) elif total_time_diff % doc.duration != 0: frappe.throw(_("Duration between from time and to time must be multiple of duration given"))
def calculate_overtime(doc,method): doc.overtime = 0 tt_in = 0 tt_out = 0 shft_intime, shft_lunchout, shft_lunchin, shft_hrs, \ shft_rounding, shft_marg = validate_time_with_shift(doc,method) pu_data = check_punch_data(doc, method) #only calculate the ot if there are any IN and OUT entries #if doc.attendance_time: for i in range(len(doc.attendance_time)-1): pass for i in range(len(pu_data)-1): if pu_data[i][1] == 'In Time': tt_in += time_diff_in_seconds(pu_data[i+1][2], pu_data[i][2]) else: tt_out += time_diff_in_seconds(pu_data[i+1][2], pu_data[i][2]) doc.overtime = ((tt_in - shft_hrs + shft_marg)- \ ((tt_in + shft_marg - shft_hrs)%shft_rounding))/3600
def bulk_total_working_hours(): # days = ["2018-07-01", "2018-07-02", "2018-07-03", "2018-07-04", "2018-07-06", # "2018-07-07", "2018-07-08", "2018-07-09", "2018-07-10", "2018-07-11", "2018-07-12", "2018-07-13", # "2018-07-14", "2018-07-15", "2018-07-16", "2018-07-17", "2018-07-18", "2018-07-19", "2018-07-20"] days = ["2018-07-22"] # # day = datetime.strptime('25042018', "%d%m%Y").date() for day in days: attendance = frappe.get_all("Attendance", fields=[ 'name', 'employee', 'attendance_date', 'out_date', 'in_time', 'out_time', 'total_working_hours' ], filters={'attendance_date': day}) for doc in attendance: if doc.attendance_date and doc.out_date and doc.in_time and doc.out_time: in_time_f = datetime.strptime(doc.in_time, '%H:%M:%S') out_time_f = datetime.strptime(doc.out_time, '%H:%M:%S') maxhr = timedelta(seconds=2400) actual_working_hours = frappe.db.get_value( "Employee", doc.employee, "working_hours") td = (out_time_f - in_time_f) - actual_working_hours if actual_working_hours > (out_time_f - in_time_f): td = (out_time_f - in_time_f) if doc.attendance_date < doc.out_date: next_day = timedelta(hours=24) worked_hrs = time_diff_in_seconds(out_time_f + next_day, in_time_f) else: worked_hrs = time_diff_in_seconds(out_time_f, in_time_f) total_working_hours = (worked_hrs / 3600.00) if td.seconds >= 2700: total_working_hours = math.ceil(total_working_hours) else: total_working_hours = math.floor(total_working_hours) att = frappe.get_doc("Attendance", doc.name) att.update({"total_working_hours": total_working_hours}) att.db_update() frappe.db.commit()
def calculate_overtime(doc, method): doc.overtime = 0 tt_in = 0 tt_out = 0 shft_intime, shft_lunchout, shft_lunchin, shft_hrs, \ shft_rounding, shft_marg = validate_time_with_shift(doc,method) pu_data = check_punch_data(doc, method) #only calculate the ot if there are any IN and OUT entries #if doc.attendance_time: for i in range(len(doc.attendance_time) - 1): pass for i in range(len(pu_data) - 1): if pu_data[i][1] == 'In Time': tt_in += time_diff_in_seconds(pu_data[i + 1][2], pu_data[i][2]) else: tt_out += time_diff_in_seconds(pu_data[i + 1][2], pu_data[i][2]) doc.overtime = ((tt_in - shft_hrs + shft_marg)- \ ((tt_in + shft_marg - shft_hrs)%shft_rounding))/3600
def get_expected_time_for(parameter, service_level, start_date_time): current_date_time = start_date_time expected_time = current_date_time start_time = end_time = None expected_time_is_set = 0 allotted_seconds = get_allotted_seconds(parameter, service_level) support_days = get_support_days(service_level) holidays = get_holidays(service_level.get("holiday_list")) weekdays = get_weekdays() while not expected_time_is_set: current_weekday = weekdays[current_date_time.weekday()] if not is_holiday(current_date_time, holidays) and current_weekday in support_days: if (getdate(current_date_time) == getdate(start_date_time) and get_time_in_timedelta(current_date_time.time()) > support_days[current_weekday].start_time): start_time = current_date_time - datetime( current_date_time.year, current_date_time.month, current_date_time.day) else: start_time = support_days[current_weekday].start_time end_time = support_days[current_weekday].end_time time_left_today = time_diff_in_seconds(end_time, start_time) # no time left for support today if time_left_today <= 0: pass elif allotted_seconds: if time_left_today >= allotted_seconds: expected_time = datetime.combine( getdate(current_date_time), get_time(start_time)) expected_time = add_to_date(expected_time, seconds=allotted_seconds) expected_time_is_set = 1 else: allotted_seconds = allotted_seconds - time_left_today if not expected_time_is_set: current_date_time = add_to_date(current_date_time, days=1) if end_time and allotted_seconds >= 86400: current_date_time = datetime.combine(getdate(current_date_time), get_time(end_time)) else: current_date_time = expected_time return current_date_time
def execute(): att = frappe.db.sql( """SELECT at.name, at.att_date, at.employee, at.employee_name, at.overtime, at.shift FROM `tabAttendance` at, `tabShift Type` sh WHERE at.docstatus = 1 AND sh.in_out_required = 1 AND at.shift = sh.name ORDER BY at.name""", as_list=1) for i in att: att = frappe.get_doc("Attendance", i[0]) shft = frappe.get_doc("Shift Type", i[5]) pu_data = [] overtime = 0 tt_in = 0 tt_out = 0 shft_hrs = shft.hours_required_per_day.seconds shft_marg = shft.time_margin.seconds shft_rounding = shft.time_rounding.seconds for d in att.attendance_time: pu_data.append([d.idx, d.time_type, d.date_time]) #only calculate the ot if there are any IN and OUT entries for j in range(len(pu_data) - 1): if pu_data[j][1] == 'In Time': tt_in += time_diff_in_seconds(pu_data[j + 1][2], pu_data[j][2]) else: tt_out += time_diff_in_seconds(pu_data[j + 1][2], pu_data[j][2]) overtime = ((tt_in - shft_hrs + shft_marg)- \ ((tt_in + shft_marg - shft_hrs)%shft_rounding))/3600 frappe.db.set_value("Attendance", i[0], 'overtime', overtime) print("Attendance Updated " + i[0] + " for date:" + i[1] + " employee: " + i[3] + " New OT=" + overtime)
def valide_date(arg, data): arg = json.loads(arg) obj = json.loads(data) from frappe.utils import time_diff_in_seconds try: from_date_time = datetime.datetime.strptime(obj.get('date'), '%d/%m/%Y %H:%M').strftime('%Y-%m-%d %H:%M:%S') except: from_date_time = datetime.datetime.strptime(obj.get('date'), '%d/%m/%Y').strftime('%Y-%m-%d %H:%M:%S') curr_date_time = datetime.datetime.strptime(arg.get('curr_date_time'), '%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d %H:%M:%S') if time_diff_in_seconds(from_date_time, curr_date_time) > 0: return False return True
def is_within_operating_hours(workstation, operation, from_datetime, to_datetime): operation_length = time_diff_in_seconds(to_datetime, from_datetime) workstation = frappe.get_doc("Workstation", workstation) for working_hour in workstation.working_hours: slot_length = (parse(working_hour.end_time) - parse(working_hour.start_time)).total_seconds() if slot_length >= operation_length: return frappe.throw( _("Operation {0} longer than any available working hours in workstation {1}, break down the operation into multiple operations" ).format(operation, workstation.name), NotInWorkingHoursError)
def valide_date(data): obj = json.loads(data) from frappe.utils import time_diff_in_seconds from_date_time = datetime.datetime.strptime( obj.get('from_date_time'), '%d/%m/%Y %H:%M').strftime('%Y-%m-%d %H:%M:%S') curr_date_time = datetime.datetime.strptime( obj.get('curr_date_time'), '%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d %H:%M:%S') if time_diff_in_seconds(from_date_time, curr_date_time) < 0: return False return True
def check_date_time_diff(date_time, type_of_check, name_of_field, days_diff=0, hours_diff=0): d = get_datetime(date_time) d1 = now_datetime() d0 = add_days(d1.date(), days_diff) if type_of_check == 'date': if d.date() >= d0 and d.date() <= d1.date(): pass else: frappe.throw(("{} Date should be between {} and {}").\ format(name_of_field, str(d0), str(d1.date()))) else: if d.date() < d1.date(): frappe.throw("{} Date cannot be less than Today's Date".format(name_of_field)) if d.date() == d1.date(): if time_diff_in_seconds(d, d1) < (3600*hours_diff): frappe.throw("{} Time has to be {} hours after the current time".\ format(name_of_field, hours_diff))
def add_time_log(self, args): last_row = [] employees = args.employees if isinstance(employees, str): employees = json.loads(employees) if self.time_logs and len(self.time_logs) > 0: last_row = self.time_logs[-1] self.reset_timer_value(args) if last_row and args.get("complete_time"): for row in self.time_logs: if not row.to_time: row.update({ "to_time": get_datetime(args.get("complete_time")), "operation": args.get("sub_operation"), "completed_qty": args.get("completed_qty") or 0.0, }) elif args.get("start_time"): new_args = frappe._dict({ "from_time": get_datetime(args.get("start_time")), "operation": args.get("sub_operation"), "completed_qty": 0.0, }) if employees: for name in employees: new_args.employee = name.get("employee") self.add_start_time_log(new_args) else: self.add_start_time_log(new_args) if not self.employee and employees: self.set_employees(employees) if self.status == "On Hold": self.current_time = time_diff_in_seconds(last_row.to_time, last_row.from_time) self.save()
def get_new_messages(): last_update = frappe.cache().hget("notifications_last_update", frappe.session.user) now_timestamp = now() frappe.cache().hset("notifications_last_update", frappe.session.user, now_timestamp) if not last_update: return [] if last_update and time_diff_in_seconds(now_timestamp, last_update) > 1800: # no update for 30 mins, consider only the last 30 mins last_update = (now_datetime() - relativedelta(seconds=1800)).strftime(DATETIME_FORMAT) return frappe.db.sql("""select comment_by_fullname, comment from tabComment where comment_doctype='Message' and comment_docname = %s and ifnull(creation, "2000-01-01") > %s order by creation desc""", (frappe.session.user, last_update), as_dict=1)
def valide_date(arg, data): arg = json.loads(arg) obj = json.loads(data) from frappe.utils import time_diff_in_seconds try: from_date_time = datetime.datetime.strptime( obj.get('date'), '%d/%m/%Y %H:%M').strftime('%Y-%m-%d %H:%M:%S') except: from_date_time = datetime.datetime.strptime( obj.get('date'), '%d/%m/%Y').strftime('%Y-%m-%d %H:%M:%S') curr_date_time = datetime.datetime.strptime( arg.get('curr_date_time'), '%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d %H:%M:%S') if time_diff_in_seconds(from_date_time, curr_date_time) > 0: return False return True
def has_permission_inter(user, doc_name, company=None, owner_type=None, owner_id=None): company = company or frappe.get_value('IOT Device', doc_name, 'company') if frappe.get_value('Cloud Company', company, 'admin') == user: return True owner_type = owner_type or frappe.get_value('IOT Device', doc_name, 'owner_type') owner_id = owner_id or frappe.get_value('IOT Device', doc_name, 'owner_id') if owner_type == '' or owner_id is None: return False if owner_type == 'User' and owner_id == user: return True if owner_type == "Cloud Company Group": from cloud.cloud.doctype.cloud_company_group.cloud_company_group import list_users for d in list_users(owner_id): if d.name == user: return True for d in frappe.db.get_values("IOT ShareGroupDevice", { "device": doc_name, "parenttype": 'IOT Share Group' }, "parent"): if frappe.get_value("IOT ShareGroupUser", { "parent": d[0], "user": user }, "parent") == d[0]: return True for d in frappe.db.get_values("IOT Device Share", { "device": doc_name, "share_to": user }, "name"): end_time = frappe.get_value("IOT Device Share", d[0], "end_time") if time_diff_in_seconds(end_time, get_datetime()) > 0: return True return False
def log_metrics(action, start_time, end_time, ctx, args, **kwargs): doc = frappe.new_doc("Action Execution Log").update(kwargs).update({ "action": action, "start_time": start_time, "end_time": end_time, "execution_time": time_diff_in_seconds(end_time, start_time), "context": frappe.as_json(ctx), "arguments": frappe.as_json(args), "stack": log_stack(start=1) }) doc.insert() frappe.db.commit()
def get_new_messages(): last_update = frappe.cache().hget("notifications_last_update", frappe.session.user) now_timestamp = now() frappe.cache().hset("notifications_last_update", frappe.session.user, now_timestamp) if not last_update: return [] if last_update and time_diff_in_seconds(now_timestamp, last_update) > 1800: # no update for 30 mins, consider only the last 30 mins last_update = (now_datetime() - relativedelta(seconds=1800)).strftime(DATETIME_FORMAT) return frappe.db.sql("""select sender_full_name, content from `tabCommunication` where communication_type in ('Chat', 'Notification') and reference_doctype='user' and reference_name = %s and creation > %s order by creation desc""", (frappe.session.user, last_update), as_dict=1)
def check_punch_data(doc,method): shft_intime, shft_lunchout, shft_lunchin, shft_hrs, \ shft_rounding, shft_marg = validate_time_with_shift(doc,method) pu_data = [] for d in doc.attendance_time: if d.idx == 1 and d.time_type != 'In Time': frappe.throw(("First Punch Data should be In Time for {0}").format(doc.name)) pu_data.append([d.idx, d.time_type, d.date_time]) for i in range(len(pu_data)-1): #Checks if In and Out are alternating if pu_data[i][1] == pu_data[i+1][1]: frappe.throw(("{1} should not be Followed by {1} for {2} check row # {3} & {4}").\ format(pu_data[i][1], pu_data[i][1], doc.name, pu_data[i][0], pu_data[i+1][0])) #Checks if Time Data is following the minimum time difference rule in shift if time_diff_in_seconds(pu_data[i+1][2], pu_data[i][2]) <= shft_marg: frappe.throw(("Difference between 2 punch data cannot be less than \ {0} mins check row# {1} and {2} for {3}").\ format(shft_marg/60, pu_data[i][0], pu_data[i+1][0], doc.name)) return pu_data
def calculate_total_hours(self): if self.to_time and self.from_time: from frappe.utils import time_diff_in_seconds self.hours = flt(time_diff_in_seconds(self.to_time, self.from_time)) / 3600
def get_availability_data(date, practitioner): """ Get availability data of 'practitioner' on 'date' :param date: Date to check in schedule :param practitioner: Name of the practitioner :return: dict containing a list of available slots, list of appointments and time of appointments """ date = getdate(date) weekday = date.strftime("%A") available_slots = [] slot_details = [] practitioner_schedule = None add_events = [] remove_events = [] employee = None practitioner_obj = frappe.get_doc("Healthcare Practitioner", practitioner) # Get practitioner employee relation if practitioner_obj.employee: employee = practitioner_obj.employee elif practitioner_obj.user_id: if frappe.db.exists({ "doctype": "Employee", "user_id": practitioner_obj.user_id }): employee = frappe.get_doc("Employee", {"user_id": practitioner_obj.user_id}).name if employee: # Check if it is Holiday if is_holiday(employee, date): frappe.throw(_("{0} is a company holiday".format(date))) # Check if He/She on Leave leave_record = frappe.db.sql("""select half_day from `tabLeave Application` where employee = %s and %s between from_date and to_date and docstatus = 1""", (employee, date), as_dict=True) if leave_record: if leave_record[0].half_day: frappe.throw(_("{0} on Half day Leave on {1}").format(practitioner, date)) else: frappe.throw(_("{0} on Leave on {1}").format(practitioner, date)) # Remove events by repeat_on def remove_events_by_repeat_on(events_list): weekdays = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] if events_list: i = 0 for event in events_list: if event.repeat_this_event: if event.repeat_on == "Every Day": if event[weekdays[getdate(date).weekday()]]: add_events.append(event.copy()) remove_events.append(event.copy()) if event.repeat_on=="Every Week": if getdate(event.from_date).weekday() == getdate(date).weekday(): add_events.append(event.copy()) remove_events.append(event.copy()) if event.repeat_on=="Every Month": if getdate(event.from_date).day == getdate(date).day: add_events.append(event.copy()) remove_events.append(event.copy()) if event.repeat_on=="Every Year": if getdate(event.from_date).strftime("%j") == getdate(date).strftime("%j"): add_events.append(event.copy()) remove_events.append(event.copy()) # Absent events absent_events = frappe.db.sql(""" select name, event, from_time, to_time, from_date, to_date, duration, service_unit, service_unit, repeat_this_event, repeat_on, repeat_till, monday, tuesday, wednesday, thursday, friday, saturday, sunday from `tabPractitioner Event` where practitioner = %(practitioner)s and present != 1 and ( (repeat_this_event = 1 and (from_date<=%(date)s and ifnull(repeat_till, "3000-01-01")>=%(date)s)) or (repeat_this_event != 1 and (from_date<=%(date)s and to_date>=%(date)s)) ) """.format(),{"practitioner": practitioner, "date": getdate(date)}, as_dict=True) if absent_events: remove_events = [] add_events = [] remove_events_by_repeat_on(absent_events) for e in remove_events: absent_events.remove(e) absent_events = absent_events + add_events # get practitioners schedule enabled_schedule = False if practitioner_obj.practitioner_schedules: for schedule in practitioner_obj.practitioner_schedules: practitioner_schedule = None if schedule.schedule: if frappe.db.exists( "Practitioner Schedule", { "name": schedule.schedule, "disabled": ['!=', 1] } ): practitioner_schedule = frappe.get_doc('Practitioner Schedule', schedule.schedule) enabled_schedule = True if practitioner_schedule: available_slots = [] for t in practitioner_schedule.time_slots: if weekday == t.day: available_slots.append(t) if available_slots: appointments = [] if schedule.service_unit: slot_name = schedule.schedule+" - "+schedule.service_unit allow_overlap = frappe.get_value('Healthcare Service Unit', schedule.service_unit, 'overlap_appointments') if allow_overlap: # fetch all appointments to practitioner by service unit appointments = frappe.get_all( "Patient Appointment", filters={"practitioner": practitioner, "service_unit": schedule.service_unit, "appointment_date": date, "status": ["not in",["Cancelled"]]}, fields=["name", "appointment_time", "duration", "status"], order_by= "appointment_date, appointment_time") else: # fetch all appointments to service unit appointments = frappe.get_all( "Patient Appointment", filters={"service_unit": schedule.service_unit, "appointment_date": date, "status": ["not in",["Cancelled"]]}, fields=["name", "appointment_time", "duration", "status"], order_by= "appointment_date, appointment_time") else: slot_name = schedule.schedule # fetch all appointments to practitioner without service unit appointments = frappe.get_all( "Patient Appointment", filters={"practitioner": practitioner, "service_unit": '', "appointment_date": date, "status": ["not in",["Cancelled"]]}, fields=["name", "appointment_time", "duration", "status"], order_by= "appointment_date, appointment_time") slot_details.append({"slot_name":slot_name, "service_unit":schedule.service_unit, "avail_slot":available_slots, 'appointments': appointments, 'absent_events': absent_events, 'fixed_duration': schedule.always_use_slot_duration_as_appointment_duration, 'appointment_type': schedule.appointment_type}) # Present events present_events = frappe.db.sql(""" select name, event, from_time, to_time, from_date, to_date, duration, service_unit, repeat_this_event, repeat_on, repeat_till, monday, tuesday, wednesday, thursday, friday, saturday, sunday from `tabPractitioner Event` where practitioner = %(practitioner)s and present = 1 and ( (repeat_this_event = 1 and (from_date<=%(date)s and ifnull(repeat_till, "3000-01-01")>=%(date)s)) or (repeat_this_event != 1 and (from_date<=%(date)s and to_date>=%(date)s)) ) """.format(),{"practitioner":practitioner, "date":getdate(date)}, as_dict=True) present_events_details = [] if present_events: remove_events = [] add_events = [] remove_events_by_repeat_on(present_events) for e in remove_events: present_events.remove(e) present_events = present_events + add_events for present_event in present_events: event_available_slots = [] total_time_diff = time_diff_in_seconds(present_event.to_time, present_event.from_time)/60 from_time = present_event.from_time slot_name = present_event.event appointments = [] if present_event.service_unit: slot_name = slot_name+" - "+present_event.service_unit allow_overlap = frappe.get_value('Healthcare Service Unit', present_event.service_unit, 'overlap_appointments') if allow_overlap: # fetch all appointments to practitioner by service unit appointments = frappe.get_all( "Patient Appointment", filters={"practitioner": practitioner, "service_unit": present_event.service_unit, "appointment_date": date, "status": ["not in",["Cancelled"]]}, fields=["name", "appointment_time", "duration", "status"], order_by= "appointment_date, appointment_time") else: # fetch all appointments to service unit appointments = frappe.get_all( "Patient Appointment", filters={"service_unit": present_event.service_unit, "appointment_date": date, "status": ["not in",["Cancelled"]]}, fields=["name", "appointment_time", "duration", "status"], order_by= "appointment_date, appointment_time") else: # fetch all appointments to practitioner without service unit appointments = frappe.get_all( "Patient Appointment", filters={"practitioner": practitioner, "service_unit": '', "appointment_date": date, "status": ["not in",["Cancelled"]]}, fields=["name", "appointment_time", "duration", "status"], order_by= "appointment_date, appointment_time") for x in range(0, int(total_time_diff), present_event.duration): to_time = from_time + datetime.timedelta(seconds=present_event.duration*60) event_available_slots.append({'from_time': from_time, 'to_time': to_time}) from_time = to_time present_events_details.append({'slot_name': slot_name, "service_unit":present_event.service_unit, 'avail_slot': event_available_slots, 'appointments': appointments, 'absent_events': absent_events}) else: if not practitioner_obj.practitioner_schedules: frappe.throw(_("{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner master".format(practitioner))) elif not enabled_schedule: frappe.throw(_("{0} does not have an enabled Healthcare Practitioner Schedule.".format(practitioner))) elif not available_slots and not slot_details: frappe.throw(_("Healthcare Practitioner not available on {0}").format(weekday)) return { "slot_details": slot_details, "present_events": present_events_details }