mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-13 06:31:48 +00:00
Merge pull request #56325 from nabinhait/test-task-timesheet-coverage
test(projects): cover untested task, timesheet, and activity-cost functions
This commit is contained in:
@@ -27,3 +27,43 @@ class TestActivityCost(ERPNextTestSuite):
|
||||
activity_cost1.insert()
|
||||
activity_cost2 = frappe.copy_doc(activity_cost1)
|
||||
self.assertRaises(DuplicationError, activity_cost2.insert)
|
||||
|
||||
def test_default_activity_cost_title_and_duplication(self):
|
||||
activity_type = self._activity_type("_Test Default Cost Type")
|
||||
|
||||
default_cost = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Activity Cost",
|
||||
"activity_type": activity_type,
|
||||
"billing_rate": 80,
|
||||
"costing_rate": 40,
|
||||
}
|
||||
).insert()
|
||||
# without an employee, the title is just the activity type
|
||||
self.assertEqual(default_cost.title, activity_type)
|
||||
|
||||
duplicate = frappe.copy_doc(default_cost)
|
||||
self.assertRaises(DuplicationError, duplicate.insert)
|
||||
|
||||
def test_employee_name_and_title_are_set(self):
|
||||
activity_type = self._activity_type("_Test Employee Cost Type")
|
||||
employee = frappe.db.get_all("Employee", filters={"first_name": "_Test Employee"})[0].name
|
||||
employee_name = frappe.db.get_value("Employee", employee, "employee_name")
|
||||
|
||||
# employee_name is left blank so set_title has to fetch it
|
||||
cost = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Activity Cost",
|
||||
"employee": employee,
|
||||
"activity_type": activity_type,
|
||||
"billing_rate": 60,
|
||||
"costing_rate": 30,
|
||||
}
|
||||
).insert()
|
||||
self.assertEqual(cost.employee_name, employee_name)
|
||||
self.assertEqual(cost.title, f"{employee_name} for {activity_type}")
|
||||
|
||||
def _activity_type(self, name):
|
||||
if not frappe.db.exists("Activity Type", name):
|
||||
frappe.get_doc({"doctype": "Activity Type", "activity_type": name}).insert()
|
||||
return name
|
||||
|
||||
@@ -158,6 +158,49 @@ class TestTask(ERPNextTestSuite):
|
||||
task.save()
|
||||
self.assertEqual(getdate(task.exp_end_date), getdate(add_days(nowdate(), 5)))
|
||||
|
||||
def test_set_multiple_status(self):
|
||||
from erpnext.projects.doctype.task.task import set_multiple_status
|
||||
|
||||
task1 = create_task("_Test Bulk Status 1")
|
||||
task2 = create_task("_Test Bulk Status 2")
|
||||
|
||||
set_multiple_status(frappe.as_json([task1.name, task2.name]), "Completed")
|
||||
|
||||
self.assertEqual(frappe.db.get_value("Task", task1.name, "status"), "Completed")
|
||||
self.assertEqual(frappe.db.get_value("Task", task2.name, "status"), "Completed")
|
||||
|
||||
def test_add_multiple_tasks_under_parent(self):
|
||||
from erpnext.projects.doctype.task.task import add_multiple_tasks
|
||||
|
||||
parent = create_task("_Test Bulk Parent", is_group=1)
|
||||
rows = [{"subject": "_Test Bulk Child A"}, {"subject": ""}, {"subject": "_Test Bulk Child B"}]
|
||||
|
||||
add_multiple_tasks(frappe.as_json(rows), parent.name)
|
||||
|
||||
children = frappe.get_all("Task", filters={"parent_task": parent.name}, pluck="subject")
|
||||
# the row with a blank subject is skipped
|
||||
self.assertEqual(sorted(children), ["_Test Bulk Child A", "_Test Bulk Child B"])
|
||||
|
||||
def test_template_task_dependency_must_be_template(self):
|
||||
normal_task = create_task("_Test Non Template Dependency")
|
||||
template_task = create_task("_Test Template With Dependency", is_template=1, save=False)
|
||||
template_task.append("depends_on", {"task": normal_task.name})
|
||||
|
||||
self.assertRaises(frappe.ValidationError, template_task.save)
|
||||
|
||||
def test_cannot_delete_task_with_children(self):
|
||||
parent = create_task("_Test Parent With Child", is_group=1)
|
||||
create_task("_Test Child Blocking Delete", parent_task=parent.name)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, parent.delete)
|
||||
|
||||
def test_child_task_registers_in_parent_depends_on(self):
|
||||
parent = create_task("_Test Parent Depends On", is_group=1)
|
||||
child = create_task("_Test Child Depends On", parent_task=parent.name)
|
||||
|
||||
parent.reload()
|
||||
self.assertIn(child.name, [row.task for row in parent.depends_on])
|
||||
|
||||
|
||||
def create_task(
|
||||
subject,
|
||||
|
||||
@@ -422,6 +422,37 @@ class TestTimesheet(ERPNextTestSuite):
|
||||
self.assertIsNotNone(row, "billed timesheet not returned by portal list")
|
||||
self.assertEqual(row.sales_invoice, si.name)
|
||||
|
||||
def test_get_activity_cost_falls_back_to_activity_type(self):
|
||||
from erpnext.projects.doctype.timesheet.timesheet import get_activity_cost
|
||||
|
||||
update_activity_type("_Test Activity Type")
|
||||
# no employee-specific Activity Cost row, so the Activity Type rates are used
|
||||
rate = get_activity_cost(employee=None, activity_type="_Test Activity Type")
|
||||
self.assertEqual(rate["billing_rate"], 50.0)
|
||||
self.assertEqual(rate["costing_rate"], 20.0)
|
||||
|
||||
# an unknown activity type yields an empty dict, not an error
|
||||
self.assertEqual(get_activity_cost(activity_type="__Nonexistent Activity__"), {})
|
||||
|
||||
def test_billing_helpers_for_timesheet_detail(self):
|
||||
from erpnext.projects.doctype.timesheet.timesheet import (
|
||||
get_timesheet_data,
|
||||
get_timesheet_detail_rate,
|
||||
)
|
||||
|
||||
employee = make_employee("_test_timesheet_billing_helpers@example.com", company="_Test Company")
|
||||
timesheet = make_timesheet(employee, is_billable=1, simulate=True)
|
||||
detail = timesheet.time_logs[0]
|
||||
|
||||
# 2 billable hours at a billing rate of 50
|
||||
data = get_timesheet_data(timesheet.name, project="")
|
||||
self.assertEqual(data["billing_hours"], 2)
|
||||
self.assertEqual(data["billing_amount"], 100)
|
||||
|
||||
# same currency on both sides, so the rate is the raw billing amount
|
||||
rate = get_timesheet_detail_rate(detail.name, timesheet.currency)
|
||||
self.assertEqual(rate, detail.billing_amount)
|
||||
|
||||
@staticmethod
|
||||
def _delete_if_exists(doctype, name):
|
||||
if frappe.db.exists(doctype, name):
|
||||
|
||||
Reference in New Issue
Block a user