fix: BOM UX
This commit is contained in:
parent
26c46282e8
commit
9326fb78f2
@ -5,6 +5,12 @@ frappe.provide("erpnext.bom");
|
||||
|
||||
frappe.ui.form.on("BOM", {
|
||||
setup: function(frm) {
|
||||
frm.custom_make_buttons = {
|
||||
'BOM': 'Duplicate BOM',
|
||||
'Work Order': 'Work Order',
|
||||
'Quality Inspection': 'Quality Inspection'
|
||||
};
|
||||
|
||||
frm.set_query("bom_no", "items", function() {
|
||||
return {
|
||||
filters: {
|
||||
@ -85,9 +91,21 @@ frappe.ui.form.on("BOM", {
|
||||
}
|
||||
|
||||
if(frm.doc.docstatus!=0) {
|
||||
frm.add_custom_button(__("Duplicate"), function() {
|
||||
frm.add_custom_button(__("Duplicate BOM"), function() {
|
||||
frm.copy_doc();
|
||||
});
|
||||
}, __("Create"));
|
||||
|
||||
frm.add_custom_button(__("Work Order"), function() {
|
||||
frm.trigger("make_work_order");
|
||||
}, __("Create"));
|
||||
|
||||
if (frm.doc.inspection_required) {
|
||||
frm.add_custom_button(__("Quality Inspection"), function() {
|
||||
frm.trigger("make_quality_inspection");
|
||||
}, __("Create"));
|
||||
}
|
||||
|
||||
frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||
}
|
||||
|
||||
if(frm.doc.items && frm.doc.allow_alternative_item) {
|
||||
@ -109,6 +127,41 @@ frappe.ui.form.on("BOM", {
|
||||
}
|
||||
},
|
||||
|
||||
make_work_order: function(frm) {
|
||||
const fields = [{
|
||||
fieldtype: 'Float',
|
||||
label: __('Qty To Manufacture'),
|
||||
fieldname: 'qty',
|
||||
reqd: 1,
|
||||
default: 1
|
||||
}];
|
||||
|
||||
frappe.prompt(fields, data => {
|
||||
frappe.call({
|
||||
method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order",
|
||||
args: {
|
||||
item: frm.doc.item,
|
||||
qty: data.qty || 0.0,
|
||||
project: frm.doc.project
|
||||
},
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
var doc = frappe.model.sync(r.message)[0];
|
||||
frappe.set_route("Form", doc.doctype, doc.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, __("Enter Value"), __("Create"));
|
||||
},
|
||||
|
||||
make_quality_inspection: function(frm) {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.stock.doctype.quality_inspection.quality_inspection.make_quality_inspection",
|
||||
frm: frm
|
||||
})
|
||||
},
|
||||
|
||||
update_cost: function(frm) {
|
||||
return frappe.call({
|
||||
doc: frm.doc,
|
||||
|
@ -3,33 +3,36 @@
|
||||
"creation": "2013-01-22 15:11:38",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"item",
|
||||
"item_name",
|
||||
"image",
|
||||
"uom",
|
||||
"quantity",
|
||||
"set_rate_of_sub_assembly_item_based_on_bom",
|
||||
"cb0",
|
||||
"is_active",
|
||||
"is_default",
|
||||
"with_operations",
|
||||
"inspection_required",
|
||||
"allow_alternative_item",
|
||||
"allow_same_item_multiple_times",
|
||||
"set_rate_of_sub_assembly_item_based_on_bom",
|
||||
"quality_inspection_template",
|
||||
"image",
|
||||
"item_name",
|
||||
"uom",
|
||||
"currency_detail",
|
||||
"company",
|
||||
"transfer_material_against",
|
||||
"project",
|
||||
"conversion_rate",
|
||||
"column_break_12",
|
||||
"currency",
|
||||
"rm_cost_as_per",
|
||||
"buying_price_list",
|
||||
"operations_section",
|
||||
"section_break_21",
|
||||
"with_operations",
|
||||
"column_break_23",
|
||||
"transfer_material_against",
|
||||
"routing",
|
||||
"operations_section",
|
||||
"operations",
|
||||
"materials_section",
|
||||
"inspection_required",
|
||||
"quality_inspection_template",
|
||||
"items",
|
||||
"scrap_section",
|
||||
"scrap_items",
|
||||
@ -41,14 +44,9 @@
|
||||
"base_operating_cost",
|
||||
"base_raw_material_cost",
|
||||
"base_scrap_material_cost",
|
||||
"total_cost_of_bom",
|
||||
"total_cost",
|
||||
"column_break_26",
|
||||
"total_cost",
|
||||
"base_total_cost",
|
||||
"more_info_section",
|
||||
"project",
|
||||
"amended_from",
|
||||
"col_break23",
|
||||
"section_break_25",
|
||||
"description",
|
||||
"column_break_27",
|
||||
@ -57,12 +55,14 @@
|
||||
"website_section",
|
||||
"show_in_website",
|
||||
"route",
|
||||
"column_break_52",
|
||||
"website_image",
|
||||
"thumbnail",
|
||||
"sb_web_spec",
|
||||
"web_long_description",
|
||||
"show_items",
|
||||
"show_operations"
|
||||
"show_operations",
|
||||
"web_long_description",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@ -152,7 +152,7 @@
|
||||
"default": "0",
|
||||
"fieldname": "inspection_required",
|
||||
"fieldtype": "Check",
|
||||
"label": "Inspection Required"
|
||||
"label": "Quality Inspection Required"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@ -160,12 +160,6 @@
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow Alternative Item"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "allow_same_item_multiple_times",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow Same Item Multiple Times"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"default": "1",
|
||||
@ -193,6 +187,7 @@
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "Work Order",
|
||||
"fieldname": "transfer_material_against",
|
||||
"fieldtype": "Select",
|
||||
"label": "Transfer Material Against",
|
||||
@ -235,10 +230,10 @@
|
||||
{
|
||||
"fieldname": "operations_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Operations",
|
||||
"oldfieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "with_operations",
|
||||
"fieldname": "routing",
|
||||
"fieldtype": "Link",
|
||||
"label": "Routing",
|
||||
@ -335,10 +330,6 @@
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "total_cost_of_bom",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "total_cost",
|
||||
"fieldtype": "Currency",
|
||||
@ -359,10 +350,6 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "more_info_section",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
@ -381,10 +368,6 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "col_break23",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_25",
|
||||
"fieldtype": "Section Break"
|
||||
@ -481,13 +464,26 @@
|
||||
"fieldname": "show_operations",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Operations"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_21",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Operations"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_23",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_52",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-sitemap",
|
||||
"idx": 1,
|
||||
"image_field": "image",
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-07-30 17:00:09.665068",
|
||||
"modified": "2019-11-22 14:35:12.142150",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM",
|
||||
|
@ -96,6 +96,7 @@ class BOM(WebsiteGenerator):
|
||||
|
||||
def get_routing(self):
|
||||
if self.routing:
|
||||
self.set("operations", [])
|
||||
for d in frappe.get_all("BOM Operation", fields = ["*"],
|
||||
filters = {'parenttype': 'Routing', 'parent': self.routing}):
|
||||
child = self.append('operations', d)
|
||||
@ -289,7 +290,7 @@ class BOM(WebsiteGenerator):
|
||||
if not valuation_rate:
|
||||
valuation_rate = frappe.db.get_value("Item", args['item_code'], "valuation_rate")
|
||||
|
||||
return valuation_rate
|
||||
return flt(valuation_rate)
|
||||
|
||||
def manage_default_bom(self):
|
||||
""" Uncheck others if current one is selected as default or
|
||||
@ -362,15 +363,9 @@ class BOM(WebsiteGenerator):
|
||||
def validate_materials(self):
|
||||
""" Validate raw material entries """
|
||||
|
||||
def get_duplicates(lst):
|
||||
seen = set()
|
||||
seen_add = seen.add
|
||||
for item in lst:
|
||||
if item.item_code in seen or seen_add(item.item_code):
|
||||
yield item
|
||||
|
||||
if not self.get('items'):
|
||||
frappe.throw(_("Raw Materials cannot be blank."))
|
||||
|
||||
check_list = []
|
||||
for m in self.get('items'):
|
||||
if m.bom_no:
|
||||
@ -379,16 +374,6 @@ class BOM(WebsiteGenerator):
|
||||
frappe.throw(_("Quantity required for Item {0} in row {1}").format(m.item_code, m.idx))
|
||||
check_list.append(m)
|
||||
|
||||
if not self.allow_same_item_multiple_times:
|
||||
duplicate_items = list(get_duplicates(check_list))
|
||||
if duplicate_items:
|
||||
li = []
|
||||
for i in duplicate_items:
|
||||
li.append("{0} on row {1}".format(i.item_code, i.idx))
|
||||
duplicate_list = '<br>' + '<br>'.join(li)
|
||||
|
||||
frappe.throw(_("Same item has been entered multiple times. {0}").format(duplicate_list))
|
||||
|
||||
def check_recursion(self, bom_list=[]):
|
||||
""" Check whether recursion occurs in any bom"""
|
||||
bom_list = self.traverse_tree()
|
||||
|
@ -17,11 +17,13 @@ def get_data():
|
||||
},
|
||||
{
|
||||
'label': _('Manufacture'),
|
||||
'items': ['BOM', 'Work Order', 'Job Card', 'Production Plan']
|
||||
'items': ['BOM', 'Work Order', 'Job Card']
|
||||
},
|
||||
{
|
||||
'label': _('Purchase'),
|
||||
'label': _('Subcontract'),
|
||||
'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
|
||||
}
|
||||
]
|
||||
],
|
||||
'disable_create_buttons': ["Item", "Purchase Order", "Purchase Receipt",
|
||||
"Purchase Invoice", "Job Card", "Stock Entry"]
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -529,7 +529,6 @@ def get_material_request_items(row, sales_order,
|
||||
required_qty = ceil(required_qty)
|
||||
|
||||
if required_qty > 0:
|
||||
print(row)
|
||||
return {
|
||||
'item_code': row.item_code,
|
||||
'item_name': row.item_name,
|
||||
|
@ -609,6 +609,22 @@ def get_item_details(item, project = None):
|
||||
|
||||
return res
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_work_order(item, qty=0, project=None):
|
||||
if not frappe.has_permission("Work Order", "write"):
|
||||
frappe.throw(_("Not permitted"), frappe.PermissionError)
|
||||
|
||||
item_details = get_item_details(item, project)
|
||||
|
||||
wo_doc = frappe.new_doc("Work Order")
|
||||
wo_doc.production_item = item
|
||||
wo_doc.update(item_details)
|
||||
if qty > 0:
|
||||
wo_doc.qty = qty
|
||||
wo_doc.get_items_and_operations_from_bom()
|
||||
|
||||
return wo_doc
|
||||
|
||||
@frappe.whitelist()
|
||||
def check_if_scrap_warehouse_mandatory(bom_no):
|
||||
res = {"set_scrap_wh_mandatory": False }
|
||||
|
@ -15,13 +15,6 @@ def execute():
|
||||
|
||||
rename_field(doctype, "allow_transfer_for_manufacture", "include_item_in_manufacturing")
|
||||
|
||||
if frappe.db.has_column('BOM', 'allow_same_item_multiple_times'):
|
||||
frappe.db.sql(""" UPDATE tabBOM
|
||||
SET
|
||||
allow_same_item_multiple_times = 0
|
||||
WHERE
|
||||
trim(coalesce(allow_same_item_multiple_times, '')) = '' """)
|
||||
|
||||
for doctype in ['BOM', 'Work Order']:
|
||||
frappe.reload_doc('manufacturing', 'doctype', frappe.scrub(doctype))
|
||||
|
||||
|
@ -6,6 +6,7 @@ import frappe
|
||||
from frappe.model.document import Document
|
||||
from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template \
|
||||
import get_template_details
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
class QualityInspection(Document):
|
||||
def validate(self):
|
||||
@ -84,3 +85,37 @@ def item_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
parent=filters.get('parent'), cond = cond, mcond = mcond, start = start,
|
||||
page_len = page_len, qi_condition = qi_condition),
|
||||
{'parent': filters.get('parent'), 'txt': "%%%s%%" % txt})
|
||||
|
||||
def quality_inspection_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.get_all('Quality Inspection',
|
||||
limit_start=start,
|
||||
limit_page_length=page_len,
|
||||
filters = {
|
||||
'docstatus': 1,
|
||||
'name': ('like', '%%%s%%' % txt),
|
||||
'item_code': filters.get("item_code"),
|
||||
'reference_name': ('in', [filters.get("reference_name", ''), ''])
|
||||
}, as_list=1)
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_quality_inspection(source_name, target_doc=None):
|
||||
def postprocess(source, doc):
|
||||
doc.inspected_by = frappe.session.user
|
||||
doc.get_quality_inspection_template()
|
||||
|
||||
doc = get_mapped_doc("BOM", source_name, {
|
||||
'BOM': {
|
||||
"doctype": "Quality Inspection",
|
||||
"validation": {
|
||||
"docstatus": ["=", 1]
|
||||
},
|
||||
"field_map": {
|
||||
"name": "bom_no",
|
||||
"item": "item_code",
|
||||
"stock_uom": "uom",
|
||||
"stock_qty": "qty"
|
||||
},
|
||||
}
|
||||
}, target_doc, postprocess)
|
||||
|
||||
return doc
|
@ -102,11 +102,12 @@ frappe.ui.form.on('Stock Entry', {
|
||||
|
||||
frm.set_query("quality_inspection", "items", function(doc, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
|
||||
return {
|
||||
query:"erpnext.stock.doctype.quality_inspection.quality_inspection.quality_inspection_query",
|
||||
filters: {
|
||||
docstatus: 1,
|
||||
item_code: d.item_code,
|
||||
reference_name: doc.name
|
||||
'item_code': d.item_code,
|
||||
'reference_name': doc.name
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -91,6 +91,7 @@ class StockEntry(StockController):
|
||||
self.update_cost_in_project()
|
||||
self.validate_reserved_serial_no_consumption()
|
||||
self.update_transferred_qty()
|
||||
self.update_quality_inspection()
|
||||
if self.work_order and self.purpose == "Manufacture":
|
||||
self.update_so_in_serial_number()
|
||||
|
||||
@ -108,6 +109,7 @@ class StockEntry(StockController):
|
||||
self.make_gl_entries_on_cancel()
|
||||
self.update_cost_in_project()
|
||||
self.update_transferred_qty()
|
||||
self.update_quality_inspection()
|
||||
|
||||
def set_job_card_data(self):
|
||||
if self.job_card and not self.work_order:
|
||||
@ -1285,6 +1287,20 @@ class StockEntry(StockController):
|
||||
|
||||
self._update_percent_field_in_targets(args, update_modified=True)
|
||||
|
||||
def update_quality_inspection(self):
|
||||
if self.inspection_required:
|
||||
reference_type = reference_name = ''
|
||||
if self.docstatus == 1:
|
||||
reference_name = self.name
|
||||
reference_type = 'Stock Entry'
|
||||
|
||||
for d in self.items:
|
||||
if d.quality_inspection:
|
||||
frappe.db.set_value("Quality Inspection", d.quality_inspection, {
|
||||
'reference_type': reference_type,
|
||||
'reference_name': reference_name
|
||||
})
|
||||
|
||||
@frappe.whitelist()
|
||||
def move_sample_to_retention_warehouse(company, items):
|
||||
if isinstance(items, string_types):
|
||||
|
Loading…
Reference in New Issue
Block a user