Task added to expense claim
all cost (expense claim and time log) against Task; task updates project cost.
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json
index ef3d617..7392138 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.json
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.json
@@ -192,6 +192,14 @@
"precision": ""
},
{
+ "fieldname": "task",
+ "fieldtype": "Link",
+ "label": "Task",
+ "options": "Task",
+ "permlevel": 0,
+ "precision": ""
+ },
+ {
"fieldname": "email_id",
"fieldtype": "Data",
"hidden": 1,
@@ -219,8 +227,8 @@
],
"icon": "icon-money",
"idx": 1,
- "is_submittable": 1,
- "modified": "2015-03-26 04:41:50.473196",
+ "is_submittable": 1,
+ "modified": "2015-03-30 05:17:43.963137",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim",
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index c86c3ef..886a55d 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -21,17 +21,18 @@
self.validate_sanctioned_amount()
self.validate_exp_details()
self.validate_expense_approver()
+ self.validate_task()
set_employee_name(self)
def on_submit(self):
if self.approval_status=="Draft":
frappe.throw(_("""Approval Status must be 'Approved' or 'Rejected'"""))
- if self.project:
- self.update_project()
+ if self.task:
+ self.update_task()
def on_cancel(self):
if self.project:
- self.update_project()
+ self.update_task()
def validate_exp_details(self):
if not self.get('expenses'):
@@ -42,10 +43,14 @@
frappe.throw(_("{0} ({1}) must have role 'Expense Approver'")\
.format(get_fullname(self.exp_approver), self.exp_approver), InvalidExpenseApproverError)
- def update_project(self):
+ def update_task(self):
expense_amount = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim`
- where project = %s and approval_status = "Approved" and docstatus=1""",self.project)
- frappe.db.set_value("Project", self.project, "total_expense_claims", expense_amount)
+ where project = %s and task = %s and approval_status = "Approved" and docstatus=1""",(self.project, self.task))
+ frappe.db.set_value("Project", self.project, "total_expense_claim", expense_amount)
+
+ def validate_task(self):
+ if self.project and not self.task:
+ frappe.throw(_("Task is Mandatory if Time Log is against a project"))
def validate_sanctioned_amount(self):
if self.total_sanctioned_amount > self.total_claimed_amount:
diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json
index f392bc8..90b4e65 100644
--- a/erpnext/projects/doctype/project/project.json
+++ b/erpnext/projects/doctype/project/project.json
@@ -206,9 +206,9 @@
"read_only": 1
},
{
- "fieldname": "total_expense_claims",
+ "fieldname": "total_expense_claim",
"fieldtype": "Currency",
- "label": "Total Expense Claims",
+ "label": "Total Expense Claim",
"permlevel": 0,
"precision": "",
"read_only": 1
@@ -278,7 +278,7 @@
"icon": "icon-puzzle-piece",
"idx": 1,
"max_attachments": 4,
- "modified": "2015-03-23 06:44:19.538443",
+ "modified": "2015-03-30 08:42:33.940104",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project",
diff --git a/erpnext/projects/doctype/task/task.js b/erpnext/projects/doctype/task/task.js
index 51aabca..9756331 100644
--- a/erpnext/projects/doctype/task/task.js
+++ b/erpnext/projects/doctype/task/task.js
@@ -25,6 +25,19 @@
this.frm.doc.project && frappe.model.remove_from_locals("Project",
this.frm.doc.project);
},
+
+ refresh: function(doc) {
+ if(!doc.__islocal) {
+ cur_frm.add_custom_button(__("Time Logs"), function() {
+ frappe.route_options = {"project": doc.project, "task": doc.name}
+ frappe.set_route("List", "Time Log");
+ }, "icon-list", true);
+ cur_frm.add_custom_button(__("Expense Claims"), function() {
+ frappe.route_options = {"project": doc.project, "task": doc.name}
+ frappe.set_route("List", "Expense Claim");
+ }, "icon-list", true);
+ }
+ }
});
diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json
index e547834..5cbef3c 100644
--- a/erpnext/projects/doctype/task/task.json
+++ b/erpnext/projects/doctype/task/task.json
@@ -7,16 +7,6 @@
"document_type": "Master",
"fields": [
{
- "fieldname": "task_details",
- "fieldtype": "Section Break",
- "label": "",
- "oldfieldtype": "Section Break",
- "permlevel": 0,
- "print_width": "50%",
- "search_index": 0,
- "width": "50%"
- },
- {
"fieldname": "subject",
"fieldtype": "Data",
"in_filter": 1,
@@ -110,29 +100,28 @@
{
"fieldname": "time_and_budget",
"fieldtype": "Section Break",
- "label": "Time and Budget",
+ "label": "",
"oldfieldtype": "Section Break",
"permlevel": 0
},
{
- "fieldname": "expected",
- "fieldtype": "Column Break",
- "label": "Expected",
- "oldfieldtype": "Column Break",
- "permlevel": 0,
- "print_width": "50%",
- "width": "50%"
- },
- {
- "fieldname": "exp_total_hrs",
- "fieldtype": "Data",
- "label": "Total Hours (Expected)",
+ "default": "0",
+ "description": "in Hours",
+ "fieldname": "expected_time",
+ "fieldtype": "Float",
+ "label": "Expected Time",
"oldfieldname": "exp_total_hrs",
"oldfieldtype": "Data",
"permlevel": 0,
"reqd": 0
},
{
+ "fieldname": "column_break_12",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
+ "precision": ""
+ },
+ {
"fieldname": "allocated_budget",
"fieldtype": "Currency",
"label": "Allocated Budget",
@@ -143,8 +132,8 @@
},
{
"fieldname": "actual",
- "fieldtype": "Column Break",
- "label": "Actual",
+ "fieldtype": "Section Break",
+ "label": "",
"oldfieldtype": "Column Break",
"permlevel": 0,
"print_width": "50%",
@@ -156,7 +145,14 @@
"label": "Actual Start Date",
"oldfieldname": "act_start_date",
"oldfieldtype": "Date",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_15",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
+ "precision": ""
},
{
"fieldname": "act_end_date",
@@ -164,16 +160,50 @@
"label": "Actual End Date",
"oldfieldname": "act_end_date",
"oldfieldtype": "Date",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 1
},
{
- "fieldname": "actual_budget",
+ "fieldname": "section_break_17",
+ "fieldtype": "Section Break",
+ "permlevel": 0,
+ "precision": ""
+ },
+ {
+ "default": "0",
+ "description": "in Hours",
+ "fieldname": "actual_time",
+ "fieldtype": "Float",
+ "label": "Actual Time",
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_20",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
+ "precision": ""
+ },
+ {
+ "fieldname": "actual_cost",
"fieldtype": "Currency",
- "label": "Actual Budget",
+ "label": "Actual Cost",
"oldfieldname": "actual_budget",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 1
+ },
+ {
+ "fieldname": "total_expense_claim",
+ "fieldtype": "Currency",
+ "label": "Total Expense Claim",
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
+ "read_only": 1
},
{
"fieldname": "more_details",
@@ -217,7 +247,7 @@
"icon": "icon-check",
"idx": 1,
"max_attachments": 5,
- "modified": "2015-02-20 05:09:27.295024",
+ "modified": "2015-03-30 05:50:04.409614",
"modified_by": "Administrator",
"module": "Projects",
"name": "Task",
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index acd0877..c921d02 100644
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe, json
-from frappe.utils import getdate, today
+from frappe.utils import getdate
from frappe import _
@@ -26,27 +26,33 @@
return ret
def validate(self):
+ self.validate_dates()
+
+ def validate_dates(self):
if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date):
frappe.throw(_("'Expected Start Date' can not be greater than 'Expected End Date'"))
if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date):
frappe.throw(_("'Actual Start Date' can not be greater than 'Actual End Date'"))
- self.update_status()
-
- def update_status(self):
- status = frappe.db.get_value("Task", self.name, "status")
- if self.status=="Working" and status !="Working" and not self.act_start_date:
- self.act_start_date = today()
-
- if self.status=="Closed" and status != "Closed" and not self.act_end_date:
- self.act_end_date = today()
-
def on_update(self):
+ self.update_percentage()
+ self.update_project()
+
+ def update_percentage(self):
"""update percent complete in project"""
if self.project and not self.flags.from_project:
project = frappe.get_doc("Project", self.project)
project.run_method("update_percent_complete")
+
+ def update_project(self):
+ total_activity_cost = frappe.db.sql("""select sum(actual_cost) from `tabTask`
+ where project = %s""",self.project)
+ frappe.db.set_value("Project", self.project, "total_activity_cost", total_activity_cost)
+
+ total_expense_claim = frappe.db.sql("""select sum(total_expense_claim) from `tabTask`
+ where project = %s""",self.project)
+ frappe.db.set_value("Project", self.project, "total_expense_claim", total_expense_claim)
@frappe.whitelist()
def get_events(start, end, filters=None):
diff --git a/erpnext/projects/doctype/time_log/time_log.json b/erpnext/projects/doctype/time_log/time_log.json
index 8b7c956..f52bcad 100644
--- a/erpnext/projects/doctype/time_log/time_log.json
+++ b/erpnext/projects/doctype/time_log/time_log.json
@@ -92,6 +92,25 @@
"read_only": 1
},
{
+ "depends_on": "",
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Project",
+ "options": "Project",
+ "permlevel": 0,
+ "read_only": 0
+ },
+ {
+ "depends_on": "",
+ "fieldname": "task",
+ "fieldtype": "Link",
+ "label": "Task",
+ "options": "Task",
+ "permlevel": 0,
+ "read_only": 0
+ },
+ {
"depends_on": "eval:!doc.for_manufacturing",
"fieldname": "activity_type",
"fieldtype": "Link",
@@ -103,15 +122,6 @@
"reqd": 0
},
{
- "depends_on": "eval:!doc.for_manufacturing",
- "fieldname": "task",
- "fieldtype": "Link",
- "label": "Task",
- "options": "Task",
- "permlevel": 0,
- "read_only": 0
- },
- {
"depends_on": "eval:doc.for_manufacturing",
"fieldname": "section_break_11",
"fieldtype": "Section Break",
@@ -189,22 +199,6 @@
"read_only": 0
},
{
- "fieldname": "section_break_9",
- "fieldtype": "Section Break",
- "permlevel": 0,
- "read_only": 0
- },
- {
- "depends_on": "",
- "fieldname": "project",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Project",
- "options": "Project",
- "permlevel": 0,
- "read_only": 0
- },
- {
"depends_on": "",
"fieldname": "section_break_24",
"fieldtype": "Section Break",
diff --git a/erpnext/projects/doctype/time_log/time_log.py b/erpnext/projects/doctype/time_log/time_log.py
index de37912..09d0bec 100644
--- a/erpnext/projects/doctype/time_log/time_log.py
+++ b/erpnext/projects/doctype/time_log/time_log.py
@@ -24,15 +24,20 @@
self.check_workstation_timings()
self.validate_production_order()
self.validate_manufacturing()
+ self.validate_task()
self.validate_cost()
def on_submit(self):
- self.update_production_order()
- self.update_project()
+ if self.for_manufacturing:
+ self.update_production_order()
+ if self.task:
+ self.update_task()
def on_cancel(self):
- self.update_production_order()
- self.update_project()
+ if self.for_manufacturing:
+ self.update_production_order()
+ if self.task:
+ self.update_task()
def before_update_after_submit(self):
self.set_status()
@@ -128,7 +133,7 @@
def update_production_order(self):
"""Updates `start_date`, `end_date`, `status` for operation in Production Order."""
- if self.for_manufacturing and self.production_order:
+ if self.production_order:
if not self.operation_id:
frappe.throw(_("Operation ID not set"))
@@ -217,10 +222,22 @@
else:
self.billing_amount = 0
- def update_project(self):
- activity_cost = frappe.db.sql("""select sum(billing_cost) from `tabTime Log`
- where project = %s and docstatus=1""",self.project)
- frappe.db.set_value("Project", self.project, "total_activity_cost", activity_cost)
+ def validate_task(self):
+ if self.project and not self.task:
+ frappe.throw(_("Task is Mandatory if Time Log is against a project"))
+
+ def update_task(self):
+ tl = frappe.db.sql("""select min(from_time) as start_date, max(to_time) as end_date, sum(billing_amount) as cost, sum(hours) as time
+ from `tabTime Log` where project = %s and task = %s and docstatus=1""",(self.project, self.task),as_dict=1)[0]
+
+ task = frappe.get_doc("Task", self.task)
+ if task.status == "Open":
+ task.status = "Working"
+ task.actual_cost= tl.cost
+ task.actual_time= tl.time
+ task.act_start_date= tl.start_date
+ task.act_end_date= tl.end_date
+ task.save()
@frappe.whitelist()
def get_events(start, end, filters=None):