[Fixes] Time sheet fixes and changed timesheets to time_logs

This commit is contained in:
Rohit Waghchaure 2016-06-29 17:57:39 +05:30
parent e6920f6a58
commit 3fa0b51e5e
13 changed files with 100 additions and 116 deletions

View File

@ -237,7 +237,7 @@ class SalesInvoice(SellingController):
return {"print_format": pos.get("print_format") }
def update_time_sheet(self, sales_invoice):
for d in self.get("timesheets"):
for d in self.timesheets:
if d.time_sheet:
timesheet = frappe.get_doc("Time Sheet", d.time_sheet)
timesheet.sales_invoice = sales_invoice
@ -246,7 +246,7 @@ class SalesInvoice(SellingController):
timesheet.save()
def validate_time_sheets_are_submitted(self):
for data in self.get("timesheets"):
for data in self.timesheets:
if data.time_sheet:
status = frappe.db.get_value("Time Sheet", data.time_sheet, "status")
if status not in ['Submitted', 'Payslip']:

View File

@ -31,13 +31,10 @@ frappe.ui.form.on("Salary Slip", {
},
toggle_fields: function(frm) {
if(frm.doc.salary_slip_based_on_timesheet) {
hide_field(['fiscal_year', 'month', 'total_days_in_month', 'leave_without_pay', 'payment_days'])
unhide_field(['start_date', 'end_date', 'hourly_wages', 'timesheets'])
}else {
unhide_field(['fiscal_year', 'month', 'total_days_in_month', 'leave_without_pay', 'payment_days'])
hide_field(['start_date', 'end_date', 'hourly_wages', 'timesheets'])
}
frm.toggle_display(['start_date', 'end_date', 'hourly_wages', 'timesheets'],
cint(frm.doc.salary_slip_based_on_timesheet)==1);
frm.toggle_display(['fiscal_year', 'month', 'total_days_in_month', 'leave_without_pay', 'payment_days'],
cint(frm.doc.salary_slip_based_on_timesheet)==0);
}
})

View File

