[Enhance] Job Card (#15244)
* [Enhance] Added job card against the work order * removed work order from timesheet * Fixed codacy * Added patch to make job card from the timesheet * Timer in job card * Dates validation in job card * Added button to make job card from work order * Added sub-assembly operation in the work order
This commit is contained in:
parent
6f77abe0dd
commit
32dc3bf082
@ -127,6 +127,23 @@ frappe.ui.form.on("BOM", {
|
||||
if(!r.exc) frm.refresh_fields();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
routing: function(frm) {
|
||||
if (frm.doc.routing) {
|
||||
frappe.call({
|
||||
doc: frm.doc,
|
||||
method: "get_routing",
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
if (!r.exc) {
|
||||
frm.refresh_fields();
|
||||
erpnext.bom.calculate_op_cost(frm.doc);
|
||||
erpnext.bom.calculate_total(frm.doc);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -472,6 +472,39 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "with_operations",
|
||||
"fieldname": "transfer_material_against_job_card",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Transfer Material Against Job Card",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -667,6 +700,39 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "routing",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Routing",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Routing",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -1877,7 +1943,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-06-01 03:45:06.731308",
|
||||
"modified": "2018-07-15 11:09:19.425998",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM",
|
||||
@ -1930,5 +1996,6 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -89,6 +89,13 @@ class BOM(WebsiteGenerator):
|
||||
|
||||
return item
|
||||
|
||||
def get_routing(self):
|
||||
if self.routing:
|
||||
for d in frappe.get_all("BOM Operation", fields = ["*"],
|
||||
filters = {'parenttype': 'Routing', 'parent': self.routing}):
|
||||
child = self.append('operations', d)
|
||||
child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2)
|
||||
|
||||
def validate_rm_item(self, item):
|
||||
if (item[0]['name'] in [it.item_code for it in self.items]) and item[0]['name'] == self.item:
|
||||
frappe.throw(_("BOM #{0}: Raw material cannot be same as main Item").format(self.name))
|
||||
@ -458,6 +465,7 @@ class BOM(WebsiteGenerator):
|
||||
self.add_to_cur_exploded_items(frappe._dict({
|
||||
'item_code' : d.item_code,
|
||||
'item_name' : d.item_name,
|
||||
'operation' : d.operation,
|
||||
'source_warehouse': d.source_warehouse,
|
||||
'description' : d.description,
|
||||
'image' : d.image,
|
||||
@ -480,7 +488,7 @@ class BOM(WebsiteGenerator):
|
||||
""" Add all items from Flat BOM of child BOM"""
|
||||
# Did not use qty_consumed_per_unit in the query, as it leads to rounding loss
|
||||
child_fb_items = frappe.db.sql("""select bom_item.item_code, bom_item.item_name,
|
||||
bom_item.description, bom_item.source_warehouse,
|
||||
bom_item.description, bom_item.source_warehouse, bom_item.operation,
|
||||
bom_item.stock_uom, bom_item.stock_qty, bom_item.rate, bom_item.allow_transfer_for_manufacture,
|
||||
bom_item.stock_qty / ifnull(bom.quantity, 1) as qty_consumed_per_unit
|
||||
from `tabBOM Explosion Item` bom_item, tabBOM bom
|
||||
@ -491,6 +499,7 @@ class BOM(WebsiteGenerator):
|
||||
'item_code' : d['item_code'],
|
||||
'item_name' : d['item_name'],
|
||||
'source_warehouse' : d['source_warehouse'],
|
||||
'operation' : d['operation'],
|
||||
'description' : d['description'],
|
||||
'stock_uom' : d['stock_uom'],
|
||||
'stock_qty' : d['qty_consumed_per_unit'] * stock_qty,
|
||||
@ -571,7 +580,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
|
||||
query = query.format(table="BOM Explosion Item",
|
||||
where_conditions="",
|
||||
is_stock_item=is_stock_item,
|
||||
select_columns = """, bom_item.source_warehouse, bom_item.allow_transfer_for_manufacture,
|
||||
select_columns = """, bom_item.source_warehouse, bom_item.operation, bom_item.allow_transfer_for_manufacture,
|
||||
(Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s ) as idx""")
|
||||
|
||||
items = frappe.db.sql(query, { "parent": bom, "qty": qty, "bom": bom, "company": company }, as_dict=True)
|
||||
@ -580,7 +589,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
|
||||
items = frappe.db.sql(query, { "qty": qty, "bom": bom, "company": company }, as_dict=True)
|
||||
else:
|
||||
query = query.format(table="BOM Item", where_conditions="", is_stock_item=is_stock_item,
|
||||
select_columns = ", bom_item.source_warehouse, bom_item.idx, bom_item.allow_transfer_for_manufacture")
|
||||
select_columns = ", bom_item.source_warehouse, bom_item.idx, bom_item.operation, bom_item.allow_transfer_for_manufacture")
|
||||
items = frappe.db.sql(query, { "qty": qty, "bom": bom, "company": company }, as_dict=True)
|
||||
|
||||
for item in items:
|
||||
|
@ -47,37 +47,6 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "cb",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -110,6 +79,37 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "cb",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -143,6 +143,39 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "operation",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Operation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Operation",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -576,7 +609,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-07-12 16:29:55.464426",
|
||||
"modified": "2018-08-27 16:32:35.152139",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Explosion Item",
|
||||
|
@ -11,6 +11,39 @@
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 1,
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "operation",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Operation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Operation",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -1000,7 +1033,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-07-12 16:16:16.815165",
|
||||
"modified": "2018-08-22 16:16:16.815165",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Item",
|
||||
|
0
erpnext/manufacturing/doctype/job_card/__init__.py
Normal file
0
erpnext/manufacturing/doctype/job_card/__init__.py
Normal file
113
erpnext/manufacturing/doctype/job_card/job_card.js
Normal file
113
erpnext/manufacturing/doctype/job_card/job_card.js
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Job Card', {
|
||||
refresh: function(frm) {
|
||||
if (frm.doc.items && frm.doc.docstatus==1) {
|
||||
if (frm.doc.for_quantity != frm.doc.transferred_qty) {
|
||||
frm.add_custom_button(__("Material Request"), () => {
|
||||
frm.trigger("make_material_request");
|
||||
});
|
||||
}
|
||||
|
||||
if (frm.doc.for_quantity != frm.doc.transferred_qty) {
|
||||
frm.add_custom_button(__("Material Transfer"), () => {
|
||||
frm.trigger("make_stock_entry");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus == 0) {
|
||||
if (!frm.doc.actual_start_date || !frm.doc.actual_end_date) {
|
||||
frm.trigger("make_dashboard");
|
||||
}
|
||||
|
||||
if (!frm.doc.actual_start_date) {
|
||||
frm.add_custom_button(__("Start Job"), () => {
|
||||
frm.set_value('actual_start_date', frappe.datetime.now_datetime());
|
||||
frm.save();
|
||||
});
|
||||
} else if (!frm.doc.actual_end_date) {
|
||||
frm.add_custom_button(__("Complete Job"), () => {
|
||||
frm.set_value('actual_end_date', frappe.datetime.now_datetime());
|
||||
frm.save();
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
make_dashboard: function(frm) {
|
||||
if(frm.doc.__islocal)
|
||||
return;
|
||||
|
||||
frm.dashboard.refresh();
|
||||
const timer = `
|
||||
<div class="stopwatch" style="font-weight:bold">
|
||||
<span class="hours">00</span>
|
||||
<span class="colon">:</span>
|
||||
<span class="minutes">00</span>
|
||||
<span class="colon">:</span>
|
||||
<span class="seconds">00</span>
|
||||
</div>`;
|
||||
|
||||
var section = frm.dashboard.add_section(timer);
|
||||
|
||||
if (frm.doc.actual_start_date) {
|
||||
let currentIncrement = moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.actual_start_date),"seconds");
|
||||
initialiseTimer();
|
||||
|
||||
function initialiseTimer() {
|
||||
const interval = setInterval(function() {
|
||||
var current = setCurrentIncrement();
|
||||
updateStopwatch(current);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function updateStopwatch(increment) {
|
||||
var hours = Math.floor(increment / 3600);
|
||||
var minutes = Math.floor((increment - (hours * 3600)) / 60);
|
||||
var seconds = increment - (hours * 3600) - (minutes * 60);
|
||||
|
||||
$(section).find(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString());
|
||||
$(section).find(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString());
|
||||
$(section).find(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString());
|
||||
}
|
||||
|
||||
function setCurrentIncrement() {
|
||||
currentIncrement += 1;
|
||||
return currentIncrement;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
for_quantity: function(frm) {
|
||||
frm.doc.items = [];
|
||||
frm.call({
|
||||
method: "get_required_items",
|
||||
doc: frm.doc,
|
||||
callback: function() {
|
||||
refresh_field("items");
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
make_material_request: function(frm) {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.manufacturing.doctype.job_card.job_card.make_material_request",
|
||||
frm: frm,
|
||||
run_link_triggers: true
|
||||
});
|
||||
},
|
||||
|
||||
make_stock_entry: function(frm) {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.manufacturing.doctype.job_card.job_card.make_stock_entry",
|
||||
frm: frm,
|
||||
run_link_triggers: true
|
||||
});
|
||||
},
|
||||
|
||||
timer: function(frm) {
|
||||
return `<button> Start </button>`
|
||||
}
|
||||
});
|
912
erpnext/manufacturing/doctype/job_card/job_card.json
Normal file
912
erpnext/manufacturing/doctype/job_card/job_card.json
Normal file
@ -0,0 +1,912 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "PO-JOB.#####",
|
||||
"beta": 0,
|
||||
"creation": "2018-07-09 17:23:29.518745",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "work_order",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Work Order",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Work Order",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "workstation",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Workstation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Workstation",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "operation",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Operation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Operation",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "wip_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "WIP Warehouse",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Warehouse",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Today",
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Posting Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Company",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "for_quantity",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "For Quantity",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fieldname": "transferred_qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Transferred Qty",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "timing_detail",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Timing Detail",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Employee",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Employee",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "time_in_mins",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Time In Mins",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_13",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "actual_start_date",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Actual Start Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "actual_end_date",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Actual End Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Raw Materials",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Items",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Job Card Item",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fieldname": "more_information",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "More Information",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "operation_id",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Operation ID",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "bom_no",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "BOM No",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "BOM",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Project",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Project",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_20",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "remarks",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Remarks",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Open",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Open\nWork In Progress\nCancelled\nCompleted",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Amended From",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Job Card",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-28 16:50:43.576151",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Job Card",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "operation",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
211
erpnext/manufacturing/doctype/job_card/job_card.py
Normal file
211
erpnext/manufacturing/doctype/job_card/job_card.py
Normal file
@ -0,0 +1,211 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt, time_diff_in_hours, get_datetime
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.model.document import Document
|
||||
|
||||
class JobCard(Document):
|
||||
def validate(self):
|
||||
self.status = 'Open'
|
||||
self.validate_actual_dates()
|
||||
self.set_time_in_mins()
|
||||
|
||||
def validate_actual_dates(self):
|
||||
if get_datetime(self.actual_start_date) > get_datetime(self.actual_end_date):
|
||||
frappe.throw(_("Actual start date must be less than actual end date"))
|
||||
|
||||
if not (self.employee and self.actual_start_date and self.actual_end_date):
|
||||
return
|
||||
|
||||
data = frappe.db.sql(""" select name from `tabJob Card`
|
||||
where
|
||||
((%(actual_start_date)s > actual_start_date and %(actual_start_date)s < actual_end_date) or
|
||||
(%(actual_end_date)s > actual_start_date and %(actual_end_date)s < actual_end_date) or
|
||||
(%(actual_start_date)s <= actual_start_date and %(actual_end_date)s >= actual_end_date)) and
|
||||
name != %(name)s and employee = %(employee)s and docstatus =1
|
||||
""", {
|
||||
'actual_start_date': self.actual_start_date,
|
||||
'actual_end_date': self.actual_end_date,
|
||||
'employee': self.employee,
|
||||
'name': self.name
|
||||
}, as_dict=1)
|
||||
|
||||
if data:
|
||||
frappe.throw(_("Start date and end date is overlapping with the job card <a href='#Form/Job Card/{0}'>{1}</a>")
|
||||
.format(data[0].name, data[0].name))
|
||||
|
||||
def set_time_in_mins(self):
|
||||
if self.actual_start_date and self.actual_end_date:
|
||||
self.time_in_mins = time_diff_in_hours(self.actual_end_date, self.actual_start_date) * 60
|
||||
|
||||
def get_required_items(self):
|
||||
if not self.get('work_order'):
|
||||
return
|
||||
|
||||
doc = frappe.get_doc('Work Order', self.get('work_order'))
|
||||
if not doc.transfer_material_against_job_card and doc.skip_transfer:
|
||||
return
|
||||
|
||||
for d in doc.required_items:
|
||||
if not d.operation:
|
||||
frappe.throw(_("Row {0} : Operation is required against the raw material item {1}")
|
||||
.format(d.idx, d.item_code))
|
||||
|
||||
if self.get('operation') == d.operation:
|
||||
child = self.append('items', {
|
||||
'item_code': d.item_code,
|
||||
'source_warehouse': d.source_warehouse,
|
||||
'uom': frappe.db.get_value("Item", d.item_code, 'stock_uom'),
|
||||
'item_name': d.item_name,
|
||||
'description': d.description,
|
||||
'required_qty': (d.required_qty * flt(self.for_quantity)) / doc.qty
|
||||
})
|
||||
|
||||
def on_submit(self):
|
||||
self.validate_dates()
|
||||
self.update_work_order()
|
||||
self.set_transferred_qty()
|
||||
|
||||
def validate_dates(self):
|
||||
if not self.actual_start_date and not self.actual_end_date:
|
||||
frappe.throw(_("Actual start date and actual end date is mandatory"))
|
||||
|
||||
def on_cancel(self):
|
||||
self.update_work_order()
|
||||
self.set_transferred_qty()
|
||||
|
||||
def update_work_order(self):
|
||||
if not self.work_order:
|
||||
return
|
||||
|
||||
data = frappe.db.get_value("Job Card", {'docstatus': 1, 'operation_id': self.operation_id},
|
||||
['sum(time_in_mins)', 'min(actual_start_date)', 'max(actual_end_date)', 'sum(for_quantity)'])
|
||||
|
||||
if data:
|
||||
time_in_mins, actual_start_date, actual_end_date, for_quantity = data
|
||||
|
||||
wo = frappe.get_doc('Work Order', self.work_order)
|
||||
|
||||
for data in wo.operations:
|
||||
if data.name == self.operation_id:
|
||||
data.completed_qty = for_quantity
|
||||
data.actual_operation_time = time_in_mins
|
||||
data.actual_start_time = actual_start_date
|
||||
data.actual_end_time = actual_end_date
|
||||
|
||||
wo.flags.ignore_validate_update_after_submit = True
|
||||
wo.update_operation_status()
|
||||
wo.calculate_operating_cost()
|
||||
wo.set_actual_dates()
|
||||
wo.save()
|
||||
|
||||
def set_transferred_qty(self):
|
||||
if not self.items:
|
||||
self.transferred_qty = self.for_quantity if self.docstatus == 1 else 0
|
||||
|
||||
if self.items:
|
||||
self.transferred_qty = frappe.db.get_value('Stock Entry', {'job_card': self.name,
|
||||
'work_order': self.work_order, 'docstatus': 1}, 'sum(fg_completed_qty)')
|
||||
|
||||
self.db_set("transferred_qty", self.transferred_qty)
|
||||
|
||||
qty = 0
|
||||
if self.work_order:
|
||||
doc = frappe.get_doc('Work Order', self.work_order)
|
||||
if doc.transfer_material_against_job_card and not doc.skip_transfer:
|
||||
completed = True
|
||||
for d in doc.operations:
|
||||
if d.status != 'Completed':
|
||||
completed = False
|
||||
break
|
||||
|
||||
if completed:
|
||||
job_cards = frappe.get_all('Job Card', filters = {'work_order': self.work_order,
|
||||
'docstatus': ('!=', 2)}, fields = 'sum(transferred_qty) as qty', group_by='operation_id')
|
||||
qty = min([d.qty for d in job_cards])
|
||||
|
||||
doc.db_set('material_transferred_for_manufacturing', qty)
|
||||
|
||||
self.set_status()
|
||||
|
||||
def set_status(self):
|
||||
status = 'Cancelled' if self.docstatus == 2 else 'Work In Progress'
|
||||
|
||||
if self.for_quantity == self.transferred_qty:
|
||||
status = 'Completed'
|
||||
|
||||
self.db_set('status', status)
|
||||
|
||||
def update_job_card_reference(name, fieldname, value):
|
||||
frappe.db.set_value('Job Card', name, fieldname, value)
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_material_request(source_name, target_doc=None):
|
||||
def update_item(obj, target, source_parent):
|
||||
target.warehouse = source_parent.wip_warehouse
|
||||
|
||||
def set_missing_values(source, target):
|
||||
target.material_request_type = "Material Transfer"
|
||||
|
||||
doclist = get_mapped_doc("Job Card", source_name, {
|
||||
"Job Card": {
|
||||
"doctype": "Material Request",
|
||||
"validation": {
|
||||
"docstatus": ["=", 1]
|
||||
},
|
||||
"field_map": {
|
||||
"name": "job_card",
|
||||
},
|
||||
},
|
||||
"Job Card Item": {
|
||||
"doctype": "Material Request Item",
|
||||
"field_map": {
|
||||
"required_qty": "qty",
|
||||
"uom": "stock_uom"
|
||||
},
|
||||
"postprocess": update_item,
|
||||
}
|
||||
}, target_doc, set_missing_values)
|
||||
|
||||
return doclist
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_stock_entry(source_name, target_doc=None):
|
||||
def update_item(obj, target, source_parent):
|
||||
target.t_warehouse = source_parent.wip_warehouse
|
||||
|
||||
def set_missing_values(source, target):
|
||||
target.purpose = "Material Transfer for Manufacture"
|
||||
target.from_bom = 1
|
||||
target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0)
|
||||
target.calculate_rate_and_amount()
|
||||
target.set_missing_values()
|
||||
|
||||
doclist = get_mapped_doc("Job Card", source_name, {
|
||||
"Job Card": {
|
||||
"doctype": "Stock Entry",
|
||||
"validation": {
|
||||
"docstatus": ["=", 1]
|
||||
},
|
||||
"field_map": {
|
||||
"name": "job_card",
|
||||
"for_quantity": "fg_completed_qty"
|
||||
},
|
||||
},
|
||||
"Job Card Item": {
|
||||
"doctype": "Stock Entry Detail",
|
||||
"field_map": {
|
||||
"source_warehouse": "s_warehouse",
|
||||
"required_qty": "qty",
|
||||
"uom": "stock_uom"
|
||||
},
|
||||
"postprocess": update_item,
|
||||
}
|
||||
}, target_doc, set_missing_values)
|
||||
|
||||
return doclist
|
12
erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
Normal file
12
erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
Normal file
@ -0,0 +1,12 @@
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'job_card',
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Transactions'),
|
||||
'items': ['Material Request', 'Stock Entry']
|
||||
}
|
||||
]
|
||||
}
|
13
erpnext/manufacturing/doctype/job_card/job_card_list.js
Normal file
13
erpnext/manufacturing/doctype/job_card/job_card_list.js
Normal file
@ -0,0 +1,13 @@
|
||||
frappe.listview_settings['Job Card'] = {
|
||||
get_indicator: function(doc) {
|
||||
if (doc.status === "Work In Progress") {
|
||||
return [__("Work In Progress"), "orange", "status,=,Work In Progress"];
|
||||
} else if (doc.status === "Completed") {
|
||||
return [__("Completed"), "green", "status,=,Completed"];
|
||||
} else if (doc.docstatus == 2) {
|
||||
return [__("Cancelled"), "red", "status,=,Cancelled"];
|
||||
} else {
|
||||
return [__("Open"), "red", "status,=,Open"];
|
||||
}
|
||||
}
|
||||
};
|
23
erpnext/manufacturing/doctype/job_card/test_job_card.js
Normal file
23
erpnext/manufacturing/doctype/job_card/test_job_card.js
Normal file
@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Job Card", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Job Card
|
||||
() => frappe.tests.make('Job Card', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
9
erpnext/manufacturing/doctype/job_card/test_job_card.py
Normal file
9
erpnext/manufacturing/doctype/job_card/test_job_card.py
Normal file
@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
class TestJobCard(unittest.TestCase):
|
||||
pass
|
363
erpnext/manufacturing/doctype/job_card_item/job_card_item.json
Normal file
363
erpnext/manufacturing/doctype/job_card_item/job_card_item.json
Normal file
@ -0,0 +1,363 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2018-07-09 17:20:44.737289",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Item Code",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Item",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "source_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 1,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Source Warehouse",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Warehouse",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "uom",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "UOM",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "UOM",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Item Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "qty_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Qty",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "required_qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Required Qty",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "allow_alternative_item",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Allow Alternative Item",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-28 15:23:48.099459",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Job Card Item",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.model.document import Document
|
||||
|
||||
class JobCardItem(Document):
|
||||
pass
|
0
erpnext/manufacturing/doctype/routing/__init__.py
Normal file
0
erpnext/manufacturing/doctype/routing/__init__.py
Normal file
58
erpnext/manufacturing/doctype/routing/routing.js
Normal file
58
erpnext/manufacturing/doctype/routing/routing.js
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Routing', {
|
||||
calculate_operating_cost: function(frm, child) {
|
||||
const operating_cost = flt(flt(child.hour_rate) * flt(child.time_in_mins) / 60, 2);
|
||||
frappe.model.set_value(child.doctype, child.name, "operating_cost", operating_cost);
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on('BOM Operation', {
|
||||
operation: function(frm, cdt, cdn) {
|
||||
const d = locals[cdt][cdn];
|
||||
|
||||
if(!d.operation) return;
|
||||
|
||||
frappe.call({
|
||||
"method": "frappe.client.get",
|
||||
args: {
|
||||
doctype: "Operation",
|
||||
name: d.operation
|
||||
},
|
||||
callback: function (data) {
|
||||
if (data.message.description) {
|
||||
frappe.model.set_value(d.doctype, d.name, "description", data.message.description);
|
||||
}
|
||||
|
||||
if (data.message.workstation) {
|
||||
frappe.model.set_value(d.doctype, d.name, "workstation", data.message.workstation);
|
||||
}
|
||||
|
||||
frm.events.calculate_operating_cost(frm, d);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
workstation: function(frm, cdt, cdn) {
|
||||
const d = locals[cdt][cdn];
|
||||
|
||||
frappe.call({
|
||||
"method": "frappe.client.get",
|
||||
args: {
|
||||
doctype: "Workstation",
|
||||
name: d.workstation
|
||||
},
|
||||
callback: function (data) {
|
||||
frappe.model.set_value(d.doctype, d.name, "base_hour_rate", data.message.hour_rate);
|
||||
frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate);
|
||||
frm.events.calculate_operating_cost(frm, d);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
time_in_mins: function(frm, cdt, cdn) {
|
||||
const d = locals[cdt][cdn];
|
||||
frm.events.calculate_operating_cost(frm, d);
|
||||
}
|
||||
});
|
180
erpnext/manufacturing/doctype/routing/routing.json
Normal file
180
erpnext/manufacturing/doctype/routing/routing.json
Normal file
@ -0,0 +1,180 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:routing_name",
|
||||
"beta": 0,
|
||||
"creation": "2018-07-15 11:03:24.191613",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "routing_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Routing Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Disabled",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "operations",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "BOM Operation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "BOM Operation",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-07-15 11:42:41.424793",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Routing",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
9
erpnext/manufacturing/doctype/routing/routing.py
Normal file
9
erpnext/manufacturing/doctype/routing/routing.py
Normal file
@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.model.document import Document
|
||||
|
||||
class Routing(Document):
|
||||
pass
|
23
erpnext/manufacturing/doctype/routing/test_routing.js
Normal file
23
erpnext/manufacturing/doctype/routing/test_routing.js
Normal file
@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Routing", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Routing
|
||||
() => frappe.tests.make('Routing', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
9
erpnext/manufacturing/doctype/routing/test_routing.py
Normal file
9
erpnext/manufacturing/doctype/routing/test_routing.py
Normal file
@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
class TestRouting(unittest.TestCase):
|
||||
pass
|
@ -73,53 +73,6 @@ class TestWorkOrder(unittest.TestCase):
|
||||
|
||||
self.assertRaises(StockOverProductionError, s.submit)
|
||||
|
||||
def test_make_time_sheet(self):
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import make_timesheet
|
||||
wo_order = make_wo_order_test_record(item="_Test FG Item 2",
|
||||
planned_start_date=now(), qty=1, do_not_save=True)
|
||||
|
||||
wo_order.set_work_order_operations()
|
||||
wo_order.insert()
|
||||
wo_order.submit()
|
||||
|
||||
d = wo_order.operations[0]
|
||||
d.completed_qty = flt(d.completed_qty)
|
||||
|
||||
name = frappe.db.get_value('Timesheet', {'work_order': wo_order.name}, 'name')
|
||||
time_sheet_doc = frappe.get_doc('Timesheet', name)
|
||||
self.assertEqual(wo_order.company, time_sheet_doc.company)
|
||||
time_sheet_doc.submit()
|
||||
|
||||
self.assertEqual(wo_order.name, time_sheet_doc.work_order)
|
||||
self.assertEqual((wo_order.qty - d.completed_qty),
|
||||
sum([d.completed_qty for d in time_sheet_doc.time_logs]))
|
||||
|
||||
manufacturing_settings = frappe.get_doc({
|
||||
"doctype": "Manufacturing Settings",
|
||||
"allow_production_on_holidays": 0
|
||||
})
|
||||
|
||||
manufacturing_settings.save()
|
||||
|
||||
wo_order.load_from_db()
|
||||
self.assertEqual(wo_order.operations[0].status, "Completed")
|
||||
self.assertEqual(wo_order.operations[0].completed_qty, wo_order.qty)
|
||||
|
||||
self.assertEqual(wo_order.operations[0].actual_operation_time, 60)
|
||||
self.assertEqual(wo_order.operations[0].actual_operating_cost, 6000)
|
||||
|
||||
time_sheet_doc1 = make_timesheet(wo_order.name, wo_order.company)
|
||||
self.assertEqual(len(time_sheet_doc1.get('time_logs')), 0)
|
||||
|
||||
time_sheet_doc.cancel()
|
||||
|
||||
wo_order.load_from_db()
|
||||
self.assertEqual(wo_order.operations[0].status, "Pending")
|
||||
self.assertEqual(flt(wo_order.operations[0].completed_qty), 0)
|
||||
|
||||
self.assertEqual(flt(wo_order.operations[0].actual_operation_time), 0)
|
||||
self.assertEqual(flt(wo_order.operations[0].actual_operating_cost), 0)
|
||||
|
||||
def test_planned_operating_cost(self):
|
||||
wo_order = make_wo_order_test_record(item="_Test FG Item 2",
|
||||
planned_start_date=now(), qty=1, do_not_save=True)
|
||||
|
@ -4,7 +4,6 @@
|
||||
frappe.ui.form.on("Work Order", {
|
||||
setup: function(frm) {
|
||||
frm.custom_make_buttons = {
|
||||
'Timesheet': 'Make Timesheet',
|
||||
'Stock Entry': 'Make Stock Entry',
|
||||
}
|
||||
|
||||
@ -113,13 +112,11 @@ frappe.ui.form.on("Work Order", {
|
||||
frm.trigger('show_progress');
|
||||
}
|
||||
|
||||
if(frm.doc.docstatus == 1 && frm.doc.status != 'Stopped'){
|
||||
frm.add_custom_button(__('Make Timesheet'), function(){
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.manufacturing.doctype.work_order.work_order.make_new_timesheet",
|
||||
frm: cur_frm
|
||||
})
|
||||
})
|
||||
if (frm.doc.docstatus === 1 && frm.doc.operations
|
||||
&& frm.doc.qty != frm.doc.material_transferred_for_manufacturing) {
|
||||
frm.add_custom_button(__('Make Job Card'), () => {
|
||||
frm.trigger("make_job_card")
|
||||
}).addClass('btn-primary');
|
||||
}
|
||||
|
||||
if(frm.doc.required_items && frm.doc.allow_alternative_item) {
|
||||
@ -139,6 +136,113 @@ frappe.ui.form.on("Work Order", {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (frm.doc.status == "Completed" &&
|
||||
frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture") {
|
||||
frm.add_custom_button(__("Make BOM"), () => {
|
||||
frm.trigger("make_bom");
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
make_job_card: function(frm) {
|
||||
let qty = 0;
|
||||
const fields = [{
|
||||
fieldtype: "Link",
|
||||
fieldname: "operation",
|
||||
options: "Operation",
|
||||
label: __("Operation"),
|
||||
get_query: () => {
|
||||
const filter_workstation = frm.doc.operations.filter(d => {
|
||||
if (d.status != "Completed") {
|
||||
return d;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
filters: {
|
||||
name: ["in", (filter_workstation || []).map(d => d.operation)]
|
||||
}
|
||||
};
|
||||
},
|
||||
reqd: true
|
||||
}, {
|
||||
fieldtype: "Link",
|
||||
fieldname: "workstation",
|
||||
options: "Workstation",
|
||||
label: __("Workstation"),
|
||||
get_query: () => {
|
||||
const operation = dialog.get_value("operation");
|
||||
const filter_workstation = frm.doc.operations.filter(d => {
|
||||
if (d.operation == operation) {
|
||||
return d;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
filters: {
|
||||
name: ["in", (filter_workstation || []).map(d => d.workstation)]
|
||||
}
|
||||
};
|
||||
},
|
||||
onchange: () => {
|
||||
const operation = dialog.get_value("operation");
|
||||
const workstation = dialog.get_value("workstation");
|
||||
if (operation && workstation) {
|
||||
const row = frm.doc.operations.filter(d => d.operation == operation && d.workstation == workstation)[0];
|
||||
qty = frm.doc.qty - row.completed_qty;
|
||||
|
||||
if (qty > 0) {
|
||||
dialog.set_value("qty", qty);
|
||||
}
|
||||
}
|
||||
},
|
||||
reqd: true
|
||||
}, {
|
||||
fieldtype: "Float",
|
||||
fieldname: "qty",
|
||||
label: __("For Quantity"),
|
||||
reqd: true
|
||||
}];
|
||||
|
||||
const dialog = frappe.prompt(fields, function(data) {
|
||||
if (data.qty > qty) {
|
||||
frappe.throw(__("For Quantity must be less than quantity {0}", [qty]));
|
||||
}
|
||||
|
||||
if (data.qty <= 0) {
|
||||
frappe.throw(__("For Quantity must be greater than zero"));
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
method: "erpnext.manufacturing.doctype.work_order.work_order.make_job_card",
|
||||
args: {
|
||||
work_order: frm.doc.name,
|
||||
operation: data.operation,
|
||||
workstation: data.workstation,
|
||||
qty: data.qty
|
||||
},
|
||||
callback: function(r){
|
||||
if (r.message) {
|
||||
var doc = frappe.model.sync(r.message)[0];
|
||||
frappe.set_route("Form", doc.doctype, doc.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, __("For Job Card"));
|
||||
},
|
||||
|
||||
make_bom: function(frm) {
|
||||
frappe.call({
|
||||
method: "make_bom",
|
||||
doc: frm.doc,
|
||||
callback: function(r){
|
||||
if (r.message) {
|
||||
var doc = frappe.model.sync(r.message)[0];
|
||||
frappe.set_route("Form", doc.doctype, doc.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
show_progress: function(frm) {
|
||||
@ -189,7 +293,8 @@ frappe.ui.form.on("Work Order", {
|
||||
frm.set_value('sales_order', "");
|
||||
frm.trigger('set_sales_order');
|
||||
erpnext.in_production_item_onchange = true;
|
||||
$.each(["description", "stock_uom", "project", "bom_no", "allow_alternative_item"], function(i, field) {
|
||||
$.each(["description", "stock_uom", "project", "bom_no",
|
||||
"allow_alternative_item", "transfer_material_against_job_card"], function(i, field) {
|
||||
frm.set_value(field, r.message[field]);
|
||||
});
|
||||
|
||||
@ -235,6 +340,9 @@ frappe.ui.form.on("Work Order", {
|
||||
before_submit: function(frm) {
|
||||
frm.toggle_reqd(["fg_warehouse", "wip_warehouse"], true);
|
||||
frm.fields_dict.required_items.grid.toggle_reqd("source_warehouse", true);
|
||||
if (frm.doc.operations) {
|
||||
frm.fields_dict.operations.grid.toggle_reqd("workstation", true);
|
||||
}
|
||||
},
|
||||
|
||||
set_sales_order: function(frm) {
|
||||
@ -316,7 +424,10 @@ erpnext.work_order = {
|
||||
}, __("Status"));
|
||||
}
|
||||
|
||||
if(!frm.doc.skip_transfer){
|
||||
const show_start_btn = (frm.doc.skip_transfer
|
||||
|| frm.doc.transfer_material_against_job_card) ? 0 : 1;
|
||||
|
||||
if (show_start_btn){
|
||||
if ((flt(doc.material_transferred_for_manufacturing) < flt(doc.qty))
|
||||
&& frm.doc.status != 'Stopped') {
|
||||
frm.has_start_btn = true;
|
||||
|
@ -552,6 +552,39 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "operations",
|
||||
"fieldname": "transfer_material_against_job_card",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Transfer Material Against Job Card",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -1639,7 +1672,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-29 06:28:22.983369",
|
||||
"modified": "2018-09-05 06:28:22.983369",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Work Order",
|
||||
|
@ -190,6 +190,9 @@ class WorkOrder(Document):
|
||||
|
||||
for purpose, fieldname in (("Manufacture", "produced_qty"),
|
||||
("Material Transfer for Manufacture", "material_transferred_for_manufacturing")):
|
||||
if (purpose == 'Material Transfer for Manufacture' and
|
||||
self.operations and self.transfer_material_against_job_card):
|
||||
continue
|
||||
|
||||
qty = flt(frappe.db.sql("""select sum(fg_completed_qty)
|
||||
from `tabStock Entry` where work_order=%s and docstatus=1
|
||||
@ -209,9 +212,6 @@ class WorkOrder(Document):
|
||||
production_plan = frappe.get_doc('Production Plan', self.production_plan)
|
||||
production_plan.run_method("update_produced_qty", self.produced_qty, self.production_plan_item)
|
||||
|
||||
def before_submit(self):
|
||||
self.make_time_logs()
|
||||
|
||||
def on_submit(self):
|
||||
if not self.wip_warehouse:
|
||||
frappe.throw(_("Work-in-Progress Warehouse is required before Submit"))
|
||||
@ -223,18 +223,27 @@ class WorkOrder(Document):
|
||||
self.update_completed_qty_in_material_request()
|
||||
self.update_planned_qty()
|
||||
self.update_ordered_qty()
|
||||
self.create_job_card()
|
||||
|
||||
def on_cancel(self):
|
||||
self.validate_cancel()
|
||||
|
||||
frappe.db.set(self,'status', 'Cancelled')
|
||||
self.update_work_order_qty_in_so()
|
||||
self.delete_timesheet()
|
||||
self.delete_job_card()
|
||||
self.update_completed_qty_in_material_request()
|
||||
self.update_planned_qty()
|
||||
self.update_ordered_qty()
|
||||
self.update_reserved_qty_for_production()
|
||||
|
||||
def create_job_card(self):
|
||||
for row in self.operations:
|
||||
if not row.workstation:
|
||||
frappe.throw(_("Row {0}: select the workstation against the operation {1}")
|
||||
.format(row.idx, row.operation))
|
||||
|
||||
create_job_card(self, row, auto_create=True)
|
||||
|
||||
def validate_cancel(self):
|
||||
if self.status == "Stopped":
|
||||
frappe.throw(_("Stopped Work Order cannot be cancelled, Unstop it first to cancel"))
|
||||
@ -312,6 +321,17 @@ class WorkOrder(Document):
|
||||
""" % ", ".join(["%s"]*len(bom_list)), tuple(bom_list), as_dict=1)
|
||||
|
||||
self.set('operations', operations)
|
||||
|
||||
if self.use_multi_level_bom and self.get('operations') and self.get('items'):
|
||||
raw_material_operations = [d.operation for d in self.get('items')]
|
||||
operations = [d.operation for d in self.get('operations')]
|
||||
|
||||
for operation in raw_material_operations:
|
||||
if operation not in operations:
|
||||
self.append('operations', {
|
||||
'operation': operation
|
||||
})
|
||||
|
||||
self.calculate_time()
|
||||
|
||||
def calculate_time(self):
|
||||
@ -335,99 +355,6 @@ class WorkOrder(Document):
|
||||
|
||||
return holidays[holiday_list]
|
||||
|
||||
def make_time_logs(self, open_new=False):
|
||||
"""Capacity Planning. Plan time logs based on earliest availablity of workstation after
|
||||
Planned Start Date. Time logs will be created and remain in Draft mode and must be submitted
|
||||
before manufacturing entry can be made."""
|
||||
|
||||
if not self.operations:
|
||||
return
|
||||
|
||||
timesheets = []
|
||||
plan_days = frappe.db.get_single_value("Manufacturing Settings", "capacity_planning_for_days") or 30
|
||||
|
||||
timesheet = make_timesheet(self.name, self.company)
|
||||
timesheet.set('time_logs', [])
|
||||
|
||||
for i, d in enumerate(self.operations):
|
||||
|
||||
if d.status != 'Completed':
|
||||
self.set_start_end_time_for_workstation(d, i)
|
||||
|
||||
args = self.get_operations_data(d)
|
||||
|
||||
add_timesheet_detail(timesheet, args)
|
||||
original_start_time = d.planned_start_time
|
||||
|
||||
# validate operating hours if workstation [not mandatory] is specified
|
||||
try:
|
||||
timesheet.validate_time_logs()
|
||||
except OverlapError:
|
||||
if frappe.message_log: frappe.message_log.pop()
|
||||
timesheet.schedule_for_work_order(d.idx)
|
||||
except WorkstationHolidayError:
|
||||
if frappe.message_log: frappe.message_log.pop()
|
||||
timesheet.schedule_for_work_order(d.idx)
|
||||
|
||||
from_time, to_time = self.get_start_end_time(timesheet, d.name)
|
||||
|
||||
if date_diff(from_time, original_start_time) > plan_days:
|
||||
frappe.throw(_("Unable to find Time Slot in the next {0} days for Operation {1}").format(plan_days, d.operation))
|
||||
break
|
||||
|
||||
d.planned_start_time = from_time
|
||||
d.planned_end_time = to_time
|
||||
d.db_update()
|
||||
|
||||
if timesheet and open_new:
|
||||
return timesheet
|
||||
|
||||
if timesheet and timesheet.get("time_logs"):
|
||||
timesheet.save()
|
||||
timesheets.append(getlink("Timesheet", timesheet.name))
|
||||
|
||||
self.planned_end_date = self.operations[-1].planned_end_time
|
||||
if timesheets:
|
||||
frappe.local.message_log = []
|
||||
frappe.msgprint(_("Timesheet created:") + "\n" + "\n".join(timesheets))
|
||||
|
||||
def get_operations_data(self, data):
|
||||
return {
|
||||
'from_time': get_datetime(data.planned_start_time),
|
||||
'hours': data.time_in_mins / 60.0,
|
||||
'to_time': get_datetime(data.planned_end_time),
|
||||
'project': self.project,
|
||||
'operation': data.operation,
|
||||
'operation_id': data.name,
|
||||
'workstation': data.workstation,
|
||||
'completed_qty': flt(self.qty) - flt(data.completed_qty)
|
||||
}
|
||||
|
||||
def set_start_end_time_for_workstation(self, data, index):
|
||||
"""Set start and end time for given operation. If first operation, set start as
|
||||
`planned_start_date`, else add time diff to end time of earlier operation."""
|
||||
|
||||
if index == 0:
|
||||
data.planned_start_time = self.planned_start_date
|
||||
else:
|
||||
data.planned_start_time = get_datetime(self.operations[index-1].planned_end_time)\
|
||||
+ get_mins_between_operations()
|
||||
|
||||
data.planned_end_time = get_datetime(data.planned_start_time) + relativedelta(minutes = data.time_in_mins)
|
||||
|
||||
if data.planned_start_time == data.planned_end_time:
|
||||
frappe.throw(_("Capacity Planning Error"))
|
||||
|
||||
def get_start_end_time(self, timesheet, operation_id):
|
||||
for data in timesheet.time_logs:
|
||||
if data.operation_id == operation_id:
|
||||
return data.from_time, data.to_time
|
||||
|
||||
def check_operation_fits_in_working_hours(self, d):
|
||||
"""Raises expection if operation is longer than working hours in the given workstation."""
|
||||
from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours
|
||||
check_if_within_operating_hours(d.workstation, d.operation, d.planned_start_time, d.planned_end_time)
|
||||
|
||||
def update_operation_status(self):
|
||||
for d in self.get("operations"):
|
||||
if not d.completed_qty:
|
||||
@ -451,9 +378,9 @@ class WorkOrder(Document):
|
||||
if actual_end_dates:
|
||||
self.actual_end_date = max(actual_end_dates)
|
||||
|
||||
def delete_timesheet(self):
|
||||
for timesheet in frappe.get_all("Timesheet", ["name"], {"work_order": self.name}):
|
||||
frappe.delete_doc("Timesheet", timesheet.name)
|
||||
def delete_job_card(self):
|
||||
for d in frappe.get_all("Job Card", ["name"], {"work_order": self.name}):
|
||||
frappe.delete_doc("Job Card", d.name)
|
||||
|
||||
def validate_production_item(self):
|
||||
if frappe.db.get_value("Item", self.production_item, "has_variants"):
|
||||
@ -523,6 +450,7 @@ class WorkOrder(Document):
|
||||
else:
|
||||
for item in sorted(item_dict.values(), key=lambda d: d['idx']):
|
||||
self.append('required_items', {
|
||||
'operation': item.operation,
|
||||
'item_code': item.item_code,
|
||||
'item_name': item.item_name,
|
||||
'description': item.description,
|
||||
@ -573,6 +501,30 @@ class WorkOrder(Document):
|
||||
|
||||
d.db_set('consumed_qty', flt(consumed_qty), update_modified = False)
|
||||
|
||||
def make_bom(self):
|
||||
data = frappe.db.sql(""" select sed.item_code, sed.qty, sed.s_warehouse
|
||||
from `tabStock Entry Detail` sed, `tabStock Entry` se
|
||||
where se.name = sed.parent and se.purpose = 'Manufacture'
|
||||
and (sed.t_warehouse is null or sed.t_warehouse = '') and se.docstatus = 1
|
||||
and se.work_order = %s""", (self.name), as_dict=1)
|
||||
|
||||
bom = frappe.new_doc("BOM")
|
||||
bom.item = self.production_item
|
||||
bom.conversion_rate = 1
|
||||
|
||||
for d in data:
|
||||
bom.append('items', {
|
||||
'item_code': d.item_code,
|
||||
'qty': d.qty,
|
||||
'source_warehouse': d.s_warehouse
|
||||
})
|
||||
|
||||
if self.operations:
|
||||
bom.set('operations', self.operations)
|
||||
bom.with_operations = 1
|
||||
|
||||
bom.set_bom_material_details()
|
||||
return bom
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_item_details(item, project = None):
|
||||
@ -609,8 +561,12 @@ def get_item_details(item, project = None):
|
||||
else:
|
||||
frappe.throw(_("Default BOM for {0} not found").format(item))
|
||||
|
||||
res['project'] = project or frappe.db.get_value('BOM', res['bom_no'], 'project')
|
||||
res['allow_alternative_item'] = frappe.db.get_value('BOM', res['bom_no'], 'allow_alternative_item')
|
||||
bom_data = frappe.db.get_value('BOM', res['bom_no'],
|
||||
['project', 'allow_alternative_item', 'transfer_material_against_job_card'], as_dict=1)
|
||||
|
||||
res['project'] = project or bom_data.project
|
||||
res['allow_alternative_item'] = bom_data.allow_alternative_item
|
||||
res['transfer_material_against_job_card'] = bom_data.transfer_material_against_job_card
|
||||
res.update(check_if_scrap_warehouse_mandatory(res["bom_no"]))
|
||||
|
||||
return res
|
||||
@ -667,25 +623,6 @@ def make_stock_entry(work_order_id, purpose, qty=None):
|
||||
stock_entry.get_items()
|
||||
return stock_entry.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_timesheet(work_order, company):
|
||||
timesheet = frappe.new_doc("Timesheet")
|
||||
timesheet.employee = ""
|
||||
timesheet.work_order = work_order
|
||||
timesheet.company = company
|
||||
return timesheet
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_timesheet_detail(timesheet, args):
|
||||
if isinstance(timesheet, string_types):
|
||||
timesheet = frappe.get_doc('Timesheet', timesheet)
|
||||
|
||||
if isinstance(args, string_types):
|
||||
args = json.loads(args)
|
||||
|
||||
timesheet.append('time_logs', args)
|
||||
return timesheet
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_default_warehouse():
|
||||
wip_warehouse = frappe.db.get_single_value("Manufacturing Settings",
|
||||
@ -694,16 +631,6 @@ def get_default_warehouse():
|
||||
"default_fg_warehouse")
|
||||
return {"wip_warehouse": wip_warehouse, "fg_warehouse": fg_warehouse}
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_new_timesheet(source_name, target_doc=None):
|
||||
po = frappe.get_doc('Work Order', source_name)
|
||||
ts = po.make_time_logs(open_new=True)
|
||||
|
||||
if not ts or not ts.get('time_logs'):
|
||||
frappe.throw(_("Already completed"))
|
||||
|
||||
return ts
|
||||
|
||||
@frappe.whitelist()
|
||||
def stop_unstop(work_order, status):
|
||||
""" Called from client side on Stop/Unstop event"""
|
||||
@ -730,3 +657,40 @@ def query_sales_order(production_item):
|
||||
""", (production_item, production_item))
|
||||
|
||||
return out
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_job_card(work_order, operation, workstation, qty=0):
|
||||
work_order = frappe.get_doc('Work Order', work_order)
|
||||
row = get_work_order_operation_data(work_order, operation, workstation)
|
||||
if row:
|
||||
return create_job_card(work_order, row, qty)
|
||||
|
||||
def create_job_card(work_order, row, qty=0, auto_create=False):
|
||||
doc = frappe.new_doc("Job Card")
|
||||
doc.update({
|
||||
'work_order': work_order.name,
|
||||
'operation': row.operation,
|
||||
'workstation': row.workstation,
|
||||
'posting_date': nowdate(),
|
||||
'for_quantity': qty or work_order.get('qty', 0),
|
||||
'operation_id': row.name,
|
||||
'bom_no': work_order.bom_no,
|
||||
'project': work_order.project,
|
||||
'company': work_order.company,
|
||||
'wip_warehouse': work_order.wip_warehouse
|
||||
})
|
||||
|
||||
if work_order.transfer_material_against_job_card and not work_order.skip_transfer:
|
||||
doc.get_required_items()
|
||||
|
||||
if auto_create:
|
||||
doc.flags.ignore_mandatory = True
|
||||
doc.insert()
|
||||
frappe.msgprint(_("Job card {0} created").format(doc.name))
|
||||
|
||||
return doc
|
||||
|
||||
def get_work_order_operation_data(work_order, operation, workstation):
|
||||
for d in work_order.operations:
|
||||
if d.operation == operation and d.workstation == workstation:
|
||||
return d
|
||||
|
@ -5,7 +5,7 @@ def get_data():
|
||||
'fieldname': 'work_order',
|
||||
'transactions': [
|
||||
{
|
||||
'items': ['Stock Entry', 'Timesheet']
|
||||
'items': ['Stock Entry', 'Job Card']
|
||||
}
|
||||
]
|
||||
}
|
@ -12,6 +12,39 @@
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "operation",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Operation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Operation",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
|
@ -563,4 +563,5 @@ erpnext.patches.v11_0.reset_publish_in_hub_for_all_items
|
||||
erpnext.patches.v11_0.update_hub_url # 2018-08-31 # 2018-09-03
|
||||
erpnext.patches.v10_0.set_discount_amount
|
||||
erpnext.patches.v10_0.recalculate_gross_margin_for_project
|
||||
erpnext.patches.v11_0.make_job_card
|
||||
erpnext.patches.v11_0.redesign_healthcare_billing_work_flow
|
||||
|
18
erpnext/patches/v11_0/make_job_card.py
Normal file
18
erpnext/patches/v11_0/make_job_card.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2017, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc('manufacturing', 'doctype', 'work_order')
|
||||
frappe.reload_doc('manufacturing', 'doctype', 'work_order_item')
|
||||
frappe.reload_doc('manufacturing', 'doctype', 'job_card')
|
||||
frappe.reload_doc('manufacturing', 'doctype', 'job_card_item')
|
||||
|
||||
for d in frappe.db.sql("""select work_order, name from tabTimesheet
|
||||
where (work_order is not null and work_order != '') and docstatus = 0""", as_dict=1):
|
||||
if d.work_order:
|
||||
doc = frappe.get_doc('Work Order', d.work_order)
|
||||
doc.make_job_card()
|
||||
frappe.delete_doc('Timesheet', d.name)
|
@ -444,6 +444,39 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "start_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "User",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "User",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -508,73 +541,6 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"collapsible_depends_on": "",
|
||||
"columns": 0,
|
||||
"depends_on": "work_order",
|
||||
"fieldname": "work_detail",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Work Detail",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "work_order",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Work Order",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Work Order",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -1066,7 +1032,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-21 14:44:32.912004",
|
||||
"modified": "2018-08-28 14:44:32.912004",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Timesheet",
|
||||
|
@ -97,19 +97,13 @@ class Timesheet(Document):
|
||||
self.set_status()
|
||||
|
||||
def on_cancel(self):
|
||||
self.update_work_order(None)
|
||||
self.update_task_and_project()
|
||||
|
||||
def on_submit(self):
|
||||
self.validate_mandatory_fields()
|
||||
self.update_work_order(self.name)
|
||||
self.update_task_and_project()
|
||||
|
||||
def validate_mandatory_fields(self):
|
||||
if self.work_order:
|
||||
work_order = frappe.get_doc("Work Order", self.work_order)
|
||||
pending_qty = flt(work_order.qty) - flt(work_order.produced_qty)
|
||||
|
||||
for data in self.time_logs:
|
||||
if not data.from_time and not data.to_time:
|
||||
frappe.throw(_("Row {0}: From Time and To Time is mandatory.").format(data.idx))
|
||||
@ -120,41 +114,6 @@ class Timesheet(Document):
|
||||
if flt(data.hours) == 0.0:
|
||||
frappe.throw(_("Row {0}: Hours value must be greater than zero.").format(data.idx))
|
||||
|
||||
if self.work_order and flt(data.completed_qty) == 0:
|
||||
frappe.throw(_("Row {0}: Completed Qty must be greater than zero.").format(data.idx))
|
||||
|
||||
if self.work_order and flt(pending_qty) < flt(data.completed_qty) and flt(pending_qty) > 0:
|
||||
frappe.throw(_("Row {0}: Completed Qty cannot be more than {1} for operation {2}").format(data.idx, pending_qty, data.operation),
|
||||
OverWorkLoggedError)
|
||||
|
||||
def update_work_order(self, time_sheet):
|
||||
if self.work_order:
|
||||
pro = frappe.get_doc('Work Order', self.work_order)
|
||||
|
||||
for timesheet in self.time_logs:
|
||||
for data in pro.operations:
|
||||
if data.name == timesheet.operation_id:
|
||||
summary = self.get_actual_timesheet_summary(timesheet.operation_id)
|
||||
data.time_sheet = time_sheet
|
||||
data.completed_qty = summary.completed_qty
|
||||
data.actual_operation_time = summary.mins
|
||||
data.actual_start_time = summary.from_time
|
||||
data.actual_end_time = summary.to_time
|
||||
|
||||
pro.flags.ignore_validate_update_after_submit = True
|
||||
pro.update_operation_status()
|
||||
pro.calculate_operating_cost()
|
||||
pro.set_actual_dates()
|
||||
pro.save()
|
||||
|
||||
def get_actual_timesheet_summary(self, operation_id):
|
||||
"""Returns 'Actual Operating Time'. """
|
||||
return frappe.db.sql("""select
|
||||
sum(tsd.hours*60) as mins, sum(tsd.completed_qty) as completed_qty, min(tsd.from_time) as from_time,
|
||||
max(tsd.to_time) as to_time from `tabTimesheet Detail` as tsd, `tabTimesheet` as ts where
|
||||
ts.work_order = %s and tsd.operation_id = %s and ts.docstatus=1 and ts.name = tsd.parent""",
|
||||
(self.work_order, operation_id), as_dict=1)[0]
|
||||
|
||||
def update_task_and_project(self):
|
||||
tasks, projects = [], []
|
||||
|
||||
@ -176,16 +135,12 @@ class Timesheet(Document):
|
||||
|
||||
def validate_time_logs(self):
|
||||
for data in self.get('time_logs'):
|
||||
self.check_workstation_timings(data)
|
||||
self.validate_overlap(data)
|
||||
|
||||
def validate_overlap(self, data):
|
||||
settings = frappe.get_single('Projects Settings')
|
||||
if self.work_order:
|
||||
self.validate_overlap_for("workstation", data, data.workstation, settings.ignore_workstation_time_overlap)
|
||||
else:
|
||||
self.validate_overlap_for("user", data, self.user, settings.ignore_user_time_overlap)
|
||||
self.validate_overlap_for("employee", data, self.employee, settings.ignore_employee_time_overlap)
|
||||
self.validate_overlap_for("user", data, self.user, settings.ignore_user_time_overlap)
|
||||
self.validate_overlap_for("employee", data, self.employee, settings.ignore_employee_time_overlap)
|
||||
|
||||
def validate_overlap_for(self, fieldname, args, value, ignore_validation=False):
|
||||
if not value or ignore_validation:
|
||||
@ -227,48 +182,6 @@ class Timesheet(Document):
|
||||
|
||||
return existing[0] if existing else None
|
||||
|
||||
def check_workstation_timings(self, args):
|
||||
"""Checks if **Time Log** is between operating hours of the **Workstation**."""
|
||||
if args.workstation and args.from_time and args.to_time:
|
||||
check_if_within_operating_hours(args.workstation, args.operation, args.from_time, args.to_time)
|
||||
|
||||
def schedule_for_work_order(self, index):
|
||||
for data in self.time_logs:
|
||||
if data.idx == index:
|
||||
self.move_to_next_day(data) #check for workstation holiday
|
||||
self.move_to_next_non_overlapping_slot(data) #check for overlap
|
||||
break
|
||||
|
||||
def move_to_next_non_overlapping_slot(self, data):
|
||||
overlapping = self.get_overlap_for("workstation", data, data.workstation)
|
||||
if overlapping:
|
||||
time_sheet = self.get_last_working_slot(overlapping.name, data.workstation)
|
||||
data.from_time = get_datetime(time_sheet.to_time) + get_mins_between_operations()
|
||||
data.to_time = self.get_to_time(data)
|
||||
self.check_workstation_working_day(data)
|
||||
|
||||
def get_last_working_slot(self, time_sheet, workstation):
|
||||
return frappe.db.sql(""" select max(from_time) as from_time, max(to_time) as to_time
|
||||
from `tabTimesheet Detail` where workstation = %(workstation)s""",
|
||||
{'workstation': workstation}, as_dict=True)[0]
|
||||
|
||||
def move_to_next_day(self, data):
|
||||
"""Move start and end time one day forward"""
|
||||
self.check_workstation_working_day(data)
|
||||
|
||||
def check_workstation_working_day(self, data):
|
||||
while True:
|
||||
try:
|
||||
self.check_workstation_timings(data)
|
||||
break
|
||||
except WorkstationHolidayError:
|
||||
if frappe.message_log: frappe.message_log.pop()
|
||||
data.from_time = get_datetime(data.from_time) + timedelta(hours=24)
|
||||
data.to_time = self.get_to_time(data)
|
||||
|
||||
def get_to_time(self, data):
|
||||
return get_datetime(data.from_time) + timedelta(hours=data.hours)
|
||||
|
||||
def update_cost(self):
|
||||
for data in self.time_logs:
|
||||
if data.activity_type or data.billable:
|
||||
|
@ -779,6 +779,39 @@
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "job_card",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Job Card",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Job Card",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
@ -793,7 +826,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2018-08-30 07:28:01.070112",
|
||||
"modified": "2018-09-05 07:28:01.070112",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Material Request",
|
||||
|
@ -15,6 +15,7 @@ from erpnext.controllers.buying_controller import BuyingController
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
|
||||
from erpnext.buying.utils import check_for_closed_status, validate_for_items
|
||||
from erpnext.stock.doctype.item.item import get_item_defaults
|
||||
from erpnext.manufacturing.doctype.job_card.job_card import update_job_card_reference
|
||||
|
||||
from six import string_types
|
||||
|
||||
@ -92,6 +93,9 @@ class MaterialRequest(BuyingController):
|
||||
if self.material_request_type == 'Purchase':
|
||||
self.validate_budget()
|
||||
|
||||
if self.job_card:
|
||||
update_job_card_reference(self.job_card, 'material_request', self.name)
|
||||
|
||||
def before_save(self):
|
||||
self.set_status(update=True)
|
||||
|
||||
@ -144,6 +148,8 @@ class MaterialRequest(BuyingController):
|
||||
def on_cancel(self):
|
||||
self.update_requested_qty()
|
||||
self.update_requested_qty_in_production_plan()
|
||||
if self.job_card:
|
||||
update_job_card_reference(self.job_card, 'material_request', None)
|
||||
|
||||
def update_completed_qty(self, mr_items=None, update_modified=True):
|
||||
if self.material_request_type == "Purchase":
|
||||
@ -407,7 +413,11 @@ def make_stock_entry(source_name, target_doc=None):
|
||||
|
||||
def set_missing_values(source, target):
|
||||
target.purpose = source.material_request_type
|
||||
if source.job_card:
|
||||
target.purpose = 'Material Transfer for Manufacture'
|
||||
|
||||
target.run_method("calculate_rate_and_amount")
|
||||
target.set_job_card_data()
|
||||
|
||||
doclist = get_mapped_doc("Material Request", source_name, {
|
||||
"Material Request": {
|
||||
|
@ -654,7 +654,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
work_order: function() {
|
||||
var me = this;
|
||||
this.toggle_enable_bom();
|
||||
if(!me.frm.doc.work_order) {
|
||||
if(!me.frm.doc.work_order || me.frm.doc.job_card) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1936,6 +1936,39 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "job_card",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Job Card",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Job Card",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -2015,7 +2048,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-29 06:27:59.630826",
|
||||
"modified": "2018-09-05 06:27:59.630826",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Entry",
|
||||
|
@ -17,6 +17,7 @@ from erpnext.stock.utils import get_bin
|
||||
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos
|
||||
|
||||
import json
|
||||
from erpnext.manufacturing.doctype.job_card.job_card import update_job_card_reference
|
||||
|
||||
from six import string_types, itervalues, iteritems
|
||||
|
||||
@ -59,6 +60,7 @@ class StockEntry(StockController):
|
||||
self.validate_batch()
|
||||
self.validate_inspection()
|
||||
self.validate_fg_completed_qty()
|
||||
self.set_job_card_data()
|
||||
|
||||
if not self.from_bom:
|
||||
self.fg_completed_qty = 0.0
|
||||
@ -88,6 +90,9 @@ class StockEntry(StockController):
|
||||
self.update_so_in_serial_number()
|
||||
|
||||
|
||||
if self.job_card:
|
||||
update_job_card_reference(self.job_card, 'stock_entry', self.name)
|
||||
|
||||
def on_cancel(self):
|
||||
|
||||
if self.purchase_order and self.purpose == "Subcontract":
|
||||
@ -102,6 +107,18 @@ class StockEntry(StockController):
|
||||
self.make_gl_entries_on_cancel()
|
||||
self.update_cost_in_project()
|
||||
|
||||
if self.job_card:
|
||||
update_job_card_reference(self.job_card, 'stock_entry', None)
|
||||
|
||||
def set_job_card_data(self):
|
||||
if self.job_card and not self.work_order:
|
||||
data = frappe.db.get_value('Job Card',
|
||||
self.job_card, ['for_quantity', 'work_order', 'bom_no'], as_dict=1)
|
||||
self.fg_completed_qty = data.for_quantity
|
||||
self.work_order = data.work_order
|
||||
self.from_bom = 1
|
||||
self.bom_no = data.bom_no
|
||||
|
||||
def validate_work_order_status(self):
|
||||
pro_doc = frappe.get_doc("Work Order", self.work_order)
|
||||
if pro_doc.status == 'Completed':
|
||||
@ -584,6 +601,10 @@ class StockEntry(StockController):
|
||||
if pro_doc.status == 'Stopped':
|
||||
frappe.throw(_("Transaction not allowed against stopped Work Order {0}").format(self.work_order))
|
||||
|
||||
if self.job_card:
|
||||
job_doc = frappe.get_doc('Job Card', self.job_card)
|
||||
job_doc.set_transferred_qty()
|
||||
|
||||
if self.work_order:
|
||||
pro_doc = frappe.get_doc("Work Order", self.work_order)
|
||||
_validate_work_order(pro_doc)
|
||||
|
Loading…
Reference in New Issue
Block a user