[Enhance] Provision to set overproduction percentage for work order (#14248)

This commit is contained in:
rohitwaghchaure 2018-05-30 15:28:09 +05:30 committed by Nabin Hait
parent 09e46a4e5f
commit 05152dad0b
11 changed files with 754 additions and 512 deletions

View File

@ -158,15 +158,15 @@ frappe.ui.form.on("Work Order", {
added_min = 0.5;
}
message = title;
// pending qty
if(!frm.doc.skip_transfer){
var pending_complete = frm.doc.material_transferred_for_manufacturing - frm.doc.produced_qty;
if(pending_complete) {
var title = __('{0} items in progress', [pending_complete]);
var width = ((pending_complete / frm.doc.qty * 100) - added_min);
bars.push({
'title': title,
'width': ((pending_complete / frm.doc.qty * 100) - added_min) + '%',
'width': (width > 100 ? "99.5" : width) + '%',
'progress_class': 'progress-bar-warning'
})
message = message + '. ' + title;
@ -356,7 +356,7 @@ erpnext.work_order = {
erpnext.work_order.make_se(frm, 'Manufacture');
});
if(doc.material_transferred_for_manufacturing==doc.qty) {
if(doc.material_transferred_for_manufacturing>=doc.qty) {
// all materials transferred for manufacturing, make this primary
finish_btn.addClass('btn-primary');
}

View File

@ -133,7 +133,7 @@ class WorkOrder(Document):
so_qty = flt(so_item_qty) + flt(dnpi_qty)
allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
"over_production_allowance_percentage"))
"overproduction_percentage_for_sales_order"))
if total_qty > so_qty + (allowance_percentage/100 * so_qty):
frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}")
@ -168,7 +168,7 @@ class WorkOrder(Document):
if stock_entries:
status = "In Process"
produced_qty = stock_entries.get("Manufacture")
if flt(produced_qty) == flt(self.qty):
if flt(produced_qty) >= flt(self.qty):
status = "Completed"
else:
status = 'Cancelled'
@ -179,15 +179,20 @@ class WorkOrder(Document):
"""Update **Manufactured Qty** and **Material Transferred for Qty** in Work Order
based on Stock Entry"""
allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
"overproduction_percentage_for_work_order"))
for purpose, fieldname in (("Manufacture", "produced_qty"),
("Material Transfer for Manufacture", "material_transferred_for_manufacturing")):
qty = flt(frappe.db.sql("""select sum(fg_completed_qty)
from `tabStock Entry` where work_order=%s and docstatus=1
and purpose=%s""", (self.name, purpose))[0][0])
if qty > self.qty:
completed_qty = self.qty + (allowance_percentage/100 * self.qty)
if qty > completed_qty:
frappe.throw(_("{0} ({1}) cannot be greater than planned quantity ({2}) in Work Order {3}").format(\
self.meta.get_label(fieldname), qty, self.qty, self.name), StockOverProductionError)
self.meta.get_label(fieldname), qty, completed_qty, self.name), StockOverProductionError)
self.db_set(fieldname, qty)

View File

@ -0,0 +1,29 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["BOM Variance Report"] = {
"filters": [
{
"fieldname":"bom_no",
"label": __("BOM No"),
"fieldtype": "Link",
"options": "BOM"
},
{
"fieldname":"work_order",
"label": __("Work Order"),
"fieldtype": "Link",
"options": "Work Order",
"get_query": function() {
var bom_no = frappe.query_report_filters_by_name.bom_no.get_value();
return{
query: "erpnext.manufacturing.report.bom_variance_report.bom_variance_report.get_work_orders",
filters: {
'bom_no': bom_no
}
}
}
},
]
}

View File

@ -0,0 +1,32 @@
{
"add_total_row": 0,
"creation": "2018-05-28 16:22:24.040106",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "Gadgets International",
"modified": "2018-05-28 16:22:24.040106",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Variance Report",
"owner": "Administrator",
"ref_doctype": "Stock Entry",
"report_name": "BOM Variance Report",
"report_type": "Script Report",
"roles": [
{
"role": "Manufacturing Manager"
},
{
"role": "Stock Manager"
},
{
"role": "Stock User"
},
{
"role": "Manufacturing User"
}
]
}

View File

@ -0,0 +1,107 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
def execute(filters=None):
columns, data = [], []
columns = get_columns(filters)
data = get_data(filters)
return columns, data
def get_columns(filters):
columns = [{
"label": _("Work Order"),
"fieldname": "work_order",
"fieldtype": "Link",
"options": "Work Order",
"width": 120
}]
if not filters.get('bom_no'):
columns.extend([
{
"label": _("BOM No"),
"fieldname": "bom_no",
"fieldtype": "Link",
"options": "BOM",
"width": 180
}
])
columns.extend([
{
"label": _("Finished Good"),
"fieldname": "production_item",
"fieldtype": "Link",
"options": "Item",
"width": 120
},
{
"label": _("Ordered Qty"),
"fieldname": "qty",
"fieldtype": "Float",
"width": 120
},
{
"label": _("Produced Qty"),
"fieldname": "produced_qty",
"fieldtype": "Float",
"width": 120
},
{
"label": _("Raw Material"),
"fieldname": "raw_material_code",
"fieldtype": "Link",
"options": "Item",
"width": 120
},
{
"label": _("Required Qty"),
"fieldname": "required_qty",
"fieldtype": "Float",
"width": 120
},
{
"label": _("Consumed Qty"),
"fieldname": "consumed_qty",
"fieldtype": "Float",
"width": 120
}
])
return columns
def get_data(filters):
cond = "1=1"
if filters.get('bom_no') and not filters.get('work_order'):
cond += " and bom_no = '%s'" % filters.get('bom_no')
if filters.get('work_order'):
cond += " and name = '%s'" % filters.get('work_order')
results = []
for d in frappe.db.sql(""" select name as work_order, qty, produced_qty, production_item, bom_no
from `tabWork Order` where produced_qty > qty and docstatus = 1 and {0}""".format(cond), as_dict=1):
results.append(d)
for data in frappe.get_all('Work Order Item', fields=["item_code as raw_material_code",
"required_qty", "consumed_qty"], filters={'parent': d.work_order, 'parenttype': 'Work Order'}):
results.append(data)
return results
@frappe.whitelist()
def get_work_orders(doctype, txt, searchfield, start, page_len, filters):
cond = "1=1"
if filters.get('bom_no'):
cond += " and bom_no = '%s'" % filters.get('bom_no')
return frappe.db.sql("""select name from `tabWork Order`
where name like %(name)s and {0} and produced_qty > qty and docstatus = 1
order by name limit {1}, {2}""".format(cond, start, page_len),{
'name': "%%%s%%" % txt
}, as_list=1)

View File

@ -542,3 +542,4 @@ erpnext.patches.v11_0.make_asset_finance_book_against_old_entries
erpnext.patches.v11_0.check_buying_selling_in_currency_exchange
erpnext.patches.v11_0.refactor_erpnext_shopify
erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany
erpnext.patches.v11_0.rename_overproduction_percent_field

View File

@ -0,0 +1,10 @@
# Copyright (c) 2018, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from frappe.model.utils.rename_field import rename_field
import frappe
def execute():
frappe.reload_doc('manufacturing', 'doctype', 'manufacturing_settings')
rename_field('Manufacturing Settings', 'over_production_allowance_percentage', 'overproduction_percentage_for_sales_order')

View File

@ -350,7 +350,7 @@ frappe.ui.form.on('Stock Entry', {
target_warehouse_address: function(frm) {
erpnext.utils.get_address_display(frm, 'target_warehouse_address', 'target_address_display', false);
},
}
})
frappe.ui.form.on('Stock Entry Detail', {

View File

@ -252,10 +252,13 @@ class StockEntry(StockController):
def check_if_operations_completed(self):
"""Check if Time Sheets are completed against before manufacturing to capture operating costs."""
prod_order = frappe.get_doc("Work Order", self.work_order)
allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
"overproduction_percentage_for_work_order"))
for d in prod_order.get("operations"):
total_completed_qty = flt(self.fg_completed_qty) + flt(prod_order.produced_qty)
if total_completed_qty > flt(d.completed_qty):
completed_qty = d.completed_qty + (allowance_percentage/100 * d.completed_qty)
if total_completed_qty > flt(completed_qty):
frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order # {3}. Please update operation status via Time Logs")
.format(d.idx, d.operation, total_completed_qty, self.work_order), OperationsNotCompleteError)
@ -469,8 +472,11 @@ class StockEntry(StockController):
def validate_finished_goods(self):
"""validation: finished good quantity should be same as manufacturing quantity"""
items_with_target_warehouse = []
allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
"overproduction_percentage_for_work_order"))
for d in self.get('items'):
if self.purpose != "Subcontract" and d.bom_no and flt(d.transfer_qty) != flt(self.fg_completed_qty) and (d.t_warehouse != getattr(self, "pro_doc", frappe._dict()).scrap_warehouse):
if self.purpose != "Subcontract" and d.bom_no and flt(d.transfer_qty) > flt(self.fg_completed_qty) and (d.t_warehouse != getattr(self, "pro_doc", frappe._dict()).scrap_warehouse):
frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \
format(d.idx, d.transfer_qty, self.fg_completed_qty))
@ -478,8 +484,14 @@ class StockEntry(StockController):
items_with_target_warehouse.append(d.item_code)
if self.work_order and self.purpose == "Manufacture":
production_item = frappe.db.get_value("Work Order",
self.work_order, "production_item")
production_item, wo_qty = frappe.db.get_value("Work Order",
self.work_order, ["production_item", "qty"])
allowed_qty = wo_qty + (allowance_percentage/100 * wo_qty)
if self.fg_completed_qty > allowed_qty:
frappe.throw(_("For quantity {0} should not be grater than work order quantity {1}")
.format(flt(self.fg_completed_qty), wo_qty))
if production_item not in items_with_target_warehouse:
frappe.throw(_("Finished Item {0} must be entered for Manufacture type entry")
.format(production_item))