[Enhance] Provision to set overproduction percentage for work order (#14248)
This commit is contained in:
parent
09e46a4e5f
commit
05152dad0b
File diff suppressed because it is too large
Load Diff
@ -158,15 +158,15 @@ frappe.ui.form.on("Work Order", {
|
|||||||
added_min = 0.5;
|
added_min = 0.5;
|
||||||
}
|
}
|
||||||
message = title;
|
message = title;
|
||||||
|
|
||||||
// pending qty
|
// pending qty
|
||||||
if(!frm.doc.skip_transfer){
|
if(!frm.doc.skip_transfer){
|
||||||
var pending_complete = frm.doc.material_transferred_for_manufacturing - frm.doc.produced_qty;
|
var pending_complete = frm.doc.material_transferred_for_manufacturing - frm.doc.produced_qty;
|
||||||
if(pending_complete) {
|
if(pending_complete) {
|
||||||
var title = __('{0} items in progress', [pending_complete]);
|
var title = __('{0} items in progress', [pending_complete]);
|
||||||
|
var width = ((pending_complete / frm.doc.qty * 100) - added_min);
|
||||||
bars.push({
|
bars.push({
|
||||||
'title': title,
|
'title': title,
|
||||||
'width': ((pending_complete / frm.doc.qty * 100) - added_min) + '%',
|
'width': (width > 100 ? "99.5" : width) + '%',
|
||||||
'progress_class': 'progress-bar-warning'
|
'progress_class': 'progress-bar-warning'
|
||||||
})
|
})
|
||||||
message = message + '. ' + title;
|
message = message + '. ' + title;
|
||||||
@ -356,7 +356,7 @@ erpnext.work_order = {
|
|||||||
erpnext.work_order.make_se(frm, 'Manufacture');
|
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
|
// all materials transferred for manufacturing, make this primary
|
||||||
finish_btn.addClass('btn-primary');
|
finish_btn.addClass('btn-primary');
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ class WorkOrder(Document):
|
|||||||
so_qty = flt(so_item_qty) + flt(dnpi_qty)
|
so_qty = flt(so_item_qty) + flt(dnpi_qty)
|
||||||
|
|
||||||
allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
|
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):
|
if total_qty > so_qty + (allowance_percentage/100 * so_qty):
|
||||||
frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}")
|
frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}")
|
||||||
@ -168,7 +168,7 @@ class WorkOrder(Document):
|
|||||||
if stock_entries:
|
if stock_entries:
|
||||||
status = "In Process"
|
status = "In Process"
|
||||||
produced_qty = stock_entries.get("Manufacture")
|
produced_qty = stock_entries.get("Manufacture")
|
||||||
if flt(produced_qty) == flt(self.qty):
|
if flt(produced_qty) >= flt(self.qty):
|
||||||
status = "Completed"
|
status = "Completed"
|
||||||
else:
|
else:
|
||||||
status = 'Cancelled'
|
status = 'Cancelled'
|
||||||
@ -179,15 +179,20 @@ class WorkOrder(Document):
|
|||||||
"""Update **Manufactured Qty** and **Material Transferred for Qty** in Work Order
|
"""Update **Manufactured Qty** and **Material Transferred for Qty** in Work Order
|
||||||
based on Stock Entry"""
|
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"),
|
for purpose, fieldname in (("Manufacture", "produced_qty"),
|
||||||
("Material Transfer for Manufacture", "material_transferred_for_manufacturing")):
|
("Material Transfer for Manufacture", "material_transferred_for_manufacturing")):
|
||||||
|
|
||||||
qty = flt(frappe.db.sql("""select sum(fg_completed_qty)
|
qty = flt(frappe.db.sql("""select sum(fg_completed_qty)
|
||||||
from `tabStock Entry` where work_order=%s and docstatus=1
|
from `tabStock Entry` where work_order=%s and docstatus=1
|
||||||
and purpose=%s""", (self.name, purpose))[0][0])
|
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(\
|
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)
|
self.db_set(fieldname, qty)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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)
|
@ -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.check_buying_selling_in_currency_exchange
|
||||||
erpnext.patches.v11_0.refactor_erpnext_shopify
|
erpnext.patches.v11_0.refactor_erpnext_shopify
|
||||||
erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany
|
erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany
|
||||||
|
erpnext.patches.v11_0.rename_overproduction_percent_field
|
||||||
|
10
erpnext/patches/v11_0/rename_overproduction_percent_field.py
Normal file
10
erpnext/patches/v11_0/rename_overproduction_percent_field.py
Normal 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')
|
@ -350,7 +350,7 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
|
|
||||||
target_warehouse_address: function(frm) {
|
target_warehouse_address: function(frm) {
|
||||||
erpnext.utils.get_address_display(frm, 'target_warehouse_address', 'target_address_display', false);
|
erpnext.utils.get_address_display(frm, 'target_warehouse_address', 'target_address_display', false);
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
frappe.ui.form.on('Stock Entry Detail', {
|
frappe.ui.form.on('Stock Entry Detail', {
|
||||||
|
@ -252,10 +252,13 @@ class StockEntry(StockController):
|
|||||||
def check_if_operations_completed(self):
|
def check_if_operations_completed(self):
|
||||||
"""Check if Time Sheets are completed against before manufacturing to capture operating costs."""
|
"""Check if Time Sheets are completed against before manufacturing to capture operating costs."""
|
||||||
prod_order = frappe.get_doc("Work Order", self.work_order)
|
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"):
|
for d in prod_order.get("operations"):
|
||||||
total_completed_qty = flt(self.fg_completed_qty) + flt(prod_order.produced_qty)
|
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")
|
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)
|
.format(d.idx, d.operation, total_completed_qty, self.work_order), OperationsNotCompleteError)
|
||||||
|
|
||||||
@ -469,8 +472,11 @@ class StockEntry(StockController):
|
|||||||
def validate_finished_goods(self):
|
def validate_finished_goods(self):
|
||||||
"""validation: finished good quantity should be same as manufacturing quantity"""
|
"""validation: finished good quantity should be same as manufacturing quantity"""
|
||||||
items_with_target_warehouse = []
|
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'):
|
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}"). \
|
frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \
|
||||||
format(d.idx, d.transfer_qty, self.fg_completed_qty))
|
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)
|
items_with_target_warehouse.append(d.item_code)
|
||||||
|
|
||||||
if self.work_order and self.purpose == "Manufacture":
|
if self.work_order and self.purpose == "Manufacture":
|
||||||
production_item = frappe.db.get_value("Work Order",
|
production_item, wo_qty = frappe.db.get_value("Work Order",
|
||||||
self.work_order, "production_item")
|
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:
|
if production_item not in items_with_target_warehouse:
|
||||||
frappe.throw(_("Finished Item {0} must be entered for Manufacture type entry")
|
frappe.throw(_("Finished Item {0} must be entered for Manufacture type entry")
|
||||||
.format(production_item))
|
.format(production_item))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user