From 458b8885f09d31d6e5ad3d68f8b2f7f58d904f66 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Tue, 24 Mar 2015 12:49:45 +0530 Subject: [PATCH] New Document Activity Cost created. In TL - billing rate and internal rate added In Project - total expense claim and total activity cost. Buttons addded - show time logs and show expense claims. In Expense Claim - Employee and project link added. update cost in project on submit and on trash. Validation added to check sanctioned amount is not greater than claim amount. --- erpnext/config/projects.py | 5 + .../hr/doctype/expense_claim/expense_claim.py | 16 ++ .../doctype/activity_cost/__init__.py | 0 .../doctype/activity_cost/activity_cost.json | 146 ++++++++++++++++++ .../doctype/activity_cost/activity_cost.py | 10 ++ .../activity_cost/test_activity_cost.py | 12 ++ erpnext/projects/doctype/project/project.js | 8 + erpnext/projects/doctype/project/project.json | 18 ++- .../projects/doctype/time_log/time_log.json | 34 +++- erpnext/projects/doctype/time_log/time_log.py | 1 + 10 files changed, 243 insertions(+), 7 deletions(-) create mode 100644 erpnext/projects/doctype/activity_cost/__init__.py create mode 100644 erpnext/projects/doctype/activity_cost/activity_cost.json create mode 100644 erpnext/projects/doctype/activity_cost/activity_cost.py create mode 100644 erpnext/projects/doctype/activity_cost/test_activity_cost.py diff --git a/erpnext/config/projects.py b/erpnext/config/projects.py index d591c9e2a8..07149e38b6 100644 --- a/erpnext/config/projects.py +++ b/erpnext/config/projects.py @@ -32,6 +32,11 @@ def get_data(): "name": "Activity Type", "description": _("Types of activities for Time Sheets"), }, + { + "type": "doctype", + "name": "Activity Cost", + "description": _("Cost of various activities"), + }, ] }, { diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index 76606e5b8a..c86c3efbc6 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -18,6 +18,7 @@ class ExpenseClaim(Document): def validate(self): validate_fiscal_year(self.posting_date, self.fiscal_year, _("Posting Date"), self) + self.validate_sanctioned_amount() self.validate_exp_details() self.validate_expense_approver() set_employee_name(self) @@ -25,6 +26,12 @@ class ExpenseClaim(Document): def on_submit(self): if self.approval_status=="Draft": frappe.throw(_("""Approval Status must be 'Approved' or 'Rejected'""")) + if self.project: + self.update_project() + + def on_cancel(self): + if self.project: + self.update_project() def validate_exp_details(self): if not self.get('expenses'): @@ -34,3 +41,12 @@ class ExpenseClaim(Document): if self.exp_approver and "Expense Approver" not in frappe.get_roles(self.exp_approver): frappe.throw(_("{0} ({1}) must have role 'Expense Approver'")\ .format(get_fullname(self.exp_approver), self.exp_approver), InvalidExpenseApproverError) + + def update_project(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) + + def validate_sanctioned_amount(self): + if self.total_sanctioned_amount > self.total_claimed_amount: + frappe.throw(_("Total sanctioned amount cannot be greater than total claimed amount.")) \ No newline at end of file diff --git a/erpnext/projects/doctype/activity_cost/__init__.py b/erpnext/projects/doctype/activity_cost/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/projects/doctype/activity_cost/activity_cost.json b/erpnext/projects/doctype/activity_cost/activity_cost.json new file mode 100644 index 0000000000..48c1247ab6 --- /dev/null +++ b/erpnext/projects/doctype/activity_cost/activity_cost.json @@ -0,0 +1,146 @@ +{ + "allow_copy": 0, + "allow_import": 1, + "allow_rename": 1, + "creation": "2015-03-23 02:00:21.861546", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "allow_on_submit": 0, + "fieldname": "employee", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Employee", + "no_copy": 0, + "options": "Employee", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "permlevel": 0, + "precision": "" + }, + { + "allow_on_submit": 0, + "fieldname": "activity_type", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Activity Type", + "no_copy": 0, + "options": "Activity Type", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0 + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "permlevel": 0, + "precision": "" + }, + { + "allow_on_submit": 0, + "fieldname": "billing_rate", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Billing Rate", + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break", + "permlevel": 0, + "precision": "" + }, + { + "allow_on_submit": 0, + "fieldname": "intrernal_rate", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Internal Rate", + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "modified": "2015-03-23 02:01:53.789728", + "modified_by": "Administrator", + "module": "Projects", + "name": "Activity Cost", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Projects User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/projects/doctype/activity_cost/activity_cost.py b/erpnext/projects/doctype/activity_cost/activity_cost.py new file mode 100644 index 0000000000..31df89aa66 --- /dev/null +++ b/erpnext/projects/doctype/activity_cost/activity_cost.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class ActivityCost(Document): + pass diff --git a/erpnext/projects/doctype/activity_cost/test_activity_cost.py b/erpnext/projects/doctype/activity_cost/test_activity_cost.py new file mode 100644 index 0000000000..6f483dec5b --- /dev/null +++ b/erpnext/projects/doctype/activity_cost/test_activity_cost.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +# test_records = frappe.get_test_records('Activity Cost') + +class TestActivityCost(unittest.TestCase): + pass diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index eeedeb0147..56cc09dbee 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -21,6 +21,14 @@ cur_frm.cscript.refresh = function(doc) { frappe.route_options = {"project": doc.name} frappe.set_route("List", "Task"); }, "icon-list", true); + cur_frm.add_custom_button(__("Time Logs"), function() { + frappe.route_options = {"project": doc.name} + frappe.set_route("List", "Time Log"); + }, "icon-list", true); + cur_frm.add_custom_button(__("Expense Claims"), function() { + frappe.route_options = {"project": doc.name} + frappe.set_route("List", "Expense Claim"); + }, "icon-list", true); } } diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 6a143ab083..f392bc8288 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -197,6 +197,22 @@ "permlevel": 0, "search_index": 0 }, + { + "fieldname": "total_activity_cost", + "fieldtype": "Currency", + "label": "Total Activity Cost", + "permlevel": 0, + "precision": "", + "read_only": 1 + }, + { + "fieldname": "total_expense_claims", + "fieldtype": "Currency", + "label": "Total Expense Claims", + "permlevel": 0, + "precision": "", + "read_only": 1 + }, { "fieldname": "cost_center", "fieldtype": "Link", @@ -262,7 +278,7 @@ "icon": "icon-puzzle-piece", "idx": 1, "max_attachments": 4, - "modified": "2015-02-22 11:17:49.051755", + "modified": "2015-03-23 06:44:19.538443", "modified_by": "Administrator", "module": "Projects", "name": "Project", diff --git a/erpnext/projects/doctype/time_log/time_log.json b/erpnext/projects/doctype/time_log/time_log.json index 20b5c0c296..217bc65960 100644 --- a/erpnext/projects/doctype/time_log/time_log.json +++ b/erpnext/projects/doctype/time_log/time_log.json @@ -50,6 +50,14 @@ "permlevel": 0, "precision": "" }, + { + "fieldname": "employee", + "fieldtype": "Link", + "label": "Employee", + "options": "Employee", + "permlevel": 0, + "precision": "" + }, { "fieldname": "column_break_3", "fieldtype": "Column Break", @@ -195,6 +203,26 @@ "permlevel": 0, "read_only": 0 }, + { + "fieldname": "billing_rate", + "fieldtype": "Currency", + "label": "Billing Rate", + "permlevel": 0, + "precision": "" + }, + { + "fieldname": "internal_rate", + "fieldtype": "Currency", + "label": "Internal Rate", + "permlevel": 0, + "precision": "" + }, + { + "fieldname": "column_break_25", + "fieldtype": "Column Break", + "permlevel": 0, + "precision": "" + }, { "description": "Will be updated when batched.", "fieldname": "time_log_batch", @@ -213,12 +241,6 @@ "permlevel": 0, "read_only": 1 }, - { - "fieldname": "column_break_16", - "fieldtype": "Column Break", - "permlevel": 0, - "read_only": 0 - }, { "fieldname": "amended_from", "fieldtype": "Link", diff --git a/erpnext/projects/doctype/time_log/time_log.py b/erpnext/projects/doctype/time_log/time_log.py index c385c0935b..36bf5a7f6c 100644 --- a/erpnext/projects/doctype/time_log/time_log.py +++ b/erpnext/projects/doctype/time_log/time_log.py @@ -63,6 +63,7 @@ class TimeLog(Document): def validate_overlap(self): """Checks if 'Time Log' entries overlap for a user, workstation. """ self.validate_overlap_for("user") + self.validate_overlap_for("employee") self.validate_overlap_for("workstation") def validate_overlap_for(self, fieldname):