Operations added in Production Order DocType Auto-Fetch added to operations in Production Order DocType made operations in production order read-only capacity & duration of cycle added to workstation workstation operation timings added operation master added, Time log added Time Log Fixed Test Records added to Production Order Test Recorde for over Production Time Log added Validation added to check if time log timings are within working hours of the workstation. Validation added to check if workstation is closed on time log dates. Test Case added for Time Log

This commit is contained in:
Neil Trini Lasrado 2014-10-16 15:05:30 +05:30 committed by Nabin Hait
parent 0a726e2335
commit 637d804abd
32 changed files with 1287 additions and 399 deletions

View File

@ -27,6 +27,11 @@ def get_data():
"name": "Workstation", "name": "Workstation",
"description": _("Where manufacturing operations are carried out."), "description": _("Where manufacturing operations are carried out."),
}, },
{
"type": "doctype",
"name": "Operation",
"description": _("Details of the operations carried out."),
},
] ]
}, },

View File

@ -5,11 +5,11 @@
"holiday_list_details": [ "holiday_list_details": [
{ {
"description": "New Year", "description": "New Year",
"doctype": "Holiday", "holiday_date": "2013-01-01"
"holiday_date": "2013-01-01", },
"parent": "_Test Holiday List", {
"parentfield": "holiday_list_details", "description": "Test Holiday",
"parenttype": "Holiday List" "holiday_date": "2013-02-01"
} }
], ],
"holiday_list_name": "_Test Holiday List", "holiday_list_name": "_Test Holiday List",

View File

