def daily_notifications(): """ sent notifications to user if 1 : ticket is open for more than 24 hrs 2 : ticket is assigned but not Closed in 24 hrs """ tickets = frappe.db.get_all("Ticket Escalation History", filters=[["status", "!=", "Closed"], ["status", "!=", "Deleted"]], fields=["*"]) for ticket in tickets: # ticket is raised but not yet assigned issue_doc = frappe.get_doc("Issue", ticket.ticket_id) args = { "user": get_fullname(issue_doc.raised_by) or "User", "email": issue_doc.raised_by, "action": "user_issue_notification", "issue": issue_doc } if ticket.raised_email_notification and not ticket.assigned_email_notification: raised_time = ticket.raised_email_notification_datetime if time_diff_in_hours(get_datetime().now(), raised_time) >= 24: # send user notification mail msg = "Your support ticket {ticket_id} is pending our representative will \ check the issue as soon as possible".format(ticket_id=ticket.ticket_id) args.update({"msg":msg}) send_mail(args, "[HelpDesk] Daily Notifications") elif ticket.assigned_email_notification and not ticket.status_closed_email_notification: assigned_time = ticket.assigned_email_notification_datetime if time_diff_in_hours(get_datetime().now(), assigned_time) >= 24: # send the user notification mail msg = "Your support ticket {ticket_id} is assigned to our support representative \ and issue will be solved as soon as possble".format(ticket_id=ticket.ticket_id) args.update({"msg":msg}) send_mail(args, "[HelpDesk] Daily Notifications")
def get_billable_and_total_hours(activity, end, start, total_hours, total_billable_hours, total_amount): total_hours += abs(time_diff_in_hours(end, start)) if activity.billable: total_billable_hours += abs(time_diff_in_hours(end, start)) total_amount += total_billable_hours * activity.billing_rate return total_hours, total_billable_hours, total_amount
def validate_dates(self): for data in self.time_logs: if data.from_time and data.to_time and time_diff_in_hours( data.to_time, data.from_time) < 0: frappe.throw(_("To date cannot be before from date")) def_over = frappe.db.get_value( "HR Settings", None, "max_working_hours_against_timesheet") if time_diff_in_hours(data.to_time, data.from_time) >= float(def_over): frappe.throw(_("Overtime cannot be more than Max value")) if self.type != 'compensatory' and is_overtime_exceeded( self.employee, data.from_time): frappe.throw(_("Overtime is exceeded!")) attendance_day = calendar.day_name[getdate( self.start_date).weekday()] employee_work_shift = frappe.db.get_value("Employee", self.employee, "work_shift") employee_start_time = frappe.db.get_value("Work Shift Details", { "parent": employee_work_shift, "day": attendance_day }, "start_work") employee_end_time = frappe.db.get_value("Work Shift Details", { "parent": employee_work_shift, "day": attendance_day }, "end_work") if not employee_start_time: frappe.throw( _("Start Time Work Shift does not exist for that day")) if not employee_end_time: frappe.throw(_("End Time Work Shift does not exist for that day")) diff_time = frappe.db.sql( "select format(((TIME_TO_SEC('%s')-TIME_TO_SEC('%s'))),0)" % (str(employee_start_time), str(data.from_time)))[0][0] diff_time2 = frappe.db.sql( "select format(((TIME_TO_SEC('%s')-TIME_TO_SEC('%s'))),0)" % (str(employee_end_time), str(data.to_time)))[0][0] #frappe.msgprint(str(employee_start_time)+" v "+str(get_time(data.from_time))+" c "+str(employee_end_time)+" v "+str(get_time(data.to_time))) #if get_time(data.to_time) < get_time(employee_end_time) and get_time(data.to_time) > get_time(employee_start_time): # frappe.msgprint(str(diff_time)+" f "+str(diff_time2)) hold_f = False holiday_list = frappe.db.get_value("Employee", self.employee, "holiday_list") if holiday_list: holidays = frappe.get_all("Holiday", fields=["holiday_date"], filters={'parent': holiday_list}) for holiday in holidays: if holiday.holiday_date == getdate( data.from_time) or holiday.holiday_date == getdate( data.to_time): hold_f = True
def set_service_level_agreement_variance(issue=None): current_time = frappe.flags.current_time or now_datetime() filters = {"status": "Open", "agreement_fulfilled": "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_hours(doc.response_by, current_time), 2) frappe.db.set_value("Issue", doc.name, "response_by_variance", variance) if variance < 0: frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed") if not doc.resolution_date: # resolution_date set when issue has been closed variance = round( time_diff_in_hours(doc.resolution_by, current_time), 2) frappe.db.set_value("Issue", doc.name, "resolution_by_variance", variance) if variance < 0: frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed")
def update_agreement_status(self): current_time = frappe.flags.current_time or now_datetime() if self.service_level_agreement: if (round(time_diff_in_hours(self.response_by, current_time), 2) < 0 or round(time_diff_in_hours(self.resolution_by, current_time), 2) < 0): self.agreement_status = "Failed" else: self.agreement_status = "Fulfilled"
def add_overtime(self, attendance_day, employee_work_shift, employee_end_time): #if is_overtime_exceeded(self.employee, self.attendance_date): # frappe.throw(_("Overtime is exceeded!")) holiday_list = frappe.db.get_value("Employee", self.employee, "holiday_list") if holiday_list: #holidays = frappe.get_list("Holiday",fields=["holiday_date"], filters={'parent':holiday_list}, as_list=1) holidays = frappe.get_all("Holiday", fields=["holiday_date"], filters={'parent': holiday_list}) from_time = frappe.db.sql( 'select TIMESTAMP(%s , %s)', (self.attendance_date, self.attendance_time))[0][0] to_time = frappe.db.sql( 'select TIMESTAMP(%s , %s)', (self.attendance_date, employee_end_time))[0][0] #hours = frappe.db.sql("select format(((TIME_TO_SEC('%s')-TIME_TO_SEC('%s'))/60),0)" %(str(to_time), str(from_time)))[0][0] for holiday in holidays: if holiday.holiday_date == getdate(self.attendance_date): if att_over: doc = frappe.db.sql( 'update `tabTimesheet Detail` set to_time=%s where parent=%s and from_time=%s and hours=%s', (to_time, att_over, from_time, str(time_diff_in_hours(to_time, from_time)))) else: overtime = frappe.new_doc('Timesheet') overtime.update({ 'name': self.employee_name, 'employee': self.employee, 'start_date': getdate(nowdate()), 'to_date': getdate(nowdate()), 'time_logs': [{ 'activity_type': _('Auto Entry: Attendance in holiday date'), 'from_time': from_time, 'to_time': to_time, 'hours': time_diff_in_hours(to_time, from_time) }], 'type': 'Normal', 'docstatus': 0, 'workflow_state': 'Pending Request' }) overtime.insert(ignore_permissions=True)
def validate_due_date(doc): # get the time limit for the role from escalation settings now = get_datetime().now() datetime_str = "{date} {time}".format(date=doc.date, time=doc.due_time) datetime_str = datetime.strptime( datetime_str.split(".")[0], "%Y-%m-%d %H:%M:%S").strftime("%Y-%m-%d %H:%M:%S") now_str = now.strftime("%Y-%m-%d %H:%M:%S") # if time_diff_in_hours(datetime_str, now_str) < 0: # frappe.throw("Can not assign past date") creation = datetime.strptime(doc.creation, "%Y-%m-%d %H:%M:%S.%f") if creation.replace(microsecond=0) == now.replace(microsecond=0): # current user highest role in doc.role if doc.assigned_by == frappe.session.user: doc.role = get_highest_role(frappe.session.user) doc.assigned_to_role = get_highest_role(doc.owner) else: doc.role = get_highest_role(doc.assigned_by) doc.assigned_to_role = get_highest_role(doc.owner) query = """ SELECT er.time FROM `tabTicket Escalation Settings Record` AS er JOIN `tabTicket Escalation Settings` AS tes ON er.parent=tes.name AND er.role='%s' AND tes.is_default=1""" % (doc.role or "Administrator") result = frappe.db.sql(query, as_dict=True) if not result: frappe.throw("Can not find the Role in Escalation Settings") else: time = result[0].get("time") if creation.replace(microsecond=0) == now.replace(microsecond=0): set_due_dttm(doc, now, time) elif time_diff_in_hours(datetime_str, now_str) < 0: set_due_dttm(doc, now, time) else: datetime_str = "{date} {time}".format(date=doc.date, time=doc.due_time) time_diff = time_diff_in_hours(datetime_str, str(now)) if time < time_diff: dttm = (now + timedelta(hours=time)).strftime("%d-%m-%Y %H:%M:%S") frappe.throw( "Invalid Due Date and Time, Due Date & time shoud be : %s" % (dttm))
def set_response_and_resolution_time(self, priority=None, service_level_agreement=None): service_level_agreement = get_active_service_level_agreement_for( priority=priority, customer=self.customer, service_level_agreement=service_level_agreement) 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 self.priority = service_level_agreement.default_priority if not priority else priority service_level_agreement = frappe.get_doc("Service Level Agreement", service_level_agreement.name) priority = service_level_agreement.get_service_level_agreement_priority( self.priority) priority.update({ "support_and_resolution": service_level_agreement.support_and_resolution, "holiday_list": service_level_agreement.holiday_list }) 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_hours(self.response_by, now_datetime())) self.resolution_by_variance = round( time_diff_in_hours(self.resolution_by, now_datetime()))
def validate_due_date(doc): # get the time limit for the role from escalation settings if doc.status == "Closed": return now = get_datetime().now() datetime_str = "{date} {time}".format(date=doc.date, time=doc.due_time) datetime_str = datetime.strptime(datetime_str.split(".")[0], "%Y-%m-%d %H:%M:%S").strftime("%Y-%m-%d %H:%M:%S") now_str = now.strftime("%Y-%m-%d %H:%M:%S") if time_diff_in_hours(datetime_str, now_str) < 0: frappe.throw("Can not assign past date") creation = datetime.strptime(doc.creation, "%Y-%m-%d %H:%M:%S.%f") if creation.replace(microsecond=0) == now.replace(microsecond=0): # current user highest role in doc.role if doc.assigned_by == frappe.session.user: doc.role = get_highest_role(frappe.session.user) doc.assigned_to_role = get_highest_role(doc.owner) else: doc.role = get_highest_role(doc.assigned_by) doc.assigned_to_role = get_highest_role(doc.owner) query = """ SELECT er.time FROM `tabTicket Escalation Settings Record` AS er JOIN `tabTicket Escalation Settings` AS tes ON er.parent=tes.name AND er.role='%s' AND tes.is_default=1"""%(doc.role or "Administrator") result = frappe.db.sql(query, as_dict=True) if not result: frappe.throw("Can not find the Role in Escalation Settings") else: time = result[0].get("time") if creation.replace(microsecond=0) == now.replace(microsecond=0): due_dttm = now + timedelta(hours=time) doc.due_time = due_dttm.strftime("%H:%M:%S") doc.date = due_dttm.strftime("%Y-%m-%d ") else: datetime_str = "{date} {time}".format(date=doc.date, time=doc.due_time) time_diff = time_diff_in_hours(datetime_str, str(now)) if time < time_diff: dttm = (now + timedelta(hours=time)).strftime("%d-%m-%Y %H:%M:%S") frappe.throw("Invalid Due Date and Time, Due Date & time shoud be : %s"%(dttm))
def update_agreement_fulfilled_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_hours(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_hours(self.resolution_by, now_datetime()), 2) self.agreement_fulfilled = "Fulfilled" if self.response_by_variance > 0 and self.resolution_by_variance > 0 else "Failed"
def validate(doc, method): # frappe.errprint("validate") latest = doc row = None if len(latest.time_logs) == 0: row = latest.append("time_logs", {}) else: row = latest.time_logs[0] row.from_time = latest.from_date row.to_time = latest.to_date row.hours = time_diff_in_hours(row.to_time, row.from_time) row.project = latest.project row.task = latest.task row.activity_type = latest.activity if latest.billable: row.billable = True row.billing_hours = row.hours else: row.billable = False row.billing_hours = 0 rate = get_activity_cost(doc.employee, doc.activity) row.billing_rate = rate.billing_rate row.costing_rate = rate.costing_rate latest.validate()
def validate_time_logs(self): self.total_completed_qty = 0.0 self.total_time_in_mins = 0.0 if self.get('time_logs'): for d in self.get('time_logs'): if get_datetime(d.from_time) > get_datetime(d.to_time): frappe.throw( _("Row {0}: From time must be less than to time"). format(d.idx)) if not frappe.db.get_single_value( "Projects Settings", "ignore_workstation_time_overlap"): data = self.get_overlap_for(d) if data: frappe.throw( _("Row {0}: From Time and To Time of {1} is overlapping with {2}" ).format(d.idx, self.name, data.name)) if d.from_time and d.to_time: d.time_in_mins = time_diff_in_hours( d.to_time, d.from_time) * 60 self.total_time_in_mins += d.time_in_mins if d.completed_qty: self.total_completed_qty += d.completed_qty
def validate_time_logs(self): self.total_completed_qty = 0.0 self.total_time_in_mins = 0.0 if self.get('time_logs'): for d in self.get('time_logs'): if not d.pre_planning: if get_datetime(d.from_time) > get_datetime(d.to_time): frappe.throw( _("Row {0}: From time must be less than to time"). format(d.idx)) data = self.get_overlap_for(d) if data: frappe.throw( _("Row {0}: From Time and To Time of {1} is overlapping with {2}" ).format(d.idx, self.name, data.name), OverlapError) if d.from_time and d.to_time: d.time_in_mins = time_diff_in_hours( d.to_time, d.from_time) * 60 self.total_time_in_mins += d.time_in_mins if d.completed_qty: self.total_completed_qty += d.completed_qty
def validate_time_logs(self): self.total_time_in_mins = 0.0 self.total_completed_qty = 0.0 if self.get("time_logs"): for d in self.get("time_logs"): if d.to_time and get_datetime(d.from_time) > get_datetime( d.to_time): frappe.throw( _("Row {0}: From time must be less than to time"). format(d.idx)) data = self.get_overlap_for(d) if data: frappe.throw( _("Row {0}: From Time and To Time of {1} is overlapping with {2}" ).format(d.idx, self.name, data.name), OverlapError, ) if d.from_time and d.to_time: d.time_in_mins = time_diff_in_hours( d.to_time, d.from_time) * 60 self.total_time_in_mins += d.time_in_mins if d.completed_qty and not self.sub_operations: self.total_completed_qty += d.completed_qty self.total_completed_qty = flt( self.total_completed_qty, self.precision("total_completed_qty")) for row in self.sub_operations: self.total_completed_qty += row.completed_qty
def get_shifts(): today = calendar.day_name[getdate(frappe.utils.today()).weekday()] data = frappe.db.sql( 'select employee,department,designation,emp.work_shift,cast(concat(CURDATE(), " ", dsh.start_work) as datetime) as start,cast(concat(CURDATE(), " ", dsh.end_work) as datetime) as end,day from `tabEmployee Employment Detail` as emp join `tabWork Shift Details` as dsh on emp.work_shift=dsh.parent where day=%s', today, as_dict=1) for emp in data: hrs_diff = time_diff_in_hours(emp.end, emp.start) progress = 100 wname = emp.employee + today if not frappe.db.get_value("Work Shifts Management", {"name": wname}): doc = frappe.new_doc('Work Shifts Management') doc.update({ 'name': wname, 'employee': emp.employee, 'department': emp.department, 'designation': emp.designation, 'work_shift': emp.work_shift, 'day': today, 'hours': hrs_diff, 'progress': progress, 'start_hour': emp.start, 'end_hour': emp.end }) doc.insert(ignore_permissions=True)
def test_make_time_log(self): prod_order = make_prod_order_test_record(item="_Test FG Item 2", planned_start_date="2014-11-25 00:00:00", qty=1, do_not_save=True) prod_order.set_production_order_operations() prod_order.insert() prod_order.submit() d = prod_order.operations[0] d.completed_qty = flt(d.completed_qty) time_log = make_time_log(prod_order.name, d.operation, \ d.planned_start_time, d.planned_end_time, prod_order.qty - d.completed_qty, operation_id=d.name) self.assertEqual(prod_order.name, time_log.production_order) self.assertEqual((prod_order.qty - d.completed_qty), time_log.completed_qty) self.assertEqual(time_diff_in_hours(d.planned_end_time, d.planned_start_time),time_log.hours) time_log.save() time_log.submit() manufacturing_settings = frappe.get_doc({ "doctype": "Manufacturing Settings", "allow_production_on_holidays": 0 }) manufacturing_settings.save() prod_order.load_from_db() self.assertEqual(prod_order.operations[0].status, "Completed") self.assertEqual(prod_order.operations[0].completed_qty, prod_order.qty) self.assertEqual(get_datetime(prod_order.operations[0].actual_start_time), get_datetime(time_log.from_time)) self.assertEqual(get_datetime(prod_order.operations[0].actual_end_time), get_datetime(time_log.to_time)) self.assertEqual(prod_order.operations[0].actual_operation_time, 60) self.assertEqual(prod_order.operations[0].actual_operating_cost, 100) time_log.cancel() prod_order.load_from_db() self.assertEqual(prod_order.operations[0].status, "Pending") self.assertEqual(flt(prod_order.operations[0].completed_qty), 0) self.assertEqual(flt(prod_order.operations[0].actual_operation_time), 0) self.assertEqual(flt(prod_order.operations[0].actual_operating_cost), 0) time_log2 = frappe.copy_doc(time_log) time_log2.update({ "completed_qty": 10, "from_time": "2014-11-26 00:00:00", "to_time": "2014-11-26 00:00:00", "docstatus": 0 }) self.assertRaises(OverProductionLoggedError, time_log2.save)
def get_billable_and_total_duration(activity, start_time, end_time): activity_duration = time_diff_in_hours(end_time, start_time) billing_duration = 0.0 if activity.billable: billing_duration = activity.billing_hours if activity_duration != activity.billing_hours: billing_duration = activity_duration * activity.billing_hours / activity.hours return flt(activity_duration, 2), flt(billing_duration, 2)
def calculate_std_hours(self): std_working_hours = frappe.get_value("Company", self.company, 'standard_working_hours') for time in self.time_logs: if time.from_time and time.to_time: if flt(std_working_hours) > 0: time.hours = flt(std_working_hours) * date_diff(time.to_time, time.from_time) else: if not time.hours: time.hours = time_diff_in_hours(time.to_time, time.from_time)
def get_billable_and_total_duration(activity, start_time, end_time): precision = frappe.get_precision("Timesheet Detail", "hours") activity_duration = time_diff_in_hours(end_time, start_time) billing_duration = 0.0 if activity.billable: billing_duration = activity.billing_hours if activity_duration != activity.billing_hours: billing_duration = activity_duration * activity.billing_hours / activity.hours return flt(activity_duration, precision), flt(billing_duration, precision)
def validate_attendance(self, method): if self.in_time > self.out_time: frappe.throw(_("Out time should be greater than in time")) time_diff_hours = time_diff_in_hours(self.out_time, self.in_time) if self.status in ["Present", "Half Day"]: if time_diff_hours >= 8: self.status = "Present" elif (time_diff_hours >= 4 and time_diff_hours < 8): self.status = "Half Day" else: frappe.throw(_("To mark attendance as Present or Half Day, hours should be greater than or equal to 4"))
def shift_type_validate(doc, method): if get_datetime(doc.end_time) > get_datetime(doc.start_time): time_diff = time_diff_in_hours(doc.end_time, doc.start_time) doc.total_hours = time_diff else: time_start_str = '23:59:59' time_start_obj = datetime.strptime(time_start_str, '%H:%M:%S') shift_start = time_start_obj.time() start_time = time_diff_in_hours(str(shift_start), doc.start_time) time_end_str = '00:00:00' time_end_obj = datetime.strptime(time_end_str, '%H:%M:%S') shift_end = time_end_obj.time() end_time = time_diff_in_hours(doc.end_time, str(shift_end)) time_diff = end_time + start_time doc.total_hours = time_diff doc.total_hours = int(doc.total_hours) + \ (doc.total_hours-int(doc.total_hours))*0.60
def get_leagend(start, end): time_diff = time_diff_in_hours(end, start) if time_diff <= 24.0: return "Winthin 1 day" elif time_diff <= 72.0: return "2-3 days" elif time_diff <= 120.0: return "3-5 days" elif time_diff <= 168.0: return "5-7 days" else: return "more than 7 days"
def get_ip_services_to_invoice(patient, company): services_to_invoice = [] ip_services = frappe.db.sql(''' SELECT ips.* FROM `tabInpatient Record` ip, `tabIP Services` ips WHERE ip.patient=%s and ip.company=%s and ips.parent=ip.name and ips.stopped=1 and ips.invoiced=0 ''', (patient.name, company), as_dict=1) for ip_service in ip_services: service_type = frappe.get_cached_doc('InPatient Service', ip_service.inpatient_service) if service_type and service_type.is_billable: if service_type.uom == 'Nos': if service_type.uom_per_day > 0: days_used = date_diff(ip_service.end_date, ip_service.start_date) + 1 qty = service_type.uom_per_day * days_used else: qty = ip_service.qty else: hours_occupied = time_diff_in_hours(ip_service.end_date, ip_service.start_date) qty = 0.5 if hours_occupied > 0: actual_qty = hours_occupied / service_type.no_of_hours floor = math.floor(actual_qty) decimal_part = actual_qty - floor if decimal_part > 0.5: qty = rounded(floor + 1, 1) elif decimal_part < 0.5 and decimal_part > 0: qty = rounded(floor + 0.5, 1) elif decimal_part == 0: qty = floor if qty <= 0: qty = 0.5 services_to_invoice.append({ 'reference_type': 'IP Services', 'reference_name': ip_service.name, 'service': service_type.item, 'qty': qty }) return services_to_invoice
def get_emp_work_shift(employee, day): emp_wshift = frappe.db.get_value("Employee", employee, "work_shift") if emp_wshift: employee_start_time = frappe.db.get_value("Work Shift Details", { "parent": emp_wshift, "day": day }, "start_work") employee_end_time = frappe.db.get_value("Work Shift Details", { "parent": emp_wshift, "day": day }, "end_work") shift_hrs = time_diff_in_hours(employee_end_time, employee_start_time) return shift_hrs
def daily_notifications(): """ sent notifications to user if 1 : ticket is open for more than 24 hrs 2 : ticket is assigned but not Closed in 24 hrs """ tickets = frappe.db.get_all("Ticket Escalation History", filters=[["status", "!=", "Closed"], ["status", "!=", "Deleted"]], fields=["*"]) for ticket in tickets: # ticket is raised but not yet assigned issue_doc = frappe.get_doc("Issue", ticket.ticket_id) args = { "user": get_fullname(issue_doc.raised_by) or "User", "email": issue_doc.raised_by, "action": "user_issue_notification", "issue": issue_doc } if ticket.raised_email_notification and not ticket.assigned_email_notification: raised_time = ticket.raised_email_notification_datetime if time_diff_in_hours(get_datetime().now(), raised_time) >= 24: # send user notification mail msg = "Your support ticket {ticket_id} is pending our representative will \ check the issue as soon as possible".format( ticket_id=ticket.ticket_id) args.update({"msg": msg}) send_mail(args, "[HelpDesk] Daily Notifications") elif ticket.assigned_email_notification and not ticket.status_closed_email_notification: assigned_time = ticket.assigned_email_notification_datetime if time_diff_in_hours(get_datetime().now(), assigned_time) >= 24: # send the user notification mail msg = "Your support ticket {ticket_id} is assigned to our support representative \ and issue will be solved as soon as possble".format( ticket_id=ticket.ticket_id) args.update({"msg": msg}) send_mail(args, "[HelpDesk] Daily Notifications")
def set_minimum_reposting_time_slot(self): """Ensure that timeslot for reposting is at least 12 hours.""" if not self.limit_reposting_timeslot: return start_time = get_datetime(self.start_time) end_time = get_datetime(self.end_time) if start_time > end_time: end_time = add_to_date(end_time, days=1, as_datetime=True) diff = time_diff_in_hours(end_time, start_time) if diff < 10: self.end_time = get_time_str(add_to_date(self.start_time, hours=10, as_datetime=True))
def execute_pos_invoices(): make_closing_entry() last_execution_time = frappe.db.get_single_value('POS Process Settings', 'last_execution_time') enabled = frappe.db.get_single_value('POS Process Settings', 'enabled') execution_interval = 5 # set Interval if enabled: hours = time_diff_in_hours(now_datetime(), last_execution_time) execution_interval = frappe.db.get_single_value('POS Process Settings', 'execution_interval') if hours >= execution_interval: check_before_exe_time_pos_invoices(last_execution_time) settings = frappe.get_single("POS Process Settings") settings.last_execution_time = now_datetime() settings.save() pos_profiles = frappe.get_list("POS Profile", filters={"disabled": 0}, fields=["name"], order_by="creation") for res in pos_profiles: make_opening_entry(res.name)
def check_for_open_support_tickets(records, esc_setting): open_tickets = [] time = 0 for record in records: opening_date = record.get("opening_date") opening_time = record.get("opening_time") datetime_str = "{date} {time}".format(date=opening_date, time=opening_time) now = str(get_datetime().now()) time = get_time_difference(esc_setting) if time_diff_in_hours(now, datetime_str) >= time or 2: open_tickets.append(record.get("ticket_id")) return open_tickets
def validate(self): # self.validate_reel_size() if self.scrap_weight_approved: if not self.approved_by: self.approved_by = frappe.session.user else: self.approved_by = "" if self.all_weight_approved: if not self.all_weight_approved_by: self.all_weight_approved_by = frappe.session.user else: self.all_weight_approved_by = "" self.manage_reel() if self.end_dt and self.start_dt: hours = time_diff_in_hours(self.end_dt, self.start_dt) frappe.db.set(self, 'operation_hours', hours)
def test_make_time_log(self): from erpnext.projects.doctype.time_log.test_time_log import make_time_log_test_record prod_order = make_prod_order_test_record(item="_Test FG Item 2", planned_start_date=now(), qty=1, do_not_save=True) prod_order.set_production_order_operations() prod_order.insert() prod_order.submit() d = prod_order.operations[0] d.completed_qty = flt(d.completed_qty) time_log = make_time_log_test_record(hours=1, production_order= prod_order.name, operation= d.operation, completed_qty= prod_order.qty - d.completed_qty, operation_id=d.name, for_manufacturing=1, simulate=True) self.assertEqual(prod_order.name, time_log.production_order) self.assertEqual((prod_order.qty - d.completed_qty), time_log.completed_qty) self.assertEqual(time_diff_in_hours(d.planned_end_time, d.planned_start_time),time_log.hours) manufacturing_settings = frappe.get_doc({ "doctype": "Manufacturing Settings", "allow_production_on_holidays": 0 }) manufacturing_settings.save() prod_order.load_from_db() self.assertEqual(prod_order.operations[0].status, "Completed") self.assertEqual(prod_order.operations[0].completed_qty, prod_order.qty) self.assertEqual(prod_order.operations[0].actual_operation_time, 60) self.assertEqual(prod_order.operations[0].actual_operating_cost, 100) time_log.cancel() prod_order.load_from_db() self.assertEqual(prod_order.operations[0].status, "Pending") self.assertEqual(flt(prod_order.operations[0].completed_qty), 0) self.assertEqual(flt(prod_order.operations[0].actual_operation_time), 0) self.assertEqual(flt(prod_order.operations[0].actual_operating_cost), 0) time_log2 = make_time_log_test_record(from_time= add_days(time_log.to_time, 1) ,production_order= prod_order.name, operation= d.operation, completed_qty= 5, operation_id=d.name, for_manufacturing=1, do_not_save=True) self.assertRaises(OverProductionLoggedError, time_log2.save)
def get_inpatient_services_to_invoice(patient, company): services_to_invoice = [] inpatient_services = frappe.db.sql( """ SELECT io.* FROM `tabInpatient Record` ip, `tabInpatient Occupancy` io WHERE ip.patient=%s and ip.company=%s and io.parent=ip.name and io.left=1 and io.invoiced=0 """, (patient.name, company), as_dict=1, ) for inpatient_occupancy in inpatient_services: service_unit_type = frappe.db.get_value( "Healthcare Service Unit", inpatient_occupancy.service_unit, "service_unit_type") service_unit_type = frappe.get_cached_doc( "Healthcare Service Unit Type", service_unit_type) if service_unit_type and service_unit_type.is_billable: hours_occupied = time_diff_in_hours(inpatient_occupancy.check_out, inpatient_occupancy.check_in) qty = 0.5 if hours_occupied > 0: actual_qty = hours_occupied / service_unit_type.no_of_hours floor = math.floor(actual_qty) decimal_part = actual_qty - floor if decimal_part > 0.5: qty = rounded(floor + 1, 1) elif decimal_part < 0.5 and decimal_part > 0: qty = rounded(floor + 0.5, 1) if qty <= 0: qty = 0.5 services_to_invoice.append({ "reference_type": "Inpatient Occupancy", "reference_name": inpatient_occupancy.name, "service": service_unit_type.item, "qty": qty, }) return services_to_invoice
def check_and_escalate_assigned_tickets(records, esc_setting): time = 0 rec_cant_be_escalate = {} for record in records: datetime_str = record.get("assigned_on") now = str(get_datetime().now()) time = get_time_difference(esc_setting, record.get("current_role")) or 2 if time_diff_in_hours(now, datetime_str) >= time: ch_entry = esc_setting.escalation_hierarchy if record.current_role in [ch.role for ch in ch_entry[:2]]: key = frappe.db.get_value("Issue", record.get("ticket_id"), "department") val = rec_cant_be_escalate.get("department").append(record) if rec_cant_be_escalate.get("department") else [record] rec_cant_be_escalate.update({ key: val, }) else: escalate_ticket_to_higher_authority(esc_setting, record)
def validate_total_hours(self): lenof = len(self.punching) if lenof >= 1: punch_in = [ i.punch_time for i in self.punching if i.punch_in_out == 'Punch In' ] punch_out = [ i.punch_time for i in self.punching if i.punch_in_out == 'Punch Out' ] if len(punch_in) != len(punch_out): punch_in.pop() differences = [ time_diff_in_hours(y, x) for x, y in zip(punch_in, punch_out) ] self.total_hours = round(sum(differences), 2) else: self.total_hours = 0
def set_se_items_finish(self, se): #set from and to warehouse se.to_warehouse = self.to_warehouse se.from_warehouse = self.source_warehouse #TODO allow multiple raw material transfer raw_material_cost = 0 operating_cost = 0 #TODO calc raw_material_cost #no timesheet entries, calculate operating cost based on workstation hourly rate and process start, end hourly_rate = None # hourly_rate = frappe.db.get_value("Workstation", self.workstation, "hour_rate") if hourly_rate: if self.operation_hours > 0: hours = self.operation_hours else: hours = time_diff_in_hours(self.end_dt, self.start_dt) frappe.db.set(self, 'operation_hours', hours) operating_cost = hours * float(hourly_rate) production_cost = raw_material_cost + operating_cost #calc total_qty and total_sale_value qty_of_total_production = 0 total_sale_value = 0 qty_of_total_production = float(qty_of_total_production) + float(self.total_stock) #add carton to stock entry cartons = {} for row in self.carton_data: if row.carton_item: count = cartons.get(row.carton_item, 0) cartons.update({row.carton_item:(count + 1)}) for data in cartons: se = self.set_se_items(se, data, None, se.from_warehouse, True, qty_of_total_production, total_sale_value, production_cost, raw_material = cartons[data]) #add paper cup item to stockentry se = self.set_se_items(se, self.item, se.to_warehouse, None, True, qty_of_total_production, total_sale_value, production_cost) return se
def check_and_escalate_assigned_tickets(records, esc_setting): time = 0 rec_cant_be_escalate = {} for record in records: datetime_str = record.get("assigned_on") now = str(get_datetime().now()) time = get_time_difference(esc_setting, record.get("current_role")) or 2 if time_diff_in_hours(now, datetime_str) >= time: ch_entry = esc_setting.escalation_hierarchy if record.current_role in [ch.role for ch in ch_entry[:2]]: key = frappe.db.get_value("Issue", record.get("ticket_id"), "department") val = rec_cant_be_escalate.get("department").append(record) if rec_cant_be_escalate.get("department") else [record] rec_cant_be_escalate.update({ key: val, }) else: escalate_ticket_to_higher_authority(esc_setting, record) if rec_cant_be_escalate: args = get_tickets_details_that_cant_be_escalate(rec_cant_be_escalate) for dept_head, mail_args in args.iteritems(): send_mail(mail_args, "[HelpDesk][Open Tickets] HelpDesk Notifications")
def calculate_total_hours(self): from frappe.utils import time_diff_in_hours self.hours = time_diff_in_hours(self.to_time, self.from_time)
def test_make_time_log(self): from erpnext.manufacturing.doctype.production_order.production_order import make_time_log from frappe.utils import cstr from frappe.utils import time_diff_in_hours prod_order = frappe.get_doc({ "doctype": "Production Order", "production_item": "_Test FG Item 2", "bom_no": "BOM/_Test FG Item 2/001", "qty": 1, "wip_warehouse": "_Test Warehouse - _TC", "fg_warehouse": "_Test Warehouse 1 - _TC", "company": "_Test Company", "planned_start_date": "2014-11-25 00:00:00" }) prod_order.set_production_order_operations() prod_order.insert() prod_order.submit() d = prod_order.operations[0] d.completed_qty = flt(d.completed_qty) time_log = make_time_log(prod_order.name, cstr(d.idx) + ". " + d.operation, \ d.planned_start_time, d.planned_end_time, prod_order.qty - d.completed_qty, operation_id=d.name) self.assertEqual(prod_order.name, time_log.production_order) self.assertEqual((prod_order.qty - d.completed_qty), time_log.completed_qty) self.assertEqual(time_diff_in_hours(d.planned_end_time, d.planned_start_time),time_log.hours) time_log.save() time_log.submit() manufacturing_settings = frappe.get_doc({ "doctype": "Manufacturing Settings", "allow_production_on_holidays": 0 }) manufacturing_settings.save() prod_order.load_from_db() self.assertEqual(prod_order.operations[0].status, "Completed") self.assertEqual(prod_order.operations[0].completed_qty, prod_order.qty) self.assertEqual(get_datetime(prod_order.operations[0].actual_start_time), get_datetime(time_log.from_time)) self.assertEqual(get_datetime(prod_order.operations[0].actual_end_time), get_datetime(time_log.to_time)) self.assertEqual(prod_order.operations[0].actual_operation_time, 60) self.assertEqual(prod_order.operations[0].actual_operating_cost, 100) time_log.cancel() prod_order.load_from_db() self.assertEqual(prod_order.operations[0].status, "Pending") self.assertEqual(flt(prod_order.operations[0].completed_qty), 0) self.assertEqual(flt(prod_order.operations[0].actual_operation_time), 0) self.assertEqual(flt(prod_order.operations[0].actual_operating_cost), 0) time_log2 = frappe.copy_doc(time_log) time_log2.update({ "completed_qty": 10, "from_time": "2014-11-26 00:00:00", "to_time": "2014-11-26 00:00:00", "docstatus": 0 }) self.assertRaises(OverProductionLoggedError, time_log2.save)
def validate_dates(self): for data in self.time_logs: if data.from_time and data.to_time and time_diff_in_hours(data.to_time, data.from_time) < 0: frappe.throw(_("To date cannot be before from date"))
def get_downtime(failure_date, completion_date): downtime = time_diff_in_hours(completion_date, failure_date) return round(downtime, 2)
def get_healthcare_services_to_invoice(patient): patient = frappe.get_doc("Patient", patient) if patient: if patient.customer: item_to_invoice = [] patient_appointments = frappe.get_list("Patient Appointment",{'patient': patient.name, 'invoiced': False}, order_by="appointment_date") if patient_appointments: fee_validity_details = [] valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") for patient_appointment in patient_appointments: patient_appointment_obj = frappe.get_doc("Patient Appointment", patient_appointment['name']) if patient_appointment_obj.procedure_template: if frappe.db.get_value("Clinical Procedure Template", patient_appointment_obj.procedure_template, "is_billable") == 1: item_to_invoice.append({'reference_type': 'Patient Appointment', 'reference_name': patient_appointment_obj.name, 'service': patient_appointment_obj.procedure_template}) else: practitioner_exist_in_list = False skip_invoice = False if fee_validity_details: for validity in fee_validity_details: if validity['practitioner'] == patient_appointment_obj.practitioner: practitioner_exist_in_list = True if validity['valid_till'] >= patient_appointment_obj.appointment_date: validity['visits'] = validity['visits']+1 if int(max_visit) > validity['visits']: skip_invoice = True if not skip_invoice: validity['visits'] = 1 validity['valid_till'] = patient_appointment_obj.appointment_date + datetime.timedelta(days=int(valid_days)) if not practitioner_exist_in_list: valid_till = patient_appointment_obj.appointment_date + datetime.timedelta(days=int(valid_days)) visits = 0 validity_exist = validity_exists(patient_appointment_obj.practitioner, patient_appointment_obj.patient) if validity_exist: fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0]) valid_till = fee_validity.valid_till visits = fee_validity.visited fee_validity_details.append({'practitioner': patient_appointment_obj.practitioner, 'valid_till': valid_till, 'visits': visits}) if not skip_invoice: practitioner_charge = 0 income_account = None service_item = None if patient_appointment_obj.practitioner: service_item, practitioner_charge = service_item_and_practitioner_charge(patient_appointment_obj) income_account = get_income_account(patient_appointment_obj.practitioner, patient_appointment_obj.company) item_to_invoice.append({'reference_type': 'Patient Appointment', 'reference_name': patient_appointment_obj.name, 'service': service_item, 'rate': practitioner_charge, 'income_account': income_account}) encounters = frappe.get_list("Patient Encounter", {'patient': patient.name, 'invoiced': False, 'docstatus': 1}) if encounters: for encounter in encounters: encounter_obj = frappe.get_doc("Patient Encounter", encounter['name']) if not encounter_obj.appointment: practitioner_charge = 0 income_account = None service_item = None if encounter_obj.practitioner: service_item, practitioner_charge = service_item_and_practitioner_charge(encounter_obj) income_account = get_income_account(encounter_obj.practitioner, encounter_obj.company) item_to_invoice.append({'reference_type': 'Patient Encounter', 'reference_name': encounter_obj.name, 'service': service_item, 'rate': practitioner_charge, 'income_account': income_account}) lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False}) if lab_tests: for lab_test in lab_tests: lab_test_obj = frappe.get_doc("Lab Test", lab_test['name']) if frappe.db.get_value("Lab Test Template", lab_test_obj.template, "is_billable") == 1: item_to_invoice.append({'reference_type': 'Lab Test', 'reference_name': lab_test_obj.name, 'service': frappe.db.get_value("Lab Test Template", lab_test_obj.template, "item")}) lab_rxs = frappe.db.sql("""select lp.name from `tabPatient Encounter` et, `tabLab Prescription` lp where et.patient=%s and lp.parent=et.name and lp.lab_test_created=0 and lp.invoiced=0""", (patient.name)) if lab_rxs: for lab_rx in lab_rxs: rx_obj = frappe.get_doc("Lab Prescription", lab_rx[0]) if rx_obj.lab_test_code and (frappe.db.get_value("Lab Test Template", rx_obj.lab_test_code, "is_billable") == 1): item_to_invoice.append({'reference_type': 'Lab Prescription', 'reference_name': rx_obj.name, 'service': frappe.db.get_value("Lab Test Template", rx_obj.lab_test_code, "item")}) procedures = frappe.get_list("Clinical Procedure", {'patient': patient.name, 'invoiced': False}) if procedures: for procedure in procedures: procedure_obj = frappe.get_doc("Clinical Procedure", procedure['name']) if not procedure_obj.appointment: if procedure_obj.procedure_template and (frappe.db.get_value("Clinical Procedure Template", procedure_obj.procedure_template, "is_billable") == 1): item_to_invoice.append({'reference_type': 'Clinical Procedure', 'reference_name': procedure_obj.name, 'service': frappe.db.get_value("Clinical Procedure Template", procedure_obj.procedure_template, "item")}) procedure_rxs = frappe.db.sql("""select pp.name from `tabPatient Encounter` et, `tabProcedure Prescription` pp where et.patient=%s and pp.parent=et.name and pp.procedure_created=0 and pp.invoiced=0 and pp.appointment_booked=0""", (patient.name)) if procedure_rxs: for procedure_rx in procedure_rxs: rx_obj = frappe.get_doc("Procedure Prescription", procedure_rx[0]) if frappe.db.get_value("Clinical Procedure Template", rx_obj.procedure, "is_billable") == 1: item_to_invoice.append({'reference_type': 'Procedure Prescription', 'reference_name': rx_obj.name, 'service': frappe.db.get_value("Clinical Procedure Template", rx_obj.procedure, "item")}) procedures = frappe.get_list("Clinical Procedure", {'patient': patient.name, 'invoice_separately_as_consumables': True, 'consumption_invoiced': False, 'consume_stock': True, 'status': 'Completed'}) if procedures: service_item = get_healthcare_service_item('clinical_procedure_consumable_item') if not service_item: msg = _(("Please Configure {0} in ").format("Clinical Procedure Consumable Item") \ + """<b><a href="#Form/Healthcare Settings">Healthcare Settings</a></b>""") frappe.throw(msg) for procedure in procedures: procedure_obj = frappe.get_doc("Clinical Procedure", procedure['name']) item_to_invoice.append({'reference_type': 'Clinical Procedure', 'reference_name': procedure_obj.name, 'service': service_item, 'rate': procedure_obj.consumable_total_amount, 'description': procedure_obj.consumption_details}) inpatient_services = frappe.db.sql("""select io.name, io.parent from `tabInpatient Record` ip, `tabInpatient Occupancy` io where ip.patient=%s and io.parent=ip.name and io.left=1 and io.invoiced=0""", (patient.name)) if inpatient_services: for inpatient_service in inpatient_services: inpatient_occupancy = frappe.get_doc("Inpatient Occupancy", inpatient_service[0]) service_unit_type = frappe.get_doc("Healthcare Service Unit Type", frappe.db.get_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "service_unit_type")) if service_unit_type and service_unit_type.is_billable == 1: hours_occupied = time_diff_in_hours(inpatient_occupancy.check_out, inpatient_occupancy.check_in) qty = 0.5 if hours_occupied > 0: actual_qty = hours_occupied / service_unit_type.no_of_hours floor = math.floor(actual_qty) decimal_part = actual_qty - floor if decimal_part > 0.5: qty = rounded(floor + 1, 1) elif decimal_part < 0.5 and decimal_part > 0: qty = rounded(floor + 0.5, 1) if qty <= 0: qty = 0.5 item_to_invoice.append({'reference_type': 'Inpatient Occupancy', 'reference_name': inpatient_occupancy.name, 'service': service_unit_type.item, 'qty': qty}) return item_to_invoice else: frappe.throw(_("The Patient {0} do not have customer refrence to invoice").format(patient.name))
def check_follow_up_time(time, now): send_reminder = 0 if 1 < (time_diff_in_hours(time, now)) < 2: send_reminder = 1 return send_reminder
def calculate_total_hours(self): from frappe.utils import time_diff_in_hours self.hours = time_diff_in_hours(self.to_time, self.from_time) if self.hours < 0: frappe.throw(_("'From Time' cannot be later than 'To Time'"))