capacity planning fixes and cleanup
This commit is contained in:
parent
1fa7171bfa
commit
0f4350dd61
@ -114,23 +114,16 @@ cur_frm.cscript.rate = function(doc, cdt, cdn) {
|
|||||||
|
|
||||||
erpnext.bom.calculate_op_cost = function(doc) {
|
erpnext.bom.calculate_op_cost = function(doc) {
|
||||||
var op = doc.bom_operations || [];
|
var op = doc.bom_operations || [];
|
||||||
total_op_cost = 0;
|
doc.total_variable_cost, doc.total_fixed_cost = 0.0, 0.0;
|
||||||
for(var i=0;i<op.length;i++) {
|
for(var i=0;i<op.length;i++) {
|
||||||
op_cost = flt(flt(op[i].hour_rate) * flt(op[i].time_in_mins) / 60, 2);
|
variable_cost = flt(flt(op[i].hour_rate) * flt(op[i].time_in_mins) / 60, 2);
|
||||||
set_multiple('BOM Operation',op[i].name, {'operating_cost': op_cost}, 'bom_operations');
|
frappe.model.set_value('BOM Operation',op[i].name, "variable_cost", variable_cost);
|
||||||
total_op_cost += op_cost;
|
|
||||||
}
|
|
||||||
doc.operating_cost = total_op_cost;
|
|
||||||
refresh_field('operating_cost');
|
|
||||||
}
|
|
||||||
|
|
||||||
erpnext.bom.calculate_fixed_cost = function(doc) {
|
doc.total_variable_cost += variable_cost;
|
||||||
var op = doc.bom_operations || [];
|
doc.total_fixed_cost += flt(op[i].fixed_cost);
|
||||||
var total_fixed_cost = 0;
|
|
||||||
for(var i=0;i<op.length;i++) {
|
|
||||||
total_fixed_cost += flt(op[i].fixed_cycle_cost);
|
|
||||||
}
|
}
|
||||||
cur_frm.set_value("total_fixed_cost", total_fixed_cost);
|
refresh_field(['total_fixed_cost', 'total_variable_cost']);
|
||||||
|
frappe.model.set_value("total_operating_cost", (doc.total_variable_cost + doc.total_fixed_cost))
|
||||||
}
|
}
|
||||||
|
|
||||||
erpnext.bom.calculate_rm_cost = function(doc) {
|
erpnext.bom.calculate_rm_cost = function(doc) {
|
||||||
@ -149,9 +142,8 @@ erpnext.bom.calculate_rm_cost = function(doc) {
|
|||||||
|
|
||||||
// Calculate Total Cost
|
// Calculate Total Cost
|
||||||
erpnext.bom.calculate_total = function(doc) {
|
erpnext.bom.calculate_total = function(doc) {
|
||||||
doc.total_variable_cost = flt(doc.raw_material_cost) + flt(doc.operating_cost) ;
|
total_cost = flt(doc.total_operating_cost) + flt(doc.raw_material_cost);
|
||||||
doc.total_cost = flt(doc.total_fixed_cost) + flt(doc.total_variable_cost);
|
frappe.model.set_value("total_cost", total_cost);
|
||||||
refresh_field(['total_variable_cost', 'total_cost']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -224,7 +216,7 @@ frappe.ui.form.on("BOM Operation", "workstation", function(frm, cdt, cdn) {
|
|||||||
},
|
},
|
||||||
callback: function (data) {
|
callback: function (data) {
|
||||||
frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate);
|
frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate);
|
||||||
frappe.model.set_value(d.doctype, d.name, "fixed_cycle_cost", data.message.fixed_cycle_cost);
|
frappe.model.set_value(d.doctype, d.name, "fixed_cost", data.message.fixed_cost);
|
||||||
erpnext.bom.calculate_op_cost(frm.doc);
|
erpnext.bom.calculate_op_cost(frm.doc);
|
||||||
erpnext.bom.calculate_fixed_cost(frm.doc);
|
erpnext.bom.calculate_fixed_cost(frm.doc);
|
||||||
erpnext.bom.calculate_total(frm.doc);
|
erpnext.bom.calculate_total(frm.doc);
|
||||||
|
@ -93,6 +93,34 @@
|
|||||||
"options": "BOM Operation",
|
"options": "BOM Operation",
|
||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_10",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "total_fixed_cost",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Total Fixed Cost",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_12",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "total_variable_cost",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Total Variable Cost",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "materials",
|
"fieldname": "materials",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@ -127,6 +155,15 @@
|
|||||||
"oldfieldtype": "Section Break",
|
"oldfieldtype": "Section Break",
|
||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "total_operating_cost",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Total Operating Cost",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "raw_material_cost",
|
"fieldname": "raw_material_cost",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
@ -135,36 +172,11 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "operating_cost",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"label": "Total Operating Cost",
|
|
||||||
"options": "Company:company:default_currency",
|
|
||||||
"permlevel": 0,
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "cb1",
|
"fieldname": "cb1",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "total_variable_cost",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Total Variable Cost",
|
|
||||||
"options": "Company:company:default_currency",
|
|
||||||
"permlevel": 0,
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "total_fixed_cost",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"label": "Total Fixed Cost",
|
|
||||||
"options": "Company:company:default_currency",
|
|
||||||
"permlevel": 0,
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "total_cost",
|
"fieldname": "total_cost",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
@ -260,7 +272,7 @@
|
|||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"modified": "2014-12-12 11:13:12.146205",
|
"modified": "2014-12-17 17:05:21.347760",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "BOM",
|
"name": "BOM",
|
||||||
|
@ -247,26 +247,28 @@ class BOM(Document):
|
|||||||
"""Calculate bom totals"""
|
"""Calculate bom totals"""
|
||||||
self.calculate_op_cost()
|
self.calculate_op_cost()
|
||||||
self.calculate_rm_cost()
|
self.calculate_rm_cost()
|
||||||
self.total_variable_cost = self.raw_material_cost + self.operating_cost
|
self.total_cost = self.total_operating_cost + self.raw_material_cost
|
||||||
self.total_cost = self.total_variable_cost + self.total_fixed_cost
|
|
||||||
|
|
||||||
def calculate_op_cost(self):
|
def calculate_op_cost(self):
|
||||||
"""Update workstation rate and calculates totals"""
|
"""Update workstation rate and calculates totals"""
|
||||||
total_op_cost, fixed_cost = 0, 0
|
total_variable_cost, total_fixed_cost = 0, 0
|
||||||
for d in self.get('bom_operations'):
|
for d in self.get('bom_operations'):
|
||||||
if d.workstation:
|
if d.workstation:
|
||||||
w = frappe.db.get_value("Workstation", d.workstation, ["hour_rate", "fixed_cycle_cost"])
|
w = frappe.db.get_value("Workstation", d.workstation, ["hour_rate", "fixed_cost"])
|
||||||
if not d.hour_rate:
|
if not d.hour_rate:
|
||||||
d.hour_rate = flt(w[0])
|
d.hour_rate = flt(w[0])
|
||||||
|
|
||||||
fixed_cost += flt(w[1])
|
total_fixed_cost += flt(w[1])
|
||||||
|
|
||||||
if d.hour_rate and d.time_in_mins:
|
if d.hour_rate and d.time_in_mins:
|
||||||
d.operating_cost = flt(d.hour_rate) * flt(d.time_in_mins) / 60.0
|
d.variable_cost = flt(d.hour_rate) * flt(d.time_in_mins) / 60.0
|
||||||
total_op_cost += flt(d.operating_cost)
|
|
||||||
|
|
||||||
self.operating_cost = total_op_cost
|
d.operating_cost = flt(d.fixed_cost) + flt(d.variable_cost)
|
||||||
self.total_fixed_cost = fixed_cost
|
total_variable_cost += flt(d.variable_cost)
|
||||||
|
|
||||||
|
self.total_variable_cost = total_variable_cost
|
||||||
|
self.total_fixed_cost = total_fixed_cost
|
||||||
|
self.total_operating_cost = self.total_variable_cost + self.total_fixed_cost
|
||||||
|
|
||||||
def calculate_rm_cost(self):
|
def calculate_rm_cost(self):
|
||||||
"""Fetch RM rate as per today's valuation rate and calculate totals"""
|
"""Fetch RM rate as per today's valuation rate and calculate totals"""
|
||||||
|
@ -1,101 +1,101 @@
|
|||||||
{
|
{
|
||||||
"creation": "2013-02-22 01:27:49",
|
"creation": "2013-02-22 01:27:49",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldname": "operation",
|
"fieldname": "operation",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Operation",
|
"label": "Operation",
|
||||||
"oldfieldname": "operation_no",
|
"oldfieldname": "operation_no",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"options": "Operation",
|
"options": "Operation",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "opn_description",
|
"fieldname": "workstation",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Operation Description",
|
"label": "Workstation",
|
||||||
"oldfieldname": "opn_description",
|
"oldfieldname": "workstation",
|
||||||
"oldfieldtype": "Text",
|
"oldfieldtype": "Link",
|
||||||
"permlevel": 0,
|
"options": "Workstation",
|
||||||
|
"permlevel": 0,
|
||||||
|
"reqd": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "opn_description",
|
||||||
|
"fieldtype": "Text",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Operation Description",
|
||||||
|
"oldfieldname": "opn_description",
|
||||||
|
"oldfieldtype": "Text",
|
||||||
|
"permlevel": 0,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "col_break1",
|
"fieldname": "col_break1",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "workstation",
|
"fieldname": "fixed_cost",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 0,
|
||||||
"label": "Workstation",
|
"label": "Fixed Cost",
|
||||||
"oldfieldname": "workstation",
|
|
||||||
"oldfieldtype": "Link",
|
|
||||||
"options": "Workstation",
|
|
||||||
"permlevel": 0,
|
|
||||||
"reqd": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "fixed_cost",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Fixed Cost",
|
|
||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "hour_rate",
|
"fieldname": "hour_rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Float",
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Hour Rate",
|
"label": "Hour Rate",
|
||||||
"oldfieldname": "hour_rate",
|
"oldfieldname": "hour_rate",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"permlevel": 0,
|
||||||
"permlevel": 0,
|
|
||||||
"reqd": 0
|
"reqd": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "In minutes",
|
"description": "In minutes",
|
||||||
"fieldname": "time_in_mins",
|
"fieldname": "time_in_mins",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Operation Time ",
|
"label": "Operation Time ",
|
||||||
"oldfieldname": "time_in_mins",
|
"oldfieldname": "time_in_mins",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "",
|
"options": "",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"reqd": 0
|
"reqd": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "variable_cost",
|
"fieldname": "variable_cost",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Variable Cost",
|
"label": "Variable Cost",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"fieldname": "operating_cost",
|
"fieldname": "operating_cost",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Operating Cost",
|
"label": "Operating Cost",
|
||||||
"oldfieldname": "operating_cost",
|
"oldfieldname": "operating_cost",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"read_only": 1,
|
||||||
"reqd": 0
|
"reqd": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2014-12-17 15:35:21.993660",
|
"modified": "2014-12-17 17:54:34.313130",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "BOM Operation",
|
"name": "BOM Operation",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": []
|
"permissions": []
|
||||||
}
|
}
|
@ -57,7 +57,7 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"modified": "2014-12-01 15:33:00.905276",
|
"modified": "2014-12-18 16:22:26.052642",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Manufacturing Settings",
|
"name": "Manufacturing Settings",
|
||||||
@ -68,7 +68,7 @@
|
|||||||
"amend": 0,
|
"amend": 0,
|
||||||
"apply_user_permissions": 0,
|
"apply_user_permissions": 0,
|
||||||
"cancel": 0,
|
"cancel": 0,
|
||||||
"create": 0,
|
"create": 1,
|
||||||
"delete": 0,
|
"delete": 0,
|
||||||
"email": 0,
|
"email": 0,
|
||||||
"export": 0,
|
"export": 0,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_copy": 0,
|
||||||
"allow_import": 0,
|
"allow_import": 1,
|
||||||
"allow_rename": 0,
|
"allow_rename": 1,
|
||||||
"autoname": "field:operation",
|
"autoname": "field:operation",
|
||||||
"creation": "2014-11-07 16:20:30.683186",
|
"creation": "2014-11-07 16:20:30.683186",
|
||||||
"custom": 0,
|
"custom": 0,
|
||||||
@ -65,7 +65,7 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"modified": "2014-11-12 15:29:01.766553",
|
"modified": "2014-12-18 16:21:59.462435",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Operation",
|
"name": "Operation",
|
||||||
@ -79,16 +79,27 @@
|
|||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 0,
|
"email": 0,
|
||||||
"export": 0,
|
"export": 1,
|
||||||
"import": 0,
|
"import": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 0,
|
"print": 0,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 0,
|
"report": 0,
|
||||||
"role": "All",
|
"role": "Manufacturing User",
|
||||||
"set_user_permissions": 0,
|
"set_user_permissions": 0,
|
||||||
"submit": 0,
|
"submit": 0,
|
||||||
"write": 1
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"export": 1,
|
||||||
|
"import": 1,
|
||||||
|
"permlevel": 0,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Manufacturing Manager",
|
||||||
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
@ -32,9 +32,12 @@ $.extend(cur_frm.cscript, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
production_item: function(doc) {
|
production_item: function(doc) {
|
||||||
return this.frm.call({
|
frappe.call({
|
||||||
method: "get_item_details",
|
method: "erpnext.manufacturing.doctype.production_order.production_order.get_item_details",
|
||||||
args: { item: doc.production_item }
|
args: { item: doc.production_item },
|
||||||
|
callback: function(r) {
|
||||||
|
cur_frm.set_value(r.message);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -164,3 +167,8 @@ cur_frm.set_query("bom_no", function(doc) {
|
|||||||
cur_frm.add_fetch('bom_no', 'total_fixed_cost', 'total_fixed_cost');
|
cur_frm.add_fetch('bom_no', 'total_fixed_cost', 'total_fixed_cost');
|
||||||
cur_frm.add_fetch('bom_no', 'total_variable_cost', 'planned_variable_cost');
|
cur_frm.add_fetch('bom_no', 'total_variable_cost', 'planned_variable_cost');
|
||||||
cur_frm.add_fetch('bom_no', 'total_operating_cost', 'total_operating_cost');
|
cur_frm.add_fetch('bom_no', 'total_operating_cost', 'total_operating_cost');
|
||||||
|
|
||||||
|
frappe.ui.form.on("Production Order", "total_fixed_cost", function(frm) {
|
||||||
|
var variable_cost = frm.doc.actual_variable_cost ? flt(frm.doc.actual_variable_cost) : flt(frm.doc.planned_variable_cost)
|
||||||
|
frm.set_value("total_operating_cost", (flt(frm.doc.total_fixed_cost) + variable_cost))
|
||||||
|
})
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "Draft",
|
||||||
"depends_on": "eval:!doc.__islocal",
|
"depends_on": "eval:!doc.__islocal",
|
||||||
"fieldname": "status",
|
"fieldname": "status",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
@ -51,7 +52,7 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "production_item",
|
"depends_on": "",
|
||||||
"description": "Bill of Material to be considered for manufacturing",
|
"description": "Bill of Material to be considered for manufacturing",
|
||||||
"fieldname": "bom_no",
|
"fieldname": "bom_no",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -64,14 +65,6 @@
|
|||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"default": "1",
|
|
||||||
"description": "If checked, BOM for sub-assembly items will be considered for getting raw materials. Otherwise, all sub-assembly items will be treated as a raw material.",
|
|
||||||
"fieldname": "use_multi_level_bom",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"label": "Use Multi-Level BOM",
|
|
||||||
"permlevel": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "column_break1",
|
"fieldname": "column_break1",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
@ -81,16 +74,7 @@
|
|||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Manufacture against Sales Order",
|
"depends_on": "",
|
||||||
"fieldname": "sales_order",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Sales Order",
|
|
||||||
"options": "Sales Order",
|
|
||||||
"permlevel": 0,
|
|
||||||
"read_only": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"depends_on": "production_item",
|
|
||||||
"fieldname": "qty",
|
"fieldname": "qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -114,6 +98,14 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"description": "If checked, BOM for sub-assembly items will be considered for getting raw materials. Otherwise, all sub-assembly items will be treated as a raw material.",
|
||||||
|
"fieldname": "use_multi_level_bom",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Use Multi-Level BOM",
|
||||||
|
"permlevel": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "time",
|
"fieldname": "time",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@ -137,14 +129,14 @@
|
|||||||
"precision": ""
|
"precision": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "start_date",
|
"fieldname": "production_start_date",
|
||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
"label": "Production Start Date",
|
"label": "Production Start Date",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": ""
|
"precision": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "end_date",
|
"fieldname": "production_end_date",
|
||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
"label": "Production End Date",
|
"label": "Production End Date",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -158,7 +150,7 @@
|
|||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "production_item",
|
"depends_on": "",
|
||||||
"description": "Manufactured quantity will be updated in this warehouse",
|
"description": "Manufactured quantity will be updated in this warehouse",
|
||||||
"fieldname": "fg_warehouse",
|
"fieldname": "fg_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -183,7 +175,7 @@
|
|||||||
"reqd": 0
|
"reqd": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "bom_no",
|
"depends_on": "",
|
||||||
"fieldname": "operations",
|
"fieldname": "operations",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Operations",
|
"label": "Operations",
|
||||||
@ -209,7 +201,7 @@
|
|||||||
"precision": ""
|
"precision": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "production_item",
|
"depends_on": "",
|
||||||
"fieldname": "total_fixed_cost",
|
"fieldname": "total_fixed_cost",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Total Fixed Cost",
|
"label": "Total Fixed Cost",
|
||||||
@ -273,6 +265,24 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "",
|
||||||
|
"fieldname": "stock_uom",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Stock UOM",
|
||||||
|
"oldfieldname": "stock_uom",
|
||||||
|
"oldfieldtype": "Data",
|
||||||
|
"options": "UOM",
|
||||||
|
"permlevel": 0,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break2",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"permlevel": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"width": "50%"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "project_name",
|
"fieldname": "project_name",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -285,22 +295,13 @@
|
|||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break2",
|
"description": "Manufacture against Sales Order",
|
||||||
"fieldtype": "Column Break",
|
"fieldname": "sales_order",
|
||||||
"permlevel": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"width": "50%"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"depends_on": "production_item",
|
|
||||||
"fieldname": "stock_uom",
|
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Stock UOM",
|
"label": "Sales Order",
|
||||||
"oldfieldname": "stock_uom",
|
"options": "Sales Order",
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"options": "UOM",
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
@ -330,7 +331,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"in_create": 0,
|
"in_create": 0,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2014-12-17 15:16:28.054620",
|
"modified": "2014-12-18 15:06:41.802390",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Production Order",
|
"name": "Production Order",
|
||||||
|
@ -2,12 +2,13 @@
|
|||||||
# 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, time, datetime
|
import frappe, json
|
||||||
|
|
||||||
from frappe.utils import flt, nowdate, now, cint, cstr
|
from frappe.utils import flt, nowdate, cstr, get_datetime, getdate
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
|
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
class OverProductionError(frappe.ValidationError): pass
|
class OverProductionError(frappe.ValidationError): pass
|
||||||
class StockOverProductionError(frappe.ValidationError): pass
|
class StockOverProductionError(frappe.ValidationError): pass
|
||||||
@ -17,6 +18,9 @@ form_grid_templates = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ProductionOrder(Document):
|
class ProductionOrder(Document):
|
||||||
|
def __setup__(self):
|
||||||
|
self.holidays = frappe._dict()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.docstatus == 0:
|
if self.docstatus == 0:
|
||||||
self.status = "Draft"
|
self.status = "Draft"
|
||||||
@ -30,7 +34,7 @@ class ProductionOrder(Document):
|
|||||||
|
|
||||||
self.validate_sales_order()
|
self.validate_sales_order()
|
||||||
self.validate_warehouse()
|
self.validate_warehouse()
|
||||||
self.set_fixed_cost()
|
self.calculate_operating_cost()
|
||||||
|
|
||||||
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", "produced_qty"])
|
validate_uom_is_integer(self, "stock_uom", ["qty", "produced_qty"])
|
||||||
@ -54,10 +58,22 @@ class ProductionOrder(Document):
|
|||||||
for w in [self.fg_warehouse, self.wip_warehouse]:
|
for w in [self.fg_warehouse, self.wip_warehouse]:
|
||||||
validate_warehouse_company(w, self.company)
|
validate_warehouse_company(w, self.company)
|
||||||
|
|
||||||
def set_fixed_cost(self):
|
def calculate_operating_cost(self):
|
||||||
if self.total_fixed_cost==None:
|
if self.total_fixed_cost==None:
|
||||||
self.total_fixed_cost = frappe.db.get_value("BOM", self.bom_no, "total_fixed_cost")
|
self.total_fixed_cost = frappe.db.get_value("BOM", self.bom_no, "total_fixed_cost")
|
||||||
|
|
||||||
|
self.planned_variable_cost, self.actual_variable_cost = 0.0, 0.0
|
||||||
|
for d in self.get("production_order_operations"):
|
||||||
|
d.actual_variable_cost = flt(d.hour_rate) * flt(d.actual_operation_time) / 60 \
|
||||||
|
if d.actual_operation_time else d.time_in_mins
|
||||||
|
|
||||||
|
self.planned_variable_cost += flt(d.variable_cost)
|
||||||
|
self.actual_variable_cost += flt(d.actual_variable_cost)
|
||||||
|
|
||||||
|
variable_cost = self.actual_variable_cost if self.actual_variable_cost else self.planned_variable_cost
|
||||||
|
self.total_operating_cost = flt(self.total_fixed_cost) + flt(variable_cost)
|
||||||
|
|
||||||
|
|
||||||
def validate_production_order_against_so(self):
|
def validate_production_order_against_so(self):
|
||||||
# already ordered qty
|
# already ordered qty
|
||||||
ordered_qty_against_so = frappe.db.sql("""select sum(qty) from `tabProduction Order`
|
ordered_qty_against_so = frappe.db.sql("""select sum(qty) from `tabProduction Order`
|
||||||
@ -150,33 +166,42 @@ class ProductionOrder(Document):
|
|||||||
update_bin(args)
|
update_bin(args)
|
||||||
|
|
||||||
def set_production_order_operations(self):
|
def set_production_order_operations(self):
|
||||||
"""Sets operations table in 'Production Order'. """
|
"""Fetch operations from BOM and set in 'Production Order'"""
|
||||||
|
|
||||||
self.set('production_order_operations', [])
|
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)
|
operations = frappe.db.sql("""select operation, opn_description, workstation,
|
||||||
|
hour_rate, time_in_mins, fixed_cost, variable_cost, "Pending" as status
|
||||||
|
from `tabBOM Operation` where parent = %s""", self.bom_no, as_dict=1)
|
||||||
|
|
||||||
self.set('production_order_operations', operations)
|
self.set('production_order_operations', operations)
|
||||||
|
|
||||||
for d in self.get('production_order_operations'):
|
self.plan_operations()
|
||||||
d.status = "Pending"
|
|
||||||
d.qty_completed=0
|
|
||||||
|
|
||||||
self.auto_caluclate_production_dates()
|
|
||||||
|
|
||||||
def auto_caluclate_production_dates(self):
|
def plan_operations(self):
|
||||||
start_delay = cint(frappe.db.get_value("Manufacturing Settings", "None", "operations_start_delay")) * 60
|
scheduled_datetime = self.production_start_date
|
||||||
time = datetime.datetime.now() + datetime.timedelta(seconds= start_delay)
|
|
||||||
for d in self.get('production_order_operations'):
|
for d in self.get('production_order_operations'):
|
||||||
holiday_list = frappe.db.get_value("Workstation", d.workstation, "holiday_list")
|
while getdate(scheduled_datetime) in self.get_holidays(d.workstation):
|
||||||
for d in frappe.db.sql("""select holiday_date from `tabHoliday` where parent = %s
|
scheduled_datetime = get_datetime(scheduled_datetime) + relativedelta(days=1)
|
||||||
order by holiday_date""", holiday_list, as_dict=1):
|
|
||||||
print "time date", time.date()
|
d.planned_start_time = scheduled_datetime
|
||||||
print "holiday ", d.holiday_date
|
scheduled_datetime = get_datetime(scheduled_datetime) + relativedelta(minutes=d.time_in_mins)
|
||||||
if d.holiday_date == time.date():
|
d.planned_end_time = scheduled_datetime
|
||||||
print "time IN ", time
|
|
||||||
time = time + datetime.timedelta(seconds= 24*60*60)
|
self.production_end_date = scheduled_datetime
|
||||||
d.planned_start_time = time.strftime('%Y-%m-%d %H:%M:%S')
|
|
||||||
time = time + datetime.timedelta(seconds= (cint(d.time_in_mins) * 60))
|
|
||||||
d.planned_end_time = time.strftime('%Y-%m-%d %H:%M:%S')
|
def get_holidays(self, workstation):
|
||||||
|
holiday_list = frappe.db.get_value("Workstation", workstation, "holiday_list")
|
||||||
|
|
||||||
|
if holiday_list not in self.holidays:
|
||||||
|
holiday_list_days = [getdate(d[0]) for d in frappe.get_all("Holiday", fields=["holiday_date"],
|
||||||
|
filters={"parent": holiday_list}, order_by="holiday_date", limit_page_length=0, as_list=1)]
|
||||||
|
|
||||||
|
self.holidays[holiday_list] = holiday_list_days
|
||||||
|
|
||||||
|
return self.holidays[holiday_list]
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_item_details(item):
|
def get_item_details(item):
|
||||||
@ -229,11 +254,11 @@ def get_events(start, end, filters=None):
|
|||||||
if filters[key]:
|
if filters[key]:
|
||||||
conditions += " and " + key + ' = "' + filters[key].replace('"', '\"') + '"'
|
conditions += " and " + key + ' = "' + filters[key].replace('"', '\"') + '"'
|
||||||
|
|
||||||
data = frappe.db.sql("""select name,production_item, start_date,end_date from `tabProduction Order`
|
data = frappe.db.sql("""select name,production_item, production_start_date, production_end_date from `tabProduction Order`
|
||||||
where ((ifnull(start_date, '0000-00-00')!= '0000-00-00') \
|
where ((ifnull(production_start_date, '0000-00-00')!= '0000-00-00') \
|
||||||
and (start_date between %(start)s and %(end)s) \
|
and (production_start_date between %(start)s and %(end)s) \
|
||||||
or ((ifnull(start_date, '0000-00-00')!= '0000-00-00') \
|
or ((ifnull(production_start_date, '0000-00-00')!= '0000-00-00') \
|
||||||
and end_date between %(start)s and %(end)s)){conditions}""".format(conditions=conditions), {
|
and production_end_date between %(start)s and %(end)s)){conditions}""".format(conditions=conditions), {
|
||||||
"start": start,
|
"start": start,
|
||||||
"end": end
|
"end": end
|
||||||
}, as_dict=True, update={"allDay": 0})
|
}, as_dict=True, update={"allDay": 0})
|
||||||
@ -248,18 +273,17 @@ def make_time_log(name, operation, from_time, to_time, qty=None, project=None, w
|
|||||||
time_log.production_order = name
|
time_log.production_order = name
|
||||||
time_log.project = project
|
time_log.project = project
|
||||||
time_log.operation= operation
|
time_log.operation= operation
|
||||||
time_log.qty= qty
|
|
||||||
time_log.workstation= workstation
|
time_log.workstation= workstation
|
||||||
if from_time and to_time :
|
if from_time and to_time :
|
||||||
time_log.calculate_total_hours()
|
time_log.calculate_total_hours()
|
||||||
return time_log
|
return time_log
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def auto_make_time_log(production_order_id):
|
def auto_make_time_log(production_order_id):
|
||||||
prod_order = frappe.get_doc("Production Order", production_order_id)
|
prod_order = frappe.get_doc("Production Order", production_order_id)
|
||||||
for d in prod_order.production_order_operations:
|
for d in prod_order.production_order_operations:
|
||||||
operation = cstr(d.idx) + ". " + d.operation
|
operation = cstr(d.idx) + ". " + d.operation
|
||||||
time_log = make_time_log(prod_order.name, operation, d.planned_start_time, d.planned_end_time,
|
time_log = make_time_log(prod_order.name, operation, d.planned_start_time, d.planned_end_time,
|
||||||
prod_order.qty, prod_order.project_name, d.workstation)
|
prod_order.qty, prod_order.project_name, d.workstation)
|
||||||
time_log.save()
|
time_log.save()
|
||||||
frappe.msgprint(_("Time Logs created."))
|
frappe.msgprint(_("Time Logs created."))
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
frappe.views.calendar["Production Order"] = {
|
frappe.views.calendar["Production Order"] = {
|
||||||
field_map: {
|
field_map: {
|
||||||
"start": "start_date",
|
"start": "production_start_date",
|
||||||
"end": "end_date",
|
"end": "production_end_date",
|
||||||
"id": "name",
|
"id": "name",
|
||||||
"title": "production_item",
|
"title": "production_item",
|
||||||
"allDay": "allDay"
|
"allDay": "allDay"
|
||||||
@ -21,7 +21,7 @@ frappe.views.calendar["Production Order"] = {
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"fieldname": "production_item",
|
"fieldname": "production_item",
|
||||||
"options": "Item",
|
"options": "Item",
|
||||||
"label": __("Production Item")
|
"label": __("Production Item")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -31,4 +31,4 @@ frappe.views.calendar["Production Order"] = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
get_events_method: "erpnext.manufacturing.doctype.production_order.production_order.get_events"
|
get_events_method: "erpnext.manufacturing.doctype.production_order.production_order.get_events"
|
||||||
}
|
}
|
||||||
|
@ -144,14 +144,15 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"fieldname": "fixed_cycle_cost",
|
"fieldname": "fixed_cost",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Fixed Cycle Cost",
|
"label": "Fixed Cost",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
@ -163,27 +164,14 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "variable_cost",
|
||||||
"description": "Hour Rate * Operating Time",
|
"fieldtype": "Currency",
|
||||||
"fieldname": "operating_cost",
|
"label": "Variable Cost",
|
||||||
"fieldtype": "Float",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Operating Cost",
|
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "operating_cost",
|
"options": "Company:company:default_currency",
|
||||||
"oldfieldtype": "Currency",
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 0,
|
"read_only": 1
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_10",
|
"fieldname": "column_break_10",
|
||||||
@ -219,6 +207,7 @@
|
|||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Planned Start Time",
|
"label": "Planned Start Time",
|
||||||
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
@ -228,6 +217,7 @@
|
|||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Planned End Time",
|
"label": "Planned End Time",
|
||||||
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
@ -244,15 +234,18 @@
|
|||||||
"fieldname": "actual_operation_time",
|
"fieldname": "actual_operation_time",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Actual Operation Time",
|
"label": "Actual Operation Time",
|
||||||
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Hour Rate * Actual Operating Cost",
|
"description": "Hour Rate * Actual Operating Cost",
|
||||||
"fieldname": "actual_operating_cost",
|
"fieldname": "actual_variable_cost",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Currency",
|
||||||
"label": "Actual Operating Cost",
|
"label": "Actual Variable Cost",
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
@ -267,6 +260,7 @@
|
|||||||
"fieldname": "actual_start_time",
|
"fieldname": "actual_start_time",
|
||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
"label": "Actual Start Time",
|
"label": "Actual Start Time",
|
||||||
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
@ -276,6 +270,7 @@
|
|||||||
"fieldname": "actual_end_time",
|
"fieldname": "actual_end_time",
|
||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
"label": "Actual End Time",
|
"label": "Actual End Time",
|
||||||
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
@ -297,7 +292,7 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2014-12-17 14:38:57.959478",
|
"modified": "2014-12-18 12:18:51.655535",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Production Order Operation",
|
"name": "Production Order Operation",
|
||||||
|
@ -51,6 +51,15 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"default": "",
|
||||||
|
"fieldname": "holiday_list",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Holiday List",
|
||||||
|
"options": "Holiday List",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "fixed_costs",
|
"fieldname": "fixed_costs",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@ -59,9 +68,9 @@
|
|||||||
"precision": ""
|
"precision": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "fixed_cycle_cost",
|
"fieldname": "fixed_cost",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Fixed Cycle Cost",
|
"label": "Fixed Cost",
|
||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -106,9 +115,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "per hour",
|
"description": "per hour",
|
||||||
"fieldname": "overhead",
|
"fieldname": "total_variable_cost",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Total Operating Cost",
|
"label": "Total Variable Cost",
|
||||||
"oldfieldname": "overhead",
|
"oldfieldname": "overhead",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -148,20 +157,11 @@
|
|||||||
"options": "Workstation Operation Hours",
|
"options": "Workstation Operation Hours",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": ""
|
"precision": ""
|
||||||
},
|
|
||||||
{
|
|
||||||
"default": "",
|
|
||||||
"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-11-27 19:04:58.125107",
|
"modified": "2014-12-18 13:01:47.143326",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Workstation",
|
"name": "Workstation",
|
||||||
|
@ -14,57 +14,59 @@ class WorkstationIsClosedError(frappe.ValidationError): pass
|
|||||||
|
|
||||||
class Workstation(Document):
|
class Workstation(Document):
|
||||||
def update_bom_operation(self):
|
def update_bom_operation(self):
|
||||||
bom_list = frappe.db.sql("""select DISTINCT parent from `tabBOM Operation`
|
bom_list = frappe.db.sql("""select DISTINCT parent from `tabBOM Operation`
|
||||||
where workstation = %s""", self.name)
|
where workstation = %s""", self.name)
|
||||||
for bom_no in bom_list:
|
for bom_no in bom_list:
|
||||||
frappe.db.sql("""update `tabBOM Operation` set hour_rate = %s
|
frappe.db.sql("""update `tabBOM Operation` set hour_rate = %s
|
||||||
where parent = %s and workstation = %s""",
|
where parent = %s and workstation = %s""",
|
||||||
(self.hour_rate, bom_no[0], self.name))
|
(self.hour_rate, bom_no[0], self.name))
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
frappe.db.set(self, 'overhead', flt(self.hour_rate_electricity) +
|
frappe.db.set(self, 'total_variable_cost', flt(self.hour_rate_electricity) +
|
||||||
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.total_variable_cost))
|
||||||
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.throw(_("Time Log timings outside workstation Operating Hours !"), WorkstationIsClosedError)
|
|
||||||
|
|
||||||
if frappe.db.get_value("Manufacturing Settings", "None", "allow_production_on_holidays") == "No":
|
|
||||||
msg = self.check_workstation_for_holiday(from_time, to_time)
|
|
||||||
if msg != None:
|
|
||||||
frappe.throw(msg, WorkstationHolidayError)
|
|
||||||
|
|
||||||
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')
|
|
||||||
max_time_diff = frappe.db.get_value("Manufacturing Settings", "None", "max_overtime")
|
|
||||||
|
|
||||||
for d in frappe.db.sql("""select time_to_sec(timediff( start_time, %s))/60 as st_diff ,
|
|
||||||
time_to_sec(timediff( %s, end_time))/60 as et_diff from `tabWorkstation Operation Hours`
|
|
||||||
where parent = %s and (%s <start_time or %s > end_time )""",
|
|
||||||
(start_time, end_time, self.workstation_name, start_time, end_time), as_dict=1):
|
|
||||||
if cint(d.st_diff) > cint(max_time_diff):
|
|
||||||
return 1
|
|
||||||
if cint(d.et_diff) > cint(max_time_diff):
|
|
||||||
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
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_default_holiday_list():
|
def get_default_holiday_list():
|
||||||
return frappe.db.get_value("Company", frappe.defaults.get_user_default("company"), "default_holiday_list")
|
return frappe.db.get_value("Company", frappe.defaults.get_user_default("company"), "default_holiday_list")
|
||||||
|
|
||||||
|
def check_if_within_operating_hours(workstation, from_time, to_time):
|
||||||
|
if check_workstation_for_operation_time(workstation, from_time, to_time):
|
||||||
|
frappe.throw(_("Time Log timings outside workstation Operating Hours !"), WorkstationIsClosedError)
|
||||||
|
|
||||||
|
if frappe.db.get_value("Manufacturing Settings", "None", "allow_production_on_holidays") == "No":
|
||||||
|
msg = check_workstation_for_holiday(workstation, from_time, to_time)
|
||||||
|
if msg != None:
|
||||||
|
frappe.throw(msg, WorkstationHolidayError)
|
||||||
|
|
||||||
|
def check_workstation_for_operation_time(workstation, 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')
|
||||||
|
max_time_diff = frappe.db.get_value("Manufacturing Settings", "None", "max_overtime")
|
||||||
|
|
||||||
|
for d in frappe.db.sql("""select time_to_sec(timediff( start_time, %s))/60 as st_diff ,
|
||||||
|
time_to_sec(timediff( %s, end_time))/60 as et_diff from `tabWorkstation Operation Hours`
|
||||||
|
where parent = %s and (%s <start_time or %s > end_time )""",
|
||||||
|
(start_time, end_time, workstation, start_time, end_time), as_dict=1):
|
||||||
|
if cint(d.st_diff) > cint(max_time_diff):
|
||||||
|
return 1
|
||||||
|
if cint(d.et_diff) > cint(max_time_diff):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def check_workstation_for_holiday(workstation, from_time, to_time):
|
||||||
|
holiday_list = frappe.db.get_value("Workstation", workstation, "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
|
||||||
|
@ -98,3 +98,4 @@ execute:frappe.reload_doc('stock', 'doctype', 'item')
|
|||||||
execute:frappe.db.sql("update `tabItem` i set apply_warehouse_wise_reorder_level=1, re_order_level=0, re_order_qty=0 where exists(select name from `tabItem Reorder` where parent=i.name)")
|
execute:frappe.db.sql("update `tabItem` i set apply_warehouse_wise_reorder_level=1, re_order_level=0, re_order_qty=0 where exists(select name from `tabItem Reorder` where parent=i.name)")
|
||||||
execute:frappe.rename_doc("DocType", "Support Ticket", "Issue", force=True)
|
execute:frappe.rename_doc("DocType", "Support Ticket", "Issue", force=True)
|
||||||
erpnext.patches.v5_0.set_default_company_in_bom
|
erpnext.patches.v5_0.set_default_company_in_bom
|
||||||
|
erpnext.patches.v5_0.capacity_planning
|
||||||
|
23
erpnext/patches/v5_0/capacity_planning.py
Normal file
23
erpnext/patches/v5_0/capacity_planning.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
for dt in ["workstation", "bom", "bom_operation"]:
|
||||||
|
frappe.reload_doc("manufacturing", "doctype", dt)
|
||||||
|
|
||||||
|
frappe.db.sql("update `tabWorkstation` set fixed_cost = fixed_cycle_cost, total_variable_cost = overhead")
|
||||||
|
|
||||||
|
frappe.db.sql("update `tabBOM Operation` set fixed_cost = fixed_cycle_cost")
|
||||||
|
|
||||||
|
for d in frappe.db.sql("select name from `tabBOM` where docstatus < 2"):
|
||||||
|
try:
|
||||||
|
bom = frappe.get_doc('BOM', d[0])
|
||||||
|
if bom.docstatus == 1:
|
||||||
|
bom.ignore_validate_update_after_submit = True
|
||||||
|
bom.calculate_cost()
|
||||||
|
bom.save()
|
||||||
|
except:
|
||||||
|
print "error", frappe.get_traceback()
|
||||||
|
pass
|
@ -26,10 +26,18 @@ frappe.ui.form.on("Time Log", "to_time", function(frm) {
|
|||||||
"hours"));
|
"hours"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cur_frm.set_query("production_order", function(doc) {
|
||||||
|
return {
|
||||||
|
"filters": {
|
||||||
|
"docstatus": 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
cur_frm.add_fetch('task','project','project');
|
cur_frm.add_fetch('task','project','project');
|
||||||
|
|
||||||
$.extend(cur_frm.cscript, {
|
$.extend(cur_frm.cscript, {
|
||||||
production_order: function(doc) {
|
production_order: function(doc) {
|
||||||
if (doc.production_order){
|
if (doc.production_order){
|
||||||
var operations = [];
|
var operations = [];
|
||||||
frappe.model.with_doc("Production Order", doc.production_order, function(pro) {
|
frappe.model.with_doc("Production Order", doc.production_order, function(pro) {
|
||||||
@ -44,13 +52,16 @@ $.extend(cur_frm.cscript, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
operation: function(doc) {
|
operation: function(doc) {
|
||||||
return cur_frm.call({
|
return frappe.call({
|
||||||
method: "erpnext.projects.doctype.time_log.time_log.get_workstation",
|
method: "erpnext.projects.doctype.time_log.time_log.get_workstation",
|
||||||
args: {
|
args: {
|
||||||
"production_order": doc.production_order,
|
"production_order": doc.production_order,
|
||||||
"operation": doc.operation
|
"operation": doc.operation
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
|
if(!r.exc) {
|
||||||
|
cur_frm.set_value("workstation", r.message)
|
||||||
|
}
|
||||||
doc.workstation = r.workstation;
|
doc.workstation = r.workstation;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -59,4 +70,4 @@ $.extend(cur_frm.cscript, {
|
|||||||
|
|
||||||
if (cur_frm.doc.time_log_for == "Manufacturing") {
|
if (cur_frm.doc.time_log_for == "Manufacturing") {
|
||||||
cur_frm.cscript.onload = cur_frm.cscript.production_order;
|
cur_frm.cscript.onload = cur_frm.cscript.production_order;
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,10 @@
|
|||||||
"fieldname": "time_log_for",
|
"fieldname": "time_log_for",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Time Log For",
|
"label": "Time Log For",
|
||||||
"options": "Project\nManufacturing",
|
"options": "\nProject\nManufacturing",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"reqd": 1
|
"reqd": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "from_time",
|
"fieldname": "from_time",
|
||||||
@ -68,7 +68,7 @@
|
|||||||
"reqd": 0
|
"reqd": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.time_log_for == 'Project'",
|
"depends_on": "eval:doc.time_log_for != 'Manufacturing'",
|
||||||
"fieldname": "activity_type",
|
"fieldname": "activity_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -79,7 +79,7 @@
|
|||||||
"reqd": 0
|
"reqd": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.time_log_for == 'Project'",
|
"depends_on": "eval:doc.time_log_for != 'Manufacturing'",
|
||||||
"fieldname": "task",
|
"fieldname": "task",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Task",
|
"label": "Task",
|
||||||
@ -112,13 +112,15 @@
|
|||||||
"label": "Workstation",
|
"label": "Workstation",
|
||||||
"options": "Workstation",
|
"options": "Workstation",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": ""
|
"precision": "",
|
||||||
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.time_log_for == 'Manufacturing'",
|
"default": "Work in Progress",
|
||||||
"fieldname": "qty",
|
"fieldname": "operation_status",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Select",
|
||||||
"label": "Quantity",
|
"label": "Operation Status",
|
||||||
|
"options": "\nWork in Progress\nCompleted",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": ""
|
"precision": ""
|
||||||
},
|
},
|
||||||
@ -150,6 +152,7 @@
|
|||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:doc.time_log_for == 'Project'",
|
||||||
"fieldname": "project",
|
"fieldname": "project",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -197,7 +200,7 @@
|
|||||||
"icon": "icon-time",
|
"icon": "icon-time",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2014-11-19 11:39:02.633802",
|
"modified": "2014-12-18 17:21:01.520646",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Projects",
|
"module": "Projects",
|
||||||
"name": "Time Log",
|
"name": "Time Log",
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, json
|
import frappe, json
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cstr, cint, comma_and
|
from frappe.utils import cstr, comma_and, flt
|
||||||
|
|
||||||
|
|
||||||
class OverlapError(frappe.ValidationError): pass
|
class OverlapError(frappe.ValidationError): pass
|
||||||
@ -16,25 +16,25 @@ class NotSubmittedError(frappe.ValidationError): pass
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class TimeLog(Document):
|
class TimeLog(Document):
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.validate_overlap()
|
self.validate_overlap()
|
||||||
self.validate_timings()
|
self.validate_timings()
|
||||||
self.calculate_total_hours()
|
self.calculate_total_hours()
|
||||||
self.check_workstation_timings()
|
self.check_workstation_timings()
|
||||||
self.validate_qty()
|
|
||||||
self.validate_production_order()
|
self.validate_production_order()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.update_production_order()
|
self.update_production_order()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.update_production_order_on_cancel()
|
self.update_production_order()
|
||||||
|
|
||||||
def calculate_total_hours(self):
|
def before_update_after_submit(self):
|
||||||
from frappe.utils import time_diff_in_hours
|
self.set_status()
|
||||||
self.hours = time_diff_in_hours(self.to_time, self.from_time)
|
|
||||||
|
def before_cancel(self):
|
||||||
|
self.set_status()
|
||||||
|
|
||||||
def set_status(self):
|
def set_status(self):
|
||||||
self.status = {
|
self.status = {
|
||||||
@ -65,127 +65,84 @@ class TimeLog(Document):
|
|||||||
|
|
||||||
if existing:
|
if existing:
|
||||||
frappe.throw(_("This Time Log conflicts with {0}").format(comma_and(existing)), OverlapError)
|
frappe.throw(_("This Time Log conflicts with {0}").format(comma_and(existing)), OverlapError)
|
||||||
|
|
||||||
def validate_timings(self):
|
def validate_timings(self):
|
||||||
if self.to_time < self.from_time:
|
if self.to_time < self.from_time:
|
||||||
frappe.throw(_("From Time cannot be greater than To Time"))
|
frappe.throw(_("From Time cannot be greater than To Time"))
|
||||||
|
|
||||||
def before_cancel(self):
|
def calculate_total_hours(self):
|
||||||
self.set_status()
|
from frappe.utils import time_diff_in_seconds
|
||||||
|
self.hours = flt(time_diff_in_seconds(self.to_time, self.from_time)) / 3600
|
||||||
|
|
||||||
def before_update_after_submit(self):
|
|
||||||
self.set_status()
|
|
||||||
|
|
||||||
def update_production_order(self):
|
|
||||||
"""Updates `start_date`, `end_date` for operation in Production Order."""
|
|
||||||
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):
|
|
||||||
"""Updates operations in 'Production Order' when an associated 'Time Log' is cancelled."""
|
|
||||||
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):
|
|
||||||
"""Returns quantity and status of Operation in 'Time Log'. """
|
|
||||||
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):
|
|
||||||
"""Returns Min From and Max To Dates of Time Logs against a specific Operation. """
|
|
||||||
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):
|
|
||||||
"""Updates 'Produuction Order' and sets 'Actual Start Time', 'Actual End Time', 'Status', 'Compleated Qty'. """
|
|
||||||
d = self.operation.split('. ',1)
|
|
||||||
actual_op_time = self.get_actual_op_time().time_diff
|
|
||||||
if actual_op_time == None:
|
|
||||||
actual_op_time = 0
|
|
||||||
actual_op_cost = self.get_actual_op_cost(actual_op_time)
|
|
||||||
frappe.db.sql("""update `tabProduction Order Operation` set actual_start_time = %s, actual_end_time = %s, qty_completed = %s,
|
|
||||||
status = %s, actual_operation_time = %s, actual_operating_cost = %s where idx=%s and parent=%s and operation = %s """,
|
|
||||||
(dates.start_date, dates.end_date, qty, status, actual_op_time, actual_op_cost, d[0], self.production_order, d[1] ))
|
|
||||||
|
|
||||||
def get_actual_op_time(self):
|
|
||||||
"""Returns 'Actual Operating Time'. """
|
|
||||||
return frappe.db.sql("""select sum(time_to_sec(timediff(to_time, from_time))/60) as time_diff from
|
|
||||||
`tabTime Log` where production_order = %s and operation = %s and docstatus=1""",
|
|
||||||
(self.production_order, self.operation), as_dict = 1)[0]
|
|
||||||
|
|
||||||
def get_actual_op_cost(self, actual_op_time):
|
|
||||||
"""Returns 'Actual Operating Cost'. """
|
|
||||||
if self.operation:
|
|
||||||
d = self.operation.split('. ',1)
|
|
||||||
idx = d[0]
|
|
||||||
operation = d[1]
|
|
||||||
|
|
||||||
hour_rate = frappe.db.sql("""select hour_rate from `tabProduction Order Operation` where idx=%s and
|
|
||||||
parent=%s and operation = %s""", (idx, self.production_order, operation), as_dict=1)[0].hour_rate
|
|
||||||
return hour_rate * actual_op_time
|
|
||||||
|
|
||||||
def check_workstation_timings(self):
|
def check_workstation_timings(self):
|
||||||
"""Checks if **Time Log** is between operating hours of the **Workstation**."""
|
"""Checks if **Time Log** is between operating hours of the **Workstation**."""
|
||||||
if self.workstation:
|
if self.workstation:
|
||||||
frappe.get_doc("Workstation", self.workstation).check_if_within_operating_hours(self.from_time, self.to_time)
|
from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours
|
||||||
|
check_if_within_operating_hours(self.workstation, self.from_time, self.to_time)
|
||||||
|
|
||||||
def validate_qty(self):
|
|
||||||
"""Throws `OverProductionError` if quantity surpasses **Production Order** quantity."""
|
|
||||||
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), OverProductionError)
|
|
||||||
|
|
||||||
def validate_production_order(self):
|
def validate_production_order(self):
|
||||||
"""Throws 'NotSubmittedError' if **production order** is not submitted. """
|
"""Throws 'NotSubmittedError' if **production order** is not submitted. """
|
||||||
|
|
||||||
if self.production_order:
|
if self.production_order:
|
||||||
if frappe.db.get_value("Production Order", self.production_order, "docstatus") != 1 :
|
if frappe.db.get_value("Production Order", self.production_order, "docstatus") != 1 :
|
||||||
frappe.throw(_("You cannot make a time log against a production order that has not been submitted.")
|
frappe.throw(_("You can make a time log only against a submitted production order"), NotSubmittedError)
|
||||||
, NotSubmittedError)
|
|
||||||
|
def update_production_order(self):
|
||||||
|
"""Updates `start_date`, `end_date`, `status` for operation in Production Order."""
|
||||||
|
|
||||||
|
if self.time_log_for=="Manufacturing" and self.operation:
|
||||||
|
dates = self.get_operation_start_end_time()
|
||||||
|
op_status = self.get_op_status()
|
||||||
|
actual_op_time = self.get_actual_op_time()
|
||||||
|
|
||||||
|
d = self.operation.split('. ',1)
|
||||||
|
|
||||||
|
frappe.db.sql("""update `tabProduction Order Operation`
|
||||||
|
set actual_start_time = %s, actual_end_time = %s, status = %s, actual_operation_time = %s
|
||||||
|
where parent=%s and idx=%s and operation = %s""",
|
||||||
|
(dates.start_date, dates.end_date, op_status, actual_op_time,
|
||||||
|
self.production_order, d[0], d[1]))
|
||||||
|
|
||||||
|
frappe.get_doc("Production Order", self.production_order).save()
|
||||||
|
|
||||||
|
def get_operation_start_end_time(self):
|
||||||
|
"""Returns Min From and Max To Dates of Time Logs against a specific Operation. """
|
||||||
|
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 get_actual_op_time(self):
|
||||||
|
"""Returns 'Actual Operating Time'. """
|
||||||
|
actual_time = frappe.db.sql("""select sum(hours*60) as time_diff from
|
||||||
|
`tabTime Log` where production_order = %s and operation = %s and docstatus=1""",
|
||||||
|
(self.production_order, self.operation))
|
||||||
|
return actual_time[0][0] if actual_time else 0
|
||||||
|
|
||||||
|
def get_op_status(self):
|
||||||
|
status = frappe.db.sql("""select operation_status from `tabTime Log`
|
||||||
|
where production_order=%s and operation=%s and docstatus=1
|
||||||
|
order by to_time desc limit 1""", (self.production_order, self.operation))
|
||||||
|
|
||||||
|
return status if status else self.status
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_workstation(production_order, operation):
|
def get_workstation(production_order, operation):
|
||||||
"""Returns workstation name from Production Order against an associated Operation.
|
"""Returns workstation name from Production Order against an associated Operation.
|
||||||
|
|
||||||
:param production_order string
|
:param production_order string
|
||||||
:param operation string
|
:param operation string
|
||||||
"""
|
"""
|
||||||
if operation:
|
if operation:
|
||||||
d = operation.split('. ',1)
|
idx, operation = operation.split('. ',1)
|
||||||
idx = d[0]
|
|
||||||
operation = d[1]
|
|
||||||
|
|
||||||
return frappe.db.sql("""select workstation from `tabProduction Order Operation` where idx=%s and
|
workstation = 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]
|
parent=%s and operation = %s""", (idx, production_order, operation))
|
||||||
|
return workstation[0][0] if workstation else ""
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_events(start, end, filters=None):
|
def get_events(start, end, filters=None):
|
||||||
"""Returns events for Gantt / Calendar view rendering.
|
"""Returns events for Gantt / Calendar view rendering.
|
||||||
|
|
||||||
:param start: Start date-time.
|
:param start: Start date-time.
|
||||||
:param end: End date-time.
|
:param end: End date-time.
|
||||||
:param filters: Filters like workstation, project etc.
|
:param filters: Filters like workstation, project etc.
|
||||||
@ -194,8 +151,6 @@ def get_events(start, end, filters=None):
|
|||||||
if not frappe.has_permission("Time Log"):
|
if not frappe.has_permission("Time Log"):
|
||||||
frappe.msgprint(_("No Permission"), raise_exception=1)
|
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||||
|
|
||||||
match = build_match_conditions("Time Log")
|
|
||||||
|
|
||||||
conditions = build_match_conditions("Time Log")
|
conditions = build_match_conditions("Time Log")
|
||||||
conditions = conditions and (" and " + conditions) or ""
|
conditions = conditions and (" and " + conditions) or ""
|
||||||
if filters:
|
if filters:
|
||||||
@ -211,7 +166,7 @@ def get_events(start, end, filters=None):
|
|||||||
"start": start,
|
"start": start,
|
||||||
"end": end
|
"end": end
|
||||||
}, as_dict=True, update={"allDay": 0})
|
}, as_dict=True, update={"allDay": 0})
|
||||||
|
|
||||||
for d in data:
|
for d in data:
|
||||||
d.title = d.name + ": " + (d.activity_type or d.production_order or "")
|
d.title = d.name + ": " + (d.activity_type or d.production_order or "")
|
||||||
if d.task:
|
if d.task:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user