@ -12,7 +12,7 @@ cur_frm.cscript.refresh = function(doc,dt,dn){
} }
cur_frm.cscript.with_operations(doc); cur_frm.cscript.with_operations(doc);
erpnext.bom.set_operation_no(doc); erpnext.bom.set_operation(doc);
} }
cur_frm.cscript.update_cost = function() { 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.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.set_column_disp("operation", doc.with_operations);
cur_frm.fields_dict["bom_materials"].grid.toggle_reqd("operation_no", 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) { erpnext.bom.set_operation = function(doc) {
var child = locals[cdt][cdn];
if(child.parentfield=="bom_operations") erpnext.bom.set_operation_no(doc);
}
erpnext.bom.set_operation_no = function(doc) {
var op_table = doc.bom_operations || []; var op_table = doc.bom_operations || [];
var operations = []; var operations = [];
for (var i=0, j=op_table.length; i<j; i++) { for (var i=0, j=op_table.length; i<j; i++) {
var op = op_table[i].operation_no; operations[i] = (i+1);
if (op && !inList(operations, op)) operations.push(op);
} }
frappe.meta.get_docfield("BOM Item", "operation_no", frappe.meta.get_docfield("BOM Item", "operation",
cur_frm.docname).options = operations.join("\n"); cur_frm.docname).options = operations.join("\n");
$.each(doc.bom_materials || [], function(i, v) {
if(!inList(operations, cstr(v.operation_no))) v.operation_no = null;
});
refresh_field("bom_materials"); refresh_field("bom_materials");
} }
cur_frm.cscript.bom_operations_remove = function(){ cur_frm.cscript.bom_operations_remove = function(){
erpnext.bom.set_operation_no(doc); erpnext.bom.set_operation(doc);
} }
cur_frm.add_fetch("item", "description", "description"); cur_frm.add_fetch("item", "description", "description");
cur_frm.add_fetch("item", "stock_uom", "uom"); cur_frm.add_fetch("item", "stock_uom", "uom");
cur_frm.cscript.workstation = function(doc,dt,dn) { cur_frm.cscript.workstation = function(doc,dt,dn) {
var d = locals[dt][dn]; get_workstation_detail(doc,dt,dn);
frappe.model.with_doc("Workstation", d.workstation, function(name, r) {
d.hour_rate = r.docs[0].hour_rate;
refresh_field("hour_rate", dn, "bom_operations");
d.fixed_cycle_cost = r.docs[0].fixed_cycle_cost;
refresh_field("fixed_cycle_cost", dn, "bom_operations");
erpnext.bom.calculate_op_cost(doc);
erpnext.bom.calculate_fixed_cost(doc);
erpnext.bom.calculate_total(doc);
});
} }
cur_frm.cscript.hour_rate = function(doc, dt, dn) { cur_frm.cscript.hour_rate = function(doc, dt, dn) {
erpnext.bom.calculate_op_cost(doc); erpnext.bom.calculate_op_cost(doc);
erpnext.bom.calculate_fixed_cost(doc); erpnext.bom.calculate_fixed_cost(doc);
erpnext.bom.calculate_total(doc); erpnext.bom.calculate_total(doc);
} }
cur_frm.cscript.time_in_mins = cur_frm.cscript.hour_rate; cur_frm.cscript.time_in_mins = cur_frm.cscript.hour_rate;
cur_frm.cscript.item_code = function(doc, cdt, cdn) { cur_frm.cscript.item_code = function(doc, cdt, cdn) {
@ -218,3 +197,29 @@ cur_frm.cscript.validate = function(doc, dt, dn) {
erpnext.bom.calculate_total(doc); erpnext.bom.calculate_total(doc);
} }
cur_frm.cscript.operation = function(doc,dt,dn) {
var d = locals[dt][dn];
if(d.parentfield=="bom_operations") {
erpnext.bom.set_operation(doc);
frappe.model.with_doc("Operation", d.operation, function(name, r) {
d.opn_description = r.docs[0].opn_description;
d.workstation = r.docs[0].workstation;
refresh_field("bom_operations");
get_workstation_detail(doc,dt,dn);
});
}
}
var get_workstation_detail = function(doc, dt, dn) {
var d = locals[dt][dn];
frappe.model.with_doc("Workstation", d.workstation, function(name, r) {
d.hour_rate = r.docs[0].hour_rate;
refresh_field("hour_rate", dn, "bom_operations");
d.fixed_cycle_cost = r.docs[0].fixed_cycle_cost;
refresh_field("fixed_cycle_cost", dn, "bom_operations");
erpnext.bom.calculate_op_cost(doc);
erpnext.bom.calculate_fixed_cost(doc);
erpnext.bom.calculate_total(doc);
});
}

View File

@ -27,7 +27,6 @@ class BOM(Document):
from erpnext.utilities.transaction_base import validate_uom_is_integer from erpnext.utilities.transaction_base import validate_uom_is_integer
validate_uom_is_integer(self, "stock_uom", "qty") validate_uom_is_integer(self, "stock_uom", "qty")
self.validate_operations()
self.validate_materials() self.validate_materials()
self.set_bom_material_details() self.set_bom_material_details()
self.calculate_cost() self.calculate_cost()
@ -171,7 +170,7 @@ class BOM(Document):
if not self.with_operations: if not self.with_operations:
self.set('bom_operations', []) self.set('bom_operations', [])
for d in self.get("bom_materials"): for d in self.get("bom_materials"):
d.operation_no = None d.operation = None
def validate_main_item(self): def validate_main_item(self):
""" Validate main FG item""" """ Validate main FG item"""
@ -183,23 +182,10 @@ class BOM(Document):
self.description = ret[0] self.description = ret[0]
self.uom = ret[1] self.uom = ret[1]
def validate_operations(self):
""" Check duplicate operation no"""
self.op = []
for d in self.get('bom_operations'):
if cstr(d.operation_no) in self.op:
frappe.throw(_("Operation {0} is repeated in Operations Table").format(d.operation_no))
else:
# add operation in op list
self.op.append(cstr(d.operation_no))
def validate_materials(self): def validate_materials(self):
""" Validate raw material entries """ """ Validate raw material entries """
check_list = [] check_list = []
for m in self.get('bom_materials'): for m in self.get('bom_materials'):
# check if operation no not in op table
if self.with_operations and cstr(m.operation_no) not in self.op:
frappe.throw(_("Operation {0} not present in Operations Table").format(m.operation_no))
if m.bom_no: if m.bom_no:
validate_bom_no(m.item_code, m.bom_no) validate_bom_no(m.item_code, m.bom_no)
@ -207,7 +193,7 @@ class BOM(Document):
if flt(m.qty) <= 0: if flt(m.qty) <= 0:
frappe.throw(_("Quantity required for Item {0} in row {1}").format(m.item_code, m.idx)) frappe.throw(_("Quantity required for Item {0} in row {1}").format(m.item_code, m.idx))
self.check_if_item_repeated(m.item_code, m.operation_no, check_list) self.check_if_item_repeated(m.item_code, m.operation, check_list)
def check_if_item_repeated(self, item, op, check_list): def check_if_item_repeated(self, item, op, check_list):
@ -426,4 +412,3 @@ def validate_bom_no(item, bom_no):
if item and not (bom.item == item or \ if item and not (bom.item == item or \
bom.item == frappe.db.get_value("Item", item, "variant_of")): bom.item == frappe.db.get_value("Item", item, "variant_of")):
frappe.throw(_("BOM {0} does not belong to Item {1}").format(bom_no, item)) frappe.throw(_("BOM {0} does not belong to Item {1}").format(bom_no, item))

View File

@ -58,7 +58,7 @@
{ {
"bom_operations": [ "bom_operations": [
{ {
"operation_no": "1", "operation": "_Test Operation 1",
"opn_description": "_Test", "opn_description": "_Test",
"workstation": "_Test Workstation 1", "workstation": "_Test Workstation 1",
"time_in_min": 60, "time_in_min": 60,
@ -67,7 +67,7 @@
], ],
"bom_materials": [ "bom_materials": [
{ {
"operation_no": 1, "operation": 1,
"amount": 5000.0, "amount": 5000.0,
"doctype": "BOM Item", "doctype": "BOM Item",
"item_code": "_Test Item", "item_code": "_Test Item",
@ -77,7 +77,7 @@
"stock_uom": "_Test UOM" "stock_uom": "_Test UOM"
}, },
{ {
"operation_no": 1, "operation": 1,
"amount": 2000.0, "amount": 2000.0,
"bom_no": "BOM/_Test Item Home Desktop Manufactured/001", "bom_no": "BOM/_Test Item Home Desktop Manufactured/001",
"doctype": "BOM Item", "doctype": "BOM Item",
@ -99,7 +99,7 @@
{ {
"bom_operations": [ "bom_operations": [
{ {
"operation_no": "1", "operation": "_Test Operation 1",
"opn_description": "_Test", "opn_description": "_Test",
"workstation": "_Test Workstation 1", "workstation": "_Test Workstation 1",
"time_in_min": 60, "time_in_min": 60,
@ -108,7 +108,7 @@
], ],
"bom_materials": [ "bom_materials": [
{ {
"operation_no": 1, "operation": 1,
"amount": 5000.0, "amount": 5000.0,
"doctype": "BOM Item", "doctype": "BOM Item",
"item_code": "_Test Item", "item_code": "_Test Item",

View File

@ -4,10 +4,10 @@
"doctype": "DocType", "doctype": "DocType",
"fields": [ "fields": [
{ {
"fieldname": "operation_no", "fieldname": "operation",
"fieldtype": "Select", "fieldtype": "Select",
"in_list_view": 1, "in_list_view": 1,
"label": "Operation No", "label": "Operation",
"oldfieldname": "operation_no", "oldfieldname": "operation_no",
"oldfieldtype": "Data", "oldfieldtype": "Data",
"permlevel": 0, "permlevel": 0,

View File

@ -4,12 +4,13 @@
"doctype": "DocType", "doctype": "DocType",
"fields": [ "fields": [
{ {
"fieldname": "operation_no", "fieldname": "operation",
"fieldtype": "Data", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
"label": "Operation No", "label": "Operation",
"oldfieldname": "operation_no", "oldfieldname": "operation_no",
"oldfieldtype": "Data", "oldfieldtype": "Data",
"options": "Operation",
"permlevel": 0, "permlevel": 0,
"reqd": 1 "reqd": 1
}, },
@ -54,9 +55,10 @@
"fieldname": "time_in_mins", "fieldname": "time_in_mins",
"fieldtype": "Float", "fieldtype": "Float",
"in_list_view": 0, "in_list_view": 0,
"label": "Operation Time (mins)", "label": "Operation Time ",
"oldfieldname": "time_in_mins", "oldfieldname": "time_in_mins",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "in min",
"permlevel": 0, "permlevel": 0,
"reqd": 0 "reqd": 0
}, },

View File

@ -0,0 +1,14 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.provide("erpnext.operation");
$.extend(cur_frm.cscript, {
time_in_min: function(doc) {
doc.operating_cost = flt(doc.hour_rate) * flt(doc.time_in_min) / 60.0;
refresh_field('operating_cost');
}
});
cur_frm.add_fetch('workstation', 'hour_rate', 'hour_rate');
cur_frm.add_fetch('workstation', 'fixed_cycle_cost', 'fixed_cycle_cost');

View File

@ -0,0 +1,98 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:operation",
"creation": "2014-11-07 16:20:30.683186",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"fields": [
{
"allow_on_submit": 0,
"fieldname": "operation",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Operation",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "workstation",
"fieldtype": "Link",
"label": "Default Workstation",
"options": "Workstation",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "section_break_4",
"fieldtype": "Section Break",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "opn_description",
"fieldtype": "Text",
"label": "Operation Description",
"permlevel": 0,
"precision": ""
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "icon-wrench",
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"modified": "2014-11-12 15:29:01.766553",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Operation",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 0,
"export": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "All",
"set_user_permissions": 0,
"submit": 0,
"write": 1
}
],
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -0,0 +1,14 @@
# 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 Operation(Document):
def calculate_op_cost(self):
if self.hour_rate and self.time_in_mins:
self.operating_cost = flt(self.hour_rate) * flt(self.time_in_mins) / 60.0
else :
self.operating_cost = 0

View File

@ -0,0 +1,10 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
import frappe
import unittest
test_records = frappe.get_test_records('Operation')
class TestOperation(unittest.TestCase):
pass

View File

@ -0,0 +1,7 @@
[
{
"doctype": "Operation",
"operation": "_Test Operation 1",
"workstation": "_Test Workstation 1"
}
]

View File

@ -53,6 +53,36 @@ $.extend(cur_frm.cscript, {
frappe.set_route("Form", doclist[0].doctype, doclist[0].name); frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
} }
}); });
},
bom_no: function() {
return this.frm.call({
doc: this.frm.doc,
method: "get_production_order_operations",
callback: function(r) {
if(!r.exc) refresh_field("get_production_order_operations");
}
});
},
make_time_log: function(doc, cdt, cdn){
var child = locals[cdt][cdn]
frappe.call({
method:"erpnext.manufacturing.doctype.production_order.production_order.make_time_log",
args: {
"name": doc.name,
"operation": child.idx + ". " + child.operation,
"from_time": child.planned_start_time,
"to_time": child.planned_end_time,
"project": doc.project,
"qty": doc.qty - child.qty_completed,
"workstation": child.workstation
},
callback: function(r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
});
} }
}); });

View File

@ -102,13 +102,7 @@
"reqd": 1 "reqd": 1
}, },
{ {
"depends_on": "production_item", "default": "0",
"fieldname": "total_fixed_cost",
"fieldtype": "Float",
"label": "Total Fixed Cost",
"permlevel": 0
},
{
"depends_on": "eval:doc.docstatus==1", "depends_on": "eval:doc.docstatus==1",
"description": "Automatically updated via Stock Entry of type Manufacture or Repack", "description": "Automatically updated via Stock Entry of type Manufacture or Repack",
"fieldname": "produced_qty", "fieldname": "produced_qty",
@ -121,12 +115,26 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "sales_order", "fieldname": "time",
"fieldtype": "Section Break",
"label": "Time",
"options": "icon-time",
"permlevel": 0,
"precision": ""
},
{
"depends_on": "",
"fieldname": "expected_delivery_date", "fieldname": "expected_delivery_date",
"fieldtype": "Date", "fieldtype": "Date",
"label": "Expected Delivery Date", "label": "Expected Delivery Date",
"permlevel": 0, "permlevel": 0,
"read_only": 1 "read_only": 0
},
{
"fieldname": "column_break_13",
"fieldtype": "Column Break",
"permlevel": 0,
"precision": ""
}, },
{ {
"fieldname": "start_date", "fieldname": "start_date",
@ -174,6 +182,31 @@
"permlevel": 0, "permlevel": 0,
"reqd": 0 "reqd": 0
}, },
{
"depends_on": "bom_no",
"fieldname": "operations",
"fieldtype": "Section Break",
"label": "Operations",
"options": "icon-wrench",
"permlevel": 0,
"precision": ""
},
{
"depends_on": "production_item",
"fieldname": "total_fixed_cost",
"fieldtype": "Float",
"label": "Total Fixed Cost",
"permlevel": 0
},
{
"fieldname": "production_order_operations",
"fieldtype": "Table",
"label": "Production Order Operations",
"options": "Production Order Operation",
"permlevel": 0,
"precision": "",
"read_only": 1
},
{ {
"fieldname": "more_info", "fieldname": "more_info",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@ -246,7 +279,7 @@
"idx": 1, "idx": 1,
"in_create": 0, "in_create": 0,
"is_submittable": 1, "is_submittable": 1,
"modified": "2014-10-27 13:42:31.476892", "modified": "2014-11-07 15:04:01.242315",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Production Order", "name": "Production Order",

View File

@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, json import frappe, json, time, datetime
from frappe.utils import flt, nowdate from frappe.utils import flt, nowdate
from frappe import _ from frappe import _
@ -145,6 +145,19 @@ class ProductionOrder(Document):
from erpnext.stock.utils import update_bin from erpnext.stock.utils import update_bin
update_bin(args) update_bin(args)
def get_production_order_operations(self):
self.set('production_order_operations', [])
operations = frappe.db.sql("""select operation, opn_description, workstation, hour_rate, time_in_mins,
operating_cost, fixed_cycle_cost from `tabBOM Operation` where parent = %s""", self.bom_no, as_dict=1)
self.set('production_order_operations', operations)
for d in self.get('production_order_operations'):
d.status = "Pending"
d.qty_completed=0
def auto_caluclate_production_dates(self):
pass
@frappe.whitelist() @frappe.whitelist()
def get_item_details(item): def get_item_details(item):
res = frappe.db.sql("""select stock_uom, description res = frappe.db.sql("""select stock_uom, description
@ -205,3 +218,17 @@ def get_events(start, end, filters=None):
"end": end "end": end
}, as_dict=True, update={"allDay": 0}) }, as_dict=True, update={"allDay": 0})
return data return data
@frappe.whitelist()
def make_time_log(name, operation, from_time=None, to_time=None, qty=None, project=None, workstation=None):
time_log = frappe.new_doc("Time Log")
time_log.time_log_for = 'Manufacturing'
time_log.from_time = from_time
time_log.to_time = to_time
time_log.production_order = name
time_log.project = project
time_log.operation= operation
time_log.qty= qty
time_log.workstation= workstation
time_log.calculate_total_hours()
return time_log

View File

@ -58,4 +58,59 @@ class TestProductionOrder(unittest.TestCase):
self.assertRaises(StockOverProductionError, s.submit) self.assertRaises(StockOverProductionError, s.submit)
def test_make_time_log(self):
prod_order = frappe.get_doc({
"doctype":"Production Order",
"production_item": "_Test FG Item 2",
"bom_no": "BOM/_Test FG Item 2/002",
"qty": 1
})
prod_order.get_production_order_operations()
prod_order.production_order_operations[0].update({
"planned_start_time": "2014-11-25 00:00:00",
"planned_end_time": "2014-11-25 10:00:00"
})
prod_order.insert()
d = prod_order.production_order_operations[0]
from erpnext.manufacturing.doctype.production_order.production_order import make_time_log
from frappe.utils import cstr
from frappe.utils import time_diff_in_hours
time_log = make_time_log( prod_order.name, cstr(d.idx) + ". " + d.operation, \
d.planned_start_time, d.planned_end_time, prod_order.qty - d.qty_completed)
self.assertEqual(prod_order.name, time_log.production_order)
self.assertEqual((prod_order.qty - d.qty_completed), time_log.qty)
self.assertEqual(time_diff_in_hours(d.planned_end_time, d.planned_start_time),time_log.hours)
time_log.save()
time_log.submit()
prod_order.load_from_db()
self.assertEqual(prod_order.production_order_operations[0].status, "Completed")
self.assertEqual(prod_order.production_order_operations[0].qty_completed, prod_order.qty)
self.assertEqual(prod_order.production_order_operations[0].actual_start_time, time_log.from_time)
self.assertEqual(prod_order.production_order_operations[0].actual_end_time, time_log.to_time)
time_log.cancel()
prod_order.load_from_db()
self.assertEqual(prod_order.production_order_operations[0].status,"Pending")
self.assertEqual(prod_order.production_order_operations[0].qty_completed,0)
time_log2 = frappe.copy_doc(time_log)
time_log2.update({
"qty": 10,
"from_time": "2014-11-26 00:00:00",
"to_time": "2014-11-26 00:00:00",
"docstatus": 0
})
self.assertRaises(frappe.ValidationError, time_log2.save)
test_records = frappe.get_test_records('Production Order') test_records = frappe.get_test_records('Production Order')

View File

@ -0,0 +1,290 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"creation": "2014-10-16 14:35:41.950175",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"fields": [
{
"fieldname": "details",
"fieldtype": "Section Break",
"label": "Details",
"permlevel": 0,
"precision": ""
},
{
"allow_on_submit": 0,
"fieldname": "operation",
"fieldtype": "Text",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Operation",
"no_copy": 0,
"oldfieldname": "operation_no",
"oldfieldtype": "Data",
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"fieldname": "opn_description",
"fieldtype": "Text",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Operation Description",
"no_copy": 0,
"oldfieldname": "opn_description",
"oldfieldtype": "Text",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"fieldname": "col_break1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"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
},
{
"default": "Pending",
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Status",
"options": "Pending\nWork in Progress\nCompleted",
"permlevel": 0,
"precision": ""
},
{
"default": "0",
"fieldname": "qty_completed",
"fieldtype": "Float",
"label": "Qty Completed",
"permlevel": 0,
"precision": ""
},
{
"allow_on_submit": 0,
"fieldname": "workstation",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Workstation",
"no_copy": 0,
"oldfieldname": "workstation",
"oldfieldtype": "Link",
"options": "Workstation",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"fieldname": "cost",
"fieldtype": "Section Break",
"label": "Cost",
"permlevel": 0,
"precision": ""
},
{
"allow_on_submit": 0,
"fieldname": "hour_rate",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Hour Rate",
"no_copy": 0,
"oldfieldname": "hour_rate",
"oldfieldtype": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"fieldname": "time_in_mins",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Operation Time (mins)",
"no_copy": 0,
"oldfieldname": "time_in_mins",
"oldfieldtype": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"permlevel": 0,
"precision": ""
},
{
"allow_on_submit": 0,
"description": "Hour rate * hours",
"fieldname": "operating_cost",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Operating Cost",
"no_copy": 0,
"oldfieldname": "operating_cost",
"oldfieldtype": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"fieldname": "fixed_cycle_cost",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Fixed Cycle Cost",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"fieldname": "section_break_9",
"fieldtype": "Section Break",
"label": "Time",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "planned_start_time",
"fieldtype": "Datetime",
"label": "Planned Start Time",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "planned_end_time",
"fieldtype": "Datetime",
"label": "Planned End Time",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "actual_start_time",
"fieldtype": "Datetime",
"label": "Actual Start Time",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "actual_end_time",
"fieldtype": "Datetime",
"label": "Actual End Time",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "make_time_log",
"fieldtype": "Button",
"label": "Make Time Log",
"permlevel": 0,
"precision": ""
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"modified": "2014-11-13 16:47:31.015973",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Order Operation",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -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 ProductionOrderOperation(Document):
pass

View File

@ -5,6 +5,12 @@
"workstation_name": "_Test Workstation 1", "workstation_name": "_Test Workstation 1",
"warehouse": "_Test warehouse - _TC", "warehouse": "_Test warehouse - _TC",
"fixed_cycle_cost": 1000, "fixed_cycle_cost": 1000,
"hour_rate":100 "hour_rate":100,
"workstation_operation_hours": [
{
"start_time": "10:00:00",
"end_time": "20:00:00"
}
]
} }
] ]

View File

@ -9,4 +9,7 @@ test_records = frappe.get_test_records('Workstation')
class TestWorkstation(unittest.TestCase): class TestWorkstation(unittest.TestCase):
pass
def test_validate_timings(self):
wks = frappe.get_doc("Workstation", "_Test Workstation 1")
self.assertEqual(1,wks.check_workstation_for_operation_time("2013-02-01 05:00:00", "2013-02-02 20:00:00"))

View File

@ -7,6 +7,13 @@
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Master",
"fields": [ "fields": [
{
"fieldname": "description_and_warehouse",
"fieldtype": "Section Break",
"label": "Description and Warehouse",
"permlevel": 0,
"precision": ""
},
{ {
"fieldname": "workstation_name", "fieldname": "workstation_name",
"fieldtype": "Data", "fieldtype": "Data",
@ -17,17 +24,6 @@
"permlevel": 0, "permlevel": 0,
"reqd": 1 "reqd": 1
}, },
{
"fieldname": "warehouse",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Warehouse",
"oldfieldname": "warehouse",
"oldfieldtype": "Link",
"options": "Warehouse",
"permlevel": 0,
"reqd": 1
},
{ {
"fieldname": "description", "fieldname": "description",
"fieldtype": "Text", "fieldtype": "Text",
@ -39,27 +35,28 @@
"width": "300px" "width": "300px"
}, },
{ {
"fieldname": "capacity", "fieldname": "column_break_4",
"fieldtype": "Data", "fieldtype": "Column Break",
"hidden": 1,
"in_list_view": 1,
"label": "Capacity",
"oldfieldname": "capacity",
"oldfieldtype": "Data",
"permlevel": 0, "permlevel": 0,
"reqd": 0 "precision": ""
}, },
{ {
"fieldname": "capacity_units", "fieldname": "warehouse",
"fieldtype": "Select", "fieldtype": "Link",
"hidden": 1,
"in_list_view": 1, "in_list_view": 1,
"label": "Capacity Units", "label": "Warehouse",
"oldfieldname": "capacity_units", "oldfieldname": "warehouse",
"oldfieldtype": "Select", "oldfieldtype": "Link",
"options": "\nUnits/Shifts\nUnits/Hour", "options": "Warehouse",
"permlevel": 0, "permlevel": 0,
"reqd": 0 "reqd": 1
},
{
"fieldname": "fixed_costs",
"fieldtype": "Section Break",
"label": "Fixed Costs",
"permlevel": 0,
"precision": ""
}, },
{ {
"fieldname": "fixed_cycle_cost", "fieldname": "fixed_cycle_cost",
@ -67,24 +64,15 @@
"label": "Fixed Cycle Cost", "label": "Fixed Cycle Cost",
"permlevel": 0 "permlevel": 0
}, },
{
"fieldname": "hour_rate_labour",
"fieldtype": "Float",
"label": "Hour Rate Labour",
"oldfieldname": "hour_rate_labour",
"oldfieldtype": "Currency",
"permlevel": 0,
"reqd": 0
},
{ {
"fieldname": "over_heads", "fieldname": "over_heads",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Overheads", "label": "Operating Costs",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "Electricity cost per hour", "description": "per hour",
"fieldname": "hour_rate_electricity", "fieldname": "hour_rate_electricity",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Electricity Cost", "label": "Electricity Cost",
@ -93,7 +81,7 @@
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "Consumable cost per hour", "description": "per hour",
"fieldname": "hour_rate_consumable", "fieldname": "hour_rate_consumable",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Consumable Cost", "label": "Consumable Cost",
@ -102,7 +90,7 @@
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "Rent per hour", "description": "per hour",
"fieldname": "hour_rate_rent", "fieldname": "hour_rate_rent",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Rent Cost", "label": "Rent Cost",
@ -111,34 +99,68 @@
"permlevel": 0 "permlevel": 0
}, },
{ {
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"permlevel": 0,
"precision": ""
},
{
"description": "per hour",
"fieldname": "overhead", "fieldname": "overhead",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Overhead", "label": "Total Operating Cost",
"oldfieldname": "overhead", "oldfieldname": "overhead",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"permlevel": 0, "permlevel": 0,
"read_only": 1 "read_only": 1
}, },
{ {
"fieldname": "hour_rate_section_break", "description": "Wages per hour",
"fieldtype": "Section Break", "fieldname": "hour_rate_labour",
"label": "Hour Rate", "fieldtype": "Float",
"oldfieldtype": "Section Break", "label": "Wages",
"permlevel": 0 "oldfieldname": "hour_rate_labour",
"oldfieldtype": "Currency",
"permlevel": 0,
"reqd": 0
}, },
{ {
"description": "per hour",
"fieldname": "hour_rate", "fieldname": "hour_rate",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Hour Rate", "label": "Net Hour Rate",
"oldfieldname": "hour_rate", "oldfieldname": "hour_rate",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"permlevel": 0, "permlevel": 0,
"read_only": 1 "read_only": 1
},
{
"fieldname": "operation_timings",
"fieldtype": "Section Break",
"label": "Operation Timings",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "workstation_operation_hours",
"fieldtype": "Table",
"label": "Workstation Operation Hours",
"options": "Workstation Operation Hours",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "holiday_list",
"fieldtype": "Link",
"label": "Holiday List",
"options": "Holiday List",
"permlevel": 0,
"precision": ""
} }
], ],
"icon": "icon-wrench", "icon": "icon-wrench",
"idx": 1, "idx": 1,
"modified": "2014-09-15 10:59:07.960814", "modified": "2014-11-07 11:39:37.720913",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Workstation", "name": "Workstation",

View File

@ -3,6 +3,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
import datetime
from frappe import _
from frappe.utils import flt from frappe.utils import flt
from frappe.model.document import Document from frappe.model.document import Document
@ -21,3 +23,35 @@ class Workstation(Document):
flt(self.hour_rate_consumable) + flt(self.hour_rate_rent)) flt(self.hour_rate_consumable) + flt(self.hour_rate_rent))
frappe.db.set(self, 'hour_rate', flt(self.hour_rate_labour) + flt(self.overhead)) frappe.db.set(self, 'hour_rate', flt(self.hour_rate_labour) + flt(self.overhead))
self.update_bom_operation() self.update_bom_operation()
def check_if_within_operating_hours(self, from_time, to_time):
if self.check_workstation_for_operation_time(from_time, to_time):
frappe.msgprint(_("Warning: Time Log timings outside workstation Operating Hours !"))
msg = self.check_workstation_for_holiday(from_time, to_time)
if msg != None:
frappe.msgprint(msg)
def check_workstation_for_operation_time(self, from_time, to_time):
start_time = datetime.datetime.strptime(from_time,'%Y-%m-%d %H:%M:%S').strftime('%H:%M:%S')
end_time = datetime.datetime.strptime(to_time,'%Y-%m-%d %H:%M:%S').strftime('%H:%M:%S')
if frappe.db.sql("""select start_time, end_time from `tabWorkstation Operation Hours`
where parent = %s and (%s <start_time or %s > 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

View File

@ -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"
}

View File

@ -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

View File

@ -27,3 +27,36 @@ frappe.ui.form.on("Time Log", "to_time", function(frm) {
}); });
cur_frm.add_fetch('task','project','project'); 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;
}

View File

@ -16,6 +16,15 @@
"read_only": 0, "read_only": 0,
"reqd": 1 "reqd": 1
}, },
{
"fieldname": "time_log_for",
"fieldtype": "Select",
"label": "Time Log For",
"options": "Project\nManufacturing",
"permlevel": 0,
"precision": "",
"reqd": 1
},
{ {
"fieldname": "from_time", "fieldname": "from_time",
"fieldtype": "Datetime", "fieldtype": "Datetime",
@ -59,6 +68,7 @@
"reqd": 0 "reqd": 0
}, },
{ {
"depends_on": "eval:doc.time_log_for == 'Project'",
"fieldname": "activity_type", "fieldname": "activity_type",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
@ -66,9 +76,10 @@
"options": "Activity Type", "options": "Activity Type",
"permlevel": 0, "permlevel": 0,
"read_only": 0, "read_only": 0,
"reqd": 1 "reqd": 0
}, },
{ {
"depends_on": "eval:doc.time_log_for == 'Project'",
"fieldname": "task", "fieldname": "task",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Task", "label": "Task",
@ -76,6 +87,41 @@
"permlevel": 0, "permlevel": 0,
"read_only": 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", "fieldname": "billable",
"fieldtype": "Check", "fieldtype": "Check",
@ -151,7 +197,7 @@
"icon": "icon-time", "icon": "icon-time",
"idx": 1, "idx": 1,
"is_submittable": 1, "is_submittable": 1,
"modified": "2014-10-22 16:53:26.993828", "modified": "2014-11-19 11:39:02.633802",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Projects", "module": "Projects",
"name": "Time Log", "name": "Time Log",

View File

@ -6,7 +6,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import cstr, comma_and from frappe.utils import cstr, cint, comma_and
class OverlapError(frappe.ValidationError): pass class OverlapError(frappe.ValidationError): pass
@ -19,6 +20,14 @@ class TimeLog(Document):
self.set_status() self.set_status()
self.validate_overlap() self.validate_overlap()
self.calculate_total_hours() 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): def calculate_total_hours(self):
from frappe.utils import time_diff_in_hours from frappe.utils import time_diff_in_hours
@ -59,6 +68,72 @@ class TimeLog(Document):
def before_update_after_submit(self): def before_update_after_submit(self):
self.set_status() 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() @frappe.whitelist()
def get_events(start, end): def get_events(start, end):
from frappe.desk.reportview import build_match_conditions from frappe.desk.reportview import build_match_conditions

View File

@ -82,8 +82,8 @@ class Company(Document):
def create_default_accounts(self): def create_default_accounts(self):
if not self.chart_of_accounts: if not self.chart_of_accounts:
frappe.throw(_("Please select Chart of Accounts")) self.chart_of_accounts = "Standard"
else:
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
create_charts(self.chart_of_accounts, self.name) create_charts(self.chart_of_accounts, self.name)