feat: BOM template (#21262)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
This commit is contained in:
rohitwaghchaure 2020-05-21 18:10:13 +05:30 committed by Nabin Hait
parent 64301c8adb
commit ed26db899a
10 changed files with 546 additions and 623 deletions

View File

@ -188,12 +188,6 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
# scan description only if items are less than 50000
description_cond = 'or tabItem.description LIKE %(txt)s'
extra_cond = " and tabItem.has_variants=0"
if (filters and isinstance(filters, dict)
and filters.get("doctype") == "BOM"):
extra_cond = ""
del filters["doctype"]
return frappe.db.sql("""select tabItem.name,
if(length(tabItem.item_name) > 40,
concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
@ -204,10 +198,10 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
from tabItem
where tabItem.docstatus < 2
and tabItem.disabled=0
and tabItem.has_variants=0
and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00')
and ({scond} or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s)
{description_cond})
{extra_cond}
{fcond} {mcond}
order by
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
@ -218,7 +212,6 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
key=searchfield,
columns=columns,
scond=searchfields,
extra_cond=extra_cond,
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
mcond=get_match_cond(doctype).replace('%', '%%'),
description_cond = description_cond),

View File

@ -29,10 +29,7 @@ frappe.ui.form.on("BOM", {
frm.set_query("item", function() {
return {
query: "erpnext.controllers.queries.item_query",
filters: {
"doctype": "BOM"
}
query: "erpnext.manufacturing.doctype.bom.bom.item_query"
};
});
@ -44,9 +41,12 @@ frappe.ui.form.on("BOM", {
};
});
frm.set_query("item_code", "items", function() {
frm.set_query("item_code", "items", function(doc) {
return {
query: "erpnext.controllers.queries.item_query"
query: "erpnext.manufacturing.doctype.bom.bom.item_query",
filters: {
"item_code": doc.item
}
};
});
@ -96,6 +96,12 @@ frappe.ui.form.on("BOM", {
frm.trigger("make_work_order");
}, __("Create"));
if (frm.doc.has_variants) {
frm.add_custom_button(__("Variant BOM"), function() {
frm.trigger("make_variant_bom");
}, __("Create"));
}
if (frm.doc.inspection_required) {
frm.add_custom_button(__("Quality Inspection"), function() {
frm.trigger("make_quality_inspection");
@ -124,7 +130,7 @@ frappe.ui.form.on("BOM", {
}
if (frm.doc.__onload && frm.doc.__onload["has_variants"]) {
if (frm.doc.has_variants) {
frm.set_intro(__('This is a Template BOM and will be used to make the work order for {0} of the item {1}',
[
`<a class="variants-intro">variants</a>`,
@ -138,9 +144,52 @@ frappe.ui.form.on("BOM", {
},
make_work_order: function(frm) {
frm.events.setup_variant_prompt(frm, "Work Order", (frm, item, data, variant_items) => {
frappe.call({
method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order",
args: {
bom_no: frm.doc.name,
item: item,
qty: data.qty || 0.0,
project: frm.doc.project,
variant_items: variant_items
},
freeze: true,
callback: function(r) {
if(r.message) {
let doc = frappe.model.sync(r.message)[0];
frappe.set_route("Form", doc.doctype, doc.name);
}
}
});
});
},
make_variant_bom: function(frm) {
frm.events.setup_variant_prompt(frm, "Variant BOM", (frm, item, data, variant_items) => {
frappe.call({
method: "erpnext.manufacturing.doctype.bom.bom.make_variant_bom",
args: {
source_name: frm.doc.name,
bom_no: frm.doc.name,
item: item,
variant_items: variant_items
},
freeze: true,
callback: function(r) {
if(r.message) {
let doc = frappe.model.sync(r.message)[0];
frappe.set_route("Form", doc.doctype, doc.name);
}
}
});
}, true);
},
setup_variant_prompt: function(frm, title, callback, skip_qty_field) {
const fields = [];
if (frm.doc.__onload && frm.doc.__onload["has_variants"]) {
if (frm.doc.has_variants) {
fields.push({
fieldtype: 'Link',
label: __('Variant Item'),
@ -158,34 +207,106 @@ frappe.ui.form.on("BOM", {
});
}
fields.push({
fieldtype: 'Float',
label: __('Qty To Manufacture'),
fieldname: 'qty',
reqd: 1,
default: 1
if (!skip_qty_field) {
fields.push({
fieldtype: 'Float',
label: __('Qty To Manufacture'),
fieldname: 'qty',
reqd: 1,
default: 1
});
}
var has_template_rm = frm.doc.items.filter(d => d.has_variants === 1) || [];
if (has_template_rm && has_template_rm.length > 0) {
fields.push({
fieldname: "items",
fieldtype: "Table",
label: __("Raw Materials"),
fields: [
{
fieldname: "item_code",
options: "Item",
label: __("Template Item"),
fieldtype: "Link",
in_list_view: 1,
reqd: 1,
},
{
fieldname: "varint_item_code",
options: "Item",
label: __("Variant Item"),
fieldtype: "Link",
in_list_view: 1,
reqd: 1,
get_query: function(data) {
if (!data.item_code) {
frappe.throw(__("Select template item"));
}
return {
query: "erpnext.controllers.queries.item_query",
filters: {
"variant_of": data.item_code
}
};
}
},
{
fieldname: "qty",
label: __("Quantity"),
fieldtype: "Float",
in_list_view: 1,
reqd: 1,
},
{
fieldname: "source_warehouse",
label: __("Source Warehouse"),
fieldtype: "Link",
options: "Warehouse"
},
{
fieldname: "operation",
label: __("Operation"),
fieldtype: "Data",
hidden: 1,
}
],
in_place_edit: true,
data: [],
get_data: function () {
return [];
},
});
}
let dialog = frappe.prompt(fields, data => {
let item = data.item || frm.doc.item;
let variant_items = data.items || [];
variant_items.forEach(d => {
if (!d.varint_item_code) {
frappe.throw(__("Select variant item code for the template item {0}", [d.item_code]));
}
})
callback(frm, item, data, variant_items);
}, __(title), __("Create"));
has_template_rm.forEach(d => {
dialog.fields_dict.items.df.data.push({
"item_code": d.item_code,
"varint_item_code": "",
"qty": d.qty,
"source_warehouse": d.source_warehouse,
"operation": d.operation
});
});
frappe.prompt(fields, data => {
let item = data.item || frm.doc.item;
frappe.call({
method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order",
args: {
bom_no: frm.doc.name,
item: 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"));
if (has_template_rm) {
dialog.fields_dict.items.grid.refresh();
}
},
make_quality_inspection: function(frm) {

View File

@ -53,6 +53,7 @@
"section_break_25",
"description",
"column_break_27",
"has_variants",
"section_break0",
"exploded_items",
"website_section",
@ -498,6 +499,17 @@
"options": "Currency",
"print_hide": 1,
"read_only": 1
},
{
"default": "0",
"fetch_from": "item.has_variants",
"fieldname": "has_variants",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Has Variants",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
}
],
"icon": "fa fa-sitemap",
@ -505,7 +517,7 @@
"image_field": "image",
"is_submittable": 1,
"links": [],
"modified": "2020-05-05 14:29:32.634952",
"modified": "2020-05-21 12:29:32.634952",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM",

View File

@ -3,13 +3,16 @@
from __future__ import unicode_literals
import frappe, erpnext
from frappe.utils import cint, cstr, flt
from frappe.utils import cint, cstr, flt, today
from frappe import _
from erpnext.setup.utils import get_exchange_rate
from frappe.website.website_generator import WebsiteGenerator
from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.stock.get_item_details import get_price_list_rate
from frappe.core.doctype.version.version import get_diff
from erpnext.controllers.queries import get_match_cond
from erpnext.stock.doctype.item.item import get_item_details
from frappe.model.mapper import get_mapped_doc
import functools
@ -59,11 +62,6 @@ class BOM(WebsiteGenerator):
self.name = name
def onload(self):
super(BOM, self).onload()
if self.get("item") and cint(frappe.db.get_value("Item", self.item, "has_variants")):
self.set_onload("has_variants", True)
def validate(self):
self.route = frappe.scrub(self.name).replace('_', '-')
self.clear_operations()
@ -103,9 +101,7 @@ class BOM(WebsiteGenerator):
self.manage_default_bom()
def get_item_det(self, item_code):
item = frappe.db.sql("""select name, item_name, docstatus, description, image,
is_sub_contracted_item, stock_uom, default_bom, last_purchase_rate, include_item_in_manufacturing
from `tabItem` where name=%s""", item_code, as_dict = 1)
item = get_item_details(item_code)
if not item:
frappe.throw(_("Item: {0} does not exist in the system").format(item_code))
@ -150,10 +146,10 @@ class BOM(WebsiteGenerator):
item = self.get_item_det(args['item_code'])
args['bom_no'] = args['bom_no'] or item and cstr(item[0]['default_bom']) or ''
args['bom_no'] = args['bom_no'] or item and cstr(item['default_bom']) or ''
args['transfer_for_manufacture'] = (cstr(args.get('include_item_in_manufacturing', '')) or
item and item[0].include_item_in_manufacturing or 0)
args.update(item[0])
item and item.include_item_in_manufacturing or 0)
args.update(item)
rate = self.get_rm_rate(args)
ret_item = {
@ -185,40 +181,14 @@ class BOM(WebsiteGenerator):
self.rm_cost_as_per = "Valuation Rate"
if arg.get('scrap_items'):
rate = self.get_valuation_rate(arg)
rate = get_valuation_rate(arg)
elif arg:
#Customer Provided parts will have zero rate
if not frappe.db.get_value('Item', arg["item_code"], 'is_customer_provided_item'):
if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom:
rate = flt(self.get_bom_unitcost(arg['bom_no'])) * (arg.get("conversion_factor") or 1)
else:
if self.rm_cost_as_per == 'Valuation Rate':
rate = self.get_valuation_rate(arg) * (arg.get("conversion_factor") or 1)
elif self.rm_cost_as_per == 'Last Purchase Rate':
rate = flt(arg.get('last_purchase_rate') \
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")) \
* (arg.get("conversion_factor") or 1)
elif self.rm_cost_as_per == "Price List":
if not self.buying_price_list:
frappe.throw(_("Please select Price List"))
args = frappe._dict({
"doctype": "BOM",
"price_list": self.buying_price_list,
"qty": arg.get("qty") or 1,
"uom": arg.get("uom") or arg.get("stock_uom"),
"stock_uom": arg.get("stock_uom"),
"transaction_type": "buying",
"company": self.company,
"currency": self.currency,
"conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function
"conversion_factor": arg.get("conversion_factor") or 1,
"plc_conversion_rate": 1,
"ignore_party": True
})
item_doc = frappe.get_doc("Item", arg.get("item_code"))
out = frappe._dict()
get_price_list_rate(args, item_doc, out)
rate = out.price_list_rate
rate = get_bom_item_rate(arg, self)
if not rate:
if self.rm_cost_as_per == "Price List":
@ -286,31 +256,6 @@ class BOM(WebsiteGenerator):
where is_active = 1 and name = %s""", bom_no, as_dict=1)
return bom and bom[0]['unit_cost'] or 0
def get_valuation_rate(self, args):
""" Get weighted average of valuation rate from all warehouses """
total_qty, total_value, valuation_rate = 0.0, 0.0, 0.0
for d in frappe.db.sql("""select actual_qty, stock_value from `tabBin`
where item_code=%s""", args['item_code'], as_dict=1):
total_qty += flt(d.actual_qty)
total_value += flt(d.stock_value)
if total_qty:
valuation_rate = total_value / total_qty
if valuation_rate <= 0:
last_valuation_rate = frappe.db.sql("""select valuation_rate
from `tabStock Ledger Entry`
where item_code = %s and valuation_rate > 0
order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code'])
valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0
if not valuation_rate:
valuation_rate = frappe.db.get_value("Item", args['item_code'], "valuation_rate")
return flt(valuation_rate)
def manage_default_bom(self):
""" Uncheck others if current one is selected as default or
check the current one as default if it the only bom for the selected item,
@ -624,6 +569,62 @@ class BOM(WebsiteGenerator):
if not d.batch_size or d.batch_size <= 0:
d.batch_size = 1
def get_bom_item_rate(args, bom_doc):
if bom_doc.rm_cost_as_per == 'Valuation Rate':
rate = get_valuation_rate(args) * (args.get("conversion_factor") or 1)
elif bom_doc.rm_cost_as_per == 'Last Purchase Rate':
rate = ( flt(args.get('last_purchase_rate')) \
or frappe.db.get_value("Item", args['item_code'], "last_purchase_rate")) \
* (args.get("conversion_factor") or 1)
elif bom_doc.rm_cost_as_per == "Price List":
if not bom_doc.buying_price_list:
frappe.throw(_("Please select Price List"))
bom_args = frappe._dict({
"doctype": "BOM",
"price_list": bom_doc.buying_price_list,
"qty": args.get("qty") or 1,
"uom": args.get("uom") or args.get("stock_uom"),
"stock_uom": args.get("stock_uom"),
"transaction_type": "buying",
"company": bom_doc.company,
"currency": bom_doc.currency,
"conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function
"conversion_factor": args.get("conversion_factor") or 1,
"plc_conversion_rate": 1,
"ignore_party": True
})
item_doc = frappe.get_cached_doc("Item", args.get("item_code"))
out = frappe._dict()
get_price_list_rate(bom_args, item_doc, out)
rate = out.price_list_rate
return rate
def get_valuation_rate(args):
""" Get weighted average of valuation rate from all warehouses """
total_qty, total_value, valuation_rate = 0.0, 0.0, 0.0
for d in frappe.db.sql("""select actual_qty, stock_value from `tabBin`
where item_code=%s""", args['item_code'], as_dict=1):
total_qty += flt(d.actual_qty)
total_value += flt(d.stock_value)
if total_qty:
valuation_rate = total_value / total_qty
if valuation_rate <= 0:
last_valuation_rate = frappe.db.sql("""select valuation_rate
from `tabStock Ledger Entry`
where item_code = %s and valuation_rate > 0
order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code'])
valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0
if not valuation_rate:
valuation_rate = frappe.db.get_value("Item", args['item_code'], "valuation_rate")
return flt(valuation_rate)
def get_list_context(context):
context.title = _("Bill of Materials")
# context.introduction = _('Boms')
@ -639,6 +640,8 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
sum(bom_item.{qty_field}/ifnull(bom.quantity, 1)) * %(qty)s as qty,
item.image,
bom.project,
bom_item.rate,
bom_item.amount,
item.stock_uom,
item.item_group,
item.allow_alternative_item,
@ -655,6 +658,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
where
bom_item.docstatus < 2
and bom.name = %(bom)s
and ifnull(item.has_variants, 0) = 0
and item.is_stock_item in (1, {is_stock_item})
{where_conditions}
group by item_code, stock_uom
@ -897,3 +901,84 @@ def get_bom_diff(bom1, bom2):
out.removed.append([df.fieldname, d.as_dict()])
return out
def item_query(doctype, txt, searchfield, start, page_len, filters):
meta = frappe.get_meta("Item", cached=True)
searchfields = meta.get_search_fields()
order_by = "idx desc, name, item_name"
fields = ["name", "item_group", "item_name", "description"]
fields.extend([field for field in searchfields
if not field in ["name", "item_group", "description"]])
searchfields = searchfields + [field for field in [searchfield or "name", "item_code", "item_group", "item_name"]
if not field in searchfields]
query_filters = {
"disabled": 0,
"ifnull(end_of_life, '5050-50-50')": (">", today())
}
or_cond_filters = {}
if txt:
for s_field in searchfields:
or_cond_filters[s_field] = ("like", "%{0}%".format(txt))
barcodes = frappe.get_all("Item Barcode",
fields=["distinct parent as item_code"],
filters = {"barcode": ("like", "%{0}%".format(txt))})
barcodes = [d.item_code for d in barcodes]
if barcodes:
or_cond_filters["name"] = ("in", barcodes)
for cond in get_match_cond(doctype, as_condition=False):
for key, value in cond.items():
if key == doctype:
key = "name"
query_filters[key] = ("in", value)
if filters and filters.get("item_code"):
has_variants = frappe.get_cached_value("Item", filters.get("item_code"), "has_variants")
if not has_variants:
query_filters["has_variants"] = 0
return frappe.get_all("Item",
fields = fields, filters=query_filters,
or_filters = or_cond_filters, order_by=order_by,
limit_start=start, limit_page_length=page_len, as_list=1)
@frappe.whitelist()
def make_variant_bom(source_name, bom_no, item, variant_items, target_doc=None):
from erpnext.manufacturing.doctype.work_order.work_order import add_variant_item
def postprocess(source, doc):
doc.item = item
doc.quantity = 1
item_data = get_item_details(item)
doc.update({
"item_name": item_data.item_name,
"description": item_data.description,
"uom": item_data.stock_uom,
"allow_alternative_item": item_data.allow_alternative_item
})
add_variant_item(variant_items, doc, source_name)
doc = get_mapped_doc('BOM', source_name, {
'BOM': {
'doctype': 'BOM',
'validation': {
'docstatus': ['=', 1]
}
},
'BOM Item': {
'doctype': 'BOM Item',
'condition': lambda doc: doc.has_variants == 0
},
}, target_doc, postprocess)
return doc

View File

@ -1,7 +1,9 @@
frappe.listview_settings['BOM'] = {
add_fields: ["is_active", "is_default", "total_cost"],
add_fields: ["is_active", "is_default", "total_cost", "has_variants"],
get_indicator: function(doc) {
if(doc.is_default) {
if(doc.is_active && doc.has_variants) {
return [__("Template"), "orange", "has_variants,=,Yes"];
} else if(doc.is_default) {
return [__("Default"), "green", "is_default,=,Yes"];
} else if(doc.is_active) {
return [__("Active"), "blue", "is_active,=,Yes"];

View File

@ -1,8 +1,10 @@
{
"actions": [],
"creation": "2013-02-22 01:27:49",
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"item_code",
"item_name",
@ -33,6 +35,7 @@
"scrap",
"qty_consumed_per_unit",
"section_break_27",
"has_variants",
"include_item_in_manufacturing",
"original_item"
],
@ -57,6 +60,7 @@
"label": "Item Name"
},
{
"depends_on": "eval:parent.with_operations == 1",
"fieldname": "operation",
"fieldtype": "Link",
"label": "Item operation",
@ -258,11 +262,22 @@
"label": "Original Item",
"options": "Item",
"read_only": 1
},
{
"default": "0",
"fetch_from": "item_code.has_variants",
"fieldname": "has_variants",
"fieldtype": "Check",
"label": "Has Variants",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
}
],
"idx": 1,
"istable": 1,
"modified": "2019-11-22 11:38:52.087303",
"links": [],
"modified": "2020-04-09 14:30:26.535546",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Item",

View File

@ -449,6 +449,32 @@ frappe.ui.form.on("Work Order Item", {
}
});
}
},
item_code: function(frm, cdt, cdn) {
let row = locals[cdt][cdn];
if (row.item_code) {
frappe.call({
method: "erpnext.stock.doctype.item.item.get_item_details",
args: {
item_code: row.item_code,
company: frm.doc.company
},
callback: function(r) {
if (r.message) {
frappe.model.set_value(cdt, cdn, {
"required_qty": 1,
"item_name": r.message.item_name,
"description": r.message.description,
"source_warehouse": r.message.default_warehouse,
"allow_alternative_item": r.message.allow_alternative_item,
"include_item_in_manufacturing": r.message.include_item_in_manufacturing
});
}
}
});
}
}
});

View File

@ -8,9 +8,9 @@ import math
from frappe import _
from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate, get_link_to_form, time_diff_in_hours
from frappe.model.document import Document
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict, get_bom_item_rate
from dateutil.relativedelta import relativedelta
from erpnext.stock.doctype.item.item import validate_end_of_life
from erpnext.stock.doctype.item.item import validate_end_of_life, get_item_defaults
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError
from erpnext.projects.doctype.timesheet.timesheet import OverlapError
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
@ -541,6 +541,8 @@ class WorkOrder(Document):
# For instance in BOM Explosion Item child table, the items coming from sub assembly items
for item in sorted(item_dict.values(), key=lambda d: d['idx'] or 9999):
self.append('required_items', {
'rate': item.rate,
'amount': item.amount,
'operation': item.operation,
'item_code': item.item_code,
'item_name': item.item_name,
@ -637,9 +639,10 @@ def get_bom_operations(doctype, txt, searchfield, start, page_len, filters):
filters = filters, fields = ['operation'], as_list=1)
@frappe.whitelist()
def get_item_details(item, project = None):
def get_item_details(item, project = None, skip_bom_info=False):
res = frappe.db.sql("""
select stock_uom, description
select stock_uom, description, item_name, allow_alternative_item,
include_item_in_manufacturing
from `tabItem`
where disabled=0
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)
@ -650,6 +653,7 @@ def get_item_details(item, project = None):
return {}
res = res[0]
if skip_bom_info: return res
filters = {"item": item, "is_default": 1}
@ -681,7 +685,7 @@ def get_item_details(item, project = None):
return res
@frappe.whitelist()
def make_work_order(bom_no, item, qty=0, project=None):
def make_work_order(bom_no, item, qty=0, project=None, variant_items=None):
if not frappe.has_permission("Work Order", "write"):
frappe.throw(_("Not permitted"), frappe.PermissionError)
@ -696,8 +700,44 @@ def make_work_order(bom_no, item, qty=0, project=None):
wo_doc.qty = flt(qty)
wo_doc.get_items_and_operations_from_bom()
if variant_items:
add_variant_item(variant_items, wo_doc, bom_no, "required_items")
return wo_doc
def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"):
if isinstance(variant_items, string_types):
variant_items = json.loads(variant_items)
for item in variant_items:
args = frappe._dict({
"item_code": item.get("varint_item_code"),
"required_qty": item.get("qty"),
"qty": item.get("qty"), # for bom
"source_warehouse": item.get("source_warehouse"),
"operation": item.get("operation")
})
bom_doc = frappe.get_cached_doc("BOM", bom_no)
item_data = get_item_details(args.item_code, skip_bom_info=True)
args.update(item_data)
args["rate"] = get_bom_item_rate({
"item_code": args.get("item_code"),
"qty": args.get("required_qty"),
"uom": args.get("stock_uom"),
"stock_uom": args.get("stock_uom"),
"conversion_factor": 1
}, bom_doc)
if not args.source_warehouse:
args["source_warehouse"] = get_item_defaults(item.get("varint_item_code"),
wo_doc.company).default_warehouse
args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate"))
args["uom"] = item_data.stock_uom
wo_doc.append(table_name, args)
@frappe.whitelist()
def check_if_scrap_warehouse_mandatory(bom_no):
res = {"set_scrap_wh_mandatory": False }

View File

@ -1,526 +1,144 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2016-04-18 07:38:26.314642",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"creation": "2016-04-18 07:38:26.314642",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"operation",
"item_code",
"source_warehouse",
"column_break_3",
"item_name",
"description",
"allow_alternative_item",
"include_item_in_manufacturing",
"qty_section",
"required_qty",
"rate",
"amount",
"column_break_11",
"transferred_qty",
"consumed_qty",
"available_qty_at_source_warehouse",
"available_qty_at_wip_warehouse"
],
"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
},
"fieldname": "operation",
"fieldtype": "Link",
"label": "Operation",
"options": "Operation"
},
{
"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
},
"fieldname": "item_code",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Item Code",
"options": "Item"
},
{
"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
},
"fieldname": "source_warehouse",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Source Warehouse",
"options": "Warehouse"
},
{
"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
},
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
{
"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
},
"fieldname": "item_name",
"fieldtype": "Data",
"label": "Item Name",
"read_only": 1
},
{
"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
},
"fieldname": "description",
"fieldtype": "Text",
"label": "Description",
"read_only": 1
},
{
"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
},
"fieldname": "qty_section",
"fieldtype": "Section Break",
"label": "Qty"
},
{
"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
},
"fieldname": "required_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Required Qty"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!parent.skip_transfer",
"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": 1,
"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
},
"depends_on": "eval:!parent.skip_transfer",
"fieldname": "transferred_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Transferred Qty",
"read_only": 1
},
{
"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
},
"default": "0",
"fieldname": "allow_alternative_item",
"fieldtype": "Check",
"label": "Allow Alternative Item"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "include_item_in_manufacturing",
"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": "Include Item In Manufacturing",
"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
},
"default": "0",
"fieldname": "include_item_in_manufacturing",
"fieldtype": "Check",
"label": "Include Item In Manufacturing"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_11",
"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
},
"fieldname": "column_break_11",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!parent.skip_transfer",
"fieldname": "consumed_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": "Consumed 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
},
"depends_on": "eval:!parent.skip_transfer",
"fieldname": "consumed_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Consumed Qty",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "available_qty_at_source_warehouse",
"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": "Available Qty at Source Warehouse",
"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
},
"fieldname": "available_qty_at_source_warehouse",
"fieldtype": "Float",
"label": "Available Qty at Source Warehouse",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "available_qty_at_wip_warehouse",
"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": "Available Qty at WIP Warehouse",
"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
"fieldname": "available_qty_at_wip_warehouse",
"fieldtype": "Float",
"label": "Available Qty at WIP Warehouse",
"read_only": 1
},
{
"fieldname": "rate",
"fieldtype": "Currency",
"label": "Rate",
"read_only": 1
},
{
"fieldname": "amount",
"fieldtype": "Currency",
"label": "Amount",
"read_only": 1
}
],
"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-11-20 19:04:38.508839",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order 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
],
"istable": 1,
"links": [],
"modified": "2020-04-13 18:46:32.966416",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order Item",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -1143,6 +1143,17 @@ def set_item_default(item_code, company, fieldname, value):
d.db_insert()
item.clear_cache()
@frappe.whitelist()
def get_item_details(item_code, company=None):
out = frappe._dict()
if company:
out = get_item_defaults(item_code, company) or frappe._dict()
doc = frappe.get_cached_doc("Item", item_code)
out.update(doc.as_dict())
return out
@frappe.whitelist()
def get_uom_conv_factor(uom, stock_uom):
uoms = [uom, stock_uom]