From ecb2031bbecea37c0aaf11d6769996772ad2b8ee Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 15 Jul 2016 17:09:29 +0530 Subject: [PATCH] [Fix] Timesheet workstation holiday issue --- .../production_order/production_order.py | 9 ++-- .../projects/doctype/timesheet/timesheet.py | 51 +++++++++++++------ 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index b949dfc7d0..cd24f9b5cc 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -7,6 +7,7 @@ import frappe import json from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate from frappe import _ +from frappe.utils import time_diff_in_seconds from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc from erpnext.manufacturing.doctype.bom.bom import validate_bom_no @@ -262,12 +263,14 @@ class ProductionOrder(Document): original_start_time = d.planned_start_time # validate operating hours if workstation [not mandatory] is specified - self.check_operation_fits_in_working_hours(d) try: timesheet.validate_time_logs() except OverlapError: if frappe.message_log: frappe.message_log.pop() - timesheet.move_to_next_non_overlapping_slot(d.idx) + timesheet.schedule_for_production_order(d.idx) + except WorkstationHolidayError: + if frappe.message_log: frappe.message_log.pop() + timesheet.schedule_for_production_order(d.idx) from_time, to_time = self.get_start_end_time(timesheet, d.name) @@ -294,7 +297,7 @@ class ProductionOrder(Document): def get_operations_data(self, data): return { 'from_time': data.planned_start_time, - 'hours': data.time_in_mins / 60, + 'hours': data.time_in_mins / 60.0, 'to_time': data.planned_end_time, 'project': self.project, 'operation': data.operation, diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 686f7c2f78..6881e0b80b 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -6,9 +6,12 @@ from __future__ import unicode_literals import frappe from frappe import _ +from datetime import timedelta from frappe.utils import flt, time_diff_in_hours, get_datetime, getdate, cint, get_datetime_str from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc +from erpnext.manufacturing.doctype.workstation.workstation import (check_if_within_operating_hours, + WorkstationHolidayError) from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations class OverlapError(frappe.ValidationError): pass @@ -136,8 +139,8 @@ class Timesheet(Document): def validate_time_logs(self): for data in self.get('time_logs'): - self.validate_overlap(data) self.check_workstation_timings(data) + self.validate_overlap(data) def validate_overlap(self, data): if self.production_order: @@ -179,29 +182,45 @@ class Timesheet(Document): def check_workstation_timings(self, args): """Checks if **Time Log** is between operating hours of the **Workstation**.""" if args.workstation and args.from_time and args.to_time: - from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours check_if_within_operating_hours(args.workstation, args.operation, args.from_time, args.to_time) - def move_to_next_non_overlapping_slot(self, index): - from datetime import timedelta - if self.time_logs: - for data in self.time_logs: - if data.idx == index: - overlapping = self.get_overlap_for("workstation", data, data.workstation) - if not overlapping: - frappe.throw(_("Logical error: Must find overlapping")) - - if overlapping: - time_sheet = self.get_last_working_slot(overlapping.name, data.workstation) - data.from_time = get_datetime(time_sheet.to_time) + get_mins_between_operations() - data.to_time = get_datetime(data.from_time) + timedelta(hours=data.hours) - break + def schedule_for_production_order(self, index): + for data in self.time_logs: + if data.idx == index: + self.move_to_next_day(data) #check for workstation holiday + self.move_to_next_non_overlapping_slot(data) #check for overlap + break + + def move_to_next_non_overlapping_slot(self, data): + overlapping = self.get_overlap_for("workstation", data, data.workstation) + if overlapping: + time_sheet = self.get_last_working_slot(overlapping.name, data.workstation) + data.from_time = get_datetime(time_sheet.to_time) + get_mins_between_operations() + data.to_time = self.get_to_time(data) + self.check_workstation_working_day(data) def get_last_working_slot(self, time_sheet, workstation): return frappe.db.sql(""" select max(from_time) as from_time, max(to_time) as to_time from `tabTimesheet Detail` where workstation = %(workstation)s""", {'workstation': workstation}, as_dict=True)[0] + def move_to_next_day(self, data): + """Move start and end time one day forward""" + self.check_workstation_working_day(data) + + def check_workstation_working_day(self, data): + while True: + try: + self.check_workstation_timings(data) + break + except WorkstationHolidayError: + if frappe.message_log: frappe.message_log.pop() + data.from_time = get_datetime(data.from_time) + timedelta(hours=24) + data.to_time = self.get_to_time(data) + + def get_to_time(self, data): + return get_datetime(data.from_time) + timedelta(hours=data.hours) + def update_cost(self): for data in self.time_logs: if data.activity_type and not data.billing_amount: