[fix] #14038-Std hours at company level to calculate timesheet hours (#15819)

* [fix] #14038

* codacy fixes

* add end time calc method

* test case and rename function

* Update timesheet.py
This commit is contained in:
Pawan Mehta 2018-11-12 16:40:07 +05:30 committed by Nabin Hait
parent 6714214951
commit aa0bed16f6
4 changed files with 169 additions and 57 deletions

View File

@ -128,6 +128,50 @@ class TestTimesheet(unittest.TestCase):
settings.ignore_employee_time_overlap = initial_setting
settings.save()
def test_timesheet_std_working_hours(self):
company = frappe.get_doc('Company', "_Test Company")
company.standard_working_hours = 8
company.save()
timesheet = frappe.new_doc("Timesheet")
timesheet.employee = "_T-Employee-00001"
timesheet.company = '_Test Company'
timesheet.append(
'time_logs',
{
"activity_type": "_Test Activity Type",
"from_time": now_datetime(),
"to_time": now_datetime() + datetime.timedelta(days= 4)
}
)
timesheet.save()
ts = frappe.get_doc('Timesheet', timesheet.name)
self.assertEqual(ts.total_hours, 32)
ts.submit()
ts.cancel()
company = frappe.get_doc('Company', "_Test Company")
company.standard_working_hours = 0
company.save()
timesheet = frappe.new_doc("Timesheet")
timesheet.employee = "_T-Employee-00001"
timesheet.company = '_Test Company'
timesheet.append(
'time_logs',
{
"activity_type": "_Test Activity Type",
"from_time": now_datetime(),
"to_time": now_datetime() + datetime.timedelta(days= 4)
}
)
timesheet.save()
ts = frappe.get_doc('Timesheet', timesheet.name)
self.assertEqual(ts.total_hours, 96)
ts.submit()
ts.cancel()
def make_salary_structure_for_timesheet(employee):
salary_structure_name = "Timesheet Salary Structure Test"

View File

