mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-31 10:49:09 +00:00
Merge pull request #51021 from nishkagosalia/gh-50390
This commit is contained in:
@@ -282,6 +282,8 @@ class Project(Document):
|
|||||||
Min(TimesheetDetail.from_time).as_("start_date"),
|
Min(TimesheetDetail.from_time).as_("start_date"),
|
||||||
Max(TimesheetDetail.to_time).as_("end_date"),
|
Max(TimesheetDetail.to_time).as_("end_date"),
|
||||||
Sum(TimesheetDetail.hours).as_("time"),
|
Sum(TimesheetDetail.hours).as_("time"),
|
||||||
|
Sum(TimesheetDetail.base_costing_amount).as_("base_costing_amount"),
|
||||||
|
Sum(TimesheetDetail.base_billing_amount).as_("base_billing_amount"),
|
||||||
)
|
)
|
||||||
.where((TimesheetDetail.project == self.name) & (TimesheetDetail.docstatus == 1))
|
.where((TimesheetDetail.project == self.name) & (TimesheetDetail.docstatus == 1))
|
||||||
).run(as_dict=True)[0]
|
).run(as_dict=True)[0]
|
||||||
@@ -289,8 +291,8 @@ class Project(Document):
|
|||||||
self.actual_start_date = from_time_sheet.start_date
|
self.actual_start_date = from_time_sheet.start_date
|
||||||
self.actual_end_date = from_time_sheet.end_date
|
self.actual_end_date = from_time_sheet.end_date
|
||||||
|
|
||||||
self.total_costing_amount = from_time_sheet.costing_amount
|
self.total_costing_amount = from_time_sheet.base_costing_amount
|
||||||
self.total_billable_amount = from_time_sheet.billing_amount
|
self.total_billable_amount = from_time_sheet.base_billing_amount
|
||||||
self.actual_time = from_time_sheet.time
|
self.actual_time = from_time_sheet.time
|
||||||
|
|
||||||
self.update_purchase_costing()
|
self.update_purchase_costing()
|
||||||
|
|||||||
@@ -20,6 +20,26 @@ class TestProject(ERPNextTestSuite):
|
|||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
cls.make_projects()
|
cls.make_projects()
|
||||||
|
|
||||||
|
def test_project_total_costing_and_billing_amount(self):
|
||||||
|
from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet
|
||||||
|
from erpnext.setup.doctype.employee.test_employee import make_employee
|
||||||
|
|
||||||
|
project_name = "Test Project Costing"
|
||||||
|
employee = make_employee("employee@frappe.io")
|
||||||
|
project = make_project({"project_name": project_name})
|
||||||
|
timesheet = make_timesheet(
|
||||||
|
employee=employee,
|
||||||
|
is_billable=1,
|
||||||
|
currency="USD",
|
||||||
|
project=project.name,
|
||||||
|
simulate=True,
|
||||||
|
exchange_rate=80,
|
||||||
|
)
|
||||||
|
timesheet.reload()
|
||||||
|
project.reload()
|
||||||
|
self.assertEqual(project.total_costing_amount, 3200)
|
||||||
|
self.assertEqual(project.total_billable_amount, 8000)
|
||||||
|
|
||||||
def test_project_with_template_having_no_parent_and_depend_tasks(self):
|
def test_project_with_template_having_no_parent_and_depend_tasks(self):
|
||||||
project_name = "Test Project with Template - No Parent and Dependend Tasks"
|
project_name = "Test Project with Template - No Parent and Dependend Tasks"
|
||||||
frappe.db.sql(""" delete from tabTask where project = %s """, project_name)
|
frappe.db.sql(""" delete from tabTask where project = %s """, project_name)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import frappe
|
|||||||
from frappe import _, throw
|
from frappe import _, throw
|
||||||
from frappe.desk.form.assign_to import clear, close_all_assignments
|
from frappe.desk.form.assign_to import clear, close_all_assignments
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
from frappe.query_builder.functions import Max, Min, Sum
|
||||||
from frappe.utils import add_days, add_to_date, cstr, date_diff, flt, get_link_to_form, getdate, today
|
from frappe.utils import add_days, add_to_date, cstr, date_diff, flt, get_link_to_form, getdate, today
|
||||||
from frappe.utils.data import format_date
|
from frappe.utils.data import format_date
|
||||||
from frappe.utils.nestedset import NestedSet
|
from frappe.utils.nestedset import NestedSet
|
||||||
@@ -218,15 +219,22 @@ class Task(NestedSet):
|
|||||||
clear(self.doctype, self.name)
|
clear(self.doctype, self.name)
|
||||||
|
|
||||||
def update_time_and_costing(self):
|
def update_time_and_costing(self):
|
||||||
tl = frappe.db.sql(
|
TimesheetDetail = frappe.qb.DocType("Timesheet Detail")
|
||||||
"""select min(from_time) as start_date, max(to_time) as end_date,
|
tl = (
|
||||||
sum(billing_amount) as total_billing_amount, sum(costing_amount) as total_costing_amount,
|
frappe.qb.from_(TimesheetDetail)
|
||||||
sum(hours) as time from `tabTimesheet Detail` where task = %s and docstatus=1""",
|
.select(
|
||||||
self.name,
|
Min(TimesheetDetail.from_time).as_("start_date"),
|
||||||
as_dict=1,
|
Max(TimesheetDetail.to_time).as_("end_date"),
|
||||||
)[0]
|
Sum(TimesheetDetail.billing_amount).as_("total_billing_amount"),
|
||||||
self.total_costing_amount = tl.total_costing_amount
|
Sum(TimesheetDetail.costing_amount).as_("total_costing_amount"),
|
||||||
self.total_billing_amount = tl.total_billing_amount
|
Sum(TimesheetDetail.hours).as_("time"),
|
||||||
|
Sum(TimesheetDetail.base_costing_amount).as_("base_costing_amount"),
|
||||||
|
Sum(TimesheetDetail.base_billing_amount).as_("base_billing_amount"),
|
||||||
|
)
|
||||||
|
.where((TimesheetDetail.task == self.name) & (TimesheetDetail.docstatus == 1))
|
||||||
|
).run(as_dict=True)[0]
|
||||||
|
self.total_costing_amount = tl.base_costing_amount
|
||||||
|
self.total_billing_amount = tl.base_billing_amount
|
||||||
self.actual_time = tl.time
|
self.actual_time = tl.time
|
||||||
self.act_start_date = tl.start_date
|
self.act_start_date = tl.start_date
|
||||||
self.act_end_date = tl.end_date
|
self.act_end_date = tl.end_date
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.tests import IntegrationTestCase
|
from frappe.tests import IntegrationTestCase
|
||||||
@@ -16,6 +15,32 @@ class TestTask(ERPNextTestSuite):
|
|||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
cls.make_projects()
|
cls.make_projects()
|
||||||
|
|
||||||
|
def test_task_total_costing_and_billing_amount(self):
|
||||||
|
from erpnext.projects.doctype.project.test_project import make_project
|
||||||
|
from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet
|
||||||
|
from erpnext.setup.doctype.employee.test_employee import make_employee
|
||||||
|
|
||||||
|
project_name = "Test Project Costing"
|
||||||
|
employee = make_employee("employee@frappe.io")
|
||||||
|
project = make_project({"project_name": project_name})
|
||||||
|
task = create_task("_Test Task 1")
|
||||||
|
task.project = project.name
|
||||||
|
task.save()
|
||||||
|
timesheet = make_timesheet(
|
||||||
|
employee=employee,
|
||||||
|
is_billable=1,
|
||||||
|
currency="USD",
|
||||||
|
project=project.name,
|
||||||
|
simulate=True,
|
||||||
|
exchange_rate=80,
|
||||||
|
task=task.name,
|
||||||
|
)
|
||||||
|
timesheet.reload()
|
||||||
|
project.reload()
|
||||||
|
task.reload()
|
||||||
|
self.assertEqual(task.total_costing_amount, 3200)
|
||||||
|
self.assertEqual(task.total_billing_amount, 8000)
|
||||||
|
|
||||||
def test_circular_reference(self):
|
def test_circular_reference(self):
|
||||||
task1 = create_task("_Test Task 1", add_days(nowdate(), -15), add_days(nowdate(), -10))
|
task1 = create_task("_Test Task 1", add_days(nowdate(), -15), add_days(nowdate(), -10))
|
||||||
task2 = create_task("_Test Task 2", add_days(nowdate(), 11), add_days(nowdate(), 15), task1.name)
|
task2 = create_task("_Test Task 2", add_days(nowdate(), 11), add_days(nowdate(), 15), task1.name)
|
||||||
|
|||||||
@@ -216,11 +216,14 @@ def make_timesheet(
|
|||||||
project=None,
|
project=None,
|
||||||
task=None,
|
task=None,
|
||||||
company=None,
|
company=None,
|
||||||
|
currency=None,
|
||||||
|
exchange_rate=None,
|
||||||
):
|
):
|
||||||
update_activity_type(activity_type)
|
update_activity_type(activity_type)
|
||||||
timesheet = frappe.new_doc("Timesheet")
|
timesheet = frappe.new_doc("Timesheet")
|
||||||
timesheet.employee = employee
|
timesheet.employee = employee
|
||||||
timesheet.company = company or "_Test Company"
|
timesheet.company = company or "_Test Company"
|
||||||
|
timesheet.exchange_rate = exchange_rate
|
||||||
timesheet_detail = timesheet.append("time_logs", {})
|
timesheet_detail = timesheet.append("time_logs", {})
|
||||||
timesheet_detail.is_billable = is_billable
|
timesheet_detail.is_billable = is_billable
|
||||||
timesheet_detail.activity_type = activity_type
|
timesheet_detail.activity_type = activity_type
|
||||||
@@ -229,6 +232,7 @@ def make_timesheet(
|
|||||||
timesheet_detail.to_time = timesheet_detail.from_time + datetime.timedelta(hours=timesheet_detail.hours)
|
timesheet_detail.to_time = timesheet_detail.from_time + datetime.timedelta(hours=timesheet_detail.hours)
|
||||||
timesheet_detail.project = project
|
timesheet_detail.project = project
|
||||||
timesheet_detail.task = task
|
timesheet_detail.task = task
|
||||||
|
timesheet_detail.currency = currency
|
||||||
|
|
||||||
for data in timesheet.get("time_logs"):
|
for data in timesheet.get("time_logs"):
|
||||||
if simulate:
|
if simulate:
|
||||||
|
|||||||
Reference in New Issue
Block a user