completed Time Log / Time Log Batch
This commit is contained in:
parent
b21eb9ac09
commit
fee642d547
@ -75,6 +75,7 @@ class DocType(SellingController):
|
|||||||
self.set_aging_date()
|
self.set_aging_date()
|
||||||
self.set_against_income_account()
|
self.set_against_income_account()
|
||||||
self.validate_c_form()
|
self.validate_c_form()
|
||||||
|
self.validate_time_logs_are_submitted()
|
||||||
self.validate_recurring_invoice()
|
self.validate_recurring_invoice()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
@ -104,7 +105,7 @@ class DocType(SellingController):
|
|||||||
self.update_against_document_in_jv()
|
self.update_against_document_in_jv()
|
||||||
|
|
||||||
self.update_c_form()
|
self.update_c_form()
|
||||||
|
self.update_time_log_batch(self.doc.name)
|
||||||
self.convert_to_recurring()
|
self.convert_to_recurring()
|
||||||
|
|
||||||
|
|
||||||
@ -122,12 +123,28 @@ class DocType(SellingController):
|
|||||||
self.check_next_docstatus()
|
self.check_next_docstatus()
|
||||||
sales_com_obj.update_prevdoc_detail(0, self)
|
sales_com_obj.update_prevdoc_detail(0, self)
|
||||||
|
|
||||||
|
self.update_time_log_batch(None)
|
||||||
self.make_gl_entries(is_cancel=1)
|
self.make_gl_entries(is_cancel=1)
|
||||||
|
|
||||||
def on_update_after_submit(self):
|
def on_update_after_submit(self):
|
||||||
self.validate_recurring_invoice()
|
self.validate_recurring_invoice()
|
||||||
self.convert_to_recurring()
|
self.convert_to_recurring()
|
||||||
|
|
||||||
|
def update_time_log_batch(self, sales_invoice):
|
||||||
|
for d in self.doclist.get({"doctype":"Sales Invoice Item"}):
|
||||||
|
if d.time_log_batch:
|
||||||
|
tlb = webnotes.bean("Time Log Batch", d.time_log_batch)
|
||||||
|
tlb.doc.sales_invoice = sales_invoice
|
||||||
|
tlb.update_after_submit()
|
||||||
|
|
||||||
|
def validate_time_logs_are_submitted(self):
|
||||||
|
for d in self.doclist.get({"doctype":"Sales Invoice Item"}):
|
||||||
|
if d.time_log_batch:
|
||||||
|
status = webnotes.conn.get_value("Time Log Batch", d.time_log_batch, "status")
|
||||||
|
if status!="Submitted":
|
||||||
|
webnotes.msgprint(_("Time Log Batch status must be 'Submitted'") + ":" + d.time_log_batch,
|
||||||
|
raise_exception=True)
|
||||||
|
|
||||||
def set_pos_fields(self):
|
def set_pos_fields(self):
|
||||||
"""Set retail related fields from pos settings"""
|
"""Set retail related fields from pos settings"""
|
||||||
pos = webnotes.conn.sql("select * from `tabPOS Setting` where ifnull(user,'') = '%s' and company = '%s'" % (session['user'], self.doc.company), as_dict=1)
|
pos = webnotes.conn.sql("select * from `tabPOS Setting` where ifnull(user,'') = '%s' and company = '%s'" % (session['user'], self.doc.company), as_dict=1)
|
||||||
|
|||||||
16
accounts/doctype/sales_invoice/sales_invoice_map.js
Normal file
16
accounts/doctype/sales_invoice/sales_invoice_map.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
wn.model.map_info["Sales Invoice"] = {
|
||||||
|
"Time Log Batch": {
|
||||||
|
table_map: {
|
||||||
|
"Sales Invoice Item": "Time Log Batch",
|
||||||
|
},
|
||||||
|
field_map: {
|
||||||
|
"Sales Invoice Item": {
|
||||||
|
"basic_rate": "rate",
|
||||||
|
"time_log_batch": "name",
|
||||||
|
"qty": "total_hours",
|
||||||
|
"stock_uom": "=Hour",
|
||||||
|
"description": "=via Time Logs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -29,6 +29,31 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertEquals(webnotes.conn.get_value("Sales Invoice", w.doc.name, "outstanding_amount"),
|
self.assertEquals(webnotes.conn.get_value("Sales Invoice", w.doc.name, "outstanding_amount"),
|
||||||
561.8)
|
561.8)
|
||||||
|
|
||||||
|
def test_time_log_batch(self):
|
||||||
|
tlb = webnotes.bean("Time Log Batch", "_T-Time Log Batch-00001")
|
||||||
|
tlb.submit()
|
||||||
|
|
||||||
|
w = webnotes.bean(webnotes.copy_doclist(test_records[0]))
|
||||||
|
w.doclist[1].time_log_batch = "_T-Time Log Batch-00001"
|
||||||
|
w.insert()
|
||||||
|
w.submit()
|
||||||
|
|
||||||
|
self.assertEquals(webnotes.conn.get_value("Time Log Batch", "_T-Time Log Batch-00001", "status"),
|
||||||
|
"Billed")
|
||||||
|
|
||||||
|
self.assertEquals(webnotes.conn.get_value("Time Log", "_T-Time Log-00001", "status"),
|
||||||
|
"Billed")
|
||||||
|
|
||||||
|
w.cancel()
|
||||||
|
|
||||||
|
self.assertEquals(webnotes.conn.get_value("Time Log Batch", "_T-Time Log Batch-00001", "status"),
|
||||||
|
"Submitted")
|
||||||
|
|
||||||
|
self.assertEquals(webnotes.conn.get_value("Time Log", "_T-Time Log-00001", "status"),
|
||||||
|
"Batched for Billing")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
test_dependencies = ["Journal Voucher"]
|
test_dependencies = ["Journal Voucher"]
|
||||||
|
|
||||||
test_records = [[
|
test_records = [[
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"creation": "2013-01-10 16:34:09",
|
"creation": "2013-01-29 19:25:49",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"modified": "2013-01-29 16:27:51",
|
"modified": "2013-03-01 13:41:51",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"owner": "Administrator"
|
"owner": "Administrator"
|
||||||
},
|
},
|
||||||
@ -320,6 +320,13 @@
|
|||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"doctype": "DocField",
|
||||||
|
"fieldname": "time_log_batch",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Time Log Batch",
|
||||||
|
"options": "Time Log Batch"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"doctype": "DocField",
|
"doctype": "DocField",
|
||||||
"fieldname": "item_tax_rate",
|
"fieldname": "item_tax_rate",
|
||||||
|
|||||||
@ -22,6 +22,7 @@ from webnotes.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_
|
|||||||
from webnotes import msgprint
|
from webnotes import msgprint
|
||||||
|
|
||||||
class LeaveDayBlockedError(Exception): pass
|
class LeaveDayBlockedError(Exception): pass
|
||||||
|
class OverlapError(Exception): pass
|
||||||
|
|
||||||
from webnotes.model.controller import DocListController
|
from webnotes.model.controller import DocListController
|
||||||
class DocType(DocListController):
|
class DocType(DocListController):
|
||||||
@ -129,17 +130,22 @@ class DocType(DocListController):
|
|||||||
(self.doc.leave_type,), raise_exception=1)
|
(self.doc.leave_type,), raise_exception=1)
|
||||||
|
|
||||||
def validate_leave_overlap(self):
|
def validate_leave_overlap(self):
|
||||||
|
if not self.doc.name:
|
||||||
|
self.doc.name = "New Leave Application"
|
||||||
|
|
||||||
for d in webnotes.conn.sql("""select name, leave_type, posting_date,
|
for d in webnotes.conn.sql("""select name, leave_type, posting_date,
|
||||||
from_date, to_date
|
from_date, to_date
|
||||||
from `tabLeave Application`
|
from `tabLeave Application`
|
||||||
where
|
where
|
||||||
(from_date <= %(to_date)s and to_date >= %(from_date)s)
|
employee = %(employee)s
|
||||||
and employee = %(employee)s
|
|
||||||
and docstatus < 2
|
and docstatus < 2
|
||||||
and status in ("Open", "Approved")
|
and status in ("Open", "Approved")
|
||||||
|
and (from_date between %(from_date)s and %(to_date)s
|
||||||
|
or to_date between %(from_date)s and %(to_date)s
|
||||||
|
or %(from_date)s between from_date and to_date)
|
||||||
and name != %(name)s""", self.doc.fields, as_dict = 1):
|
and name != %(name)s""", self.doc.fields, as_dict = 1):
|
||||||
|
|
||||||
msgprint("Employee : %s has already applied for %s between %s and %s on %s. Please refer Leave Application : <a href=\"#Form/Leave Application/%s\">%s</a>" % (self.doc.employee, cstr(d['leave_type']), formatdate(d['from_date']), formatdate(d['to_date']), formatdate(d['posting_date']), d['name'], d['name']), raise_exception = 1)
|
msgprint("Employee : %s has already applied for %s between %s and %s on %s. Please refer Leave Application : <a href=\"#Form/Leave Application/%s\">%s</a>" % (self.doc.employee, cstr(d['leave_type']), formatdate(d['from_date']), formatdate(d['to_date']), formatdate(d['posting_date']), d['name'], d['name']), raise_exception = OverlapError)
|
||||||
|
|
||||||
def validate_max_days(self):
|
def validate_max_days(self):
|
||||||
max_days = webnotes.conn.sql("select max_days_allowed from `tabLeave Type` where name = '%s'" %(self.doc.leave_type))
|
max_days = webnotes.conn.sql("select max_days_allowed from `tabLeave Type` where name = '%s'" %(self.doc.leave_type))
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import webnotes
|
import webnotes
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from hr.doctype.leave_application.leave_application import LeaveDayBlockedError
|
from hr.doctype.leave_application.leave_application import LeaveDayBlockedError, OverlapError
|
||||||
|
|
||||||
class TestLeaveApplication(unittest.TestCase):
|
class TestLeaveApplication(unittest.TestCase):
|
||||||
def get_application(self, doclist):
|
def get_application(self, doclist):
|
||||||
@ -24,12 +24,21 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
from webnotes.profile import add_role
|
from webnotes.profile import add_role
|
||||||
add_role("test1@example.com", "HR User")
|
add_role("test1@example.com", "HR User")
|
||||||
|
|
||||||
|
# clear other applications
|
||||||
|
webnotes.conn.sql("delete from `tabLeave Application`")
|
||||||
|
|
||||||
application = self.get_application(test_records[1])
|
application = self.get_application(test_records[1])
|
||||||
self.assertTrue(application.insert())
|
self.assertTrue(application.insert())
|
||||||
|
|
||||||
|
def test_overlap(self):
|
||||||
|
application = self.get_application(test_records[1])
|
||||||
|
self.assertRaises(OverlapError, application.insert)
|
||||||
|
|
||||||
def test_global_block_list(self):
|
def test_global_block_list(self):
|
||||||
|
|
||||||
application = self.get_application(test_records[3])
|
application = self.get_application(test_records[3])
|
||||||
application.doc.leave_approver = "test@example.com"
|
application.doc.leave_approver = "test@example.com"
|
||||||
|
|
||||||
webnotes.conn.set_value("Leave Block List", "_Test Leave Block List",
|
webnotes.conn.set_value("Leave Block List", "_Test Leave Block List",
|
||||||
"applies_to_all_departments", 1)
|
"applies_to_all_departments", 1)
|
||||||
webnotes.conn.set_value("Employee", "_T-Employee-0002", "department",
|
webnotes.conn.set_value("Employee", "_T-Employee-0002", "department",
|
||||||
|
|||||||
@ -200,4 +200,5 @@ patch_list = [
|
|||||||
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Modern") # 2013-02-26',
|
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Modern") # 2013-02-26',
|
||||||
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Spartan") # 2013-02-26',
|
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Spartan") # 2013-02-26',
|
||||||
"execute:(not webnotes.conn.exists('Role', 'Projects Manager')) and webnotes.doc({'doctype':'Role', 'role_name':'Projects Manager'}).insert()",
|
"execute:(not webnotes.conn.exists('Role', 'Projects Manager')) and webnotes.doc({'doctype':'Role', 'role_name':'Projects Manager'}).insert()",
|
||||||
|
"execute:(not webnotes.conn.exists('UOM', 'Hour')) and webnotes.doc({'uom_name': 'Unit', 'doctype': 'UOM', 'name': 'Hour'}).insert()",
|
||||||
]
|
]
|
||||||
5
projects/doctype/activity_type/test_activity_type.py
Normal file
5
projects/doctype/activity_type/test_activity_type.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
test_records = [
|
||||||
|
[{"activity_type":"_Test Activity Type"}],
|
||||||
|
[{"activity_type":"_Test Activity Type 1"}],
|
||||||
|
[{"activity_type":"_Test Activity Type 2"}]
|
||||||
|
]
|
||||||
8
projects/doctype/project/test_project.py
Normal file
8
projects/doctype/project/test_project.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
test_records = [[{
|
||||||
|
"project_name": "_Test Project",
|
||||||
|
"status": "Open"
|
||||||
|
}],
|
||||||
|
[{
|
||||||
|
"project_name": "_Test Project 1",
|
||||||
|
"status": "Open"
|
||||||
|
}]]
|
||||||
7
projects/doctype/task/test_task.py
Normal file
7
projects/doctype/task/test_task.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
test_records = [
|
||||||
|
[{"subject": "_Test Task", "project":"_Test Project", "status":"Open"}],
|
||||||
|
[{"subject": "_Test Task 1", "status":"Open"}],
|
||||||
|
[{"subject": "_Test Task 2", "status":"Open"}]
|
||||||
|
]
|
||||||
|
|
||||||
|
test_ignore = ["Customer"]
|
||||||
19
projects/doctype/time_log/test_time_log.py
Normal file
19
projects/doctype/time_log/test_time_log.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import webnotes
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from projects.doctype.time_log.time_log import OverlapError
|
||||||
|
|
||||||
|
class TestTimeLog(unittest.TestCase):
|
||||||
|
def test_duplication(self):
|
||||||
|
ts = webnotes.bean(webnotes.copy_doclist(test_records[0]))
|
||||||
|
self.assertRaises(OverlapError, ts.insert)
|
||||||
|
|
||||||
|
test_records = [[{
|
||||||
|
"from_time": "2013-01-01 10:00:00",
|
||||||
|
"to_time": "2013-01-01 11:00:00",
|
||||||
|
"activity_type": "_Test Activity Type",
|
||||||
|
"note": "_Test Note",
|
||||||
|
"docstatus": 1
|
||||||
|
}]]
|
||||||
|
|
||||||
|
test_ignore = ["Sales Invoice", "Time Log Batch"]
|
||||||
5
projects/doctype/time_log/time_log.js
Normal file
5
projects/doctype/time_log/time_log.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
$.extend(cur_frm.cscript, {
|
||||||
|
refresh: function(doc) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -6,6 +6,8 @@ from webnotes import _
|
|||||||
|
|
||||||
from webnotes.widgets.reportview import build_match_conditions
|
from webnotes.widgets.reportview import build_match_conditions
|
||||||
|
|
||||||
|
class OverlapError(webnotes.ValidationError): pass
|
||||||
|
|
||||||
class DocType:
|
class DocType:
|
||||||
def __init__(self, d, dl):
|
def __init__(self, d, dl):
|
||||||
self.doc, self.doclist = d, dl
|
self.doc, self.doclist = d, dl
|
||||||
@ -13,27 +15,45 @@ class DocType:
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.validate_overlap()
|
self.validate_overlap()
|
||||||
|
self.calculate_total_hours()
|
||||||
|
|
||||||
|
def calculate_total_hours(self):
|
||||||
|
from webnotes.utils import time_diff_in_hours
|
||||||
|
self.doc.hours = time_diff_in_hours(self.doc.to_time, self.doc.from_time)
|
||||||
|
|
||||||
def set_status(self):
|
def set_status(self):
|
||||||
if self.doc.docstatus==0:
|
self.doc.status = {
|
||||||
self.doc.status = "Draft"
|
"0": "Draft",
|
||||||
elif self.doc.docstatus==1:
|
"1": "Submitted",
|
||||||
self.doc.status = "Submitted"
|
"2": "Cancelled"
|
||||||
elif self.doc.docstatus==2:
|
}[str(self.doc.docstatus or 0)]
|
||||||
self.doc.status = "Cancelled"
|
|
||||||
|
|
||||||
# billed will be set directly
|
if self.doc.time_log_batch:
|
||||||
|
self.doc.status="Batched for Billing"
|
||||||
|
|
||||||
|
if self.doc.sales_invoice:
|
||||||
|
self.doc.status="Billed"
|
||||||
|
|
||||||
def validate_overlap(self):
|
def validate_overlap(self):
|
||||||
existing = webnotes.conn.sql_list("""select name from `tabTime Log` where owner=%s and
|
existing = webnotes.conn.sql_list("""select name from `tabTime Log` where owner=%s and
|
||||||
((from_time between %s and %s) or (to_time between %s and %s)) and name!=%s""",
|
(
|
||||||
|
(from_time between %s and %s) or
|
||||||
|
(to_time between %s and %s) or
|
||||||
|
(%s between from_time and to_time))
|
||||||
|
and name!=%s
|
||||||
|
and docstatus < 2""",
|
||||||
(self.doc.owner, self.doc.from_time, self.doc.to_time, self.doc.from_time,
|
(self.doc.owner, self.doc.from_time, self.doc.to_time, self.doc.from_time,
|
||||||
self.doc.to_time, self.doc.name))
|
self.doc.to_time, self.doc.from_time, self.doc.name or "No Name"))
|
||||||
|
|
||||||
if existing:
|
if existing:
|
||||||
webnotes.msgprint(_("This Time Log conflicts with") + ":" + ', '.join(existing),
|
webnotes.msgprint(_("This Time Log conflicts with") + ":" + ', '.join(existing),
|
||||||
raise_exception=True)
|
raise_exception=OverlapError)
|
||||||
|
|
||||||
|
def before_cancel(self):
|
||||||
|
self.set_status()
|
||||||
|
|
||||||
|
def before_update_after_submit(self):
|
||||||
|
self.set_status()
|
||||||
|
|
||||||
@webnotes.whitelist()
|
@webnotes.whitelist()
|
||||||
def get_events(start, end):
|
def get_events(start, end):
|
||||||
|
|||||||
@ -2,13 +2,13 @@
|
|||||||
{
|
{
|
||||||
"creation": "2013-02-26 14:58:28",
|
"creation": "2013-02-26 14:58:28",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"modified": "2013-02-28 18:41:40",
|
"modified": "2013-03-01 17:48:09",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"owner": "Administrator"
|
"owner": "Administrator"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_attach": 1,
|
"allow_attach": 1,
|
||||||
"autoname": "TL-.######",
|
"autoname": "naming_series:",
|
||||||
"description": "Log of Activities performed by users against Tasks that can be used for tracking time, billing.",
|
"description": "Log of Activities performed by users against Tasks that can be used for tracking time, billing.",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Master",
|
"document_type": "Master",
|
||||||
@ -40,14 +40,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"doctype": "DocField",
|
"doctype": "DocField",
|
||||||
"fieldname": "status",
|
"fieldname": "naming_series",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_list_view": 1,
|
"label": "Naming Series",
|
||||||
"label": "Status",
|
"options": "TL-",
|
||||||
"options": "Draft\nSubmitted\nBatched for Billing\nBilled\nCancelled",
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1,
|
"reqd": 1
|
||||||
"reqd": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"doctype": "DocField",
|
"doctype": "DocField",
|
||||||
@ -67,12 +65,31 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"doctype": "DocField",
|
||||||
|
"fieldname": "hours",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Hours",
|
||||||
|
"permlevel": 0,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"doctype": "DocField",
|
"doctype": "DocField",
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"doctype": "DocField",
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Status",
|
||||||
|
"options": "Draft\nSubmitted\nBatched for Billing\nBilled\nCancelled",
|
||||||
|
"permlevel": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"reqd": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"doctype": "DocField",
|
"doctype": "DocField",
|
||||||
"fieldname": "activity_type",
|
"fieldname": "activity_type",
|
||||||
@ -127,6 +144,26 @@
|
|||||||
"options": "Project",
|
"options": "Project",
|
||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Will be updated when batched.",
|
||||||
|
"doctype": "DocField",
|
||||||
|
"fieldname": "time_log_batch",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Time Log Batch",
|
||||||
|
"options": "Time Log Batch",
|
||||||
|
"permlevel": 0,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Will be updated when billed.",
|
||||||
|
"doctype": "DocField",
|
||||||
|
"fieldname": "sales_invoice",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Sales Invoice",
|
||||||
|
"options": "Sales Invoice",
|
||||||
|
"permlevel": 0,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"doctype": "DocField",
|
"doctype": "DocField",
|
||||||
"fieldname": "file_list",
|
"fieldname": "file_list",
|
||||||
@ -143,7 +180,7 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Amended From",
|
"label": "Amended From",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Sales Invoice",
|
"options": "Time Log",
|
||||||
"permlevel": 1,
|
"permlevel": 1,
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// render
|
// render
|
||||||
wn.listview_settings['Time Log'] = {
|
wn.listview_settings['Time Log'] = {
|
||||||
|
add_fields: ["`tabTime Log`.`status`", "`tabTime Log`.`billable`", "`tabTime Log`.`activity_type`"],
|
||||||
selectable: true,
|
selectable: true,
|
||||||
onload: function(me) {
|
onload: function(me) {
|
||||||
me.appframe.add_button(wn._("Make Time Log Batch"), function() {
|
me.appframe.add_button(wn._("Make Time Log Batch"), function() {
|
||||||
@ -16,12 +17,28 @@ wn.listview_settings['Time Log'] = {
|
|||||||
msgprint(wn._("Time Log is not billable") + ": " + d.name);
|
msgprint(wn._("Time Log is not billable") + ": " + d.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(d.sales_invoice) {
|
if(d.status!="Submitted") {
|
||||||
msgprint(wn._("Time Log has been Invoiced") + ": " + d.name);
|
msgprint(wn._("Time Log Status must be Submitted."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// make batch
|
||||||
|
wn.model.with_doctype("Time Log Batch", function() {
|
||||||
|
var tlb = wn.model.get_new_doc("Time Log Batch");
|
||||||
|
$.each(selected, function(i, d) {
|
||||||
|
var detail = wn.model.get_new_doc("Time Log Batch Detail");
|
||||||
|
$.extend(detail, {
|
||||||
|
"parenttype": "Time Log Batch",
|
||||||
|
"parentfield": "time_log_batch_details",
|
||||||
|
"parent": tlb.name,
|
||||||
|
"time_log": d.name,
|
||||||
|
"activity_type": d.activity_type,
|
||||||
|
"created_by": d.owner,
|
||||||
|
"idx": i+1
|
||||||
|
});
|
||||||
|
})
|
||||||
|
wn.set_route("Form", "Time Log Batch", tlb.name);
|
||||||
|
})
|
||||||
|
|
||||||
}, "icon-file-alt");
|
}, "icon-file-alt");
|
||||||
}
|
}
|
||||||
|
|||||||
20
projects/doctype/time_log_batch/test_time_log_batch.py
Normal file
20
projects/doctype/time_log_batch/test_time_log_batch.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import webnotes, unittest
|
||||||
|
|
||||||
|
class TimeLogBatchTest(unittest.TestCase):
|
||||||
|
def test_time_log_status(self):
|
||||||
|
self.assertEquals(webnotes.conn.get_value("Time Log", "_T-Time Log-00001", "status"), "Submitted")
|
||||||
|
tlb = webnotes.bean("Time Log Batch", "_T-Time Log Batch-00001")
|
||||||
|
tlb.submit()
|
||||||
|
self.assertEquals(webnotes.conn.get_value("Time Log", "_T-Time Log-00001", "status"), "Batched for Billing")
|
||||||
|
tlb.cancel()
|
||||||
|
self.assertEquals(webnotes.conn.get_value("Time Log", "_T-Time Log-00001", "status"), "Submitted")
|
||||||
|
|
||||||
|
test_records = [[
|
||||||
|
{"rate": "500"},
|
||||||
|
{
|
||||||
|
"doctype": "Time Log Batch Detail",
|
||||||
|
"parenttype": "Time Log Batch",
|
||||||
|
"parentfield": "time_log_batch_details",
|
||||||
|
"time_log": "_T-Time Log-00001",
|
||||||
|
}
|
||||||
|
]]
|
||||||
@ -1,5 +1,6 @@
|
|||||||
cur_frm.add_fetch("time_log", "activity_type", "activity_type");
|
cur_frm.add_fetch("time_log", "activity_type", "activity_type");
|
||||||
cur_frm.add_fetch("time_log", "owner", "created_by");
|
cur_frm.add_fetch("time_log", "owner", "created_by");
|
||||||
|
cur_frm.add_fetch("time_log", "hours", "hours");
|
||||||
|
|
||||||
cur_frm.set_query("time_log", "time_log_batch_details", function(doc) {
|
cur_frm.set_query("time_log", "time_log_batch_details", function(doc) {
|
||||||
return {
|
return {
|
||||||
@ -12,7 +13,24 @@ cur_frm.set_query("time_log", "time_log_batch_details", function(doc) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$.extend(cur_frm.cscript, {
|
$.extend(cur_frm.cscript, {
|
||||||
refresh: function() {
|
refresh: function(doc) {
|
||||||
|
cur_frm.set_intro({
|
||||||
|
"Draft": wn._("Select Time Logs and Submit to create a new Sales Invoice."),
|
||||||
|
"Submitted": wn._("Click on 'Make Sales Invoice' button to create a new Sales Invoice."),
|
||||||
|
"Billed": wn._("This Time Log Batch has been billed."),
|
||||||
|
"Cancelled": wn._("This Time Log Batch has been cancelled.")
|
||||||
|
}[doc.status]);
|
||||||
|
|
||||||
|
if(doc.status=="Submitted") {
|
||||||
|
cur_frm.add_custom_button("Make Sales Invoice", function() { cur_frm.cscript.make_invoice() },
|
||||||
|
"icon-file-alt");
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
make_invoice: function() {
|
||||||
|
var doc = cur_frm.doc;
|
||||||
|
wn.model.map({
|
||||||
|
source: wn.model.get_doclist(doc.doctype, doc.name),
|
||||||
|
target: "Sales Invoice"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -2,10 +2,59 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import webnotes
|
import webnotes
|
||||||
|
from webnotes import _
|
||||||
|
|
||||||
class DocType:
|
class DocType:
|
||||||
def __init__(self, d, dl):
|
def __init__(self, d, dl):
|
||||||
self.doc, self.doclist = d, dl
|
self.doc, self.doclist = d, dl
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
self.set_status()
|
||||||
|
self.doc.total_hours = 0.0
|
||||||
|
for d in self.doclist.get({"doctype":"Time Log Batch Detail"}):
|
||||||
|
tl = webnotes.doc("Time Log", d.time_log)
|
||||||
|
self.update_time_log_values(d, tl)
|
||||||
|
self.validate_time_log_is_submitted(tl)
|
||||||
|
self.doc.total_hours += float(tl.hours or 0.0)
|
||||||
|
|
||||||
|
def update_time_log_values(self, d, tl):
|
||||||
|
d.fields.update({
|
||||||
|
"hours": tl.hours,
|
||||||
|
"activity_type": tl.activity_type,
|
||||||
|
"created_by": tl.owner
|
||||||
|
})
|
||||||
|
|
||||||
|
def validate_time_log_is_submitted(self, tl):
|
||||||
|
if tl.status != "Submitted":
|
||||||
|
webnotes.msgprint(_("Time Log must have status 'Submitted'") + \
|
||||||
|
" :" + tl.name + " (" + _(tl.status) + ")", raise_exception=True)
|
||||||
|
|
||||||
|
def set_status(self):
|
||||||
|
self.doc.status = {
|
||||||
|
"0": "Draft",
|
||||||
|
"1": "Submitted",
|
||||||
|
"2": "Cancelled"
|
||||||
|
}[str(self.doc.docstatus or 0)]
|
||||||
|
|
||||||
|
if self.doc.sales_invoice:
|
||||||
|
self.doc.status = "Billed"
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
# update time logs as batched
|
self.update_status(self.doc.name)
|
||||||
|
|
||||||
|
def before_cancel(self):
|
||||||
|
self.update_status(None)
|
||||||
|
|
||||||
|
def before_update_after_submit(self):
|
||||||
|
self.update_status(self.doc.name)
|
||||||
|
|
||||||
|
def update_status(self, time_log_batch):
|
||||||
|
self.set_status()
|
||||||
|
for d in self.doclist.get({"doctype":"Time Log Batch Detail"}):
|
||||||
|
tl = webnotes.bean("Time Log", d.time_log)
|
||||||
|
tl.doc.time_log_batch = time_log_batch
|
||||||
|
tl.doc.sales_invoice = self.doc.sales_invoice
|
||||||
|
tl.update_after_submit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
"creation": "2013-02-28 17:57:33",
|
"creation": "2013-02-28 17:57:33",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"modified": "2013-02-28 18:36:28",
|
"modified": "2013-03-01 17:54:57",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"owner": "Administrator"
|
"owner": "Administrator"
|
||||||
},
|
},
|
||||||
@ -24,6 +24,7 @@
|
|||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"doctype": "DocPerm",
|
"doctype": "DocPerm",
|
||||||
@ -57,19 +58,51 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Rate"
|
"label": "Rate"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"doctype": "DocField",
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Draft",
|
||||||
|
"doctype": "DocField",
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Status",
|
||||||
|
"options": "Draft\nSubmitted\nBilled\nCancelled",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Will be updated after Sales Invoice is Submitted.",
|
||||||
|
"doctype": "DocField",
|
||||||
|
"fieldname": "sales_invoice",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Sales Invoice",
|
||||||
|
"options": "Sales Invoice",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "DocField",
|
||||||
|
"fieldname": "section_break_5",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"doctype": "DocField",
|
"doctype": "DocField",
|
||||||
"fieldname": "time_log_batch_details",
|
"fieldname": "time_log_batch_details",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Time Log Batch Details",
|
"label": "Time Log Batch Details",
|
||||||
"options": "Time Log Batch Detail"
|
"options": "Time Log Batch Detail",
|
||||||
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "In Hours",
|
"description": "In Hours",
|
||||||
"doctype": "DocField",
|
"doctype": "DocField",
|
||||||
"fieldname": "total_time",
|
"fieldname": "total_hours",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Total Time",
|
"in_list_view": 1,
|
||||||
|
"label": "Total Hours",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
"creation": "2013-02-28 17:56:12",
|
"creation": "2013-02-28 17:56:12",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"modified": "2013-02-28 17:56:12",
|
"modified": "2013-03-01 15:20:17",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"owner": "Administrator"
|
"owner": "Administrator"
|
||||||
},
|
},
|
||||||
@ -47,5 +47,11 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Activity Type",
|
"label": "Activity Type",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "DocField",
|
||||||
|
"fieldname": "hours",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Hours"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -56,7 +56,18 @@ wn.module_page["Projects"] = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
{
|
||||||
|
title: wn._("Reports"),
|
||||||
|
right: true,
|
||||||
|
icon: "icon-list",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
"label":wn._("Time Log Summary"),
|
||||||
|
route: "Report2/Time Log/Time Log Summary",
|
||||||
|
doctype: "Time Log"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
|
||||||
pscript['onload_projects-home'] = function(wrapper) {
|
pscript['onload_projects-home'] = function(wrapper) {
|
||||||
wn.views.moduleview.make(wrapper, "Projects");
|
wn.views.moduleview.make(wrapper, "Projects");
|
||||||
|
|||||||
0
projects/report/__init__.py
Normal file
0
projects/report/__init__.py
Normal file
0
projects/report/time_log_summary/__init__.py
Normal file
0
projects/report/time_log_summary/__init__.py
Normal file
22
projects/report/time_log_summary/time_log_summary.txt
Normal file
22
projects/report/time_log_summary/time_log_summary.txt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"creation": "2013-03-01 17:36:35",
|
||||||
|
"docstatus": 0,
|
||||||
|
"modified": "2013-03-01 18:17:13",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"owner": "Administrator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "Report",
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"json": "{\"filters\":[],\"columns\":[[\"name\",\"Time Log\"],[\"status\",\"Time Log\"],[\"from_time\",\"Time Log\"],[\"hours\",\"Time Log\"],[\"activity_type\",\"Time Log\"],[\"owner\",\"Time Log\"],[\"billable\",\"Time Log\"],[\"time_log_batch\",\"Time Log\"],[\"sales_invoice\",\"Time Log\"]],\"sort_by\":\"Time Log.name\",\"sort_order\":\"desc\",\"sort_by_next\":\"\",\"sort_order_next\":\"desc\"}",
|
||||||
|
"name": "__common__",
|
||||||
|
"ref_doctype": "Time Log",
|
||||||
|
"report_name": "Time Log Summary",
|
||||||
|
"report_type": "Report Builder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "Report",
|
||||||
|
"name": "Time Log Summary"
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -162,6 +162,7 @@ def import_defaults():
|
|||||||
|
|
||||||
# UOM
|
# UOM
|
||||||
{'uom_name': 'Unit', 'doctype': 'UOM', 'name': 'Unit'},
|
{'uom_name': 'Unit', 'doctype': 'UOM', 'name': 'Unit'},
|
||||||
|
{'uom_name': 'Unit', 'doctype': 'UOM', 'name': 'Hour'},
|
||||||
{'uom_name': 'Box', 'doctype': 'UOM', 'name': 'Box'},
|
{'uom_name': 'Box', 'doctype': 'UOM', 'name': 'Box'},
|
||||||
{'uom_name': 'Ft', 'doctype': 'UOM', 'name': 'Ft'},
|
{'uom_name': 'Ft', 'doctype': 'UOM', 'name': 'Ft'},
|
||||||
{'uom_name': 'Kg', 'doctype': 'UOM', 'name': 'Kg'},
|
{'uom_name': 'Kg', 'doctype': 'UOM', 'name': 'Kg'},
|
||||||
|
|||||||
@ -19,5 +19,4 @@ observer_map = {
|
|||||||
"*:on_submit": "home.update_feed",
|
"*:on_submit": "home.update_feed",
|
||||||
"Stock Entry:on_submit": "stock.doctype.material_request.material_request.update_completed_qty",
|
"Stock Entry:on_submit": "stock.doctype.material_request.material_request.update_completed_qty",
|
||||||
"Stock Entry:on_cancel": "stock.doctype.material_request.material_request.update_completed_qty",
|
"Stock Entry:on_cancel": "stock.doctype.material_request.material_request.update_completed_qty",
|
||||||
# "*:on_update": "webnotes.widgets.moduleview.update_count"
|
|
||||||
}
|
}
|
||||||
@ -27,4 +27,6 @@ queries = {
|
|||||||
"Production Order": {"docstatus":0},
|
"Production Order": {"docstatus":0},
|
||||||
"BOM": {"docstatus":0},
|
"BOM": {"docstatus":0},
|
||||||
"Timesheet": {"docstatus":0},
|
"Timesheet": {"docstatus":0},
|
||||||
|
"Time Log": {"status":"Draft"},
|
||||||
|
"Time Log Batch": {"status":"Draft"},
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user