@ -90,6 +90,13 @@ frappe.ui.form.on("Timesheet", {
}
},
company: function(frm) {
frappe.db.get_value('Company', { 'company_name' : frm.doc.company }, 'standard_working_hours')
.then(({ message }) => {
(frappe.working_hours = message.standard_working_hours || 0);
});
},
make_invoice: function(frm) {
let dialog = new frappe.ui.Dialog({
title: __("Select Item (optional)"),
@ -142,11 +149,21 @@ frappe.ui.form.on("Timesheet Detail", {
to_time: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
var time_diff = (moment(child.to_time).diff(moment(child.from_time),"seconds")) / ( 60 * 60 * 24);
var std_working_hours = 0;
if(frm._setting_hours) return;
frappe.model.set_value(cdt, cdn, "hours", moment(child.to_time).diff(moment(child.from_time),
"seconds") / 3600);
var hours = moment(child.to_time).diff(moment(child.from_time), "seconds") / 3600;
std_working_hours = time_diff * frappe.working_hours;
if (std_working_hours < hours && std_working_hours > 0) {
frappe.model.set_value(cdt, cdn, "hours", std_working_hours);
} else {
frappe.model.set_value(cdt, cdn, "hours", hours);
}
},
time_logs_add: function(frm) {
var $trigger_again = $('.form-grid').find('.grid-row').find('.btn-open-row');
$trigger_again.on('click', () => {
@ -209,17 +226,23 @@ var calculate_end_time = function(frm, cdt, cdn) {
let d = moment(child.from_time);
if(child.hours) {
d.add(child.hours, "hours");
frm._setting_hours = true;
frappe.model.set_value(cdt, cdn, "to_time",
d.format(frappe.defaultDatetimeFormat)).then(() => {
frm._setting_hours = false;
});
}
var time_diff = (moment(child.to_time).diff(moment(child.from_time),"seconds")) / (60 * 60 * 24);
var std_working_hours = 0;
var hours = moment(child.to_time).diff(moment(child.from_time), "seconds") / 3600;
std_working_hours = time_diff * frappe.working_hours;
if((frm.doc.__islocal || frm.doc.__onload.maintain_bill_work_hours_same) && child.hours){
frappe.model.set_value(cdt, cdn, "billing_hours", child.hours);
if (std_working_hours < hours && std_working_hours > 0) {
frappe.model.set_value(cdt, cdn, "hours", std_working_hours);
frappe.model.set_value(cdt, cdn, "to_time", d.add(hours, "hours").format(frappe.defaultDatetimeFormat));
} else {
d.add(child.hours, "hours");
frm._setting_hours = true;
frappe.model.set_value(cdt, cdn, "to_time",
d.format(frappe.defaultDatetimeFormat)).then(() => {
frm._setting_hours = false;
});
}
}
}

View File

@ -9,7 +9,7 @@ from frappe import _
import json
from datetime import timedelta
from erpnext.controllers.queries import get_match_cond
from frappe.utils import flt, time_diff_in_hours, get_datetime, getdate, cint
from frappe.utils import flt, time_diff_in_hours, get_datetime, getdate, cint, date_diff, add_to_date
from frappe.model.document import Document
from erpnext.manufacturing.doctype.workstation.workstation import (check_if_within_operating_hours,
WorkstationHolidayError)
@ -27,6 +27,7 @@ class Timesheet(Document):
self.set_status()
self.validate_dates()
self.validate_time_logs()
self.calculate_std_hours()
self.update_cost()
self.calculate_total_amounts()
self.calculate_percentage_billed()
@ -93,6 +94,17 @@ class Timesheet(Document):
self.start_date = getdate(start_date)
self.end_date = getdate(end_date)
def calculate_std_hours(self):
std_working_hours = frappe.get_value("Company", self.company, 'standard_working_hours')
for time in self.time_logs:
if time.from_time and time.to_time:
if flt(std_working_hours) > 0:
time.hours = flt(std_working_hours) * date_diff(time.to_time, time.from_time)
else:
if not time.hours:
time.hours = time_diff_in_hours(time.to_time, time.from_time)
def before_cancel(self):
self.set_status()

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
@ -723,6 +724,38 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "standard_working_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Standard Working Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -837,7 +870,7 @@
"label": "Create Chart Of Accounts Based On",
"length": 0,
"no_copy": 0,
"options": "\nStandard Template\nExisting Company",
"options": "\nStandard Template\nExisting Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -871,7 +904,7 @@
"label": "Chart Of Accounts Template",
"length": 0,
"no_copy": 1,
"options": "",
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -1158,39 +1191,39 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "round_off_cost_center",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Round Off Cost Center",
"length": 0,
"no_copy": 0,
"options": "Cost Center",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"fieldname": "round_off_cost_center",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Round Off Cost Center",
"length": 0,
"no_copy": 0,
"options": "Cost Center",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "write_off_account",
"fieldtype": "Link",
"hidden": 0,
@ -1491,7 +1524,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.__islocal",
"fieldname": "default_deferred_expense_account",
"fieldname": "default_deferred_expense_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
@ -1500,7 +1533,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Deferred Expense Account",
"label": "Default Deferred Expense Account",
"length": 0,
"no_copy": 1,
"options": "Account",
@ -1525,7 +1558,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.__islocal",
"fieldname": "default_payroll_payable_account",
"fieldname": "default_payroll_payable_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
@ -1534,7 +1567,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Payroll Payable Account",
"label": "Default Payroll Payable Account",
"length": 0,
"no_copy": 1,
"options": "Account",
@ -1558,20 +1591,20 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.__islocal",
"fieldname": "default_expense_claim_payable_account",
"depends_on": "eval:!doc.__islocal",
"fieldname": "default_expense_claim_payable_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Expense Claim Payable Account",
"label": "Default Expense Claim Payable Account",
"length": 0,
"no_copy": 1,
"options": "Account",
"no_copy": 1,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -2870,8 +2903,8 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-09-13 10:00:47.915706",
"modified_by": "Administrator",
"modified": "2018-10-24 12:57:46.776452",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
"owner": "Administrator",