Merge pull request #6327 from rohitwaghchaure/timesheet_enhancement
[Enhancement] Timesheet billing based on project
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 4886dea..47f1a5c 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -462,26 +462,41 @@
frappe.ui.form.on('Sales Invoice', {
setup: function(frm){
frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(doc, cdt, cdn){
- return {
- filters: [
- ["Timesheet", "status", "in", ["Submitted", "Payslip"]]
- ]
+ return{
+ query: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet",
+ filters: {'project': doc.project}
}
}
- }
-})
+ },
-frappe.ui.form.on('Sales Invoice Timesheet', {
- time_sheet: function(frm){
+ project: function(frm){
frm.call({
- method: "calculate_billing_amount_from_timesheet",
+ method: "add_timesheet_data",
doc: frm.doc,
callback: function(r, rt) {
- refresh_field('total_billing_amount')
+ refresh_field(['timesheets'])
}
})
}
})
-cur_frm.add_fetch("time_sheet", "total_billing_hours", "billing_hours");
-cur_frm.add_fetch("time_sheet", "total_billing_amount", "billing_amount");
\ No newline at end of file
+frappe.ui.form.on('Sales Invoice Timesheet', {
+ time_sheet: function(frm, cdt, cdn){
+ var d = locals[cdt][cdn];
+ frappe.call({
+ method: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet_data",
+ args: {
+ 'name': d.time_sheet,
+ 'project': frm.doc.project || null
+ },
+ callback: function(r, rt) {
+ if(r.message){
+ data = r.message;
+ frappe.model.set_value(cdt, cdn, "billing_hours", data.billing_hours);
+ frappe.model.set_value(cdt, cdn, "billing_amount", data.billing_amount);
+ frappe.model.set_value(cdt, cdn, "timesheet_detail", data.timesheet_detail);
+ }
+ }
+ })
+ }
+})
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 562eaaa..99b4793 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -342,6 +342,34 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 1,
+ "in_list_view": 0,
+ "label": "Project",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "project_name",
+ "oldfieldtype": "Link",
+ "options": "Project",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
@@ -2878,34 +2906,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "project",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 1,
- "in_list_view": 0,
- "label": "Project",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "project_name",
- "oldfieldtype": "Link",
- "options": "Project",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "col_break23",
"fieldtype": "Column Break",
"hidden": 0,
@@ -3867,7 +3867,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-08-31 15:47:32.064861",
+ "modified": "2016-09-08 09:05:02.895682",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 8b2113a..e9f142d 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -14,6 +14,7 @@
from erpnext.controllers.selling_controller import SellingController
from erpnext.accounts.utils import get_account_currency
from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so
+from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
from erpnext.accounts.doctype.asset.depreciation \
import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal
@@ -84,7 +85,7 @@
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
self.update_packing_list()
self.set_billing_hours_and_amount()
- self.calculate_billing_amount_from_timesheet()
+ self.update_timesheet_billing_for_project()
def before_save(self):
set_account_for_mode_of_payment(self)
@@ -221,11 +222,21 @@
for d in self.timesheets:
if d.time_sheet:
timesheet = frappe.get_doc("Timesheet", d.time_sheet)
- timesheet.sales_invoice = sales_invoice
+ self.update_time_sheet_detail(timesheet, d, sales_invoice)
+ timesheet.calculate_total_amounts()
+ timesheet.calculate_percentage_billed()
timesheet.flags.ignore_validate_update_after_submit = True
timesheet.set_status()
timesheet.save()
+ def update_time_sheet_detail(self, timesheet, args, sales_invoice):
+ for data in timesheet.time_logs:
+ if (self.project and args.timesheet_detail == data.name) or \
+ (not self.project and not data.sales_invoice) or \
+ (not sales_invoice and data.sales_invoice == self.name):
+ data.sales_invoice = sales_invoice
+ if self.project: return
+
def on_update(self):
self.set_paid_amount()
@@ -450,13 +461,32 @@
def set_billing_hours_and_amount(self):
for timesheet in self.timesheets:
ts_doc = frappe.get_doc('Timesheet', timesheet.time_sheet)
- if not timesheet.billing_hours and ts_doc.total_billing_hours:
- timesheet.billing_hours = ts_doc.total_billing_hours
+ if not timesheet.billing_hours and ts_doc.total_billable_hours:
+ timesheet.billing_hours = ts_doc.total_billable_hours
- if not timesheet.billing_amount and ts_doc.total_billing_amount:
- timesheet.billing_amount = ts_doc.total_billing_amount
+ if not timesheet.billing_amount and ts_doc.total_billable_amount:
+ timesheet.billing_amount = ts_doc.total_billable_amount
- def calculate_billing_amount_from_timesheet(self):
+ def update_timesheet_billing_for_project(self):
+ if not self.timesheets and self.project:
+ self.add_timesheet_data()
+ else:
+ self.calculate_billing_amount_for_timesheet()
+
+ def add_timesheet_data(self):
+ self.set('timesheets', [])
+ if self.project:
+ for data in get_projectwise_timesheet_data(self.project):
+ self.append('timesheets', {
+ 'time_sheet': data.parent,
+ 'billing_hours': data.billing_hours,
+ 'billing_amount': data.billing_amt,
+ 'timesheet_detail': data.name
+ })
+
+ self.calculate_billing_amount_for_timesheet()
+
+ def calculate_billing_amount_for_timesheet(self):
total_billing_amount = 0.0
for data in self.timesheets:
if data.billing_amount:
diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
index 25dd3cb..1191ea7 100644
--- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
+++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
@@ -14,6 +14,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "time_sheet",
"fieldtype": "Link",
"hidden": 0,
@@ -40,6 +41,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "billing_hours",
"fieldtype": "Float",
"hidden": 0,
@@ -65,6 +67,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "billing_amount",
"fieldtype": "Currency",
"hidden": 0,
@@ -85,6 +88,32 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "timesheet_detail",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Timesheet Detail",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
}
],
"hide_heading": 0,
@@ -97,7 +126,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-08-22 21:32:55.504103",
+ "modified": "2016-09-09 14:01:04.095775",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Timesheet",
diff --git a/erpnext/demo/user/projects.py b/erpnext/demo/user/projects.py
index 505ccfd..9802447 100644
--- a/erpnext/demo/user/projects.py
+++ b/erpnext/demo/user/projects.py
@@ -22,7 +22,7 @@
ts = make_timesheet(employee, simulate = True, billable = 1,
activity_type=get_random("Activity Type"), project=data.project, task =data.name)
- if flt(ts.total_billing_amount) > 0.0:
+ if flt(ts.total_billable_amount) > 0.0:
make_sales_invoice_for_timesheet(ts.name)
frappe.db.commit()
diff --git a/erpnext/docs/user/manual/en/projects/timesheet/timesheet-against-project.md b/erpnext/docs/user/manual/en/projects/timesheet/timesheet-against-project.md
index 7460a4b..a8b2a6b 100644
--- a/erpnext/docs/user/manual/en/projects/timesheet/timesheet-against-project.md
+++ b/erpnext/docs/user/manual/en/projects/timesheet/timesheet-against-project.md
@@ -8,6 +8,9 @@
To bill Customer based on Timesheet, check "Is Billable" in the Timesheet created against Project and Task. To learn more about billing Customer from Timesheet, click [here]({{docs_base_url}}/user/manual/en/projects/timesheet/sales-invoice-from-timesheet.html).
+User can also make invoice against timesheet by selecting the project on the invoice. System will fetch the records from the timesheet based on selected project, for mode detail check below video
+<iframe width="560" height="315" src="https://www.youtube.com/embed/hVAjtOFFhDI" frameborder="0" allowfullscreen></iframe>
+
####Project Costing
When creating Timesheet, Employee will have to select an Activity Type. For each Activity Type, you can create an Activity Cost master. In the Activity Cost, Billing Rate and Costing rate is defined for each Employee.
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 0706a2c..802e045 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -295,7 +295,8 @@
erpnext.patches.v7_0.rename_time_sheet_doctype
execute:frappe.delete_doc_if_exists("Report", "Customers Not Buying Since Long Time")
erpnext.patches.v7_0.make_is_group_fieldtype_as_check
-execute:frappe.reload_doc('projects', 'doctype', 'timesheet', force=True) #2016-08-23
+execute:frappe.reload_doc('projects', 'doctype', 'timesheet') #2016-09-12
+erpnext.patches.v7_1.rename_field_timesheet
execute:frappe.delete_doc_if_exists("Report", "Employee Holiday Attendance")
execute:frappe.delete_doc_if_exists("DocType", "Payment Tool")
execute:frappe.delete_doc_if_exists("DocType", "Payment Tool Detail")
@@ -323,4 +324,5 @@
erpnext.patches.v7_0.update_status_for_timesheet
erpnext.patches.v7_0.set_party_name_in_payment_entry
erpnext.patches.v7_1.set_student_guardian
-erpnext.patches.v7_0.update_conversion_factor_in_supplier_quotation_item
\ No newline at end of file
+erpnext.patches.v7_0.update_conversion_factor_in_supplier_quotation_item
+erpnext.patches.v7_1.move_sales_invoice_from_parent_to_child_timesheet
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py b/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
index 695c552..a365f65 100644
--- a/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
+++ b/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
@@ -3,12 +3,12 @@
def execute():
frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
frappe.reload_doc('accounts', 'doctype', 'sales_invoice_payment')
- for time_sheet in frappe.db.sql(""" select sales_invoice, name, total_billing_amount from `tabTimesheet`
+ for time_sheet in frappe.db.sql(""" select sales_invoice, name, total_billable_amount from `tabTimesheet`
where sales_invoice is not null and docstatus < 2""", as_dict=True):
si_doc = frappe.get_doc('Sales Invoice', time_sheet.sales_invoice)
ts = si_doc.append('timesheets',{})
ts.time_sheet = time_sheet.name
- ts.billing_amount = time_sheet.total_billing_amount
+ ts.billing_amount = time_sheet.total_billable_amount
si_doc.update_time_sheet(time_sheet.sales_invoice)
si_doc.flags.ignore_validate_update_after_submit = True
si_doc.save()
\ No newline at end of file
diff --git a/erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py b/erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py
new file mode 100644
index 0000000..d1ec7c6
--- /dev/null
+++ b/erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py
@@ -0,0 +1,20 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc('projects', 'doctype', 'timesheet_detail')
+ frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet')
+
+ frappe.db.sql(""" update
+ `tabTimesheet` as ts,
+ (select
+ sum(billing_amount) as billing_amount, sum(billing_hours) as billing_hours, time_sheet
+ from `tabSales Invoice Timesheet` where docstatus = 1 group by time_sheet
+ ) as sit
+ set
+ ts.total_billed_amount = sit.billing_amount, ts.total_billed_hours = sit.billing_hours,
+ ts.per_billed = ((sit.billing_amount * 100)/ts.total_billable_amount)
+ where ts.name = sit.time_sheet and ts.docstatus = 1""")
+
+ frappe.db.sql(""" update `tabTimesheet Detail` tsd, `tabTimesheet` ts set tsd.sales_invoice = ts.sales_invoice
+ where tsd.parent = ts.name and ts.sales_invoice is not null""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_1/rename_field_timesheet.py b/erpnext/patches/v7_1/rename_field_timesheet.py
new file mode 100644
index 0000000..3690a2e
--- /dev/null
+++ b/erpnext/patches/v7_1/rename_field_timesheet.py
@@ -0,0 +1,11 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+def execute():
+ doctype = 'Timesheet'
+ fields_dict = {'total_billing_amount': 'total_billable_amount', 'total_billing_hours': 'total_billable_hours'}
+
+ for old_fieldname, new_fieldname in fields_dict.items():
+ if old_fieldname in frappe.db.get_table_columns(doctype):
+ rename_field(doctype, old_fieldname, new_fieldname)
diff --git a/erpnext/patches/v7_1/update_total_billing_hours.py b/erpnext/patches/v7_1/update_total_billing_hours.py
index a38b88d..b9c9602 100644
--- a/erpnext/patches/v7_1/update_total_billing_hours.py
+++ b/erpnext/patches/v7_1/update_total_billing_hours.py
@@ -5,10 +5,10 @@
frappe.reload_doc('projects', 'doctype', 'timesheet_detail')
frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet')
- frappe.db.sql("""update tabTimesheet set total_billing_hours=total_hours
- where total_billing_amount>0 and docstatus = 1""")
+ frappe.db.sql("""update tabTimesheet set total_billable_hours=total_hours
+ where total_billable_amount>0 and docstatus = 1""")
frappe.db.sql("""update `tabTimesheet Detail` set billing_hours=hours where docstatus < 2""")
- frappe.db.sql(""" update `tabSales Invoice Timesheet` set billing_hours = (select total_billing_hours from `tabTimesheet`
+ frappe.db.sql(""" update `tabSales Invoice Timesheet` set billing_hours = (select total_billable_hours from `tabTimesheet`
where name = time_sheet) where time_sheet is not null""")
\ No newline at end of file
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index 8e7e562..369be6d 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -9,20 +9,22 @@
from frappe.utils import now_datetime, nowdate
from erpnext.projects.doctype.timesheet.timesheet import OverlapError
from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
class TestTimesheet(unittest.TestCase):
def test_timesheet_billing_amount(self):
salary_structure = make_salary_structure("_T-Employee-0001")
- timesheet = make_timesheet("_T-Employee-0001", True)
+ timesheet = make_timesheet("_T-Employee-0001", simulate = True, billable=1)
self.assertEquals(timesheet.total_hours, 2)
- self.assertEquals(timesheet.total_billing_hours, 2)
+ self.assertEquals(timesheet.total_billable_hours, 2)
self.assertEquals(timesheet.time_logs[0].billing_rate, 50)
self.assertEquals(timesheet.time_logs[0].billing_amount, 100)
+ self.assertEquals(timesheet.total_billable_amount, 100)
def test_salary_slip_from_timesheet(self):
salary_structure = make_salary_structure("_T-Employee-0001")
- timesheet = make_timesheet("_T-Employee-0001", simulate = True)
+ timesheet = make_timesheet("_T-Employee-0001", simulate = True, billable=1)
salary_slip = make_salary_slip(timesheet.name)
salary_slip.submit()
@@ -51,11 +53,20 @@
item.rate = 100
sales_invoice.submit()
-
timesheet = frappe.get_doc('Timesheet', timesheet.name)
self.assertEquals(sales_invoice.total_billing_amount, 100)
self.assertEquals(timesheet.status, 'Billed')
+ def test_timesheet_billing_based_on_project(self):
+ timesheet = make_timesheet("_T-Employee-0001", simulate=True, billable=1, project = '_Test Project', company='_Test Company')
+ sales_invoice = create_sales_invoice(do_not_save=True)
+ sales_invoice.project = '_Test Project'
+ sales_invoice.submit()
+
+ ts = frappe.get_doc('Timesheet', timesheet.name)
+ self.assertEquals(ts.per_billed, 100)
+ self.assertEquals(ts.time_logs[0].sales_invoice, sales_invoice.name)
+
def make_salary_structure(employee):
name = frappe.db.get_value('Salary Structure Employee', {'employee': employee}, 'parent')
if name:
@@ -93,7 +104,7 @@
return salary_structure
-def make_timesheet(employee, simulate=False, billable = 0, activity_type="_Test Activity Type", project=None, task=None):
+def make_timesheet(employee, simulate=False, billable = 0, activity_type="_Test Activity Type", project=None, task=None, company=None):
update_activity_type(activity_type)
timesheet = frappe.new_doc("Timesheet")
timesheet.employee = employee
@@ -105,6 +116,7 @@
timesheet_detail.to_time = timesheet_detail.from_time + datetime.timedelta(hours= timesheet_detail.hours)
timesheet_detail.project = project
timesheet_detail.task = task
+ timesheet_detail.company = company or '_Test Company'
for data in timesheet.get('time_logs'):
if simulate:
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 42fe005..7d0e71a 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -21,6 +21,14 @@
}
}
}
+
+ frm.fields_dict['time_logs'].grid.get_field('project').get_query = function() {
+ return{
+ filters: {
+ 'company': frm.doc.company
+ }
+ }
+ }
},
onload: function(frm){
@@ -31,7 +39,7 @@
refresh: function(frm) {
if(frm.doc.docstatus==1) {
- if(!frm.doc.sales_invoice && frm.doc.total_billing_amount > 0){
+ if(frm.doc.per_billed < 100){
frm.add_custom_button(__("Make Sales Invoice"), function() { frm.trigger("make_invoice") },
"icon-file-alt");
}
@@ -42,8 +50,9 @@
}
}
- if(frm.doc.sales_invoice) {
+ if(frm.doc.per_billed > 0) {
cur_frm.fields_dict["time_logs"].grid.toggle_enable("billing_hours", false);
+ cur_frm.fields_dict["time_logs"].grid.toggle_enable("billable", false);
}
},
@@ -150,19 +159,22 @@
var tl = frm.doc.time_logs || [];
total_working_hr = 0;
total_billing_hr = 0;
- total_billing_amount = 0;
+ total_billable_amount = 0;
total_costing_amount = 0;
for(var i=0; i<tl.length; i++) {
if (tl[i].hours) {
total_working_hr += tl[i].hours;
- total_billing_hr += tl[i].billing_hours;
- total_billing_amount += tl[i].billing_amount;
+ total_billable_amount += tl[i].billing_amount;
total_costing_amount += tl[i].costing_amount;
+
+ if(tl[i].billable){
+ total_billing_hr += tl[i].billing_hours;
+ }
}
}
- cur_frm.set_value("total_billing_hours", total_billing_hr);
+ cur_frm.set_value("total_billable_hours", total_billing_hr);
cur_frm.set_value("total_hours", total_working_hr);
- cur_frm.set_value("total_billing_amount", total_billing_amount);
+ cur_frm.set_value("total_billable_amount", total_billable_amount);
cur_frm.set_value("total_costing_amount", total_costing_amount);
}
\ No newline at end of file
diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json
index cba4859..168bc63 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.json
+++ b/erpnext/projects/doctype/timesheet/timesheet.json
@@ -470,8 +470,8 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "working_hours",
"columns": 0,
+ "fieldname": "working_hours",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -522,6 +522,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
+ "columns": 0,
"fieldname": "billing_details",
"fieldtype": "Section Break",
"hidden": 0,
@@ -544,24 +545,77 @@
"unique": 0
},
{
- "allow_on_submit": 0,
+ "allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
- "fieldname": "total_billing_hours",
+ "columns": 0,
+ "fieldname": "total_billable_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Total Billing Hours",
+ "label": "Total Billable Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
- "print_hide": 0,
+ "print_hide": 1,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "total_billed_hours",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Total Billed Hours",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "total_costing_amount",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Total Costing Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -601,14 +655,14 @@
"default": "0",
"depends_on": "",
"description": "",
- "fieldname": "total_billing_amount",
+ "fieldname": "total_billable_amount",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Total Billing Amount",
+ "label": "Total Billable Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -623,23 +677,49 @@
"unique": 0
},
{
- "allow_on_submit": 0,
+ "allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "total_costing_amount",
+ "fieldname": "total_billed_amount",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Total Costing Amount",
+ "label": "Total Billed Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
- "print_hide": 0,
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "per_billed",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "% Amount Billed",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
@@ -737,7 +817,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-09-01 11:33:38.110421",
+ "modified": "2016-09-12 13:19:22.298036",
"modified_by": "Administrator",
"module": "Projects",
"name": "Timesheet",
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index 31a98d9..e489c28 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -25,21 +25,31 @@
self.validate_time_logs()
self.update_cost()
self.calculate_total_amounts()
+ self.calculate_percentage_billed()
def calculate_total_amounts(self):
self.total_hours = 0.0
- self.total_billing_hours = 0.0
- self.total_billing_amount = 0.0
+ self.total_billable_hours = 0.0
+ self.total_billed_hours = 0.0
+ self.total_billable_amount = 0.0
self.total_costing_amount = 0.0
+ self.total_billed_amount = 0.0
for d in self.get("time_logs"):
self.update_billing_hours(d)
self.total_hours += flt(d.hours)
- self.total_billing_hours += flt(d.billing_hours)
- if d.billable:
- self.total_billing_amount += flt(d.billing_amount)
+ if d.billable:
+ self.total_billable_hours += flt(d.billing_hours)
+ self.total_billable_amount += flt(d.billing_amount)
self.total_costing_amount += flt(d.costing_amount)
+ self.total_billed_amount += flt(d.billing_amount) if d.sales_invoice else 0.0
+ self.total_billed_hours += flt(d.billing_hours) if d.sales_invoice else 0.0
+
+ def calculate_percentage_billed(self):
+ self.per_billed = 0
+ if self.total_billed_amount > 0 and self.total_billable_amount > 0:
+ self.per_billed = (self.total_billed_amount * 100) / self.total_billable_amount
def update_billing_hours(self, args):
if cint(args.billing_hours) == 0:
@@ -52,7 +62,7 @@
"2": "Cancelled"
}[str(self.docstatus or 0)]
- if self.sales_invoice:
+ if self.per_billed == 100:
self.status = "Billed"
if self.salary_slip:
@@ -236,31 +246,70 @@
def update_cost(self):
for data in self.time_logs:
- if data.activity_type and (not data.billing_amount or not data.costing_amount):
+ if data.activity_type and data.billable:
rate = get_activity_cost(self.employee, data.activity_type)
hours = data.billing_hours or 0
if rate:
- data.billing_rate = flt(rate.get('billing_rate'))
- data.costing_rate = flt(rate.get('costing_rate'))
+ data.billing_rate = flt(rate.get('billing_rate')) if flt(data.billing_rate) == 0 else data.billing_rate
+ data.costing_rate = flt(rate.get('costing_rate')) if flt(data.costing_rate) == 0 else data.costing_rate
data.billing_amount = data.billing_rate * hours
data.costing_amount = data.costing_rate * hours
@frappe.whitelist()
+def get_projectwise_timesheet_data(project, parent=None):
+ cond = ''
+ if parent:
+ cond = "and parent = %(parent)s"
+
+ return frappe.db.sql("""select name, parent, billing_hours, billing_amount as billing_amt
+ from `tabTimesheet Detail` where docstatus=1 and project = %(project)s {0} and billable = 1
+ and sales_invoice is null""".format(cond), {'project': project, 'parent': parent}, as_dict=1)
+
+@frappe.whitelist()
+def get_timesheet(doctype, txt, searchfield, start, page_len, filters):
+ if not filters: filters = {}
+
+ condition = ""
+ if filters.get("project"):
+ condition = "and tsd.project = %(project)s"
+
+ return frappe.db.sql("""select distinct tsd.parent from `tabTimesheet Detail` tsd,
+ `tabTimesheet` ts where
+ ts.status in ('Submitted', 'Payslip') and tsd.parent = ts.name and
+ tsd.docstatus = 1 and ts.total_billable_amount > 0
+ and tsd.parent LIKE %(txt)s {condition}
+ order by tsd.parent limit %(start)s, %(page_len)s"""
+ .format(condition=condition), {
+ "txt": "%%%s%%" % frappe.db.escape(txt),
+ "start": start, "page_len": page_len, 'project': filters.get("project")
+ })
+
+@frappe.whitelist()
+def get_timesheet_data(name, project):
+ if project and project!='':
+ data = get_projectwise_timesheet_data(project, name)
+ else:
+ data = frappe.get_all('Timesheet',
+ fields = ["(total_billable_amount - total_billed_amount) as billing_amt", "total_billable_hours as billing_hours"], filters = {'name': name})
+
+ return {
+ 'billing_hours': data[0].billing_hours,
+ 'billing_amount': data[0].billing_amt,
+ 'timesheet_detail': data[0].name if project and project!= '' else None
+ }
+
+@frappe.whitelist()
def make_sales_invoice(source_name, target=None):
target = frappe.new_doc("Sales Invoice")
+ timesheet = frappe.get_doc('Timesheet', source_name)
- target.append("timesheets", get_mapped_doc("Timesheet", source_name, {
- "Timesheet": {
- "doctype": "Sales Invoice Timesheet",
- "field_map": {
- "total_billing_amount": "billing_amount",
- "total_billing_hours": "billing_hours",
- "name": "time_sheet"
- },
- }
- }))
-
- target.run_method("calculate_billing_amount_from_timesheet")
+ target.append('timesheets', {
+ 'time_sheet': timesheet.name,
+ 'billing_hours': flt(timesheet.total_billable_hours) - flt(timesheet.total_billed_hours),
+ 'billing_amount': flt(timesheet.total_billable_amount) - flt(timesheet.total_billed_amount)
+ })
+
+ target.run_method("calculate_billing_amount_for_timesheet")
return target
@@ -300,7 +349,7 @@
["costing_rate", "billing_rate"], as_dict=True)
return rate[0] if rate else {}
-
+
@frappe.whitelist()
def get_events(start, end, filters=None):
"""Returns events for Gantt / Calendar view rendering.
diff --git a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
index 9bd4b8d..b1d3232 100644
--- a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
+++ b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
@@ -11,33 +11,6 @@
"editable_grid": 1,
"fields": [
{
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 1,
- "depends_on": "",
- "fieldname": "billable",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Bill",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -170,192 +143,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "billable",
- "fieldname": "section_break_11",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "billing_hours",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Billing Hours",
- "length": 0,
- "no_copy": 0,
- "permlevel": 1,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fieldname": "billing_rate",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Billing Rate",
- "length": 0,
- "no_copy": 0,
- "permlevel": 1,
- "precision": "2",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "costing_rate",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Costing Rate",
- "length": 0,
- "no_copy": 0,
- "permlevel": 1,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_14",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0",
- "depends_on": "",
- "description": "",
- "fieldname": "billing_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Billing Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 1,
- "precision": "2",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0",
- "description": "",
- "fieldname": "costing_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Costing Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 1,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "section_break_7",
"fieldtype": "Section Break",
"hidden": 0,
@@ -407,51 +194,24 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "columns": 3,
- "fieldname": "project",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Project",
- "length": 0,
- "no_copy": 0,
- "options": "Project",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
"columns": 0,
- "depends_on": "",
- "fieldname": "task",
+ "depends_on": "eval:parent.production_order",
+ "fieldname": "workstation",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Task",
+ "label": "Workstation",
"length": 0,
"no_copy": 0,
- "options": "Task",
+ "options": "Workstation",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -489,34 +249,6 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:parent.production_order",
- "fieldname": "workstation",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Workstation",
- "length": 0,
- "no_copy": 0,
- "options": "Workstation",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:parent.production_order",
"fieldname": "operation",
"fieldtype": "Link",
"hidden": 0,
@@ -565,6 +297,429 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "project_details",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 3,
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Project",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Project",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "task",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Task",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Task",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 1,
+ "depends_on": "",
+ "fieldname": "billable",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Bill",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_8",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "billable",
+ "fieldname": "billing_hours",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Billing Hours",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 1,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "billable",
+ "fieldname": "section_break_11",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "billing_rate",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Billing Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 1,
+ "precision": "2",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "depends_on": "",
+ "description": "",
+ "fieldname": "billing_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Billing Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 1,
+ "precision": "2",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_14",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "costing_rate",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Costing Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 1,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "description": "",
+ "fieldname": "costing_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Costing Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 1,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "reference",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Reference",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "sales_invoice",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Sales Invoice",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Sales Invoice",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
}
],
"hide_heading": 0,
@@ -577,7 +732,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-08-26 03:00:11.431794",
+ "modified": "2016-09-09 13:36:03.057513",
"modified_by": "Administrator",
"module": "Projects",
"name": "Timesheet Detail",