diff --git a/erpnext/config/manufacturing.py b/erpnext/config/manufacturing.py index a1644a2da9..43b46381a5 100644 --- a/erpnext/config/manufacturing.py +++ b/erpnext/config/manufacturing.py @@ -27,6 +27,11 @@ def get_data(): "name": "Workstation", "description": _("Where manufacturing operations are carried out."), }, + { + "type": "doctype", + "name": "Operation", + "description": _("Details of the operations carried out."), + }, ] }, diff --git a/erpnext/hr/doctype/holiday_list/test_records.json b/erpnext/hr/doctype/holiday_list/test_records.json index 9ef8c8efde..1c4abe7862 100644 --- a/erpnext/hr/doctype/holiday_list/test_records.json +++ b/erpnext/hr/doctype/holiday_list/test_records.json @@ -5,11 +5,11 @@ "holiday_list_details": [ { "description": "New Year", - "doctype": "Holiday", - "holiday_date": "2013-01-01", - "parent": "_Test Holiday List", - "parentfield": "holiday_list_details", - "parenttype": "Holiday List" + "holiday_date": "2013-01-01" + }, + { + "description": "Test Holiday", + "holiday_date": "2013-02-01" } ], "holiday_list_name": "_Test Holiday List", diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 34598b662b..5c099c43e3 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -12,7 +12,7 @@ cur_frm.cscript.refresh = function(doc,dt,dn){ } cur_frm.cscript.with_operations(doc); - erpnext.bom.set_operation_no(doc); + erpnext.bom.set_operation(doc); } cur_frm.cscript.update_cost = function() { @@ -26,62 +26,41 @@ cur_frm.cscript.update_cost = function() { } cur_frm.cscript.with_operations = function(doc) { - cur_frm.fields_dict["bom_materials"].grid.set_column_disp("operation_no", doc.with_operations); - cur_frm.fields_dict["bom_materials"].grid.toggle_reqd("operation_no", doc.with_operations); + cur_frm.fields_dict["bom_materials"].grid.set_column_disp("operation", doc.with_operations); + cur_frm.fields_dict["bom_materials"].grid.toggle_reqd("operation", doc.with_operations); } -cur_frm.cscript.operation_no = function(doc, cdt, cdn) { - var child = locals[cdt][cdn]; - if(child.parentfield=="bom_operations") erpnext.bom.set_operation_no(doc); -} - -erpnext.bom.set_operation_no = function(doc) { +erpnext.bom.set_operation = function(doc) { var op_table = doc.bom_operations || []; var operations = []; for (var i=0, j=op_table.length; i end_time )""",(self.workstation_name, start_time, end_time), as_dict=1): + return 1 + + def check_workstation_for_holiday(self, from_time, to_time): + holiday_list = frappe.db.get_value("Workstation", self.workstation_name, "holiday_list") + start_date = datetime.datetime.strptime(from_time,'%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d') + end_date = datetime.datetime.strptime(to_time,'%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d') + msg = _("Workstation is closed on the following dates as per Holiday List:") + flag = 0 + for d in frappe.db.sql("""select holiday_date from `tabHoliday` where parent = %s and holiday_date between + %s and %s """,(holiday_list, start_date, end_date), as_dict=1): + flag = 1 + msg = msg + "\n" + d.holiday_date + + if flag ==1: + return msg + else: + return None \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/workstation_operation_hours/__init__.py b/erpnext/manufacturing/doctype/workstation_operation_hours/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/manufacturing/doctype/workstation_operation_hours/workstation_operation_hours.json b/erpnext/manufacturing/doctype/workstation_operation_hours/workstation_operation_hours.json new file mode 100644 index 0000000000..8a064ca40f --- /dev/null +++ b/erpnext/manufacturing/doctype/workstation_operation_hours/workstation_operation_hours.json @@ -0,0 +1,76 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "creation": "2014-10-29 13:00:43.921508", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "allow_on_submit": 0, + "fieldname": "start_time", + "fieldtype": "Time", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Start Time", + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "fieldname": "section_break_2", + "fieldtype": "Column Break", + "permlevel": 0, + "precision": "" + }, + { + "allow_on_submit": 0, + "fieldname": "end_time", + "fieldtype": "Time", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "End Time", + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "modified": "2014-10-29 13:02:24.631554", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Workstation Operation Hours", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/workstation_operation_hours/workstation_operation_hours.py b/erpnext/manufacturing/doctype/workstation_operation_hours/workstation_operation_hours.py new file mode 100644 index 0000000000..dfac1f8c42 --- /dev/null +++ b/erpnext/manufacturing/doctype/workstation_operation_hours/workstation_operation_hours.py @@ -0,0 +1,9 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class WorkstationOperationHours(Document): + pass diff --git a/erpnext/projects/doctype/time_log/test_time_log.py b/erpnext/projects/doctype/time_log/test_time_log.py index 4a312adb41..bc0a9dc24b 100644 --- a/erpnext/projects/doctype/time_log/test_time_log.py +++ b/erpnext/projects/doctype/time_log/test_time_log.py @@ -16,6 +16,6 @@ class TestTimeLog(unittest.TestCase): self.assertRaises(OverlapError, ts.insert) frappe.db.sql("delete from `tabTime Log`") - + test_records = frappe.get_test_records('Time Log') test_ignore = ["Time Log Batch", "Sales Invoice"] diff --git a/erpnext/projects/doctype/time_log/time_log.js b/erpnext/projects/doctype/time_log/time_log.js index d4d109d6f9..36ca2d2ba8 100644 --- a/erpnext/projects/doctype/time_log/time_log.js +++ b/erpnext/projects/doctype/time_log/time_log.js @@ -27,3 +27,36 @@ frappe.ui.form.on("Time Log", "to_time", function(frm) { }); cur_frm.add_fetch('task','project','project'); + +$.extend(cur_frm.cscript, { + production_order: function(doc) { + if (doc.production_order){ + var operations = []; + frappe.model.with_doc("Production Order", doc.production_order, function(pro) { + doc = frappe.get_doc("Production Order",pro); + $.each(doc.production_order_operations , function(i, row){ + operations[i] = (i+1) +". "+ row.operation; + }); + frappe.meta.get_docfield("Time Log", "operation", me.frm.doc.name).options = operations.join("\n"); + refresh_field("operation"); + }) + } + }, + + operation: function(doc) { + return cur_frm.call({ + method: "erpnext.projects.doctype.time_log.time_log.get_workstation", + args: { + "production_order": doc.production_order, + "operation": doc.operation + }, + callback: function(r) { + doc.workstation = r.workstation; + } + }); + } +}); + +if (cur_frm.doc.time_log_for == "Manufacturing") { + cur_frm.cscript.onload = cur_frm.cscript.production_order; +} \ No newline at end of file diff --git a/erpnext/projects/doctype/time_log/time_log.json b/erpnext/projects/doctype/time_log/time_log.json index 0eed1fc9af..6e2706de2c 100644 --- a/erpnext/projects/doctype/time_log/time_log.json +++ b/erpnext/projects/doctype/time_log/time_log.json @@ -16,6 +16,15 @@ "read_only": 0, "reqd": 1 }, + { + "fieldname": "time_log_for", + "fieldtype": "Select", + "label": "Time Log For", + "options": "Project\nManufacturing", + "permlevel": 0, + "precision": "", + "reqd": 1 + }, { "fieldname": "from_time", "fieldtype": "Datetime", @@ -59,6 +68,7 @@ "reqd": 0 }, { + "depends_on": "eval:doc.time_log_for == 'Project'", "fieldname": "activity_type", "fieldtype": "Link", "in_list_view": 1, @@ -66,9 +76,10 @@ "options": "Activity Type", "permlevel": 0, "read_only": 0, - "reqd": 1 + "reqd": 0 }, { + "depends_on": "eval:doc.time_log_for == 'Project'", "fieldname": "task", "fieldtype": "Link", "label": "Task", @@ -76,6 +87,41 @@ "permlevel": 0, "read_only": 0 }, + { + "depends_on": "eval:doc.time_log_for == 'Manufacturing'", + "fieldname": "production_order", + "fieldtype": "Link", + "label": "Production Order", + "options": "Production Order", + "permlevel": 0, + "precision": "" + }, + { + "depends_on": "eval:doc.time_log_for == 'Manufacturing'", + "fieldname": "operation", + "fieldtype": "Select", + "label": "Operation", + "options": "", + "permlevel": 0, + "precision": "" + }, + { + "depends_on": "eval:doc.time_log_for == 'Manufacturing'", + "fieldname": "workstation", + "fieldtype": "Link", + "label": "Workstation", + "options": "Workstation", + "permlevel": 0, + "precision": "" + }, + { + "depends_on": "eval:doc.time_log_for == 'Manufacturing'", + "fieldname": "qty", + "fieldtype": "Float", + "label": "Quantity", + "permlevel": 0, + "precision": "" + }, { "fieldname": "billable", "fieldtype": "Check", @@ -151,7 +197,7 @@ "icon": "icon-time", "idx": 1, "is_submittable": 1, - "modified": "2014-10-22 16:53:26.993828", + "modified": "2014-11-19 11:39:02.633802", "modified_by": "Administrator", "module": "Projects", "name": "Time Log", diff --git a/erpnext/projects/doctype/time_log/time_log.py b/erpnext/projects/doctype/time_log/time_log.py index 6678392037..84430e9e0d 100644 --- a/erpnext/projects/doctype/time_log/time_log.py +++ b/erpnext/projects/doctype/time_log/time_log.py @@ -6,7 +6,8 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import cstr, comma_and +from frappe.utils import cstr, cint, comma_and + class OverlapError(frappe.ValidationError): pass @@ -19,6 +20,14 @@ class TimeLog(Document): self.set_status() self.validate_overlap() self.calculate_total_hours() + self.check_workstation_timings() + self.validate_qty() + + def on_submit(self): + self.update_production_order() + + def on_cancel(self): + self.update_production_order_on_cancel() def calculate_total_hours(self): from frappe.utils import time_diff_in_hours @@ -59,6 +68,72 @@ class TimeLog(Document): def before_update_after_submit(self): self.set_status() + def update_production_order(self): + if self.time_log_for=="Manufacturing" and self.operation: + d = self.get_qty_and_status() + required_qty = cint(frappe.db.get_value("Production Order" , self.production_order, "qty")) + if d.get('qty') == required_qty: + d['status'] = "Completed" + + dates = self.get_production_dates() + if self.from_time < dates.start_date: + dates.start_date = self.from_time + if self.to_time > dates.end_date: + dates.end_date = self.to_time + + self.production_order_update(dates, d.get('qty'), d['status']) + + def update_production_order_on_cancel(self): + if self.time_log_for=="Manufacturing" and self.operation: + d = frappe._dict() + d = self.get_qty_and_status() + dates = self.get_production_dates() + self.production_order_update(dates, d.get('qty'), d.get('status')) + + def get_qty_and_status(self): + status = "Work in Progress" + qty = cint(frappe.db.sql("""select sum(qty) as qty from `tabTime Log` where production_order = %s + and operation = %s and docstatus=1""", (self.production_order, self.operation),as_dict=1)[0].qty) + if qty == 0: + status = "Pending" + return { + "qty": qty, + "status": status + } + + def get_production_dates(self): + return frappe.db.sql("""select min(from_time) as start_date, max(to_time) as end_date from `tabTime Log` + where production_order = %s and operation = %s and docstatus=1""", + (self.production_order, self.operation), as_dict=1)[0] + + def production_order_update(self, dates, qty, status): + d = self.operation.split('. ',1) + frappe.db.sql("""update `tabProduction Order Operation` set actual_start_time = %s, actual_end_time = %s, + qty_completed = %s, status = %s where idx=%s and parent=%s and operation = %s """, + (dates.start_date, dates.end_date, qty, status, d[0], self.production_order, d[1] )) + + def check_workstation_timings(self): + if self.workstation: + frappe.get_doc("Workstation", self.workstation).check_if_within_operating_hours(self.from_time, self.to_time) + + def validate_qty(self): + if self.qty == None: + self.qty=0 + required_qty = cint(frappe.db.get_value("Production Order" , self.production_order, "qty")) + completed_qty = self.get_qty_and_status().get('qty') + if (completed_qty + cint(self.qty)) > required_qty: + frappe.throw(_("Quantity cannot be greater than pending quantity that is {0}").format(required_qty)) + +@frappe.whitelist() +def get_workstation(production_order, operation): + if operation: + d = operation.split('. ',1) + idx = d[0] + operation = d[1] + + return frappe.db.sql("""select workstation from `tabProduction Order Operation` where idx=%s and + parent=%s and operation = %s""", (idx, production_order, operation), as_dict=1)[0] + @frappe.whitelist() def get_events(start, end): from frappe.desk.reportview import build_match_conditions diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 5868fd4cf8..308eb744fa 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -82,10 +82,10 @@ class Company(Document): def create_default_accounts(self): if not self.chart_of_accounts: - frappe.throw(_("Please select Chart of Accounts")) - else: - from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts - create_charts(self.chart_of_accounts, self.name) + self.chart_of_accounts = "Standard" + + from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts + create_charts(self.chart_of_accounts, self.name) frappe.db.set(self, "default_receivable_account", frappe.db.get_value("Account", {"company": self.name, "account_type": "Receivable"}))