@ -83,7 +83,7 @@ class SalarySlip(TransactionBase):
struct = frappe.db.sql("""select name from `tabSalary Structure`
where employee=%s and is_active = 'Yes'
and (from_date <= %s or from_date <= %s)
and (to_date is null or to_date >= %s or to_date >= %s)""",
and (to_date is null or to_date >= %s or to_date >= %s) order by from_date desc limit 1""",
(self.employee, self.start_date, joining_date, self.end_date, relieving_date))
if not struct:

View File

@ -12,13 +12,27 @@ cur_frm.cscript.onload = function(doc, dt, dn){
}
cur_frm.cscript.refresh = function(doc, dt, dn){
if((!doc.__islocal) && (doc.is_active == 'Yes') && (doc.salary_slip_based_on_timesheet == 0)){
if((!doc.__islocal) && (doc.is_active == 'Yes') && cint(doc.salary_slip_based_on_timesheet == 0)){
cur_frm.add_custom_button(__('Salary Slip'),
cur_frm.cscript['Make Salary Slip'], __("Make"));
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
}
}
frappe.ui.form.on('Salary Structure', {
refresh: function(frm) {
frm.trigger("toggle_fields")
},
salary_slip_based_on_timesheet: function(frm) {
frm.trigger("toggle_fields")
},
toggle_fields: function(frm) {
frm.toggle_display('time_sheet_earning_detail', cint(frm.doc.salary_slip_based_on_timesheet)==1);
}
})
cur_frm.cscript['Make Salary Slip'] = function() {
frappe.model.open_mapped_doc({
method: "erpnext.hr.doctype.salary_structure.salary_structure.make_salary_slip",

View File

@ -353,7 +353,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval:!doc.__islocal",
"depends_on": "",
"fieldname": "salary_slip_based_on_timesheet",
"fieldtype": "Check",
"hidden": 0,
@ -379,14 +379,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval:doc.salary_slip_based_on_timesheet",
"fieldname": "section_break_15",
"fieldname": "time_sheet_earning_detail",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -779,7 +779,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-06-27 16:23:16.856237",
"modified": "2016-06-29 15:41:48.243771",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure",

View File

@ -160,7 +160,7 @@ class ProductionOrder(Document):
def before_submit(self):
self.set_required_items()
self.make_timesheets()
self.make_time_logs()
def before_cancel(self):
for data in self.operations:
@ -240,7 +240,7 @@ class ProductionOrder(Document):
return holidays[holiday_list]
def make_timesheets(self):
def make_time_logs(self):
"""Capacity Planning. Plan time logs based on earliest availablity of workstation after
Planned Start Date. Time logs will be created and remain in Draft mode and must be submitted
before manufacturing entry can be made."""
@ -267,7 +267,7 @@ class ProductionOrder(Document):
# validate operating hours if workstation [not mandatory] is specified
self.check_operation_fits_in_working_hours(d)
try:
time_sheet.validate_timesheets()
time_sheet.validate_time_logs()
except OverlapError:
time_sheet.move_to_next_non_overlapping_slot(d.idx)
@ -319,7 +319,7 @@ class ProductionOrder(Document):
frappe.throw(_("Capacity Planning Error"))
def get_start_end_time(self, time_sheet, operation_id):
for data in time_sheet.timesheets:
for data in time_sheet.time_logs:
if data.operation_id == operation_id:
return data.from_time, data.to_time
@ -518,7 +518,7 @@ def add_timesheet_detail(time_sheet, args):
if isinstance(args, unicode):
args = json.loads(args)
time_sheet.append('timesheets', args)
time_sheet.append('time_logs', args)
return time_sheet
@frappe.whitelist()

View File

@ -89,7 +89,7 @@ class TestProductionOrder(unittest.TestCase):
self.assertEqual(prod_order.name, time_sheet_doc.production_order)
self.assertEqual((prod_order.qty - d.completed_qty), sum([d.completed_qty for d in time_sheet_doc.timesheets]))
self.assertEqual((prod_order.qty - d.completed_qty), sum([d.completed_qty for d in time_sheet_doc.time_logs]))
manufacturing_settings = frappe.get_doc({
"doctype": "Manufacturing Settings",
@ -106,7 +106,7 @@ class TestProductionOrder(unittest.TestCase):
self.assertEqual(prod_order.operations[0].actual_operating_cost, 100)
time_sheet_doc1 = make_timesheet(prod_order.name)
self.assertEqual(len(time_sheet_doc1.get('timesheets')), 0)
self.assertEqual(len(time_sheet_doc1.get('time_logs')), 0)
time_sheet_doc.cancel()

View File

@ -9,12 +9,13 @@ def execute():
time_sheet.employee= ""
time_sheet.company = frappe.db.get_single_value('Global Defaults', 'default_company')
time_sheet.sales_invoice = tlb.sales_invoice
for data in tlb.time_logs:
args = get_timesheet_data(data)
add_timesheet_detail(time_sheet, args)
if tlb.get('time_logs'):
for data in tlb.time_logs:
args = get_timesheet_data(data)
add_timesheet_detail(time_sheet, args)
time_sheet.docstatus = tlb.docstatus
time_sheet.save(ignore_permissions=True)
time_sheet.docstatus = tlb.docstatus
time_sheet.save(ignore_permissions=True)
def get_timesheet_data(data):
time_log = frappe.get_doc('Time Log', data.time_log)

View File

@ -16,8 +16,8 @@ class TestTimeSheet(unittest.TestCase):
time_sheet = make_time_sheet("_T-Employee-0001", True)
self.assertEquals(time_sheet.total_hours, 2)
self.assertEquals(time_sheet.timesheets[0].billing_rate, 50)
self.assertEquals(time_sheet.timesheets[0].billing_amount, 100)
self.assertEquals(time_sheet.time_logs[0].billing_rate, 50)
self.assertEquals(time_sheet.time_logs[0].billing_amount, 100)
def test_salary_slip_from_timesheet(self):
salary_structure = make_salary_structure("_T-Employee-0001")
@ -58,9 +58,10 @@ class TestTimeSheet(unittest.TestCase):
def make_salary_structure(employee):
name = frappe.db.get_value('Salary Structure', {'employee': employee, 'salary_slip_based_on_timesheet': 1}, 'name')
if name:
frappe.delete_doc('Salary Structure', name)
salary_structure = frappe.get_doc('Salary Structure', name)
else:
salary_structure = frappe.new_doc("Salary Structure")
salary_structure = frappe.new_doc("Salary Structure")
salary_structure.salary_slip_based_on_timesheet = 1
salary_structure.employee = employee
salary_structure.from_date = nowdate()
@ -89,14 +90,14 @@ def make_time_sheet(employee, simulate=False, billable = 0):
update_activity_type("_Test Activity Type")
time_sheet = frappe.new_doc("Time Sheet")
time_sheet.employee = employee
time_sheet_detail = time_sheet.append('timesheets', {})
time_sheet_detail = time_sheet.append('time_logs', {})
time_sheet_detail.billable = billable
time_sheet_detail.activity_type = "_Test Activity Type"
time_sheet_detail.from_time = now_datetime()
time_sheet_detail.hours = 2
time_sheet_detail.to_time = time_sheet_detail.from_time + datetime.timedelta(hours= time_sheet_detail.hours)
for data in time_sheet.get('timesheets'):
for data in time_sheet.get('time_logs'):
if simulate:
while True:
try:

View File

@ -1,11 +1,9 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
cur_frm.add_fetch("activity_type", "billing_rate", "billing_rate");
frappe.ui.form.on("Time Sheet", {
setup: function(frm) {
frm.get_field('timesheets').grid.editable_fields = [
frm.get_field('time_logs').grid.editable_fields = [
{fieldname: 'activity_type', columns: 2},
{fieldname: 'from_time', columns: 2},
{fieldname: 'hours', columns: 2},
@ -18,7 +16,7 @@ frappe.ui.form.on("Time Sheet", {
}
}
frm.fields_dict['timesheets'].grid.get_field('task').get_query = function(frm, cdt, cdn) {
frm.fields_dict['time_logs'].grid.get_field('task').get_query = function(frm, cdt, cdn) {
child = locals[cdt][cdn];
return{
filters: {
@ -29,7 +27,7 @@ frappe.ui.form.on("Time Sheet", {
},
onload: function(frm){
if (frm.doc.__islocal && frm.doc.timesheets) {
if (frm.doc.__islocal && frm.doc.time_logs) {
frm.set_value("employee", "")
calculate_time_and_amount(frm);
}
@ -66,7 +64,7 @@ frappe.ui.form.on("Time Sheet", {
})
frappe.ui.form.on("Time Sheet Detail", {
timesheets_remove: function(frm) {
time_logs_remove: function(frm) {
calculate_time_and_amount(frm);
},
@ -94,54 +92,64 @@ frappe.ui.form.on("Time Sheet Detail", {
},
billing_rate: function(frm, cdt, cdn) {
var child = locals[cdt][cdn]
if(child.hours && child.billing_rate){
frappe.mode.set_value(cdt, cdn, 'total_billing_amount', flt(child.billing_rate * child.hours))
}
calculate_billing_amount(frm, cdt, cdn)
calculate_billing_costing_amount(frm, cdt, cdn)
},
costing_rate: function(frm, cdt, cdn) {
var child = locals[cdt][cdn]
frappe.mode.set_value(cdt, cdn, 'total_costing_amount', flt(child.costing_rate * child.hours))
calculate_billing_amount(frm, cdt, cdn)
calculate_billing_costing_amount(frm, cdt, cdn)
},
billable: function(frm, cdt, cdn) {
calculate_billing_amount(frm, cdt, cdn)
calculate_billing_costing_amount(frm, cdt, cdn)
},
activity_type: function(frm, cdt, cdn) {
child = locals[cdt][cdn];
frappe.call({
method: "erpnext.projects.doctype.time_sheet.time_sheet.get_activity_cost",
args: {
employee: frm.doc.employee,
activity_type: child.activity_type
},
callback: function(r){
if(r.message){
frappe.model.set_value(cdt, cdn, 'billing_rate', r.message['billing_rate']);
frappe.model.set_value(cdt, cdn, 'costing_rate', r.message['costing_rate']);
calculate_billing_costing_amount(frm, cdt, cdn)
}
}
})
}
});
calculate_end_time = function(frm, cdt, cdn){
var child = locals[cdt][cdn];
if(!child.from_time) {
frappe.model.set_value(cdt, cdn, "from_time", frappe.datetime.now_datetime());
}
var d = moment(child.from_time);
d.add(child.hours, "hours");
frm._setting_hours = true;
frappe.model.set_value(cdt, cdn, "to_time", d.format(moment.defaultDatetimeFormat));
frm._setting_hours = false;
calculate_billing_amount(frm, cdt, cdn)
calculate_billing_costing_amount(frm, cdt, cdn)
}
var calculate_billing_amount = function(frm, cdt, cdn){
var calculate_billing_costing_amount = function(frm, cdt, cdn){
child = locals[cdt][cdn]
billing_amount = 0.0
billing_amount = costing_amount = 0.0;
if(child.hours && child.billable){
billing_amount = (child.hours * child.billing_rate)
billing_amount = (child.hours * child.billing_rate);
costing_amount = flt(child.costing_rate * child.hours);
}
frappe.model.set_value(cdt, cdn, 'billing_amount', billing_amount)
frappe.model.set_value(cdt, cdn, 'billing_amount', billing_amount);
frappe.model.set_value(cdt, cdn, 'costing_amount', costing_amount);
calculate_time_and_amount(frm)
}
var calculate_time_and_amount = function(frm) {
var tl = frm.doc.timesheets || [];
var tl = frm.doc.time_logs || [];
total_hr = 0;
total_billing_amount = 0;
for(var i=0; i<tl.length; i++) {

View File

@ -426,7 +426,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "timesheets",
"fieldname": "time_logs",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
@ -586,7 +586,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-06-27 01:45:26.226915",
"modified": "2016-06-29 15:16:31.668367",
"modified_by": "Administrator",
"module": "Projects",
"name": "Time Sheet",
@ -611,46 +611,6 @@
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 1,
"apply_user_permissions": 0,
"cancel": 1,
"create": 0,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Projects Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Manufacturing User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
}
],
"quick_entry": 0,

View File

@ -22,9 +22,9 @@ class TimeSheet(Document):
self.total_hours = 0.0
self.total_billing_amount = 0.0
self.validate_dates()
self.validate_timesheets()
self.validate_time_logs()
self.update_cost()
for d in self.get("timesheets"):
for d in self.get("time_logs"):
self.total_hours += flt(d.hours)
if d.billable: self.total_billing_amount += flt(d.billing_amount)
@ -46,8 +46,8 @@ class TimeSheet(Document):
def set_dates(self):
if self.docstatus < 2:
start_date = min([d.from_time for d in self.timesheets])
end_date = max([d.to_time for d in self.timesheets])
start_date = min([d.from_time for d in self.time_logs])
end_date = max([d.to_time for d in self.time_logs])
if start_date and end_date:
self.start_date = getdate(start_date)
@ -73,7 +73,7 @@ class TimeSheet(Document):
production_order = frappe.get_doc("Production Order", self.production_order)
pending_qty = flt(production_order.qty) - flt(production_order.produced_qty)
for data in self.timesheets:
for data in self.time_logs:
if not data.from_time and not data.to_time:
frappe.throw(_("Row {0}: From Time and To Time is mandatory.").format(data.idx))
@ -94,7 +94,7 @@ class TimeSheet(Document):
if self.production_order:
pro = frappe.get_doc('Production Order', self.production_order)
for timesheet in self.timesheets:
for timesheet in self.time_logs:
for data in pro.operations:
if data.name == timesheet.operation_id:
summary = self.get_actual_timesheet_summary(timesheet.operation_id)
@ -119,7 +119,7 @@ class TimeSheet(Document):
(self.production_order, operation_id), as_dict=1)[0]
def update_task_and_project(self):
for data in self.timesheets:
for data in self.time_logs:
if data.task:
task = frappe.get_doc("Task", data.task)
task.update_time_and_costing()
@ -129,12 +129,12 @@ class TimeSheet(Document):
frappe.get_doc("Project", data.project).update_project()
def validate_dates(self):
for data in self.timesheets:
for data in self.time_logs:
if time_diff_in_hours(data.to_time, data.from_time) < 0:
frappe.throw(_("To date cannot be before from date"))
def validate_timesheets(self):
for data in self.get('timesheets'):
def validate_time_logs(self):
for data in self.get('time_logs'):
self.validate_overlap(data)
self.check_workstation_timings(data)
@ -180,8 +180,8 @@ class TimeSheet(Document):
def move_to_next_non_overlapping_slot(self, index):
from datetime import timedelta
if self.timesheets:
for data in self.timesheets:
if self.time_logs:
for data in self.time_logs:
if data.idx == index:
overlapping = self.get_overlap_for("workstation", data)
if not overlapping:
@ -199,7 +199,7 @@ class TimeSheet(Document):
{'workstation': workstation}, as_dict=True)[0]
def update_cost(self):
for data in self.timesheets:
for data in self.time_logs:
if data.activity_type and not data.billing_amount:
rate = get_activity_cost(self.employee, data.activity_type)
hours = data.hours or 0
@ -253,7 +253,7 @@ def set_missing_values(time_sheet, target):
target.salary_slip_based_on_timesheet = 1
target.start_date = doc.start_date
target.end_date = doc.end_date
@frappe.whitelist()
def get_activity_cost(employee=None, activity_type=None):
rate = frappe.db.get_values("Activity Cost", {"employee": employee,
@ -263,7 +263,7 @@ def get_activity_cost(employee=None, activity_type=None):
["costing_rate", "billing_rate"], as_dict=True)
return rate[0] if rate else {}
@frappe.whitelist()
def get_employee_list(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select distinct(employee) as employee

View File

@ -295,6 +295,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval:parent.production_order",
"fieldname": "completed_qty",
"fieldtype": "Float",
"hidden": 0,
@ -421,6 +422,7 @@
"collapsible": 0,
"default": "0",
"depends_on": "",
"description": "To display value check Billable",
"fieldname": "billing_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -447,6 +449,7 @@
"bold": 0,
"collapsible": 0,
"default": "0",
"description": "To display value check Billable",
"fieldname": "costing_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -479,7 +482,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-06-24 15:55:30.746870",
"modified": "2016-06-29 17:18:37.442965",
"modified_by": "Administrator",
"module": "Projects",
"name": "Time Sheet Detail",