You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

154 lines
3.8 KiB

  1. # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
  2. # For license information, please see license.txt
  3. import frappe
  4. from frappe import _
  5. from frappe.utils import flt, time_diff_in_hours
  6. def get_columns():
  7. return [
  8. {
  9. "label": _("Employee ID"),
  10. "fieldtype": "Link",
  11. "fieldname": "employee",
  12. "options": "Employee",
  13. "width": 300,
  14. },
  15. {
  16. "label": _("Employee Name"),
  17. "fieldtype": "data",
  18. "fieldname": "employee_name",
  19. "hidden": 1,
  20. "width": 200,
  21. },
  22. {
  23. "label": _("Timesheet"),
  24. "fieldtype": "Link",
  25. "fieldname": "timesheet",
  26. "options": "Timesheet",
  27. "width": 150,
  28. },
  29. {"label": _("Working Hours"), "fieldtype": "Float", "fieldname": "total_hours", "width": 150},
  30. {
  31. "label": _("Billable Hours"),
  32. "fieldtype": "Float",
  33. "fieldname": "total_billable_hours",
  34. "width": 150,
  35. },
  36. {"label": _("Billing Amount"), "fieldtype": "Currency", "fieldname": "amount", "width": 150},
  37. ]
  38. def get_data(filters):
  39. data = []
  40. if filters.from_date > filters.to_date:
  41. frappe.msgprint(_("From Date can not be greater than To Date"))
  42. return data
  43. timesheets = get_timesheets(filters)
  44. filters.from_date = frappe.utils.get_datetime(filters.from_date)
  45. filters.to_date = frappe.utils.add_to_date(
  46. frappe.utils.get_datetime(filters.to_date), days=1, seconds=-1
  47. )
  48. timesheet_details = get_timesheet_details(filters, timesheets.keys())
  49. for ts, ts_details in timesheet_details.items():
  50. total_hours = 0
  51. total_billing_hours = 0
  52. total_amount = 0
  53. for row in ts_details:
  54. from_time, to_time = filters.from_date, filters.to_date
  55. if row.to_time < from_time or row.from_time > to_time:
  56. continue
  57. if row.from_time > from_time:
  58. from_time = row.from_time
  59. if row.to_time < to_time:
  60. to_time = row.to_time
  61. activity_duration, billing_duration = get_billable_and_total_duration(row, from_time, to_time)
  62. total_hours += activity_duration
  63. total_billing_hours += billing_duration
  64. total_amount += billing_duration * flt(row.billing_rate)
  65. if total_hours:
  66. data.append(
  67. {
  68. "employee": timesheets.get(ts).employee,
  69. "employee_name": timesheets.get(ts).employee_name,
  70. "timesheet": ts,
  71. "total_billable_hours": total_billing_hours,
  72. "total_hours": total_hours,
  73. "amount": total_amount,
  74. }
  75. )
  76. return data
  77. def get_timesheets(filters):
  78. record_filters = [
  79. ["start_date", "<=", filters.to_date],
  80. ["end_date", ">=", filters.from_date],
  81. ["docstatus", "=", 1],
  82. ]
  83. if "employee" in filters:
  84. record_filters.append(["employee", "=", filters.employee])
  85. timesheets = frappe.get_all(
  86. "Timesheet", filters=record_filters, fields=["employee", "employee_name", "name"]
  87. )
  88. timesheet_map = frappe._dict()
  89. for d in timesheets:
  90. timesheet_map.setdefault(d.name, d)
  91. return timesheet_map
  92. def get_timesheet_details(filters, timesheet_list):
  93. timesheet_details_filter = {"parent": ["in", timesheet_list]}
  94. if "project" in filters:
  95. timesheet_details_filter["project"] = filters.project
  96. timesheet_details = frappe.get_all(
  97. "Timesheet Detail",
  98. filters=timesheet_details_filter,
  99. fields=[
  100. "from_time",
  101. "to_time",
  102. "hours",
  103. "is_billable",
  104. "billing_hours",
  105. "billing_rate",
  106. "parent",
  107. ],
  108. )
  109. timesheet_details_map = frappe._dict()
  110. for d in timesheet_details:
  111. timesheet_details_map.setdefault(d.parent, []).append(d)
  112. return timesheet_details_map
  113. def get_billable_and_total_duration(activity, start_time, end_time):
  114. precision = frappe.get_precision("Timesheet Detail", "hours")
  115. activity_duration = time_diff_in_hours(end_time, start_time)
  116. billing_duration = 0.0
  117. if activity.is_billable:
  118. billing_duration = activity.billing_hours
  119. if activity_duration != activity.billing_hours:
  120. billing_duration = activity_duration * activity.billing_hours / activity.hours
  121. return flt(activity_duration, precision), flt(billing_duration, precision)