[ Enhance ] Production to Work Order (#12902)
* remove occurrences of Production Order * rename from report and jsons * Change Production Order to Work Order * change occurences of production order from other files * resolve minor conflict issues and reports * patch added * codacy fix * updated patches, leftover changes * rename reports, rectify patches
This commit is contained in:
parent
c893268dea
commit
13ddc7e188
@ -219,7 +219,7 @@ def get_data():
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Production Order"),
|
||||
"label": _("Work Order"),
|
||||
"youtube_id": "ZotgLyp2YFY"
|
||||
},
|
||||
|
||||
|
@ -9,13 +9,13 @@ def get_data():
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Production Order",
|
||||
"name": "Work Order",
|
||||
"description": _("Orders released for production."),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Production Plan",
|
||||
"description": _("Generate Material Requests (MRP) and Production Orders."),
|
||||
"description": _("Generate Material Requests (MRP) and Work Orders."),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
@ -92,26 +92,26 @@ def get_data():
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Open Production Orders",
|
||||
"doctype": "Production Order"
|
||||
"name": "Open Work Orders",
|
||||
"doctype": "Work Order"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Production Orders in Progress",
|
||||
"doctype": "Production Order"
|
||||
"name": "Work Orders in Progress",
|
||||
"doctype": "Work Order"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Issued Items Against Production Order",
|
||||
"doctype": "Production Order"
|
||||
"name": "Issued Items Against Work Order",
|
||||
"doctype": "Work Order"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Completed Production Orders",
|
||||
"doctype": "Production Order"
|
||||
"name": "Completed Work Orders",
|
||||
"doctype": "Work Order"
|
||||
},{
|
||||
"type": "page",
|
||||
"name": "production-analytics",
|
||||
@ -143,7 +143,7 @@ def get_data():
|
||||
},
|
||||
{
|
||||
"type": "help",
|
||||
"label": _("Production Order"),
|
||||
"label": _("Work Order"),
|
||||
"youtube_id": "ZotgLyp2YFY"
|
||||
},
|
||||
]
|
||||
|
@ -7,7 +7,7 @@ import frappe, random, erpnext
|
||||
from frappe.utils.make_random import how_many
|
||||
from frappe.desk import query_report
|
||||
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError
|
||||
from erpnext.manufacturing.doctype.production_order.test_production_order import make_prod_order_test_record
|
||||
from erpnext.manufacturing.doctype.work_order.test_work_order import make_prod_order_test_record
|
||||
|
||||
def work():
|
||||
frappe.set_user(frappe.db.get_global('demo_manufacturing_user'))
|
||||
@ -21,13 +21,13 @@ def work():
|
||||
ppt.purchase_request_for_warehouse = "Stores - WPL"
|
||||
ppt.run_method("get_open_sales_orders")
|
||||
ppt.run_method("get_items")
|
||||
ppt.run_method("raise_production_orders")
|
||||
ppt.run_method("raise_work_orders")
|
||||
ppt.run_method("raise_material_requests")
|
||||
frappe.db.commit()
|
||||
|
||||
# submit production orders
|
||||
for pro in frappe.db.get_values("Production Order", {"docstatus": 0}, "name"):
|
||||
b = frappe.get_doc("Production Order", pro[0])
|
||||
# submit work orders
|
||||
for pro in frappe.db.get_values("Work Order", {"docstatus": 0}, "name"):
|
||||
b = frappe.get_doc("Work Order", pro[0])
|
||||
b.wip_warehouse = "Work in Progress - WPL"
|
||||
b.submit()
|
||||
frappe.db.commit()
|
||||
@ -40,12 +40,12 @@ def work():
|
||||
|
||||
# stores -> wip
|
||||
if random.random() < 0.3:
|
||||
for pro in query_report.run("Open Production Orders")["result"][:how_many("Stock Entry for WIP")]:
|
||||
for pro in query_report.run("Open Work Orders")["result"][:how_many("Stock Entry for WIP")]:
|
||||
make_stock_entry_from_pro(pro[0], "Material Transfer for Manufacture")
|
||||
|
||||
# wip -> fg
|
||||
if random.random() < 0.3:
|
||||
for pro in query_report.run("Production Orders in Progress")["result"][:how_many("Stock Entry for FG")]:
|
||||
for pro in query_report.run("Work Orders in Progress")["result"][:how_many("Stock Entry for FG")]:
|
||||
make_stock_entry_from_pro(pro[0], "Manufacture")
|
||||
|
||||
for bom in frappe.get_all('BOM', fields=['item'], filters = {'with_operations': 1}):
|
||||
@ -57,7 +57,7 @@ def work():
|
||||
|
||||
# submit time logs
|
||||
for timesheet in frappe.get_all("Timesheet", ["name"], {"docstatus": 0,
|
||||
"production_order": ("!=", ""), "to_time": ("<", frappe.flags.current_date)}):
|
||||
"work_order": ("!=", ""), "to_time": ("<", frappe.flags.current_date)}):
|
||||
timesheet = frappe.get_doc("Timesheet", timesheet.name)
|
||||
try:
|
||||
timesheet.submit()
|
||||
@ -68,10 +68,10 @@ def work():
|
||||
pass
|
||||
|
||||
def make_stock_entry_from_pro(pro_id, purpose):
|
||||
from erpnext.manufacturing.doctype.production_order.production_order import make_stock_entry
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry
|
||||
from erpnext.stock.stock_ledger import NegativeStockError
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError, \
|
||||
DuplicateEntryForProductionOrderError, OperationsNotCompleteError
|
||||
DuplicateEntryForWorkOrderError, OperationsNotCompleteError
|
||||
|
||||
try:
|
||||
st = frappe.get_doc(make_stock_entry(pro_id, purpose))
|
||||
@ -83,6 +83,6 @@ def make_stock_entry_from_pro(pro_id, purpose):
|
||||
frappe.db.commit()
|
||||
st.submit()
|
||||
frappe.db.commit()
|
||||
except (NegativeStockError, IncorrectValuationRateError, DuplicateEntryForProductionOrderError,
|
||||
except (NegativeStockError, IncorrectValuationRateError, DuplicateEntryForWorkOrderError,
|
||||
OperationsNotCompleteError):
|
||||
frappe.db.rollback()
|
||||
|
@ -6,7 +6,7 @@ data = {
|
||||
'Supplier',
|
||||
'Sales Order',
|
||||
'Purchase Order',
|
||||
'Production Order',
|
||||
'Work Order',
|
||||
'Task',
|
||||
'Accounts',
|
||||
'HR',
|
||||
|
@ -49,7 +49,7 @@ my_account_context = "erpnext.shopping_cart.utils.update_my_account_context"
|
||||
|
||||
email_append_to = ["Job Applicant", "Lead", "Opportunity", "Issue"]
|
||||
|
||||
calendars = ["Task", "Production Order", "Leave Application", "Sales Order", "Holiday List", "Course Schedule"]
|
||||
calendars = ["Task", "Work Order", "Leave Application", "Sales Order", "Holiday List", "Course Schedule"]
|
||||
|
||||
|
||||
|
||||
|
@ -72,7 +72,7 @@ class Employee(NestedSet):
|
||||
user.flags.ignore_permissions = True
|
||||
|
||||
if "Employee" not in user.get("roles"):
|
||||
user.add_roles("Employee")
|
||||
user.append_roles("Employee")
|
||||
|
||||
# copy details like Fullname, DOB and Image to User
|
||||
if self.employee_name and not (user.first_name and user.last_name):
|
||||
|
@ -48,7 +48,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Disables creation of time logs against Production Orders. Operations shall not be tracked against Production Order",
|
||||
"description": "Disables creation of time logs against Work Orders. Operations shall not be tracked against Work Order",
|
||||
"fieldname": "disable_capacity_planning",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
@ -454,7 +454,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-07-31 19:25:04.242693",
|
||||
"modified": "2018-02-16 13:18:17.964103",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Manufacturing Settings",
|
||||
|
@ -37,8 +37,8 @@ frappe.ui.form.on('Production Plan', {
|
||||
|
||||
if (frm.doc.docstatus === 1 && frm.doc.po_items
|
||||
&& frm.doc.status != 'Completed') {
|
||||
frm.add_custom_button(__("Production Order"), ()=> {
|
||||
frm.trigger("make_production_order");
|
||||
frm.add_custom_button(__("Work Order"), ()=> {
|
||||
frm.trigger("make_work_order");
|
||||
}, __("Make"));
|
||||
}
|
||||
|
||||
@ -52,9 +52,9 @@ frappe.ui.form.on('Production Plan', {
|
||||
frm.trigger("material_requirement");
|
||||
},
|
||||
|
||||
make_production_order: function(frm) {
|
||||
make_work_order: function(frm) {
|
||||
frappe.call({
|
||||
method: "make_production_order",
|
||||
method: "make_work_order",
|
||||
freeze: true,
|
||||
doc: frm.doc,
|
||||
callback: function() {
|
||||
|
@ -44,6 +44,7 @@
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -76,6 +77,7 @@
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -108,6 +110,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -137,6 +140,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -168,6 +172,7 @@
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -201,6 +206,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -232,6 +238,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -264,6 +271,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -296,6 +304,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -328,6 +337,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -357,6 +367,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
@ -388,6 +399,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -418,6 +430,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -451,6 +464,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -483,6 +497,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -514,6 +529,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -546,6 +562,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -578,6 +595,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -609,6 +627,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -640,6 +659,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -658,7 +678,7 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Get Items For Production Order",
|
||||
"label": "Get Items For Work Order",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
@ -672,6 +692,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -703,6 +724,7 @@
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -734,6 +756,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -765,6 +788,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -795,6 +819,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -824,6 +849,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -855,6 +881,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -885,6 +912,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -915,6 +943,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -946,6 +975,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -976,6 +1006,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -1007,6 +1038,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -1038,6 +1070,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -1067,6 +1100,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -1099,6 +1133,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@ -1129,6 +1164,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
@ -1143,7 +1179,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-02-15 13:18:59.092921",
|
||||
"modified": "2018-03-05 01:36:45.048493",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Production Plan",
|
||||
|
@ -8,7 +8,7 @@ from frappe import msgprint, _
|
||||
from frappe.model.document import Document
|
||||
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
|
||||
from frappe.utils import cstr, flt, cint, nowdate, add_days, comma_and, now_datetime
|
||||
from erpnext.manufacturing.doctype.production_order.production_order import get_item_details
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
|
||||
from six import string_types
|
||||
|
||||
class ProductionPlan(Document):
|
||||
@ -229,12 +229,12 @@ class ProductionPlan(Document):
|
||||
|
||||
def on_cancel(self):
|
||||
self.db_set('status', 'Cancelled')
|
||||
self.delete_draft_production_order()
|
||||
self.delete_draft_work_order()
|
||||
|
||||
def delete_draft_production_order(self):
|
||||
for d in frappe.get_all('Production Order', fields = ["name"],
|
||||
def delete_draft_work_order(self):
|
||||
for d in frappe.get_all('Work Order', fields = ["name"],
|
||||
filters = {'docstatus': 0, 'production_plan': ("=", self.name)}):
|
||||
frappe.delete_doc('Production Order', d.name)
|
||||
frappe.delete_doc('Work Order', d.name)
|
||||
|
||||
def set_status(self):
|
||||
self.status = {
|
||||
@ -392,37 +392,37 @@ class ProductionPlan(Document):
|
||||
'sales_order': data.sales_order
|
||||
})
|
||||
|
||||
def make_production_order(self):
|
||||
pro_list = []
|
||||
def make_work_order(self):
|
||||
wo_list = []
|
||||
self.validate_data()
|
||||
items_data = self.get_production_items()
|
||||
|
||||
for key, item in items_data.items():
|
||||
production_order = self.create_production_order(item)
|
||||
if production_order:
|
||||
pro_list.append(production_order)
|
||||
work_order = self.create_work_order(item)
|
||||
if work_order:
|
||||
wo_list.append(work_order)
|
||||
|
||||
frappe.flags.mute_messages = False
|
||||
|
||||
if pro_list:
|
||||
pro_list = ["""<a href="#Form/Production Order/%s" target="_blank">%s</a>""" % \
|
||||
(p, p) for p in pro_list]
|
||||
msgprint(_("{0} created").format(comma_and(pro_list)))
|
||||
if wo_list:
|
||||
wo_list = ["""<a href="#Form/Work Order/%s" target="_blank">%s</a>""" % \
|
||||
(p, p) for p in wo_list]
|
||||
msgprint(_("{0} created").format(comma_and(wo_list)))
|
||||
else :
|
||||
msgprint(_("No Production Orders created"))
|
||||
msgprint(_("No Work Orders created"))
|
||||
|
||||
def create_production_order(self, item):
|
||||
from erpnext.manufacturing.doctype.production_order.production_order import OverProductionError, get_default_warehouse
|
||||
def create_work_order(self, item):
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError, get_default_warehouse
|
||||
warehouse = get_default_warehouse()
|
||||
pro = frappe.new_doc("Production Order")
|
||||
pro.update(item)
|
||||
pro.set_production_order_operations()
|
||||
wo = frappe.new_doc("Work Order")
|
||||
wo.update(item)
|
||||
wo.set_work_order_operations()
|
||||
|
||||
if not pro.fg_warehouse:
|
||||
pro.fg_warehouse = warehouse.get('fg_warehouse')
|
||||
if not wo.fg_warehouse:
|
||||
wo.fg_warehouse = warehouse.get('fg_warehouse')
|
||||
try:
|
||||
pro.insert()
|
||||
return pro.name
|
||||
wo.insert()
|
||||
return wo.name
|
||||
except OverProductionError:
|
||||
pass
|
||||
|
||||
|
@ -6,7 +6,7 @@ def get_data():
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Related'),
|
||||
'items': ['Production Order', 'Material Request']
|
||||
'items': ['Work Order', 'Material Request']
|
||||
},
|
||||
]
|
||||
}
|
@ -41,18 +41,18 @@ class TestProductionPlan(unittest.TestCase):
|
||||
|
||||
self.assertTrue(len(material_requests), 2)
|
||||
|
||||
pln.make_production_order()
|
||||
production_orders = frappe.get_all('Production Order', fields = ['name'],
|
||||
pln.make_work_order()
|
||||
work_orders = frappe.get_all('Work Order', fields = ['name'],
|
||||
filters = {'production_plan': pln.name}, as_list=1)
|
||||
|
||||
self.assertTrue(len(production_orders), len(pln.po_items))
|
||||
self.assertTrue(len(work_orders), len(pln.po_items))
|
||||
|
||||
for name in material_requests:
|
||||
mr = frappe.get_doc('Material Request', name[0])
|
||||
mr.cancel()
|
||||
|
||||
for name in production_orders:
|
||||
mr = frappe.delete_doc('Production Order', name[0])
|
||||
for name in work_orders:
|
||||
mr = frappe.delete_doc('Work Order', name[0])
|
||||
|
||||
pln = frappe.get_doc('Production Plan', pln.name)
|
||||
pln.cancel()
|
||||
|
@ -9,7 +9,7 @@ from frappe import msgprint, _
|
||||
|
||||
from frappe.model.document import Document
|
||||
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
|
||||
from erpnext.manufacturing.doctype.production_order.production_order import get_item_details
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
|
||||
|
||||
class ProductionPlanningTool(Document):
|
||||
def clear_table(self, table_name):
|
||||
@ -204,8 +204,8 @@ class ProductionPlanningTool(Document):
|
||||
if not flt(d.planned_qty):
|
||||
frappe.throw(_("Please enter Planned Qty for Item {0} at row {1}").format(d.item_code, d.idx))
|
||||
|
||||
def raise_production_orders(self):
|
||||
"""It will raise production order (Draft) for all distinct FG items"""
|
||||
def raise_work_orders(self):
|
||||
"""It will raise work order (Draft) for all distinct FG items"""
|
||||
self.validate_data()
|
||||
|
||||
from erpnext.utilities.transaction_base import validate_uom_is_integer
|
||||
@ -213,22 +213,22 @@ class ProductionPlanningTool(Document):
|
||||
|
||||
items = self.get_production_items()
|
||||
|
||||
pro_list = []
|
||||
wo_list = []
|
||||
frappe.flags.mute_messages = True
|
||||
|
||||
for key in items:
|
||||
production_order = self.create_production_order(items[key])
|
||||
if production_order:
|
||||
pro_list.append(production_order)
|
||||
work_order = self.create_work_order(items[key])
|
||||
if work_order:
|
||||
wo_list.append(work_order)
|
||||
|
||||
frappe.flags.mute_messages = False
|
||||
|
||||
if pro_list:
|
||||
pro_list = ["""<a href="#Form/Production Order/%s" target="_blank">%s</a>""" % \
|
||||
(p, p) for p in pro_list]
|
||||
msgprint(_("{0} created").format(comma_and(pro_list)))
|
||||
if wo_list:
|
||||
wo_list = ["""<a href="#Form/Work Order/%s" target="_blank">%s</a>""" % \
|
||||
(p, p) for p in wo_list]
|
||||
msgprint(_("{0} created").format(comma_and(wo_list)))
|
||||
else :
|
||||
msgprint(_("No Production Orders created"))
|
||||
msgprint(_("No Work Orders created"))
|
||||
|
||||
def get_production_items(self):
|
||||
item_dict = {}
|
||||
@ -264,21 +264,21 @@ class ProductionPlanningTool(Document):
|
||||
|
||||
return item_dict
|
||||
|
||||
def create_production_order(self, item_dict):
|
||||
"""Create production order. Called from Production Planning Tool"""
|
||||
from erpnext.manufacturing.doctype.production_order.production_order import OverProductionError, get_default_warehouse
|
||||
def create_work_order(self, item_dict):
|
||||
"""Create work order. Called from Production Planning Tool"""
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError, get_default_warehouse
|
||||
warehouse = get_default_warehouse()
|
||||
pro = frappe.new_doc("Production Order")
|
||||
pro.update(item_dict)
|
||||
pro.set_production_order_operations()
|
||||
wo = frappe.new_doc("Work Order")
|
||||
wo.update(item_dict)
|
||||
wo.set_work_order_operations()
|
||||
if warehouse:
|
||||
pro.wip_warehouse = warehouse.get('wip_warehouse')
|
||||
if not pro.fg_warehouse:
|
||||
pro.fg_warehouse = warehouse.get('fg_warehouse')
|
||||
wo.wip_warehouse = warehouse.get('wip_warehouse')
|
||||
if not wo.fg_warehouse:
|
||||
wo.fg_warehouse = warehouse.get('fg_warehouse')
|
||||
|
||||
try:
|
||||
pro.insert()
|
||||
return pro.name
|
||||
wo.insert()
|
||||
return wo.name
|
||||
except OverProductionError:
|
||||
pass
|
||||
|
||||
|
@ -2,11 +2,11 @@
|
||||
{
|
||||
"bom_no": "BOM-_Test FG Item-001",
|
||||
"company": "_Test Company",
|
||||
"doctype": "Production Order",
|
||||
"doctype": "Work Order",
|
||||
"fg_warehouse": "_Test Warehouse 1 - _TC",
|
||||
"production_item": "_Test FG Item",
|
||||
"qty": 10.0,
|
||||
"stock_uom": "_Test UOM",
|
||||
"wip_warehouse": "_Test Warehouse - _TC"
|
||||
}
|
||||
]
|
||||
]
|
@ -1,4 +1,4 @@
|
||||
QUnit.test("test: production order", function (assert) {
|
||||
QUnit.test("test: work order", function (assert) {
|
||||
assert.expect(25);
|
||||
let done = assert.async();
|
||||
let laptop_quantity = 5;
|
||||
@ -14,13 +14,13 @@ QUnit.test("test: production order", function (assert) {
|
||||
};
|
||||
|
||||
frappe.run_serially([
|
||||
// test production order
|
||||
() => frappe.set_route("List", "Production Order", "List"),
|
||||
// test work order
|
||||
() => frappe.set_route("List", "Work Order", "List"),
|
||||
() => frappe.timeout(3),
|
||||
|
||||
// Create a laptop production order
|
||||
// Create a laptop work order
|
||||
() => {
|
||||
return frappe.tests.make('Production Order', [
|
||||
return frappe.tests.make('Work Order', [
|
||||
{production_item: 'Laptop'},
|
||||
{company: 'For Testing'},
|
||||
{qty: laptop_quantity},
|
||||
@ -50,13 +50,13 @@ QUnit.test("test: production order", function (assert) {
|
||||
});
|
||||
},
|
||||
|
||||
// Submit the production order
|
||||
// Submit the work order
|
||||
() => cur_frm.savesubmit(),
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.click_button('Yes'),
|
||||
() => frappe.timeout(2.5),
|
||||
|
||||
// Confirm the production order timesheet, save and submit it
|
||||
// Confirm the work order timesheet, save and submit it
|
||||
() => frappe.click_link("TS-00"),
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.click_button("Submit"),
|
||||
@ -64,8 +64,8 @@ QUnit.test("test: production order", function (assert) {
|
||||
() => frappe.click_button("Yes"),
|
||||
() => frappe.timeout(2.5),
|
||||
|
||||
// Start the production order process
|
||||
() => frappe.set_route("List", "Production Order", "List"),
|
||||
// Start the work order process
|
||||
() => frappe.set_route("List", "Work Order", "List"),
|
||||
() => frappe.timeout(2),
|
||||
() => frappe.click_link("Laptop"),
|
||||
() => frappe.timeout(1),
|
||||
@ -82,20 +82,20 @@ QUnit.test("test: production order", function (assert) {
|
||||
assert.equal(cur_frm.doc.total_outgoing_value, "99000",
|
||||
"Outgoing cost is correct"); // Price of each item x5
|
||||
},
|
||||
// Submit for production
|
||||
// Submit for work
|
||||
() => frappe.click_button("Submit"),
|
||||
() => frappe.timeout(0.5),
|
||||
() => frappe.click_button("Yes"),
|
||||
() => frappe.timeout(0.5),
|
||||
|
||||
// Finish the production order by sending for manufacturing
|
||||
() => frappe.set_route("List", "Production Order"),
|
||||
// Finish the work order by sending for manufacturing
|
||||
() => frappe.set_route("List", "Work Order"),
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.click_link("Laptop"),
|
||||
() => frappe.timeout(1),
|
||||
|
||||
() => {
|
||||
assert.ok(frappe.tests.is_visible("5 items in progress", 'p'), "Production order initiated");
|
||||
assert.ok(frappe.tests.is_visible("5 items in progress", 'p'), "Work order initiated");
|
||||
assert.ok(frappe.tests.is_visible("Finish"), "Finish button visible");
|
||||
},
|
||||
|
||||
@ -118,12 +118,12 @@ QUnit.test("test: production order", function (assert) {
|
||||
() => frappe.timeout(1),
|
||||
|
||||
// Manufacturing finished
|
||||
() => frappe.set_route("List", "Production Order", "List"),
|
||||
() => frappe.set_route("List", "Work Order", "List"),
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.click_link("Laptop"),
|
||||
() => frappe.timeout(1),
|
||||
|
||||
() => assert.ok(frappe.tests.is_visible("5 items produced", 'p'), "Production order completed"),
|
||||
() => assert.ok(frappe.tests.is_visible("5 items produced", 'p'), "Work order completed"),
|
||||
|
||||
() => done()
|
||||
]);
|
@ -7,13 +7,13 @@ import unittest
|
||||
import frappe
|
||||
from frappe.utils import flt, time_diff_in_hours, now, add_days, cint
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||
from erpnext.manufacturing.doctype.production_order.production_order \
|
||||
from erpnext.manufacturing.doctype.work_order.work_order \
|
||||
import make_stock_entry, ItemHasVariantError, stop_unstop
|
||||
from erpnext.stock.doctype.stock_entry import test_stock_entry
|
||||
from erpnext.stock.utils import get_bin
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
|
||||
class TestProductionOrder(unittest.TestCase):
|
||||
class TestWorkOrder(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.warehouse = '_Test Warehouse 2 - _TC'
|
||||
self.item = '_Test Item'
|
||||
@ -24,7 +24,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
planned0 = frappe.db.get_value("Bin", {"item_code": "_Test FG Item",
|
||||
"warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty") or 0
|
||||
|
||||
pro_order = make_prod_order_test_record()
|
||||
wo_order = make_wo_order_test_record()
|
||||
|
||||
planned1 = frappe.db.get_value("Bin", {"item_code": "_Test FG Item",
|
||||
"warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty")
|
||||
@ -38,60 +38,59 @@ class TestProductionOrder(unittest.TestCase):
|
||||
target="Stores - _TC", qty=100, basic_rate=100)
|
||||
|
||||
# from stores to wip
|
||||
s = frappe.get_doc(make_stock_entry(pro_order.name, "Material Transfer for Manufacture", 4))
|
||||
s = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 4))
|
||||
for d in s.get("items"):
|
||||
d.s_warehouse = "Stores - _TC"
|
||||
s.insert()
|
||||
s.submit()
|
||||
|
||||
# from wip to fg
|
||||
s = frappe.get_doc(make_stock_entry(pro_order.name, "Manufacture", 4))
|
||||
s = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 4))
|
||||
s.insert()
|
||||
s.submit()
|
||||
|
||||
self.assertEqual(frappe.db.get_value("Production Order", pro_order.name, "produced_qty"), 4)
|
||||
self.assertEqual(frappe.db.get_value("Work Order", wo_order.name, "produced_qty"), 4)
|
||||
|
||||
planned2 = frappe.db.get_value("Bin", {"item_code": "_Test FG Item",
|
||||
"warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty")
|
||||
|
||||
self.assertEqual(planned2, planned0 + 6)
|
||||
|
||||
return pro_order
|
||||
return wo_order
|
||||
|
||||
def test_over_production(self):
|
||||
from erpnext.manufacturing.doctype.production_order.production_order import StockOverProductionError
|
||||
pro_doc = self.check_planned_qty()
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import StockOverProductionError
|
||||
wo_doc = self.check_planned_qty()
|
||||
|
||||
test_stock_entry.make_stock_entry(item_code="_Test Item",
|
||||
target="_Test Warehouse - _TC", qty=100, basic_rate=100)
|
||||
test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
|
||||
target="_Test Warehouse - _TC", qty=100, basic_rate=100)
|
||||
|
||||
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 7))
|
||||
s = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 7))
|
||||
s.insert()
|
||||
|
||||
self.assertRaises(StockOverProductionError, s.submit)
|
||||
|
||||
def test_make_time_sheet(self):
|
||||
from erpnext.manufacturing.doctype.production_order.production_order import make_timesheet
|
||||
prod_order = make_prod_order_test_record(item="_Test FG Item 2",
|
||||
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)
|
||||
|
||||
prod_order.set_production_order_operations()
|
||||
prod_order.insert()
|
||||
prod_order.submit()
|
||||
wo_order.set_work_order_operations()
|
||||
wo_order.insert()
|
||||
wo_order.submit()
|
||||
|
||||
d = prod_order.operations[0]
|
||||
d = wo_order.operations[0]
|
||||
d.completed_qty = flt(d.completed_qty)
|
||||
|
||||
name = frappe.db.get_value('Timesheet', {'production_order': prod_order.name}, 'name')
|
||||
name = frappe.db.get_value('Timesheet', {'work_order': wo_order.name}, 'name')
|
||||
time_sheet_doc = frappe.get_doc('Timesheet', name)
|
||||
self.assertEqual(prod_order.company, time_sheet_doc.company)
|
||||
self.assertEqual(wo_order.company, time_sheet_doc.company)
|
||||
time_sheet_doc.submit()
|
||||
|
||||
|
||||
self.assertEqual(prod_order.name, time_sheet_doc.production_order)
|
||||
self.assertEqual((prod_order.qty - d.completed_qty),
|
||||
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({
|
||||
@ -101,49 +100,49 @@ class TestProductionOrder(unittest.TestCase):
|
||||
|
||||
manufacturing_settings.save()
|
||||
|
||||
prod_order.load_from_db()
|
||||
self.assertEqual(prod_order.operations[0].status, "Completed")
|
||||
self.assertEqual(prod_order.operations[0].completed_qty, prod_order.qty)
|
||||
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(prod_order.operations[0].actual_operation_time, 60)
|
||||
self.assertEqual(prod_order.operations[0].actual_operating_cost, 6000)
|
||||
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(prod_order.name, prod_order.company)
|
||||
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()
|
||||
|
||||
prod_order.load_from_db()
|
||||
self.assertEqual(prod_order.operations[0].status, "Pending")
|
||||
self.assertEqual(flt(prod_order.operations[0].completed_qty), 0)
|
||||
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(prod_order.operations[0].actual_operation_time), 0)
|
||||
self.assertEqual(flt(prod_order.operations[0].actual_operating_cost), 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):
|
||||
prod_order = make_prod_order_test_record(item="_Test FG Item 2",
|
||||
wo_order = make_wo_order_test_record(item="_Test FG Item 2",
|
||||
planned_start_date=now(), qty=1, do_not_save=True)
|
||||
prod_order.set_production_order_operations()
|
||||
cost = prod_order.planned_operating_cost
|
||||
prod_order.qty = 2
|
||||
prod_order.set_production_order_operations()
|
||||
self.assertEqual(prod_order.planned_operating_cost, cost*2)
|
||||
wo_order.set_work_order_operations()
|
||||
cost = wo_order.planned_operating_cost
|
||||
wo_order.qty = 2
|
||||
wo_order.set_work_order_operations()
|
||||
self.assertEqual(wo_order.planned_operating_cost, cost*2)
|
||||
|
||||
def test_production_item(self):
|
||||
prod_order = make_prod_order_test_record(item="_Test FG Item", qty=1, do_not_save=True)
|
||||
wo_order = make_wo_order_test_record(item="_Test FG Item", qty=1, do_not_save=True)
|
||||
frappe.db.set_value("Item", "_Test FG Item", "end_of_life", "2000-1-1")
|
||||
|
||||
self.assertRaises(frappe.ValidationError, prod_order.save)
|
||||
self.assertRaises(frappe.ValidationError, wo_order.save)
|
||||
|
||||
frappe.db.set_value("Item", "_Test FG Item", "end_of_life", None)
|
||||
frappe.db.set_value("Item", "_Test FG Item", "disabled", 1)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, prod_order.save)
|
||||
self.assertRaises(frappe.ValidationError, wo_order.save)
|
||||
|
||||
frappe.db.set_value("Item", "_Test FG Item", "disabled", 0)
|
||||
|
||||
prod_order = make_prod_order_test_record(item="_Test Variant Item", qty=1, do_not_save=True)
|
||||
self.assertRaises(ItemHasVariantError, prod_order.save)
|
||||
wo_order = make_wo_order_test_record(item="_Test Variant Item", qty=1, do_not_save=True)
|
||||
self.assertRaises(ItemHasVariantError, wo_order.save)
|
||||
|
||||
def test_reserved_qty_for_production_submit(self):
|
||||
self.bin1_at_start = get_bin(self.item, self.warehouse)
|
||||
@ -151,7 +150,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
# reset to correct value
|
||||
self.bin1_at_start.update_reserved_qty_for_production()
|
||||
|
||||
self.pro_order = make_prod_order_test_record(item="_Test FG Item", qty=2,
|
||||
self.wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2,
|
||||
source_warehouse=self.warehouse)
|
||||
|
||||
self.bin1_on_submit = get_bin(self.item, self.warehouse)
|
||||
@ -165,7 +164,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
def test_reserved_qty_for_production_cancel(self):
|
||||
self.test_reserved_qty_for_production_submit()
|
||||
|
||||
self.pro_order.cancel()
|
||||
self.wo_order.cancel()
|
||||
|
||||
bin1_on_cancel = get_bin(self.item, self.warehouse)
|
||||
|
||||
@ -183,7 +182,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
|
||||
self.test_reserved_qty_for_production_submit()
|
||||
|
||||
s = frappe.get_doc(make_stock_entry(self.pro_order.name,
|
||||
s = frappe.get_doc(make_stock_entry(self.wo_order.name,
|
||||
"Material Transfer for Manufacture", 2))
|
||||
|
||||
s.submit()
|
||||
@ -198,7 +197,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
self.assertEqual(cint(self.bin1_at_start.projected_qty),
|
||||
cint(bin1_on_start_production.projected_qty) + 2)
|
||||
|
||||
s = frappe.get_doc(make_stock_entry(self.pro_order.name, "Manufacture", 2))
|
||||
s = frappe.get_doc(make_stock_entry(self.wo_order.name, "Manufacture", 2))
|
||||
|
||||
bin1_on_end_production = get_bin(self.item, self.warehouse)
|
||||
|
||||
@ -220,7 +219,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
|
||||
#2 0 -2
|
||||
|
||||
s = frappe.get_doc(make_stock_entry(self.pro_order.name,
|
||||
s = frappe.get_doc(make_stock_entry(self.wo_order.name,
|
||||
"Material Transfer for Manufacture", 1))
|
||||
|
||||
s.submit()
|
||||
@ -238,7 +237,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
cint(bin1_on_start_production.projected_qty) + 2)
|
||||
|
||||
# STOP
|
||||
stop_unstop(self.pro_order.name, "Stopped")
|
||||
stop_unstop(self.wo_order.name, "Stopped")
|
||||
|
||||
bin1_on_stop_production = get_bin(self.item, self.warehouse)
|
||||
|
||||
@ -249,7 +248,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
cint(self.bin1_at_start.projected_qty))
|
||||
|
||||
def test_scrap_material_qty(self):
|
||||
prod_order = make_prod_order_test_record(planned_start_date=now(), qty=2)
|
||||
wo_order = make_wo_order_test_record(planned_start_date=now(), qty=2)
|
||||
|
||||
# add raw materials to stores
|
||||
test_stock_entry.make_stock_entry(item_code="_Test Item",
|
||||
@ -257,27 +256,27 @@ class TestProductionOrder(unittest.TestCase):
|
||||
test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
|
||||
target="Stores - _TC", qty=10, basic_rate=1000.0)
|
||||
|
||||
s = frappe.get_doc(make_stock_entry(prod_order.name, "Material Transfer for Manufacture", 2))
|
||||
s = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 2))
|
||||
for d in s.get("items"):
|
||||
d.s_warehouse = "Stores - _TC"
|
||||
s.insert()
|
||||
s.submit()
|
||||
|
||||
s = frappe.get_doc(make_stock_entry(prod_order.name, "Manufacture", 2))
|
||||
s = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2))
|
||||
s.insert()
|
||||
s.submit()
|
||||
|
||||
prod_order_details = frappe.db.get_value("Production Order", prod_order.name,
|
||||
wo_order_details = frappe.db.get_value("Work Order", wo_order.name,
|
||||
["scrap_warehouse", "qty", "produced_qty", "bom_no"], as_dict=1)
|
||||
|
||||
scrap_item_details = get_scrap_item_details(prod_order_details.bom_no)
|
||||
scrap_item_details = get_scrap_item_details(wo_order_details.bom_no)
|
||||
|
||||
self.assertEqual(prod_order_details.produced_qty, 2)
|
||||
self.assertEqual(wo_order_details.produced_qty, 2)
|
||||
|
||||
for item in s.items:
|
||||
if item.bom_no and item.item_code in scrap_item_details:
|
||||
self.assertEqual(prod_order_details.scrap_warehouse, item.t_warehouse)
|
||||
self.assertEqual(flt(prod_order_details.qty)*flt(scrap_item_details[item.item_code]), item.qty)
|
||||
self.assertEqual(wo_order_details.scrap_warehouse, item.t_warehouse)
|
||||
self.assertEqual(flt(wo_order_details.qty)*flt(scrap_item_details[item.item_code]), item.qty)
|
||||
|
||||
def get_scrap_item_details(bom_no):
|
||||
scrap_items = {}
|
||||
@ -287,34 +286,34 @@ def get_scrap_item_details(bom_no):
|
||||
|
||||
return scrap_items
|
||||
|
||||
def make_prod_order_test_record(**args):
|
||||
def make_wo_order_test_record(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
pro_order = frappe.new_doc("Production Order")
|
||||
pro_order.production_item = args.production_item or args.item or args.item_code or "_Test FG Item"
|
||||
pro_order.bom_no = frappe.db.get_value("BOM", {"item": pro_order.production_item,
|
||||
wo_order = frappe.new_doc("Work Order")
|
||||
wo_order.production_item = args.production_item or args.item or args.item_code or "_Test FG Item"
|
||||
wo_order.bom_no = frappe.db.get_value("BOM", {"item": wo_order.production_item,
|
||||
"is_active": 1, "is_default": 1})
|
||||
pro_order.qty = args.qty or 10
|
||||
pro_order.wip_warehouse = args.wip_warehouse or "_Test Warehouse - _TC"
|
||||
pro_order.fg_warehouse = args.fg_warehouse or "_Test Warehouse 1 - _TC"
|
||||
pro_order.scrap_warehouse = args.fg_warehouse or "_Test Scrap Warehouse - _TC"
|
||||
pro_order.company = args.company or "_Test Company"
|
||||
pro_order.stock_uom = args.stock_uom or "_Test UOM"
|
||||
pro_order.use_multi_level_bom=0
|
||||
pro_order.get_items_and_operations_from_bom()
|
||||
wo_order.qty = args.qty or 10
|
||||
wo_order.wip_warehouse = args.wip_warehouse or "_Test Warehouse - _TC"
|
||||
wo_order.fg_warehouse = args.fg_warehouse or "_Test Warehouse 1 - _TC"
|
||||
wo_order.scrap_warehouse = args.fg_warehouse or "_Test Scrap Warehouse - _TC"
|
||||
wo_order.company = args.company or "_Test Company"
|
||||
wo_order.stock_uom = args.stock_uom or "_Test UOM"
|
||||
wo_order.use_multi_level_bom=0
|
||||
wo_order.get_items_and_operations_from_bom()
|
||||
|
||||
if args.source_warehouse:
|
||||
for item in pro_order.get("required_items"):
|
||||
for item in wo_order.get("required_items"):
|
||||
item.source_warehouse = args.source_warehouse
|
||||
|
||||
if args.planned_start_date:
|
||||
pro_order.planned_start_date = args.planned_start_date
|
||||
wo_order.planned_start_date = args.planned_start_date
|
||||
|
||||
if not args.do_not_save:
|
||||
pro_order.insert()
|
||||
wo_order.insert()
|
||||
|
||||
if not args.do_not_submit:
|
||||
pro_order.submit()
|
||||
return pro_order
|
||||
wo_order.submit()
|
||||
return wo_order
|
||||
|
||||
test_records = frappe.get_test_records('Production Order')
|
||||
test_records = frappe.get_test_records('Work Order')
|
@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Production Order", {
|
||||
frappe.ui.form.on("Work Order", {
|
||||
setup: function(frm) {
|
||||
frm.custom_make_buttons = {
|
||||
'Timesheet': 'Make Timesheet',
|
||||
@ -80,7 +80,7 @@ frappe.ui.form.on("Production Order", {
|
||||
}
|
||||
});
|
||||
|
||||
// formatter for production order operation
|
||||
// formatter for work order operation
|
||||
frm.set_indicator_formatter('operation',
|
||||
function(doc) { return (frm.doc.qty==doc.completed_qty) ? "green" : "orange" });
|
||||
},
|
||||
@ -96,17 +96,17 @@ frappe.ui.form.on("Production Order", {
|
||||
"actual_start_date": "",
|
||||
"actual_end_date": ""
|
||||
});
|
||||
erpnext.production_order.set_default_warehouse(frm);
|
||||
erpnext.work_order.set_default_warehouse(frm);
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
erpnext.toggle_naming_series();
|
||||
erpnext.production_order.set_custom_buttons(frm);
|
||||
erpnext.work_order.set_custom_buttons(frm);
|
||||
frm.set_intro("");
|
||||
|
||||
if (frm.doc.docstatus === 0 && !frm.doc.__islocal) {
|
||||
frm.set_intro(__("Submit this Production Order for further processing."));
|
||||
frm.set_intro(__("Submit this Work Order for further processing."));
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus===1) {
|
||||
@ -116,7 +116,7 @@ frappe.ui.form.on("Production Order", {
|
||||
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.production_order.production_order.make_new_timesheet",
|
||||
method: "erpnext.manufacturing.doctype.work_order.work_order.make_new_timesheet",
|
||||
frm: cur_frm
|
||||
})
|
||||
})
|
||||
@ -160,7 +160,7 @@ frappe.ui.form.on("Production Order", {
|
||||
production_item: function(frm) {
|
||||
if (frm.doc.production_item) {
|
||||
frappe.call({
|
||||
method: "erpnext.manufacturing.doctype.production_order.production_order.get_item_details",
|
||||
method: "erpnext.manufacturing.doctype.work_order.work_order.get_item_details",
|
||||
args: {
|
||||
item: frm.doc.production_item,
|
||||
project: frm.doc.project
|
||||
@ -222,7 +222,7 @@ frappe.ui.form.on("Production Order", {
|
||||
set_sales_order: function(frm) {
|
||||
if(frm.doc.production_item) {
|
||||
frappe.call({
|
||||
method: "erpnext.manufacturing.doctype.production_order.production_order.query_sales_order",
|
||||
method: "erpnext.manufacturing.doctype.work_order.work_order.query_sales_order",
|
||||
args: { production_item: frm.doc.production_item },
|
||||
callback: function(r) {
|
||||
frm.set_query("sales_order", function() {
|
||||
@ -239,7 +239,7 @@ frappe.ui.form.on("Production Order", {
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Production Order Item", {
|
||||
frappe.ui.form.on("Work Order Item", {
|
||||
source_warehouse: function(frm, cdt, cdn) {
|
||||
var row = locals[cdt][cdn];
|
||||
if(!row.item_code) {
|
||||
@ -260,7 +260,7 @@ frappe.ui.form.on("Production Order Item", {
|
||||
}
|
||||
})
|
||||
|
||||
frappe.ui.form.on("Production Order Operation", {
|
||||
frappe.ui.form.on("Work Order Operation", {
|
||||
workstation: function(frm, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
if (d.workstation) {
|
||||
@ -272,29 +272,29 @@ frappe.ui.form.on("Production Order Operation", {
|
||||
},
|
||||
callback: function (data) {
|
||||
frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate);
|
||||
erpnext.production_order.calculate_cost(frm.doc);
|
||||
erpnext.production_order.calculate_total_cost(frm);
|
||||
erpnext.work_order.calculate_cost(frm.doc);
|
||||
erpnext.work_order.calculate_total_cost(frm);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
time_in_mins: function(frm, cdt, cdn) {
|
||||
erpnext.production_order.calculate_cost(frm.doc);
|
||||
erpnext.production_order.calculate_total_cost(frm);
|
||||
erpnext.work_order.calculate_cost(frm.doc);
|
||||
erpnext.work_order.calculate_total_cost(frm);
|
||||
},
|
||||
});
|
||||
|
||||
erpnext.production_order = {
|
||||
erpnext.work_order = {
|
||||
set_custom_buttons: function(frm) {
|
||||
var doc = frm.doc;
|
||||
if (doc.docstatus === 1) {
|
||||
if (doc.status != 'Stopped' && doc.status != 'Completed') {
|
||||
frm.add_custom_button(__('Stop'), function() {
|
||||
erpnext.production_order.stop_production_order(frm, "Stopped");
|
||||
erpnext.wokr_order.stop_work_order(frm, "Stopped");
|
||||
}, __("Status"));
|
||||
} else if (doc.status == 'Stopped') {
|
||||
frm.add_custom_button(__('Re-open'), function() {
|
||||
erpnext.production_order.stop_production_order(frm, "Resumed");
|
||||
erpnext.work_order.stop_work_order(frm, "Resumed");
|
||||
}, __("Status"));
|
||||
}
|
||||
|
||||
@ -303,7 +303,7 @@ erpnext.production_order = {
|
||||
&& frm.doc.status != 'Stopped') {
|
||||
frm.has_start_btn = true;
|
||||
var start_btn = frm.add_custom_button(__('Start'), function() {
|
||||
erpnext.production_order.make_se(frm, 'Material Transfer for Manufacture');
|
||||
erpnext.work_order.make_se(frm, 'Material Transfer for Manufacture');
|
||||
});
|
||||
start_btn.addClass('btn-primary');
|
||||
}
|
||||
@ -314,7 +314,7 @@ erpnext.production_order = {
|
||||
&& frm.doc.status != 'Stopped') {
|
||||
frm.has_finish_btn = true;
|
||||
var finish_btn = frm.add_custom_button(__('Finish'), function() {
|
||||
erpnext.production_order.make_se(frm, 'Manufacture');
|
||||
erpnext.work_order.make_se(frm, 'Manufacture');
|
||||
});
|
||||
|
||||
if(doc.material_transferred_for_manufacturing==doc.qty) {
|
||||
@ -326,7 +326,7 @@ erpnext.production_order = {
|
||||
if ((flt(doc.produced_qty) < flt(doc.qty)) && frm.doc.status != 'Stopped') {
|
||||
frm.has_finish_btn = true;
|
||||
var finish_btn = frm.add_custom_button(__('Finish'), function() {
|
||||
erpnext.production_order.make_se(frm, 'Manufacture');
|
||||
erpnext.work_order.make_se(frm, 'Manufacture');
|
||||
});
|
||||
finish_btn.addClass('btn-primary');
|
||||
}
|
||||
@ -340,7 +340,7 @@ erpnext.production_order = {
|
||||
doc.planned_operating_cost = 0.0;
|
||||
for(var i=0;i<op.length;i++) {
|
||||
var planned_operating_cost = flt(flt(op[i].hour_rate) * flt(op[i].time_in_mins) / 60, 2);
|
||||
frappe.model.set_value('Production Order Operation', op[i].name,
|
||||
frappe.model.set_value('Work Order Operation', op[i].name,
|
||||
"planned_operating_cost", planned_operating_cost);
|
||||
doc.planned_operating_cost += planned_operating_cost;
|
||||
}
|
||||
@ -357,7 +357,7 @@ erpnext.production_order = {
|
||||
set_default_warehouse: function(frm) {
|
||||
if (!(frm.doc.wip_warehouse || frm.doc.fg_warehouse)) {
|
||||
frappe.call({
|
||||
method: "erpnext.manufacturing.doctype.production_order.production_order.get_default_warehouse",
|
||||
method: "erpnext.manufacturing.doctype.work_order.work_order.get_default_warehouse",
|
||||
callback: function(r) {
|
||||
if(!r.exe) {
|
||||
frm.set_value("wip_warehouse", r.message.wip_warehouse);
|
||||
@ -379,32 +379,32 @@ erpnext.production_order = {
|
||||
|
||||
max = flt(max, precision("qty"));
|
||||
frappe.prompt({fieldtype:"Float", label: __("Qty for {0}", [purpose]), fieldname:"qty",
|
||||
description: __("Max: {0}", [max]), 'default': max },
|
||||
function(data) {
|
||||
if(data.qty > max) {
|
||||
frappe.msgprint(__("Quantity must not be more than {0}", [max]));
|
||||
return;
|
||||
description: __("Max: {0}", [max]), 'default': max }, function(data)
|
||||
{
|
||||
if(data.qty > max) {
|
||||
frappe.msgprint(__("Quantity must not be more than {0}", [max]));
|
||||
return;
|
||||
}
|
||||
frappe.call({
|
||||
method:"erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry",
|
||||
args: {
|
||||
"work_order_id": frm.doc.name,
|
||||
"purpose": purpose,
|
||||
"qty": data.qty
|
||||
},
|
||||
callback: function(r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
frappe.call({
|
||||
method:"erpnext.manufacturing.doctype.production_order.production_order.make_stock_entry",
|
||||
args: {
|
||||
"production_order_id": frm.doc.name,
|
||||
"purpose": purpose,
|
||||
"qty": data.qty
|
||||
},
|
||||
callback: function(r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
});
|
||||
}, __("Select Quantity"), __("Make"));
|
||||
});
|
||||
}, __("Select Quantity"), __("Make"));
|
||||
},
|
||||
|
||||
stop_production_order: function(frm, status) {
|
||||
stop_work_order: function(frm, status) {
|
||||
frappe.call({
|
||||
method: "erpnext.manufacturing.doctype.production_order.production_order.stop_unstop",
|
||||
method: "erpnext.manufacturing.doctype.work_order.work_order.stop_unstop",
|
||||
args: {
|
||||
production_order: frm.doc.name,
|
||||
work_order: frm.doc.name,
|
||||
status: status
|
||||
},
|
||||
callback: function(r) {
|
@ -48,7 +48,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "PRO-",
|
||||
"default": "WO-",
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
@ -61,7 +61,7 @@
|
||||
"label": "Series",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "PRO-",
|
||||
"options": "WO-",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
@ -626,7 +626,7 @@
|
||||
"label": "Required Items",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Production Order Item",
|
||||
"options": "Work Order Item",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
@ -901,7 +901,7 @@
|
||||
"label": "Operations",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Production Order Operation",
|
||||
"options": "Work Order Operation",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
@ -1425,7 +1425,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "amended_from",
|
||||
"oldfieldtype": "Data",
|
||||
"options": "Production Order",
|
||||
"options": "Work Order",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
@ -1449,10 +1449,10 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-12-20 05:31:56.636724",
|
||||
"modified": "2018-02-13 02:58:11.328693",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Production Order",
|
||||
"name": "Work Order",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
@ -25,10 +25,10 @@ class OperationTooLongError(frappe.ValidationError): pass
|
||||
class ItemHasVariantError(frappe.ValidationError): pass
|
||||
|
||||
form_grid_templates = {
|
||||
"operations": "templates/form_grid/production_order_grid.html"
|
||||
"operations": "templates/form_grid/work_order_grid.html"
|
||||
}
|
||||
|
||||
class ProductionOrder(Document):
|
||||
class WorkOrder(Document):
|
||||
def validate(self):
|
||||
self.validate_production_item()
|
||||
if self.bom_no:
|
||||
@ -79,7 +79,7 @@ class ProductionOrder(Document):
|
||||
self.project = so[0].project
|
||||
|
||||
if not self.material_request:
|
||||
self.validate_production_order_against_so()
|
||||
self.validate_work_order_against_so()
|
||||
else:
|
||||
frappe.throw(_("Sales Order {0} is not valid").format(self.sales_order))
|
||||
|
||||
@ -111,9 +111,9 @@ class ProductionOrder(Document):
|
||||
else self.planned_operating_cost
|
||||
self.total_operating_cost = flt(self.additional_operating_cost) + flt(variable_cost)
|
||||
|
||||
def validate_production_order_against_so(self):
|
||||
def validate_work_order_against_so(self):
|
||||
# already ordered qty
|
||||
ordered_qty_against_so = frappe.db.sql("""select sum(qty) from `tabProduction Order`
|
||||
ordered_qty_against_so = frappe.db.sql("""select sum(qty) from `tabWork Order`
|
||||
where production_item = %s and sales_order = %s and docstatus < 2 and name != %s""",
|
||||
(self.production_item, self.sales_order, self.name))[0][0]
|
||||
|
||||
@ -138,7 +138,7 @@ class ProductionOrder(Document):
|
||||
.format(self.production_item, so_qty), OverProductionError)
|
||||
|
||||
def update_status(self, status=None):
|
||||
'''Update status of production order if unknown'''
|
||||
'''Update status of work order if unknown'''
|
||||
if status != "Stopped":
|
||||
status = self.get_status(status)
|
||||
|
||||
@ -150,7 +150,7 @@ class ProductionOrder(Document):
|
||||
return status
|
||||
|
||||
def get_status(self, status=None):
|
||||
'''Return the status based on stock entries against this production order'''
|
||||
'''Return the status based on stock entries against this work order'''
|
||||
if not status:
|
||||
status = self.status
|
||||
|
||||
@ -159,7 +159,7 @@ class ProductionOrder(Document):
|
||||
elif self.docstatus==1:
|
||||
if status != 'Stopped':
|
||||
stock_entries = frappe._dict(frappe.db.sql("""select purpose, sum(fg_completed_qty)
|
||||
from `tabStock Entry` where production_order=%s and docstatus=1
|
||||
from `tabStock Entry` where work_order=%s and docstatus=1
|
||||
group by purpose""", self.name))
|
||||
|
||||
status = "Not Started"
|
||||
@ -173,18 +173,18 @@ class ProductionOrder(Document):
|
||||
|
||||
return status
|
||||
|
||||
def update_production_order_qty(self):
|
||||
"""Update **Manufactured Qty** and **Material Transferred for Qty** in Production Order
|
||||
def update_work_order_qty(self):
|
||||
"""Update **Manufactured Qty** and **Material Transferred for Qty** in Work Order
|
||||
based on Stock Entry"""
|
||||
|
||||
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 production_order=%s and docstatus=1
|
||||
from `tabStock Entry` where work_order=%s and docstatus=1
|
||||
and purpose=%s""", (self.name, purpose))[0][0])
|
||||
|
||||
if qty > self.qty:
|
||||
frappe.throw(_("{0} ({1}) cannot be greater than planned quanitity ({2}) in Production Order {3}").format(\
|
||||
frappe.throw(_("{0} ({1}) cannot be greater than planned quanitity ({2}) in Work Order {3}").format(\
|
||||
self.meta.get_label(fieldname), qty, self.qty, self.name), StockOverProductionError)
|
||||
|
||||
self.db_set(fieldname, qty)
|
||||
@ -222,11 +222,11 @@ class ProductionOrder(Document):
|
||||
|
||||
def validate_cancel(self):
|
||||
if self.status == "Stopped":
|
||||
frappe.throw(_("Stopped Production Order cannot be cancelled, Unstop it first to cancel"))
|
||||
frappe.throw(_("Stopped Work Order cannot be cancelled, Unstop it first to cancel"))
|
||||
|
||||
# Check whether any stock entry exists against this Production Order
|
||||
# Check whether any stock entry exists against this Work Order
|
||||
stock_entry = frappe.db.sql("""select name from `tabStock Entry`
|
||||
where production_order = %s and docstatus = 1""", self.name)
|
||||
where work_order = %s and docstatus = 1""", self.name)
|
||||
if stock_entry:
|
||||
frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(stock_entry[0][0]))
|
||||
|
||||
@ -253,8 +253,8 @@ class ProductionOrder(Document):
|
||||
if self.material_request:
|
||||
frappe.get_doc("Material Request", self.material_request).update_completed_qty([self.material_request_item])
|
||||
|
||||
def set_production_order_operations(self):
|
||||
"""Fetch operations from BOM and set in 'Production Order'"""
|
||||
def set_work_order_operations(self):
|
||||
"""Fetch operations from BOM and set in 'Work Order'"""
|
||||
self.set('operations', [])
|
||||
|
||||
if not self.bom_no \
|
||||
@ -330,10 +330,10 @@ class ProductionOrder(Document):
|
||||
timesheet.validate_time_logs()
|
||||
except OverlapError:
|
||||
if frappe.message_log: frappe.message_log.pop()
|
||||
timesheet.schedule_for_production_order(d.idx)
|
||||
timesheet.schedule_for_work_order(d.idx)
|
||||
except WorkstationHolidayError:
|
||||
if frappe.message_log: frappe.message_log.pop()
|
||||
timesheet.schedule_for_production_order(d.idx)
|
||||
timesheet.schedule_for_work_order(d.idx)
|
||||
|
||||
from_time, to_time = self.get_start_end_time(timesheet, d.name)
|
||||
|
||||
@ -418,12 +418,12 @@ class ProductionOrder(Document):
|
||||
self.actual_end_date = max(actual_end_dates)
|
||||
|
||||
def delete_timesheet(self):
|
||||
for timesheet in frappe.get_all("Timesheet", ["name"], {"production_order": self.name}):
|
||||
for timesheet in frappe.get_all("Timesheet", ["name"], {"work_order": self.name}):
|
||||
frappe.delete_doc("Timesheet", timesheet.name)
|
||||
|
||||
def validate_production_item(self):
|
||||
if frappe.db.get_value("Item", self.production_item, "has_variants"):
|
||||
frappe.throw(_("Production Order cannot be raised against a Item Template"), ItemHasVariantError)
|
||||
frappe.throw(_("Work Order cannot be raised against a Item Template"), ItemHasVariantError)
|
||||
|
||||
if self.production_item:
|
||||
validate_end_of_life(self.production_item)
|
||||
@ -458,7 +458,7 @@ class ProductionOrder(Document):
|
||||
|
||||
def get_items_and_operations_from_bom(self):
|
||||
self.set_required_items()
|
||||
self.set_production_order_operations()
|
||||
self.set_work_order_operations()
|
||||
|
||||
return check_if_scrap_warehouse_mandatory(self.bom_no)
|
||||
|
||||
@ -497,13 +497,13 @@ class ProductionOrder(Document):
|
||||
|
||||
def update_transaferred_qty_for_required_items(self):
|
||||
'''update transferred qty from submitted stock entries for that item against
|
||||
the production order'''
|
||||
the work order'''
|
||||
|
||||
for d in self.required_items:
|
||||
transferred_qty = frappe.db.sql('''select sum(qty)
|
||||
from `tabStock Entry` entry, `tabStock Entry Detail` detail
|
||||
where
|
||||
entry.production_order = %s
|
||||
entry.work_order = %s
|
||||
and entry.purpose = "Material Transfer for Manufacture"
|
||||
and entry.docstatus = 1
|
||||
and detail.parent = entry.name
|
||||
@ -564,50 +564,50 @@ def check_if_scrap_warehouse_mandatory(bom_no):
|
||||
return res
|
||||
|
||||
@frappe.whitelist()
|
||||
def set_production_order_ops(name):
|
||||
po = frappe.get_doc('Production Order', name)
|
||||
po.set_production_order_operations()
|
||||
def set_work_order_ops(name):
|
||||
po = frappe.get_doc('Work Order', name)
|
||||
po.set_work_order_operations()
|
||||
po.save()
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_stock_entry(production_order_id, purpose, qty=None):
|
||||
production_order = frappe.get_doc("Production Order", production_order_id)
|
||||
if not frappe.db.get_value("Warehouse", production_order.wip_warehouse, "is_group") \
|
||||
and not production_order.skip_transfer:
|
||||
wip_warehouse = production_order.wip_warehouse
|
||||
def make_stock_entry(work_order_id, purpose, qty=None):
|
||||
work_order = frappe.get_doc("Work Order", work_order_id)
|
||||
if not frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group") \
|
||||
and not work_order.skip_transfer:
|
||||
wip_warehouse = work_order.wip_warehouse
|
||||
else:
|
||||
wip_warehouse = None
|
||||
|
||||
stock_entry = frappe.new_doc("Stock Entry")
|
||||
stock_entry.purpose = purpose
|
||||
stock_entry.production_order = production_order_id
|
||||
stock_entry.company = production_order.company
|
||||
stock_entry.work_order = work_order_id
|
||||
stock_entry.company = work_order.company
|
||||
stock_entry.from_bom = 1
|
||||
stock_entry.bom_no = production_order.bom_no
|
||||
stock_entry.use_multi_level_bom = production_order.use_multi_level_bom
|
||||
stock_entry.fg_completed_qty = qty or (flt(production_order.qty) - flt(production_order.produced_qty))
|
||||
if production_order.bom_no:
|
||||
stock_entry.bom_no = work_order.bom_no
|
||||
stock_entry.use_multi_level_bom = work_order.use_multi_level_bom
|
||||
stock_entry.fg_completed_qty = qty or (flt(work_order.qty) - flt(work_order.produced_qty))
|
||||
if work_order.bom_no:
|
||||
stock_entry.inspection_required = frappe.db.get_value('BOM',
|
||||
production_order.bom_no, 'inspection_required')
|
||||
work_order.bom_no, 'inspection_required')
|
||||
|
||||
if purpose=="Material Transfer for Manufacture":
|
||||
stock_entry.to_warehouse = wip_warehouse
|
||||
stock_entry.project = production_order.project
|
||||
stock_entry.project = work_order.project
|
||||
else:
|
||||
stock_entry.from_warehouse = wip_warehouse
|
||||
stock_entry.to_warehouse = production_order.fg_warehouse
|
||||
additional_costs = get_additional_costs(production_order, fg_qty=stock_entry.fg_completed_qty)
|
||||
stock_entry.project = production_order.project
|
||||
stock_entry.to_warehouse = work_order.fg_warehouse
|
||||
additional_costs = get_additional_costs(work_order, fg_qty=stock_entry.fg_completed_qty)
|
||||
stock_entry.project = work_order.project
|
||||
stock_entry.set("additional_costs", additional_costs)
|
||||
|
||||
stock_entry.get_items()
|
||||
return stock_entry.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_timesheet(production_order, company):
|
||||
def make_timesheet(work_order, company):
|
||||
timesheet = frappe.new_doc("Timesheet")
|
||||
timesheet.employee = ""
|
||||
timesheet.production_order = production_order
|
||||
timesheet.work_order = work_order
|
||||
timesheet.company = company
|
||||
return timesheet
|
||||
|
||||
@ -632,7 +632,7 @@ def get_default_warehouse():
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_new_timesheet(source_name, target_doc=None):
|
||||
po = frappe.get_doc('Production Order', source_name)
|
||||
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'):
|
||||
@ -641,16 +641,16 @@ def make_new_timesheet(source_name, target_doc=None):
|
||||
return ts
|
||||
|
||||
@frappe.whitelist()
|
||||
def stop_unstop(production_order, status):
|
||||
def stop_unstop(work_order, status):
|
||||
""" Called from client side on Stop/Unstop event"""
|
||||
|
||||
if not frappe.has_permission("Production Order", "write"):
|
||||
if not frappe.has_permission("Work Order", "write"):
|
||||
frappe.throw(_("Not permitted"), frappe.PermissionError)
|
||||
|
||||
pro_order = frappe.get_doc("Production Order", production_order)
|
||||
pro_order = frappe.get_doc("Work Order", work_order)
|
||||
pro_order.update_status(status)
|
||||
pro_order.update_planned_qty()
|
||||
frappe.msgprint(_("Production Order has been {0}").format(status))
|
||||
frappe.msgprint(_("Work Order has been {0}").format(status))
|
||||
pro_order.notify_update()
|
||||
|
||||
return pro_order.status
|
@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.views.calendar["Production Order"] = {
|
||||
frappe.views.calendar["Work Order"] = {
|
||||
field_map: {
|
||||
"start": "planned_start_date",
|
||||
"end": "planned_end_date",
|
@ -2,7 +2,7 @@ from frappe import _
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'production_order',
|
||||
'fieldname': 'work_order',
|
||||
'transactions': [
|
||||
{
|
||||
'items': ['Stock Entry', 'Timesheet']
|
@ -1,4 +1,4 @@
|
||||
frappe.listview_settings['Production Order'] = {
|
||||
frappe.listview_settings['Work Order'] = {
|
||||
add_fields: ["bom_no", "status", "sales_order", "qty",
|
||||
"produced_qty", "expected_delivery_date", "planned_start_date", "planned_end_date"],
|
||||
filters: [["status", "!=", "Stopped"]],
|
@ -354,10 +354,10 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-18 18:11:10.311736",
|
||||
"modified": "2018-02-13 02:58:11.328693",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Production Order Item",
|
||||
"name": "Work Order Item",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
@ -6,8 +6,8 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class ProductionOrderItem(Document):
|
||||
class WorkOrderItem(Document):
|
||||
pass
|
||||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("Production Order Item", ["item_code", "source_warehouse"])
|
||||
frappe.db.add_index("Work Order Item", ["item_code", "source_warehouse"])
|
@ -672,10 +672,10 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-05-29 18:02:04.252419",
|
||||
"modified": "2018-02-13 02:58:11.328693",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Production Order Operation",
|
||||
"name": "Work Order Operation",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
@ -5,5 +5,5 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class ProductionOrderOperation(Document):
|
||||
class WorkOrderOperation(Document):
|
||||
pass
|
@ -19,7 +19,7 @@ erpnext.ProductionAnalytics = frappe.views.GridReportWithPlot.extend({
|
||||
title: __("Production Analytics"),
|
||||
parent: $(wrapper).find('.layout-main'),
|
||||
page: wrapper.page,
|
||||
doctypes: ["Item", "Company", "Fiscal Year", "Production Order"]
|
||||
doctypes: ["Item", "Company", "Fiscal Year", "Work Order"]
|
||||
});
|
||||
|
||||
},
|
||||
@ -86,7 +86,7 @@ erpnext.ProductionAnalytics = frappe.views.GridReportWithPlot.extend({
|
||||
// add Opening, Closing, Totals rows
|
||||
// if filtered by account and / or voucher
|
||||
var me = this;
|
||||
var all_open_orders = {name:"All Production Orders", "id": "all-open-pos",
|
||||
var all_open_orders = {name:"All Work Orders", "id": "all-open-pos",
|
||||
checked:true};
|
||||
var not_started = {name:"Not Started", "id":"not-started-pos",
|
||||
checked:true};
|
||||
@ -97,7 +97,7 @@ erpnext.ProductionAnalytics = frappe.views.GridReportWithPlot.extend({
|
||||
var completed = {name:"Completed", "id":"completed-pos",
|
||||
checked:true};
|
||||
|
||||
$.each(frappe.report_dump.data["Production Order"], function(i, d) {
|
||||
$.each(frappe.report_dump.data["Work Order"], function(i, d) {
|
||||
var dateobj = frappe.datetime.str_to_obj(d.creation);
|
||||
var date = frappe.datetime.str_to_user(d.creation.split(" ")[0]);
|
||||
|
||||
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-08-12 12:44:27",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-02-24 20:10:32.460097",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Completed Production Orders",
|
||||
"owner": "Administrator",
|
||||
"query": "SELECT\n `tabProduction Order`.name as \"Production Order:Link/Production Order:200\",\n `tabProduction Order`.creation as \"Date:Date:120\",\n `tabProduction Order`.production_item as \"Item:Link/Item:150\",\n `tabProduction Order`.qty as \"To Produce:Int:100\",\n `tabProduction Order`.produced_qty as \"Produced:Int:100\",\n `tabProduction Order`.company as \"Company:Link/Company:\"\nFROM\n `tabProduction Order`\nWHERE\n `tabProduction Order`.docstatus=1\n AND ifnull(`tabProduction Order`.produced_qty,0) = `tabProduction Order`.qty",
|
||||
"ref_doctype": "Production Order",
|
||||
"report_name": "Completed Production Orders",
|
||||
"report_type": "Query Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Manufacturing User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-08-12 12:44:27",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2018-02-13 04:58:51.549413",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Completed Work Orders",
|
||||
"owner": "Administrator",
|
||||
"query": "SELECT\n `tabWork Order`.name as \"Work Order:Link/Work Order:200\",\n `tabWork Order`.creation as \"Date:Date:120\",\n `tabWork Order`.production_item as \"Item:Link/Item:150\",\n `tabWork Order`.qty as \"To Produce:Int:100\",\n `tabWork Order`.produced_qty as \"Produced:Int:100\",\n `tabWork Order`.company as \"Company:Link/Company:\"\nFROM\n `tabWork Order`\nWHERE\n `tabWork Order`.docstatus=1\n AND ifnull(`tabWork Order`.produced_qty,0) = `tabWork Order`.qty",
|
||||
"ref_doctype": "Work Order",
|
||||
"report_name": "Completed Work Orders",
|
||||
"report_type": "Query Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Manufacturing User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-05-03 17:48:46",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-02-24 20:00:38.101588",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Issued Items Against Production Order",
|
||||
"owner": "Administrator",
|
||||
"query": "select\n ste.production_order as \"Production Order:Link/Production Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Float:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\tpro.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\",\n\tste.company as \"Company:Link/Company:\",\n\t(select bin.projected_qty from `tabBin` bin \n\t\t\twhere bin.item_code= ste_item.item_code and bin.warehouse= ste_item.s_warehouse) as \"Projected Quantity as Source:Float:200\"\nfrom\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabProduction Order` pro\nwhere\n\tifnull(ste.production_order, '') != '' and ste.name = ste_item.parent \n\tand ste.production_order = pro.name and ste.docstatus = 1\n\tand ste.purpose = 'Material Transfer for Manufacture'\norder by ste.posting_date, ste.production_order, ste_item.item_code",
|
||||
"ref_doctype": "Production Order",
|
||||
"report_name": "Issued Items Against Production Order",
|
||||
"report_type": "Query Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Manufacturing User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-05-03 17:48:46",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2018-02-13 04:56:57.040163",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Issued Items Against Work Order",
|
||||
"owner": "Administrator",
|
||||
"query": "select\n ste.work_order as \"Work Order:Link/Work Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Float:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\two.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\",\n\tste.company as \"Company:Link/Company:\",\n\t(select bin.projected_qty from `tabBin` bin \n\t\t\twhere bin.item_code= ste_item.item_code and bin.warehouse= ste_item.s_warehouse) as \"Projected Quantity as Source:Float:200\"\nfrom\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabWork Order` wo\nwhere\n\tifnull(ste.work_order, '') != '' and ste.name = ste_item.parent \n\tand ste.work_order = wo.name and ste.docstatus = 1\n\tand ste.purpose = 'Material Transfer for Manufacture'\norder by ste.posting_date, ste.work_order, ste_item.item_code",
|
||||
"ref_doctype": "Work Order",
|
||||
"report_name": "Issued Items Against Work Order",
|
||||
"report_type": "Query Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Manufacturing User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-08-12 12:32:30",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-02-24 20:10:23.179130",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Open Production Orders",
|
||||
"owner": "Administrator",
|
||||
"query": "SELECT\n `tabProduction Order`.name as \"Production Order:Link/Production Order:200\",\n `tabProduction Order`.creation as \"Date:Date:120\",\n `tabProduction Order`.production_item as \"Item:Link/Item:150\",\n `tabProduction Order`.qty as \"To Produce:Int:100\",\n `tabProduction Order`.produced_qty as \"Produced:Int:100\",\n `tabProduction Order`.company as \"Company:Link/Company:\"\nFROM\n `tabProduction Order`\nWHERE\n `tabProduction Order`.docstatus=1\n AND ifnull(`tabProduction Order`.produced_qty,0) < `tabProduction Order`.qty\n AND NOT EXISTS (SELECT name from `tabStock Entry` where production_order =`tabProduction Order`.name) ",
|
||||
"ref_doctype": "Production Order",
|
||||
"report_name": "Open Production Orders",
|
||||
"report_type": "Query Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Manufacturing User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-08-12 12:32:30",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2018-02-13 04:58:29.780472",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Open Work Orders",
|
||||
"owner": "Administrator",
|
||||
"query": "SELECT\n `tabWork Order`.name as \"Work Order:Link/Work Order:200\",\n `tabWork Order`.creation as \"Date:Date:120\",\n `tabWork Order`.production_item as \"Item:Link/Item:150\",\n `tabWork Order`.qty as \"To Produce:Int:100\",\n `tabWork Order`.produced_qty as \"Produced:Int:100\",\n `tabWork Order`.company as \"Company:Link/Company:\"\nFROM\n `tabWork Order`\nWHERE\n `tabWork Order`.docstatus=1\n AND ifnull(`tabWork Order`.produced_qty,0) < `tabWork Order`.qty\n AND NOT EXISTS (SELECT name from `tabStock Entry` where work_order =`tabWork Order`.name) ",
|
||||
"ref_doctype": "Work Order",
|
||||
"report_name": "Open Work Orders",
|
||||
"report_type": "Query Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Manufacturing User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-08-12 12:43:47",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-02-24 20:10:40.304828",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Production Orders in Progress",
|
||||
"owner": "Administrator",
|
||||
"query": "SELECT\n `tabProduction Order`.name as \"Production Order:Link/Production Order:200\",\n `tabProduction Order`.creation as \"Date:Date:120\",\n `tabProduction Order`.production_item as \"Item:Link/Item:150\",\n `tabProduction Order`.qty as \"To Produce:Int:100\",\n `tabProduction Order`.produced_qty as \"Produced:Int:100\",\n `tabProduction Order`.company as \"Company:Link/Company:\"\nFROM\n `tabProduction Order`\nWHERE\n `tabProduction Order`.docstatus=1\n AND ifnull(`tabProduction Order`.produced_qty,0) < `tabProduction Order`.qty\n AND EXISTS (SELECT name from `tabStock Entry` where production_order =`tabProduction Order`.name) ",
|
||||
"ref_doctype": "Production Order",
|
||||
"report_name": "Production Orders in Progress",
|
||||
"report_type": "Query Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Manufacturing User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
// Copyright (c) 2016, Velometro Mobility Inc and contributors
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Production Order Stock Report"] = {
|
||||
frappe.query_reports["Work Order Stock Report"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname": "warehouse",
|
@ -8,13 +8,13 @@
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letter_head": "",
|
||||
"modified": "2017-02-24 19:59:07.792058",
|
||||
"modified": "2018-02-13 04:47:04.158213",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Production Order Stock Report",
|
||||
"name": "Work Order Stock Report",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Production Order",
|
||||
"report_name": "Production Order Stock Report",
|
||||
"ref_doctype": "Work Order",
|
||||
"report_name": "Work Order Stock Report",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
@ -6,20 +6,20 @@ from frappe.utils import cint
|
||||
import frappe
|
||||
|
||||
def execute(filters=None):
|
||||
prod_list = get_production_orders()
|
||||
data = get_item_list(prod_list, filters)
|
||||
wo_list = get_work_orders()
|
||||
data = get_item_list(wo_list, filters)
|
||||
columns = get_columns()
|
||||
return columns, data
|
||||
|
||||
def get_item_list(prod_list, filters):
|
||||
def get_item_list(wo_list, filters):
|
||||
out = []
|
||||
|
||||
#Add a row for each item/qty
|
||||
for prod_details in prod_list:
|
||||
desc = frappe.db.get_value("BOM", prod_details.bom_no, "description")
|
||||
for wo_details in wo_list:
|
||||
desc = frappe.db.get_value("BOM", wo_details.bom_no, "description")
|
||||
|
||||
for prod_item_details in frappe.db.get_values("Production Order Item",
|
||||
{"parent": prod_details.name}, ["item_code", "source_warehouse"], as_dict=1):
|
||||
for wo_item_details in frappe.db.get_values("Work Order Item",
|
||||
{"parent": wo_details.name}, ["item_code", "source_warehouse"], as_dict=1):
|
||||
|
||||
item_list = frappe.db.sql("""SELECT
|
||||
bom_item.item_code as item_code,
|
||||
@ -35,15 +35,15 @@ def get_item_list(prod_list, filters):
|
||||
and bom.name = %(bom)s
|
||||
GROUP BY
|
||||
bom_item.item_code""",
|
||||
{"bom": prod_details.bom_no, "warehouse": prod_item_details.source_warehouse,
|
||||
"filterhouse": filters.warehouse, "item_code": prod_item_details.item_code}, as_dict=1)
|
||||
{"bom": wo_details.bom_no, "warehouse": wo_item_details.source_warehouse,
|
||||
"filterhouse": filters.warehouse, "item_code": wo_item_details.item_code}, as_dict=1)
|
||||
|
||||
stock_qty = 0
|
||||
count = 0
|
||||
buildable_qty = prod_details.qty
|
||||
buildable_qty = wo_details.qty
|
||||
for item in item_list:
|
||||
count = count + 1
|
||||
if item.build_qty >= (prod_details.qty - prod_details.produced_qty):
|
||||
if item.build_qty >= (wo_details.qty - wo_details.produced_qty):
|
||||
stock_qty = stock_qty + 1
|
||||
elif buildable_qty >= item.build_qty:
|
||||
buildable_qty = item.build_qty
|
||||
@ -54,15 +54,15 @@ def get_item_list(prod_list, filters):
|
||||
build = "N"
|
||||
|
||||
row = frappe._dict({
|
||||
"production_order": prod_details.name,
|
||||
"status": prod_details.status,
|
||||
"work_order": wo_details.name,
|
||||
"status": wo_details.status,
|
||||
"req_items": cint(count),
|
||||
"instock": stock_qty,
|
||||
"description": desc,
|
||||
"source_warehouse": prod_item_details.source_warehouse,
|
||||
"item_code": prod_item_details.item_code,
|
||||
"bom_no": prod_details.bom_no,
|
||||
"qty": prod_details.qty,
|
||||
"source_warehouse": wo_item_details.source_warehouse,
|
||||
"item_code": wo_item_details.item_code,
|
||||
"bom_no": wo_details.bom_no,
|
||||
"qty": wo_details.qty,
|
||||
"buildable_qty": buildable_qty,
|
||||
"ready_to_build": build
|
||||
})
|
||||
@ -71,18 +71,18 @@ def get_item_list(prod_list, filters):
|
||||
|
||||
return out
|
||||
|
||||
def get_production_orders():
|
||||
out = frappe.get_all("Production Order", filters={"docstatus": 1, "status": ( "!=","Completed")},
|
||||
def get_work_orders():
|
||||
out = frappe.get_all("Work Order", filters={"docstatus": 1, "status": ( "!=","Completed")},
|
||||
fields=["name","status", "bom_no", "qty", "produced_qty"], order_by='name')
|
||||
|
||||
return out
|
||||
|
||||
def get_columns():
|
||||
columns = [{
|
||||
"fieldname": "production_order",
|
||||
"label": "Production Order",
|
||||
"fieldname": "work_order",
|
||||
"label": "Work Order",
|
||||
"fieldtype": "Link",
|
||||
"options": "Production Order",
|
||||
"options": "Work Order",
|
||||
"width": 110
|
||||
}, {
|
||||
"fieldname": "bom_no",
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-08-12 12:43:47",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2018-02-13 04:56:02.206353",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Work Orders in Progress",
|
||||
"owner": "Administrator",
|
||||
"query": "SELECT\n `tabWork Order`.name as \"Work Order:Link/Work Order:200\",\n `tabWork Order`.creation as \"Date:Date:120\",\n `tabWork Order`.production_item as \"Item:Link/Item:150\",\n `tabWork Order`.qty as \"To Produce:Int:100\",\n `tabWork Order`.produced_qty as \"Produced:Int:100\",\n `tabWork Order`.company as \"Company:Link/Company:\"\nFROM\n `tabWork Order`\nWHERE\n `tabWork Order`.docstatus=1\n AND ifnull(`tabWork Order`.produced_qty,0) < `tabWork Order`.qty\n AND EXISTS (SELECT name from `tabStock Entry` where work_order =`tabWork Order`.name) ",
|
||||
"ref_doctype": "Work Order",
|
||||
"report_name": "Work Orders in Progress",
|
||||
"report_type": "Query Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Manufacturing User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
execute:import unidecode # new requirement
|
||||
erpnext.patches.v8_0.move_perpetual_inventory_setting
|
||||
erpnext.patches.v10_0.rename_schools_to_education
|
||||
erpnext.patches.v11_0.rename_production_order_to_work_order
|
||||
erpnext.patches.v4_0.validate_v3_patch
|
||||
erpnext.patches.v4_0.fix_employee_user_id
|
||||
erpnext.patches.v4_0.remove_employee_role_if_no_employee
|
||||
@ -198,7 +199,7 @@ execute:frappe.db.sql("update `tabStock Ledger Entry` set stock_queue = '[]' whe
|
||||
erpnext.patches.v5_4.fix_missing_item_images
|
||||
erpnext.patches.v5_4.stock_entry_additional_costs
|
||||
erpnext.patches.v5_4.cleanup_journal_entry #2015-08-14
|
||||
execute:frappe.db.sql("update `tabProduction Order` pro set description = (select description from tabItem where name=pro.production_item) where ifnull(description, '') = ''")
|
||||
erpnext.patches.v5_7.update_item_description_based_on_item_master
|
||||
erpnext.patches.v5_7.item_template_attributes
|
||||
execute:frappe.delete_doc_if_exists("DocType", "Manage Variants")
|
||||
execute:frappe.delete_doc_if_exists("DocType", "Manage Variants Item")
|
||||
|
@ -0,0 +1,35 @@
|
||||
# Copyright (c) 2018, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.model.rename_doc import rename_doc
|
||||
from frappe.model.utils.rename_field import rename_field
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
rename_doc('DocType', 'Production Order', 'Work Order', force=True)
|
||||
frappe.reload_doc('manufacturing', 'doctype', 'work_order')
|
||||
|
||||
rename_doc('DocType', 'Production Order Item', 'Work Order Item', force=True)
|
||||
frappe.reload_doc('manufacturing', 'doctype', 'work_order_item')
|
||||
|
||||
rename_doc('DocType', 'Production Order Operation', 'Work Order Operation', force=True)
|
||||
frappe.reload_doc('manufacturing', 'doctype', 'work_order_operation')
|
||||
|
||||
frappe.reload_doc('projects', 'doctype', 'timesheet')
|
||||
frappe.reload_doc('stock', 'doctype', 'stock_entry')
|
||||
rename_field("Timesheet", "production_order", "work_order")
|
||||
rename_field("Stock Entry", "production_order", "work_order")
|
||||
|
||||
frappe.rename_doc("Report", "Production Orders in Progress", "Work Orders in Progress", force=True)
|
||||
frappe.rename_doc("Report", "Completed Production Orders", "Completed Work Orders", force=True)
|
||||
frappe.rename_doc("Report", "Open Production Orders", "Open Work Orders", force=True)
|
||||
frappe.rename_doc("Report", "Issued Items Against Production Order", "Issued Items Against Work Order", force=True)
|
||||
frappe.rename_doc("Report", "Production Order Stock Report", "Work Order Stock Report", force=True)
|
||||
|
||||
frappe.db.sql("""update `tabDesktop Icon` \
|
||||
set label='Work Order', module_name='Work Order' \
|
||||
where label='Production Order'""")
|
||||
frappe.db.sql("""update `tabDesktop Icon` \
|
||||
set link='List/Work Order' \
|
||||
where link='List/Production Order'""")
|
@ -18,7 +18,7 @@ doctype_series_map = {
|
||||
'Lead': 'LEAD-',
|
||||
'Opportunity': 'OPTY-',
|
||||
'Packing Slip': 'PS-',
|
||||
'Production Order': 'PRO-',
|
||||
'Work Order': 'WO-',
|
||||
'Purchase Invoice': 'PINV-',
|
||||
'Purchase Order': 'PO-',
|
||||
'Purchase Receipt': 'PREC-',
|
||||
|
@ -5,5 +5,5 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.db.sql("""update `tabStock Entry` set purpose='Manufacture' where purpose='Manufacture/Repack' and ifnull(production_order,"")!="" """)
|
||||
frappe.db.sql("""update `tabStock Entry` set purpose='Repack' where purpose='Manufacture/Repack' and ifnull(production_order,"")="" """)
|
||||
frappe.db.sql("""update `tabStock Entry` set purpose='Manufacture' where purpose='Manufacture/Repack' and ifnull(work_order,"")!="" """)
|
||||
frappe.db.sql("""update `tabStock Entry` set purpose='Repack' where purpose='Manufacture/Repack' and ifnull(work_order,"")="" """)
|
@ -5,9 +5,9 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
for po in frappe.db.sql("""select name from `tabProduction Order` where docstatus < 2""", as_dict=1):
|
||||
prod_order = frappe.get_doc("Production Order", po.name)
|
||||
if prod_order.operations:
|
||||
prod_order.flags.ignore_validate_update_after_submit = True
|
||||
prod_order.calculate_time()
|
||||
prod_order.save()
|
||||
for wo in frappe.db.sql("""select name from `tabWork Order` where docstatus < 2""", as_dict=1):
|
||||
work_order = frappe.get_doc("Work Order", wo.name)
|
||||
if work_order.operations:
|
||||
work_order.flags.ignore_validate_update_after_submit = True
|
||||
work_order.calculate_time()
|
||||
work_order.save()
|
@ -51,7 +51,7 @@ rename_map = {
|
||||
["other_charges", "taxes"],
|
||||
["advance_allocation_details", "advances"]
|
||||
],
|
||||
"Production Order": [
|
||||
"Work Order": [
|
||||
["production_order_operations", "operations"]
|
||||
],
|
||||
"BOM": [
|
||||
@ -216,7 +216,7 @@ def execute():
|
||||
frappe.rename_doc("DocType", old_dt, new_dt, force=True)
|
||||
|
||||
# reload new child doctypes
|
||||
frappe.reload_doc("manufacturing", "doctype", "production_order_operation")
|
||||
frappe.reload_doc("manufacturing", "doctype", "work_order_operation")
|
||||
frappe.reload_doc("manufacturing", "doctype", "workstation_working_hour")
|
||||
frappe.reload_doc("stock", "doctype", "item_variant")
|
||||
frappe.reload_doc("hr", "doctype", "salary_detail")
|
||||
|
@ -2,4 +2,4 @@ import frappe
|
||||
|
||||
def execute():
|
||||
frappe.db.sql("""update `tabStock Entry` set purpose='Material Transfer for Manufacture'
|
||||
where ifnull(production_order, '')!='' and purpose='Material Transfer'""")
|
||||
where ifnull(work_order, '')!='' and purpose='Material Transfer'""")
|
||||
|
@ -1,9 +1,9 @@
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doctype("Production Order")
|
||||
frappe.db.sql("""update `tabProduction Order` set material_transferred_for_manufacturing=
|
||||
frappe.reload_doctype("Work Order")
|
||||
frappe.db.sql("""update `tabWork Order` set material_transferred_for_manufacturing=
|
||||
(select sum(fg_completed_qty) from `tabStock Entry`
|
||||
where docstatus=1
|
||||
and production_order=`tabProduction Order`.name
|
||||
and work_order=`tabWork Order`.name
|
||||
and purpose = "Material Transfer for Manufacture")""")
|
||||
|
@ -1,18 +1,18 @@
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
pro_order_qty_transferred = frappe._dict()
|
||||
for se in frappe.db.sql("""select production_order, sum(fg_completed_qty) as transferred_qty
|
||||
wo_order_qty_transferred = frappe._dict()
|
||||
for se in frappe.db.sql("""select work_order, sum(fg_completed_qty) as transferred_qty
|
||||
from `tabStock Entry`
|
||||
where docstatus=1 and ifnull(production_order, '') != ''
|
||||
where docstatus=1 and ifnull(work_order, '') != ''
|
||||
and purpose = 'Material Transfer for Manufacture'
|
||||
group by production_order""", as_dict=1):
|
||||
pro_order_qty_transferred.setdefault(se.production_order, se.transferred_qty)
|
||||
group by work_order""", as_dict=1):
|
||||
wo_order_qty_transferred.setdefault(se.work_order, se.transferred_qty)
|
||||
|
||||
for d in frappe.get_all("Production Order", filters={"docstatus": 1}, fields=["name", "qty"]):
|
||||
if d.name in pro_order_qty_transferred:
|
||||
material_transferred_for_manufacturing = pro_order_qty_transferred.get(d.name) \
|
||||
if pro_order_qty_transferred.get(d.name) <= d.qty else d.qty
|
||||
for d in frappe.get_all("Work Order", filters={"docstatus": 1}, fields=["name", "qty"]):
|
||||
if d.name in wo_order_qty_transferred:
|
||||
material_transferred_for_manufacturing = wo_order_qty_transferred.get(d.name) \
|
||||
if wo_order_qty_transferred.get(d.name) <= d.qty else d.qty
|
||||
|
||||
frappe.db.sql("""update `tabProduction Order` set material_transferred_for_manufacturing=%s
|
||||
frappe.db.sql("""update `tabWork Order` set material_transferred_for_manufacturing=%s
|
||||
where name=%s""", (material_transferred_for_manufacturing, d.name))
|
@ -0,0 +1,12 @@
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
name = frappe.db.sql(""" select name from `tabPatch Log` \
|
||||
where \
|
||||
patch like 'execute:frappe.db.sql("update `tabProduction Order` pro set description%' """)
|
||||
if not name:
|
||||
frappe.db.sql("update `tabProduction Order` pro \
|
||||
set \
|
||||
description = (select description from tabItem where name=pro.production_item) \
|
||||
where \
|
||||
ifnull(description, '') = ''")
|
@ -7,7 +7,7 @@ from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
|
||||
|
||||
def execute():
|
||||
for item_code, warehouse in frappe.db.sql("""select distinct production_item, fg_warehouse
|
||||
from `tabProduction Order`"""):
|
||||
from `tabWork Order`"""):
|
||||
if frappe.db.exists("Item", item_code) and frappe.db.exists("Warehouse", warehouse):
|
||||
update_bin_qty(item_code, warehouse, {
|
||||
"planned_qty": get_planned_qty(item_code, warehouse)
|
||||
|
@ -7,7 +7,7 @@ from frappe.model.utils.rename_field import rename_field
|
||||
|
||||
def execute():
|
||||
|
||||
doc_list = ["Production Order", "BOM", "Purchase Invoice Item", "Sales Invoice",
|
||||
doc_list = ["Work Order", "BOM", "Purchase Invoice Item", "Sales Invoice",
|
||||
"Purchase Order Item", "Stock Entry", "Delivery Note", "Sales Order",
|
||||
"Purchase Receipt Item", "Supplier Quotation Item"]
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import frappe
|
||||
from erpnext.manufacturing.doctype.production_order.production_order \
|
||||
from erpnext.manufacturing.doctype.work_order.work_order \
|
||||
import make_timesheet, add_timesheet_detail
|
||||
|
||||
def execute():
|
||||
@ -11,12 +11,12 @@ def execute():
|
||||
for data in frappe.db.sql("select * from `tabTime Log`", as_dict=1):
|
||||
if data.task:
|
||||
company = frappe.db.get_value("Task", data.task, "company")
|
||||
elif data.production_order:
|
||||
company = frappe.db.get_value("Prodction Order", data.production_order, "company")
|
||||
elif data.work_order:
|
||||
company = frappe.db.get_value("Work Order", data.work_order, "company")
|
||||
else:
|
||||
company = frappe.db.get_single_value('Global Defaults', 'default_company')
|
||||
|
||||
time_sheet = make_timesheet(data.production_order, company)
|
||||
time_sheet = make_timesheet(data.work_order, company)
|
||||
args = get_timelog_data(data)
|
||||
add_timesheet_detail(time_sheet, args)
|
||||
if data.docstatus == 2:
|
||||
@ -40,7 +40,7 @@ def execute():
|
||||
time_sheet.db_set("docstatus", 1)
|
||||
for d in time_sheet.get("time_logs"):
|
||||
d.db_set("docstatus", 1)
|
||||
time_sheet.update_production_order(time_sheet.name)
|
||||
time_sheet.update_work_order(time_sheet.name)
|
||||
time_sheet.update_task_and_project()
|
||||
if data.docstatus == 2:
|
||||
time_sheet.db_set("docstatus", 2)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import frappe
|
||||
from frappe.utils import cint
|
||||
from erpnext.manufacturing.doctype.production_order.production_order import add_timesheet_detail
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import add_timesheet_detail
|
||||
from erpnext.patches.v7_0.convert_timelog_to_timesheet import get_timelog_data
|
||||
|
||||
def execute():
|
||||
|
@ -3,8 +3,8 @@ import frappe
|
||||
from erpnext.stock.stock_balance import repost_stock
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc('manufacturing', 'doctype', 'production_order_item')
|
||||
frappe.reload_doc('manufacturing', 'doctype', 'production_order')
|
||||
frappe.reload_doc('manufacturing', 'doctype', 'work_order_item')
|
||||
frappe.reload_doc('manufacturing', 'doctype', 'work_order')
|
||||
|
||||
modified_items = frappe.db.sql_list("""
|
||||
select name from `tabItem`
|
||||
@ -26,12 +26,12 @@ def execute():
|
||||
|
||||
item_warehouses_with_transactions += list(frappe.db.sql("""
|
||||
select distinct production_item, fg_warehouse
|
||||
from `tabProduction Order` where docstatus=1 and production_item in ({0})"""
|
||||
from `tabWork Order` where docstatus=1 and production_item in ({0})"""
|
||||
.format(', '.join(['%s']*len(modified_items))), tuple(modified_items)))
|
||||
|
||||
item_warehouses_with_transactions += list(frappe.db.sql("""
|
||||
select distinct pr_item.item_code, pr_item.source_warehouse
|
||||
from `tabProduction Order` pr, `tabProduction Order Item` pr_item
|
||||
from `tabWork Order` pr, `tabWork Order Item` pr_item
|
||||
where pr_item.parent and pr.name and pr.docstatus=1 and pr_item.item_code in ({0})"""
|
||||
.format(', '.join(['%s']*len(modified_items))), tuple(modified_items)))
|
||||
|
||||
|
@ -6,41 +6,41 @@ import frappe
|
||||
|
||||
def execute():
|
||||
# reload schema
|
||||
for doctype in ("Production Order", "Production Order Item", "Production Order Operation",
|
||||
for doctype in ("Work Order", "Work Order Item", "Work Order Operation",
|
||||
"BOM Item", "BOM Explosion Item", "BOM"):
|
||||
frappe.reload_doctype(doctype)
|
||||
|
||||
# fetch all draft and submitted production orders
|
||||
# fetch all draft and submitted work orders
|
||||
fields = ["name"]
|
||||
if "source_warehouse" in frappe.db.get_table_columns("Production Order"):
|
||||
if "source_warehouse" in frappe.db.get_table_columns("Work Order"):
|
||||
fields.append("source_warehouse")
|
||||
|
||||
pro_orders = frappe.get_all("Production Order", filters={"docstatus": ["!=", 2]}, fields=fields)
|
||||
wo_orders = frappe.get_all("Work Order", filters={"docstatus": ["!=", 2]}, fields=fields)
|
||||
|
||||
count = 0
|
||||
for p in pro_orders:
|
||||
pro_order = frappe.get_doc("Production Order", p.name)
|
||||
for p in wo_orders:
|
||||
wo_order = frappe.get_doc("Work Order", p.name)
|
||||
count += 1
|
||||
|
||||
# set required items table
|
||||
pro_order.set_required_items()
|
||||
wo_order.set_required_items()
|
||||
|
||||
for item in pro_order.get("required_items"):
|
||||
for item in wo_order.get("required_items"):
|
||||
# set source warehouse based on parent
|
||||
if not item.source_warehouse and "source_warehouse" in fields:
|
||||
item.source_warehouse = pro_order.get("source_warehouse")
|
||||
item.source_warehouse = wo_order.get("source_warehouse")
|
||||
item.db_update()
|
||||
|
||||
if pro_order.docstatus == 1:
|
||||
if wo_order.docstatus == 1:
|
||||
# update transferred qty based on Stock Entry, it also updates db
|
||||
pro_order.update_transaferred_qty_for_required_items()
|
||||
wo_order.update_transaferred_qty_for_required_items()
|
||||
|
||||
# Set status where it was 'Unstopped', as it is deprecated
|
||||
if pro_order.status == "Unstopped":
|
||||
status = pro_order.get_status()
|
||||
pro_order.db_set("status", status)
|
||||
elif pro_order.status == "Stopped":
|
||||
pro_order.update_reserved_qty_for_production()
|
||||
if wo_order.status == "Unstopped":
|
||||
status = wo_order.get_status()
|
||||
wo_order.db_set("status", status)
|
||||
elif wo_order.status == "Stopped":
|
||||
wo_order.update_reserved_qty_for_production()
|
||||
|
||||
if count % 200 == 0:
|
||||
frappe.db.commit()
|
@ -5,5 +5,5 @@ import frappe
|
||||
|
||||
def execute():
|
||||
for dt in ("Sales Order Item", "Purchase Order Item",
|
||||
"Material Request Item", "Production Order Item", "Packed Item"):
|
||||
"Material Request Item", "Work Order Item", "Packed Item"):
|
||||
frappe.get_doc("DocType", dt).run_module_method("on_doctype_update")
|
@ -11,5 +11,5 @@ def execute():
|
||||
#Check more than one company exists
|
||||
if len(company) > 1:
|
||||
frappe.db.sql(""" update `tabTimesheet` set `tabTimesheet`.company =
|
||||
(select company from `tabProduction Order` where name = `tabTimesheet`.production_order)
|
||||
where production_order is not null and production_order !=''""")
|
||||
(select company from `tabWork Order` where name = `tabTimesheet`.work_order)
|
||||
where workn_order is not null and work_order !=''""")
|
@ -3,7 +3,7 @@ import frappe
|
||||
def execute():
|
||||
|
||||
frappe.db.sql("""
|
||||
update `tabBOM Item` bom, `tabProduction Order Item` po_item
|
||||
update `tabBOM Item` bom, `tabWork Order Item` po_item
|
||||
set po_item.item_name = bom.item_name,
|
||||
po_item.description = bom.description
|
||||
where po_item.item_code = bom.item_code
|
||||
|
23
erpnext/projects/doctype/timesheet/test_timesheet.js
Normal file
23
erpnext/projects/doctype/timesheet/test_timesheet.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: Timesheet", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Timesheet
|
||||
() => frappe.tests.make('Timesheet', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
@ -204,7 +204,7 @@
|
||||
"collapsible": 0,
|
||||
"collapsible_depends_on": "",
|
||||
"columns": 0,
|
||||
"depends_on": "eval:!doc.production_order || doc.docstatus == 1",
|
||||
"depends_on": "eval:!doc.work_order || doc.docstatus == 1",
|
||||
"fieldname": "employee_detail",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@ -421,8 +421,8 @@
|
||||
"collapsible": 0,
|
||||
"collapsible_depends_on": "",
|
||||
"columns": 0,
|
||||
"depends_on": "production_order",
|
||||
"fieldname": "production_detail",
|
||||
"depends_on": "work_order",
|
||||
"fieldname": "work_detail",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@ -431,7 +431,7 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Production Detail",
|
||||
"label": "Work Detail",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -452,7 +452,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "production_order",
|
||||
"fieldname": "work_order",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@ -461,10 +461,10 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Production Order",
|
||||
"label": "Work Order",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Production Order",
|
||||
"options": "Work Order",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
@ -938,7 +938,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-06-13 14:28:51.838769",
|
||||
"modified": "2018-01-07 11:44:09.573900",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Timesheet",
|
||||
|
@ -16,7 +16,7 @@ from erpnext.manufacturing.doctype.workstation.workstation import (check_if_with
|
||||
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
|
||||
|
||||
class OverlapError(frappe.ValidationError): pass
|
||||
class OverProductionLoggedError(frappe.ValidationError): pass
|
||||
class OverWorkLoggedError(frappe.ValidationError): pass
|
||||
|
||||
class Timesheet(Document):
|
||||
def onload(self):
|
||||
@ -97,18 +97,18 @@ class Timesheet(Document):
|
||||
self.set_status()
|
||||
|
||||
def on_cancel(self):
|
||||
self.update_production_order(None)
|
||||
self.update_work_order(None)
|
||||
self.update_task_and_project()
|
||||
|
||||
def on_submit(self):
|
||||
self.validate_mandatory_fields()
|
||||
self.update_production_order(self.name)
|
||||
self.update_work_order(self.name)
|
||||
self.update_task_and_project()
|
||||
|
||||
def validate_mandatory_fields(self):
|
||||
if self.production_order:
|
||||
production_order = frappe.get_doc("Production Order", self.production_order)
|
||||
pending_qty = flt(production_order.qty) - flt(production_order.produced_qty)
|
||||
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:
|
||||
@ -120,16 +120,16 @@ 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.production_order and flt(data.completed_qty) == 0:
|
||||
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.production_order and flt(pending_qty) < flt(data.completed_qty) and flt(pending_qty) > 0:
|
||||
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),
|
||||
OverProductionLoggedError)
|
||||
OverWorkLoggedError)
|
||||
|
||||
def update_production_order(self, time_sheet):
|
||||
if self.production_order:
|
||||
pro = frappe.get_doc('Production Order', self.production_order)
|
||||
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:
|
||||
@ -152,8 +152,8 @@ class Timesheet(Document):
|
||||
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.production_order = %s and tsd.operation_id = %s and ts.docstatus=1 and ts.name = tsd.parent""",
|
||||
(self.production_order, operation_id), as_dict=1)[0]
|
||||
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 = [], []
|
||||
@ -181,7 +181,7 @@ class Timesheet(Document):
|
||||
|
||||
def validate_overlap(self, data):
|
||||
settings = frappe.get_single('Projects Settings')
|
||||
if self.production_order:
|
||||
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)
|
||||
@ -232,7 +232,7 @@ class Timesheet(Document):
|
||||
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_production_order(self, index):
|
||||
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
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
@ -11,6 +12,7 @@
|
||||
"editable_grid": 1,
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -21,7 +23,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Activity Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -38,6 +42,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -48,7 +53,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "From Time",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -65,6 +72,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -75,7 +83,9 @@
|
||||
"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,
|
||||
@ -91,6 +101,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -101,7 +112,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Hrs",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -117,6 +130,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -127,7 +141,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "To Time",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -144,6 +160,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -154,7 +171,9 @@
|
||||
"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,
|
||||
@ -170,18 +189,21 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:parent.production_order",
|
||||
"depends_on": "eval:parent.work_order",
|
||||
"fieldname": "completed_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": "Completed Qty",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -198,18 +220,21 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:parent.production_order",
|
||||
"depends_on": "eval:parent.work_order",
|
||||
"fieldname": "workstation",
|
||||
"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": "Workstation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -227,6 +252,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -237,7 +263,9 @@
|
||||
"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,
|
||||
@ -253,18 +281,21 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:parent.production_order",
|
||||
"depends_on": "eval:parent.work_order",
|
||||
"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,
|
||||
@ -282,18 +313,21 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:parent.production_order",
|
||||
"depends_on": "eval:parent.work_order",
|
||||
"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,
|
||||
@ -310,6 +344,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -320,7 +355,9 @@
|
||||
"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,
|
||||
@ -336,6 +373,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -346,7 +384,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Project",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -364,6 +404,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -374,7 +415,9 @@
|
||||
"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,
|
||||
@ -390,6 +433,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -401,7 +445,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Task",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -419,6 +465,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -429,7 +476,9 @@
|
||||
"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,
|
||||
@ -445,6 +494,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -456,7 +506,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Bill",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -473,6 +525,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -483,7 +536,9 @@
|
||||
"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,
|
||||
@ -499,6 +554,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -510,7 +566,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Billing Hours",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -527,6 +585,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -538,7 +597,9 @@
|
||||
"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,
|
||||
@ -554,6 +615,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -565,7 +627,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Billing Rate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -582,6 +646,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -595,7 +660,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Billing Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -612,6 +679,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -622,7 +690,9 @@
|
||||
"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,
|
||||
@ -638,6 +708,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -648,7 +719,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Costing Rate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -665,6 +738,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -677,7 +751,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Costing Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -694,6 +770,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -704,7 +781,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reference",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -721,6 +800,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -731,7 +811,9 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Sales Invoice",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
@ -749,17 +831,17 @@
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2016-11-03 16:01:10.519549",
|
||||
"modified": "2018-01-07 11:46:04.045313",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Timesheet Detail",
|
||||
@ -768,6 +850,8 @@
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
}
|
@ -492,8 +492,8 @@ frappe.help.help_links['Form/BOM'] = [
|
||||
{ label: 'Nested BOM Structure', url: 'https://frappe.github.io/erpnext/user/manual/en/manufacturing/articles/nested-bom-structure' },
|
||||
]
|
||||
|
||||
frappe.help.help_links['Form/Production Order'] = [
|
||||
{ label: 'Production Order', url: 'https://frappe.github.io/erpnext/user/manual/en/manufacturing/production-order' },
|
||||
frappe.help.help_links['Form/Work Order'] = [
|
||||
{ label: 'Work Order', url: 'https://frappe.github.io/erpnext/user/manual/en/manufacturing/production-order' },
|
||||
]
|
||||
|
||||
frappe.help.help_links['Form/Workstation'] = [
|
||||
|
@ -106,8 +106,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) {
|
||||
this.frm.add_custom_button(__('Delivery'),
|
||||
function() { me.make_delivery_note_based_on_delivery_date(); }, __("Make"));
|
||||
this.frm.add_custom_button(__('Production Order'),
|
||||
function() { me.make_production_order() }, __("Make"));
|
||||
this.frm.add_custom_button(__('Work Order'),
|
||||
function() { me.make_work_order() }, __("Make"));
|
||||
|
||||
this.frm.page.set_inner_btn_group_as_primary(__("Make"));
|
||||
}
|
||||
@ -192,15 +192,15 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
this.order_type(doc);
|
||||
},
|
||||
|
||||
make_production_order() {
|
||||
make_work_order() {
|
||||
var me = this;
|
||||
this.frm.call({
|
||||
doc: this.frm.doc,
|
||||
method: 'get_production_order_items',
|
||||
method: 'get_work_order_items',
|
||||
callback: function(r) {
|
||||
if(!r.message) {
|
||||
frappe.msgprint({
|
||||
title: __('Production Order not created'),
|
||||
title: __('Work Order not created'),
|
||||
message: __('No Items with Bill of Materials to Manufacture'),
|
||||
indicator: 'orange'
|
||||
});
|
||||
@ -208,8 +208,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
}
|
||||
else if(!r.message) {
|
||||
frappe.msgprint({
|
||||
title: __('Production Order not created'),
|
||||
message: __('Production Order already created for all items with BOM'),
|
||||
title: __('Work Order not created'),
|
||||
message: __('Work Order already created for all items with BOM'),
|
||||
indicator: 'orange'
|
||||
});
|
||||
return;
|
||||
@ -240,7 +240,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
primary_action: function() {
|
||||
var data = d.get_values();
|
||||
me.frm.call({
|
||||
method: 'make_production_orders',
|
||||
method: 'make_work_orders',
|
||||
args: {
|
||||
items: data,
|
||||
company: me.frm.doc.company,
|
||||
@ -251,9 +251,9 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
frappe.msgprint({
|
||||
message: __('Production Orders Created: {0}',
|
||||
message: __('Work Orders Created: {0}',
|
||||
[r.message.map(function(d) {
|
||||
return repl('<a href="#Form/Production Order/%(name)s">%(name)s</a>', {name:d})
|
||||
return repl('<a href="#Form/Work Order/%(name)s">%(name)s</a>', {name:d})
|
||||
}).join(', ')]),
|
||||
indicator: 'green'
|
||||
})
|
||||
|
@ -242,14 +242,14 @@ class SalesOrder(SellingController):
|
||||
frappe.throw(_("Maintenance Visit {0} must be cancelled before cancelling this Sales Order")
|
||||
.format(comma_and(submit_mv)))
|
||||
|
||||
# check production order
|
||||
# check work order
|
||||
pro_order = frappe.db.sql_list("""
|
||||
select name
|
||||
from `tabProduction Order`
|
||||
from `tabWork Order`
|
||||
where sales_order = %s and docstatus = 1""", self.name)
|
||||
|
||||
if pro_order:
|
||||
frappe.throw(_("Production Order {0} must be cancelled before cancelling this Sales Order")
|
||||
frappe.throw(_("Work Order {0} must be cancelled before cancelling this Sales Order")
|
||||
.format(comma_and(pro_order)))
|
||||
|
||||
def check_modified_date(self):
|
||||
@ -347,8 +347,8 @@ class SalesOrder(SellingController):
|
||||
self.indicator_color = "green"
|
||||
self.indicator_title = _("Paid")
|
||||
|
||||
def get_production_order_items(self):
|
||||
'''Returns items with BOM that already do not have a linked production order'''
|
||||
def get_work_order_items(self):
|
||||
'''Returns items with BOM that already do not have a linked work order'''
|
||||
items = []
|
||||
|
||||
for table in [self.items, self.packed_items]:
|
||||
@ -356,7 +356,7 @@ class SalesOrder(SellingController):
|
||||
bom = get_default_bom_item(i.item_code)
|
||||
if bom:
|
||||
stock_qty = i.qty if i.doctype == 'Packed Item' else i.stock_qty
|
||||
pending_qty= stock_qty - flt(frappe.db.sql('''select sum(qty) from `tabProduction Order`
|
||||
pending_qty= stock_qty - flt(frappe.db.sql('''select sum(qty) from `tabWork Order`
|
||||
where production_item=%s and sales_order=%s and sales_order_item = %s and docstatus<2''', (i.item_code, self.name, i.name))[0][0])
|
||||
if pending_qty:
|
||||
items.append(dict(
|
||||
@ -776,8 +776,8 @@ def get_supplier(doctype, txt, searchfield, start, page_len, filters):
|
||||
})
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_production_orders(items, sales_order, company, project=None):
|
||||
'''Make Production Orders against the given Sales Order for the given `items`'''
|
||||
def make_work_orders(items, sales_order, company, project=None):
|
||||
'''Make Work Orders against the given Sales Order for the given `items`'''
|
||||
items = json.loads(items).get('items')
|
||||
out = []
|
||||
|
||||
@ -787,8 +787,8 @@ def make_production_orders(items, sales_order, company, project=None):
|
||||
if not i.get("pending_qty"):
|
||||
frappe.throw(_("Please select Qty against item {0}").format(i.get("item_code")))
|
||||
|
||||
production_order = frappe.get_doc(dict(
|
||||
doctype='Production Order',
|
||||
work_order = frappe.get_doc(dict(
|
||||
doctype='Work Order',
|
||||
production_item=i['item_code'],
|
||||
bom_no=i.get('bom'),
|
||||
qty=i['pending_qty'],
|
||||
@ -798,9 +798,9 @@ def make_production_orders(items, sales_order, company, project=None):
|
||||
project=project,
|
||||
fg_warehouse=i['warehouse']
|
||||
)).insert()
|
||||
production_order.set_production_order_operations()
|
||||
production_order.save()
|
||||
out.append(production_order)
|
||||
work_order.set_work_order_operations()
|
||||
work_order.save()
|
||||
out.append(work_order)
|
||||
|
||||
return [p.name for p in out]
|
||||
|
||||
|
@ -28,7 +28,7 @@ def get_data():
|
||||
},
|
||||
{
|
||||
'label': _('Manufacturing'),
|
||||
'items': ['Production Order']
|
||||
'items': ['Work Order']
|
||||
},
|
||||
{
|
||||
'label': _('Reference'),
|
||||
|
@ -9,7 +9,7 @@ from erpnext.selling.doctype.sales_order.sales_order \
|
||||
import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from frappe.tests.test_permissions import set_user_permission_doctypes
|
||||
from erpnext.selling.doctype.sales_order.sales_order import make_production_orders
|
||||
from erpnext.selling.doctype.sales_order.sales_order import make_work_orders
|
||||
import json
|
||||
|
||||
|
||||
@ -530,7 +530,7 @@ class TestSalesOrder(unittest.TestCase):
|
||||
si.insert()
|
||||
self.assertTrue(si.get('payment_schedule'))
|
||||
|
||||
def test_make_production_order(self):
|
||||
def test_make_work_order(self):
|
||||
# Make a new Sales Order
|
||||
so = make_sales_order(**{
|
||||
"item_list": [{
|
||||
@ -545,10 +545,10 @@ class TestSalesOrder(unittest.TestCase):
|
||||
}]
|
||||
})
|
||||
|
||||
# Raise Production Orders
|
||||
# Raise Work Orders
|
||||
po_items= []
|
||||
so_item_name= {}
|
||||
for item in so.get_production_order_items():
|
||||
for item in so.get_work_order_items():
|
||||
po_items.append({
|
||||
"warehouse": item.get("warehouse"),
|
||||
"item_code": item.get("item_code"),
|
||||
@ -557,12 +557,12 @@ class TestSalesOrder(unittest.TestCase):
|
||||
"bom": item.get("bom")
|
||||
})
|
||||
so_item_name[item.get("sales_order_item")]= item.get("pending_qty")
|
||||
make_production_orders(json.dumps({"items":po_items}), so.name, so.company)
|
||||
make_work_orders(json.dumps({"items":po_items}), so.name, so.company)
|
||||
|
||||
# Check if Production Orders were raised
|
||||
# Check if Work Orders were raised
|
||||
for item in so_item_name:
|
||||
po_qty = frappe.db.sql("select sum(qty) from `tabProduction Order` where sales_order=%s and sales_order_item=%s", (so.name, item))
|
||||
self.assertEqual(po_qty[0][0], so_item_name.get(item))
|
||||
wo_qty = frappe.db.sql("select sum(qty) from `tabWork Order` where sales_order=%s and sales_order_item=%s", (so.name, item))
|
||||
self.assertEquals(wo_qty[0][0], so_item_name.get(item))
|
||||
|
||||
def make_sales_order(**args):
|
||||
so = frappe.new_doc("Sales Order")
|
||||
|
@ -4,7 +4,7 @@ QUnit.test("Test: Company", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
frappe.run_serially([
|
||||
// Added company for Production Order testing
|
||||
// Added company for Work Order testing
|
||||
() => frappe.set_route("List", "Company"),
|
||||
() => frappe.new_doc("Company"),
|
||||
() => frappe.timeout(1),
|
||||
|
@ -52,7 +52,7 @@ def get_notification_config():
|
||||
"status": ("not in", ("Completed", "Closed")),
|
||||
"docstatus": ("<", 2)
|
||||
},
|
||||
"Production Order": { "status": ("in", ("Draft", "Not Started", "In Process")) },
|
||||
"Work Order": { "status": ("in", ("Draft", "Not Started", "In Process")) },
|
||||
"BOM": {"docstatus": 0},
|
||||
|
||||
"Timesheet": {"status": "Draft"},
|
||||
|
@ -97,7 +97,7 @@ data_map = {
|
||||
"conditions": ["docstatus=1"],
|
||||
"order_by": "posting_date, posting_time, name",
|
||||
},
|
||||
"Production Order": {
|
||||
"Work Order": {
|
||||
"columns": ["name", "production_item as item_code",
|
||||
"(qty - produced_qty) as qty",
|
||||
"fg_warehouse as warehouse"],
|
||||
@ -293,7 +293,7 @@ data_map = {
|
||||
},
|
||||
|
||||
# Manufacturing
|
||||
"Production Order": {
|
||||
"Work Order": {
|
||||
"columns": ["name","status","creation","planned_start_date","planned_end_date","status","actual_start_date","actual_end_date", "modified"],
|
||||
"conditions": ["docstatus = 1"],
|
||||
"order_by": "creation"
|
||||
|
@ -49,7 +49,7 @@ class TestBatch(unittest.TestCase):
|
||||
return receipt
|
||||
|
||||
def test_stock_entry_incoming(self):
|
||||
'''Test batch creation via Stock Entry (Production Order)'''
|
||||
'''Test batch creation via Stock Entry (Work Order)'''
|
||||
|
||||
self.make_batch_item('ITEM-BATCH-1')
|
||||
|
||||
|
@ -75,9 +75,9 @@ class Bin(Document):
|
||||
|
||||
def update_reserved_qty_for_production(self):
|
||||
'''Update qty reserved for production from Production Item tables
|
||||
in open production orders'''
|
||||
in open work orders'''
|
||||
self.reserved_qty_for_production = frappe.db.sql('''select sum(required_qty - transferred_qty)
|
||||
from `tabProduction Order` pro, `tabProduction Order Item` item
|
||||
from `tabWork Order` pro, `tabWork Order Item` item
|
||||
where
|
||||
item.item_code = %s
|
||||
and item.parent = pro.name
|
||||
|
@ -7,7 +7,7 @@ def get_data():
|
||||
.format('<a href="#query-report/Stock Ledger">' + _('Stock Ledger') + '</a>'),
|
||||
'fieldname': 'item_code',
|
||||
'non_standard_fieldnames': {
|
||||
'Production Order': 'production_item',
|
||||
'Work Order': 'production_item',
|
||||
'Product Bundle': 'new_item_code',
|
||||
'BOM': 'item',
|
||||
'Batch': 'item'
|
||||
@ -40,7 +40,7 @@ def get_data():
|
||||
},
|
||||
{
|
||||
'label': _('Manufacture'),
|
||||
'items': ['Production Order']
|
||||
'items': ['Work Order']
|
||||
}
|
||||
]
|
||||
}
|
@ -10,7 +10,7 @@ frappe.ui.form.on('Material Request', {
|
||||
'Purchase Order': 'Purchase Order',
|
||||
'Request for Quotation': 'Request for Quotation',
|
||||
'Supplier Quotation': 'Supplier Quotation',
|
||||
'Production Order': 'Production Order'
|
||||
'Work Order': 'Work Order'
|
||||
}
|
||||
|
||||
// formatter for material request item
|
||||
@ -98,8 +98,8 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten
|
||||
this.make_supplier_quotation, __("Make"));
|
||||
|
||||
if(doc.material_request_type === "Manufacture")
|
||||
cur_frm.add_custom_button(__("Production Order"),
|
||||
function() { me.raise_production_orders() }, __("Make"));
|
||||
cur_frm.add_custom_button(__("Work Order"),
|
||||
function() { me.raise_work_orders() }, __("Make"));
|
||||
|
||||
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
|
||||
|
||||
@ -224,10 +224,10 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten
|
||||
});
|
||||
},
|
||||
|
||||
raise_production_orders: function() {
|
||||
raise_work_orders: function() {
|
||||
var me = this;
|
||||
frappe.call({
|
||||
method:"erpnext.stock.doctype.material_request.material_request.raise_production_orders",
|
||||
method:"erpnext.stock.doctype.material_request.material_request.raise_work_orders",
|
||||
args: {
|
||||
"material_request": me.frm.doc.name
|
||||
},
|
||||
|
@ -12,7 +12,7 @@ from frappe import msgprint, _
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty
|
||||
from erpnext.controllers.buying_controller import BuyingController
|
||||
from erpnext.manufacturing.doctype.production_order.production_order import get_item_details
|
||||
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 six import string_types
|
||||
@ -168,7 +168,7 @@ class MaterialRequest(BuyingController):
|
||||
|
||||
elif self.material_request_type == "Manufacture":
|
||||
d.ordered_qty = flt(frappe.db.sql("""select sum(qty)
|
||||
from `tabProduction Order` where material_request = %s
|
||||
from `tabWork Order` where material_request = %s
|
||||
and material_request_item = %s and docstatus = 1""",
|
||||
(self.name, d.name))[0][0])
|
||||
|
||||
@ -415,36 +415,36 @@ def make_stock_entry(source_name, target_doc=None):
|
||||
return doclist
|
||||
|
||||
@frappe.whitelist()
|
||||
def raise_production_orders(material_request):
|
||||
def raise_work_orders(material_request):
|
||||
mr= frappe.get_doc("Material Request", material_request)
|
||||
errors =[]
|
||||
production_orders = []
|
||||
work_orders = []
|
||||
default_wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse")
|
||||
for d in mr.items:
|
||||
if (d.qty - d.ordered_qty) >0:
|
||||
if frappe.db.get_value("BOM", {"item": d.item_code, "is_default": 1}):
|
||||
prod_order = frappe.new_doc("Production Order")
|
||||
prod_order.production_item = d.item_code
|
||||
prod_order.qty = d.qty - d.ordered_qty
|
||||
prod_order.fg_warehouse = d.warehouse
|
||||
prod_order.wip_warehouse = default_wip_warehouse
|
||||
prod_order.description = d.description
|
||||
prod_order.stock_uom = d.stock_uom
|
||||
prod_order.expected_delivery_date = d.schedule_date
|
||||
prod_order.sales_order = d.sales_order
|
||||
prod_order.bom_no = get_item_details(d.item_code).bom_no
|
||||
prod_order.material_request = mr.name
|
||||
prod_order.material_request_item = d.name
|
||||
prod_order.planned_start_date = mr.transaction_date
|
||||
prod_order.company = mr.company
|
||||
prod_order.save()
|
||||
production_orders.append(prod_order.name)
|
||||
wo_order = frappe.new_doc("Work Order")
|
||||
wo_order.production_item = d.item_code
|
||||
wo_order.qty = d.qty - d.ordered_qty
|
||||
wo_order.fg_warehouse = d.warehouse
|
||||
wo_order.wip_warehouse = default_wip_warehouse
|
||||
wo_order.description = d.description
|
||||
wo_order.stock_uom = d.stock_uom
|
||||
wo_order.expected_delivery_date = d.schedule_date
|
||||
wo_order.sales_order = d.sales_order
|
||||
wo_order.bom_no = get_item_details(d.item_code).bom_no
|
||||
wo_order.material_request = mr.name
|
||||
wo_order.material_request_item = d.name
|
||||
wo_order.planned_start_date = mr.transaction_date
|
||||
wo_order.company = mr.company
|
||||
wo_order.save()
|
||||
work_orders.append(wo_order.name)
|
||||
else:
|
||||
errors.append(_("Row {0}: Bill of Materials not found for the Item {1}").format(d.idx, d.item_code))
|
||||
if production_orders:
|
||||
message = ["""<a href="#Form/Production Order/%s" target="_blank">%s</a>""" % \
|
||||
(p, p) for p in production_orders]
|
||||
msgprint(_("The following Production Orders were created:") + '\n' + new_line_sep(message))
|
||||
if work_orders:
|
||||
message = ["""<a href="#Form/Work Order/%s" target="_blank">%s</a>""" % \
|
||||
(p, p) for p in work_orders]
|
||||
msgprint(_("The following Work Orders were created:") + '\n' + new_line_sep(message))
|
||||
if errors:
|
||||
frappe.throw(_("Productions Orders cannot be raised for:") + '\n' + new_line_sep(errors))
|
||||
return production_orders
|
||||
return work_orders
|
||||
|
@ -11,7 +11,7 @@ def get_data():
|
||||
},
|
||||
{
|
||||
'label': _('Manufacturing'),
|
||||
'items': ['Production Order']
|
||||
'items': ['Work Order']
|
||||
}
|
||||
]
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe, unittest, erpnext
|
||||
from frappe.utils import flt, today
|
||||
from erpnext.stock.doctype.material_request.material_request import raise_production_orders
|
||||
from erpnext.stock.doctype.material_request.material_request import raise_work_orders
|
||||
|
||||
class TestMaterialRequest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@ -536,8 +536,8 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
requested_qty = frappe.db.sql("""select indented_qty from `tabBin` where \
|
||||
item_code= %s and warehouse= %s """, (mr.items[0].item_code, mr.items[0].warehouse))[0][0]
|
||||
|
||||
prod_order = raise_production_orders(mr.name)
|
||||
po = frappe.get_doc("Production Order", prod_order[0])
|
||||
prod_order = raise_work_orders(mr.name)
|
||||
po = frappe.get_doc("Work Order", prod_order[0])
|
||||
po.wip_warehouse = "_Test Warehouse 1 - _TC"
|
||||
po.submit()
|
||||
|
||||
|
@ -4,12 +4,12 @@ frappe.provide("erpnext.stock");
|
||||
|
||||
frappe.ui.form.on('Stock Entry', {
|
||||
setup: function(frm) {
|
||||
frm.set_query('production_order', function() {
|
||||
frm.set_query('work_order', function() {
|
||||
return {
|
||||
filters: [
|
||||
['Production Order', 'docstatus', '=', 1],
|
||||
['Production Order', 'qty', '>','`tabProduction Order`.produced_qty'],
|
||||
['Production Order', 'company', '=', frm.doc.company]
|
||||
['Work Order', 'docstatus', '=', 1],
|
||||
['Work Order', 'qty', '>','`tabWork Order`.produced_qty'],
|
||||
['Work Order', 'company', '=', frm.doc.company]
|
||||
]
|
||||
}
|
||||
});
|
||||
@ -565,11 +565,11 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
},
|
||||
|
||||
clean_up: function() {
|
||||
// Clear Production Order record from locals, because it is updated via Stock Entry
|
||||
if(this.frm.doc.production_order &&
|
||||
// Clear Work Order record from locals, because it is updated via Stock Entry
|
||||
if(this.frm.doc.work_order &&
|
||||
in_list(["Manufacture", "Material Transfer for Manufacture"], this.frm.doc.purpose)) {
|
||||
frappe.model.remove_from_locals("Production Order",
|
||||
this.frm.doc.production_order);
|
||||
frappe.model.remove_from_locals("Work Order",
|
||||
this.frm.doc.work_order);
|
||||
}
|
||||
},
|
||||
|
||||
@ -578,8 +578,8 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
if(!this.frm.doc.fg_completed_qty || !this.frm.doc.bom_no)
|
||||
frappe.throw(__("BOM and Manufacturing Quantity are required"));
|
||||
|
||||
if(this.frm.doc.production_order || this.frm.doc.bom_no) {
|
||||
// if production order / bom is mentioned, get items
|
||||
if(this.frm.doc.work_order || this.frm.doc.bom_no) {
|
||||
// if work order / bom is mentioned, get items
|
||||
return this.frm.call({
|
||||
doc: me.frm.doc,
|
||||
method: "get_items",
|
||||
@ -590,17 +590,17 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
}
|
||||
},
|
||||
|
||||
production_order: function() {
|
||||
work_order: function() {
|
||||
var me = this;
|
||||
this.toggle_enable_bom();
|
||||
if(!me.frm.doc.production_order) {
|
||||
if(!me.frm.doc.work_order) {
|
||||
return;
|
||||
}
|
||||
|
||||
return frappe.call({
|
||||
method: "erpnext.stock.doctype.stock_entry.stock_entry.get_production_order_details",
|
||||
method: "erpnext.stock.doctype.stock_entry.stock_entry.get_work_order_details",
|
||||
args: {
|
||||
production_order: me.frm.doc.production_order
|
||||
work_order: me.frm.doc.work_order
|
||||
},
|
||||
callback: function(r) {
|
||||
if (!r.exc) {
|
||||
@ -630,7 +630,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
},
|
||||
|
||||
toggle_enable_bom: function() {
|
||||
this.frm.toggle_enable("bom_no", !!!this.frm.doc.production_order);
|
||||
this.frm.toggle_enable("bom_no", !!!this.frm.doc.work_order);
|
||||
},
|
||||
|
||||
add_excise_button: function() {
|
||||
|
@ -178,7 +178,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:in_list([\"Material Transfer for Manufacture\", \"Manufacture\"], doc.purpose)",
|
||||
"fieldname": "production_order",
|
||||
"fieldname": "work_order",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@ -187,12 +187,12 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Production Order",
|
||||
"label": "Work Order",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "production_order",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Production Order",
|
||||
"options": "Work Order",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
@ -1894,7 +1894,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-03-01 12:27:12.884611",
|
||||
"modified": "2018-03-13 12:27:12.884611",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Entry",
|
||||
|
@ -17,7 +17,7 @@ import json
|
||||
from six import string_types
|
||||
|
||||
class IncorrectValuationRateError(frappe.ValidationError): pass
|
||||
class DuplicateEntryForProductionOrderError(frappe.ValidationError): pass
|
||||
class DuplicateEntryForWorkOrderError(frappe.ValidationError): pass
|
||||
class OperationsNotCompleteError(frappe.ValidationError): pass
|
||||
class MaxSampleAlreadyRetainedError(frappe.ValidationError): pass
|
||||
|
||||
@ -37,8 +37,8 @@ class StockEntry(StockController):
|
||||
|
||||
def validate(self):
|
||||
self.pro_doc = frappe._dict()
|
||||
if self.production_order:
|
||||
self.pro_doc = frappe.get_doc('Production Order', self.production_order)
|
||||
if self.work_order:
|
||||
self.pro_doc = frappe.get_doc('Work Order', self.work_order)
|
||||
|
||||
self.validate_posting_time()
|
||||
self.validate_purpose()
|
||||
@ -47,7 +47,7 @@ class StockEntry(StockController):
|
||||
self.validate_uom_is_integer("uom", "qty")
|
||||
self.validate_uom_is_integer("stock_uom", "transfer_qty")
|
||||
self.validate_warehouse()
|
||||
self.validate_production_order()
|
||||
self.validate_work_order()
|
||||
self.validate_bom()
|
||||
self.validate_finished_goods()
|
||||
self.validate_with_material_request()
|
||||
@ -72,7 +72,7 @@ class StockEntry(StockController):
|
||||
|
||||
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
|
||||
update_serial_nos_after_submit(self, "items")
|
||||
self.update_production_order()
|
||||
self.update_work_order()
|
||||
self.validate_purchase_order()
|
||||
if self.purchase_order and self.purpose == "Subcontract":
|
||||
self.update_purchase_order_supplied_items()
|
||||
@ -80,7 +80,7 @@ class StockEntry(StockController):
|
||||
|
||||
def on_cancel(self):
|
||||
self.update_stock_ledger()
|
||||
self.update_production_order()
|
||||
self.update_work_order()
|
||||
if self.purchase_order and self.purpose == "Subcontract":
|
||||
self.update_purchase_order_supplied_items()
|
||||
self.make_gl_entries_on_cancel()
|
||||
@ -172,7 +172,7 @@ class StockEntry(StockController):
|
||||
frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
|
||||
|
||||
elif self.pro_doc and (cstr(d.t_warehouse) != self.pro_doc.fg_warehouse and cstr(d.t_warehouse) != self.pro_doc.scrap_warehouse):
|
||||
frappe.throw(_("Target warehouse in row {0} must be same as Production Order").format(d.idx))
|
||||
frappe.throw(_("Target warehouse in row {0} must be same as Work Order").format(d.idx))
|
||||
|
||||
else:
|
||||
d.t_warehouse = None
|
||||
@ -182,39 +182,39 @@ class StockEntry(StockController):
|
||||
if cstr(d.s_warehouse) == cstr(d.t_warehouse) and not self.purpose == "Material Transfer for Manufacture":
|
||||
frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx))
|
||||
|
||||
def validate_production_order(self):
|
||||
def validate_work_order(self):
|
||||
if self.purpose in ("Manufacture", "Material Transfer for Manufacture"):
|
||||
# check if production order is entered
|
||||
# check if work order is entered
|
||||
|
||||
if self.purpose=="Manufacture" and self.production_order:
|
||||
if self.purpose=="Manufacture" and self.work_order:
|
||||
if not self.fg_completed_qty:
|
||||
frappe.throw(_("For Quantity (Manufactured Qty) is mandatory"))
|
||||
self.check_if_operations_completed()
|
||||
self.check_duplicate_entry_for_production_order()
|
||||
self.check_duplicate_entry_for_work_order()
|
||||
elif self.purpose != "Material Transfer":
|
||||
self.production_order = None
|
||||
self.work_order = None
|
||||
|
||||
def check_if_operations_completed(self):
|
||||
"""Check if Time Sheets are completed against before manufacturing to capture operating costs."""
|
||||
prod_order = frappe.get_doc("Production Order", self.production_order)
|
||||
prod_order = frappe.get_doc("Work Order", self.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):
|
||||
frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Production Order # {3}. Please update operation status via Time Logs")
|
||||
.format(d.idx, d.operation, total_completed_qty, self.production_order), OperationsNotCompleteError)
|
||||
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)
|
||||
|
||||
def check_duplicate_entry_for_production_order(self):
|
||||
def check_duplicate_entry_for_work_order(self):
|
||||
other_ste = [t[0] for t in frappe.db.get_values("Stock Entry", {
|
||||
"production_order": self.production_order,
|
||||
"work_order": self.work_order,
|
||||
"purpose": self.purpose,
|
||||
"docstatus": ["!=", 2],
|
||||
"name": ["!=", self.name]
|
||||
}, "name")]
|
||||
|
||||
if other_ste:
|
||||
production_item, qty = frappe.db.get_value("Production Order",
|
||||
self.production_order, ["production_item", "qty"])
|
||||
production_item, qty = frappe.db.get_value("Work Order",
|
||||
self.work_order, ["production_item", "qty"])
|
||||
args = other_ste + [production_item]
|
||||
fg_qty_already_entered = frappe.db.sql("""select sum(transfer_qty)
|
||||
from `tabStock Entry Detail`
|
||||
@ -223,8 +223,8 @@ class StockEntry(StockController):
|
||||
and ifnull(s_warehouse,'')='' """ % (", ".join(["%s" * len(other_ste)]), "%s"), args)[0][0]
|
||||
|
||||
if fg_qty_already_entered >= qty:
|
||||
frappe.throw(_("Stock Entries already created for Production Order ")
|
||||
+ self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError)
|
||||
frappe.throw(_("Stock Entries already created for Work Order ")
|
||||
+ self.work_order + ":" + ", ".join(other_ste), DuplicateEntryForWorkOrderError)
|
||||
|
||||
def set_incoming_rate(self):
|
||||
for d in self.items:
|
||||
@ -261,8 +261,8 @@ class StockEntry(StockController):
|
||||
frappe.bold(d.transfer_qty)),
|
||||
NegativeStockError, title=_('Insufficient Stock'))
|
||||
|
||||
def set_serial_nos(self, production_order):
|
||||
previous_se = frappe.db.get_value("Stock Entry", {"production_order": production_order,
|
||||
def set_serial_nos(self, work_order):
|
||||
previous_se = frappe.db.get_value("Stock Entry", {"work_order": work_order,
|
||||
"purpose": "Material Transfer for Manufacture"}, "name")
|
||||
|
||||
for d in self.get('items'):
|
||||
@ -273,7 +273,7 @@ class StockEntry(StockController):
|
||||
d.serial_no = transferred_serial_no
|
||||
|
||||
def get_stock_and_rate(self):
|
||||
self.set_production_order_details()
|
||||
self.set_work_order_details()
|
||||
self.set_transfer_qty()
|
||||
self.set_actual_qty()
|
||||
self.calculate_rate_and_amount()
|
||||
@ -418,12 +418,12 @@ class StockEntry(StockController):
|
||||
frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \
|
||||
format(d.idx, d.transfer_qty, self.fg_completed_qty))
|
||||
|
||||
if self.production_order and self.purpose == "Manufacture" and d.t_warehouse:
|
||||
if self.work_order and self.purpose == "Manufacture" and d.t_warehouse:
|
||||
items_with_target_warehouse.append(d.item_code)
|
||||
|
||||
if self.production_order and self.purpose == "Manufacture":
|
||||
production_item = frappe.db.get_value("Production Order",
|
||||
self.production_order, "production_item")
|
||||
if self.work_order and self.purpose == "Manufacture":
|
||||
production_item = frappe.db.get_value("Work Order",
|
||||
self.work_order, "production_item")
|
||||
if production_item not in items_with_target_warehouse:
|
||||
frappe.throw(_("Finished Item {0} must be entered for Manufacture type entry")
|
||||
.format(production_item))
|
||||
@ -489,20 +489,20 @@ class StockEntry(StockController):
|
||||
|
||||
return gl_entries
|
||||
|
||||
def update_production_order(self):
|
||||
def _validate_production_order(pro_doc):
|
||||
def update_work_order(self):
|
||||
def _validate_work_order(pro_doc):
|
||||
if flt(pro_doc.docstatus) != 1:
|
||||
frappe.throw(_("Production Order {0} must be submitted").format(self.production_order))
|
||||
frappe.throw(_("Work Order {0} must be submitted").format(self.work_order))
|
||||
|
||||
if pro_doc.status == 'Stopped':
|
||||
frappe.throw(_("Transaction not allowed against stopped Production Order {0}").format(self.production_order))
|
||||
frappe.throw(_("Transaction not allowed against stopped Work Order {0}").format(self.work_order))
|
||||
|
||||
if self.production_order:
|
||||
pro_doc = frappe.get_doc("Production Order", self.production_order)
|
||||
_validate_production_order(pro_doc)
|
||||
if self.work_order:
|
||||
pro_doc = frappe.get_doc("Work Order", self.work_order)
|
||||
_validate_work_order(pro_doc)
|
||||
pro_doc.run_method("update_status")
|
||||
if self.fg_completed_qty:
|
||||
pro_doc.run_method("update_production_order_qty")
|
||||
pro_doc.run_method("update_work_order_qty")
|
||||
if self.purpose == "Manufacture":
|
||||
pro_doc.run_method("update_planned_qty")
|
||||
|
||||
@ -567,24 +567,24 @@ class StockEntry(StockController):
|
||||
|
||||
def get_items(self):
|
||||
self.set('items', [])
|
||||
self.validate_production_order()
|
||||
self.validate_work_order()
|
||||
|
||||
if not self.posting_date or not self.posting_time:
|
||||
frappe.throw(_("Posting date and posting time is mandatory"))
|
||||
|
||||
self.set_production_order_details()
|
||||
self.set_work_order_details()
|
||||
|
||||
if self.bom_no:
|
||||
if self.purpose in ["Material Issue", "Material Transfer", "Manufacture", "Repack",
|
||||
"Subcontract", "Material Transfer for Manufacture"]:
|
||||
if self.production_order and self.purpose == "Material Transfer for Manufacture":
|
||||
if self.work_order and self.purpose == "Material Transfer for Manufacture":
|
||||
item_dict = self.get_pending_raw_materials()
|
||||
if self.to_warehouse and self.pro_doc:
|
||||
for item in item_dict.values():
|
||||
item["to_warehouse"] = self.pro_doc.wip_warehouse
|
||||
self.add_to_stock_entry_detail(item_dict)
|
||||
|
||||
elif self.production_order and self.purpose == "Manufacture" and \
|
||||
elif self.work_order and self.purpose == "Manufacture" and \
|
||||
frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on")== "Material Transferred for Manufacture":
|
||||
self.get_transfered_raw_materials()
|
||||
|
||||
@ -595,6 +595,7 @@ class StockEntry(StockController):
|
||||
item_dict = self.get_bom_raw_materials(self.fg_completed_qty)
|
||||
|
||||
#Get PO Supplied Items Details
|
||||
print('Purchase Order', self.purchase_order, self.purpose)
|
||||
if self.purchase_order and self.purpose == "Subcontract":
|
||||
#Get PO Supplied Items Details
|
||||
item_wh = frappe._dict(frappe.db.sql("""
|
||||
@ -621,8 +622,8 @@ class StockEntry(StockController):
|
||||
self.add_to_stock_entry_detail(scrap_item_dict, bom_no=self.bom_no)
|
||||
|
||||
# fetch the serial_no of the first stock entry for the second stock entry
|
||||
if self.production_order and self.purpose == "Manufacture":
|
||||
self.set_serial_nos(self.production_order)
|
||||
if self.work_order and self.purpose == "Manufacture":
|
||||
self.set_serial_nos(self.work_order)
|
||||
|
||||
# add finished goods item
|
||||
if self.purpose in ("Manufacture", "Repack"):
|
||||
@ -631,23 +632,23 @@ class StockEntry(StockController):
|
||||
self.set_actual_qty()
|
||||
self.calculate_rate_and_amount(raise_error_if_no_rate=False)
|
||||
|
||||
def set_production_order_details(self):
|
||||
def set_work_order_details(self):
|
||||
if not getattr(self, "pro_doc", None):
|
||||
self.pro_doc = frappe._dict()
|
||||
|
||||
if self.production_order:
|
||||
if self.work_order:
|
||||
# common validations
|
||||
if not self.pro_doc:
|
||||
self.pro_doc = frappe.get_doc('Production Order', self.production_order)
|
||||
self.pro_doc = frappe.get_doc('Work Order', self.work_order)
|
||||
|
||||
if self.pro_doc:
|
||||
self.bom_no = self.pro_doc.bom_no
|
||||
else:
|
||||
# invalid production order
|
||||
self.production_order = None
|
||||
# invalid work order
|
||||
self.work_order = None
|
||||
|
||||
def load_items_from_bom(self):
|
||||
if self.production_order:
|
||||
if self.work_order:
|
||||
item_code = self.pro_doc.production_item
|
||||
to_warehouse = self.pro_doc.fg_warehouse
|
||||
else:
|
||||
@ -657,7 +658,7 @@ class StockEntry(StockController):
|
||||
item = frappe.db.get_value("Item", item_code, ["item_name",
|
||||
"description", "stock_uom", "expense_account", "buying_cost_center", "name", "default_warehouse"], as_dict=1)
|
||||
|
||||
if not self.production_order and not to_warehouse:
|
||||
if not self.work_order and not to_warehouse:
|
||||
# in case of BOM
|
||||
to_warehouse = item.default_warehouse
|
||||
|
||||
@ -705,9 +706,9 @@ class StockEntry(StockController):
|
||||
from `tabStock Entry` se,`tabStock Entry Detail` sed
|
||||
where
|
||||
se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture'
|
||||
and se.production_order= %s and ifnull(sed.t_warehouse, '') != ''
|
||||
and se.work_order= %s and ifnull(sed.t_warehouse, '') != ''
|
||||
group by sed.item_code, sed.t_warehouse
|
||||
""", self.production_order, as_dict=1)
|
||||
""", self.work_order, as_dict=1)
|
||||
|
||||
materials_already_backflushed = frappe.db.sql("""
|
||||
select
|
||||
@ -716,16 +717,16 @@ class StockEntry(StockController):
|
||||
`tabStock Entry` se, `tabStock Entry Detail` sed
|
||||
where
|
||||
se.name = sed.parent and se.docstatus=1 and se.purpose='Manufacture'
|
||||
and se.production_order= %s and ifnull(sed.s_warehouse, '') != ''
|
||||
and se.work_order= %s and ifnull(sed.s_warehouse, '') != ''
|
||||
group by sed.item_code, sed.s_warehouse
|
||||
""", self.production_order, as_dict=1)
|
||||
""", self.work_order, as_dict=1)
|
||||
|
||||
backflushed_materials= {}
|
||||
for d in materials_already_backflushed:
|
||||
backflushed_materials.setdefault(d.item_code,[]).append({d.warehouse: d.qty})
|
||||
|
||||
po_qty = frappe.db.sql("""select qty, produced_qty, material_transferred_for_manufacturing from
|
||||
`tabProduction Order` where name=%s""", self.production_order, as_dict=1)[0]
|
||||
`tabWork Order` where name=%s""", self.work_order, as_dict=1)[0]
|
||||
manufacturing_qty = flt(po_qty.qty)
|
||||
produced_qty = flt(po_qty.produced_qty)
|
||||
trans_qty = flt(po_qty.material_transferred_for_manufacturing)
|
||||
@ -780,13 +781,13 @@ class StockEntry(StockController):
|
||||
|
||||
# show some message
|
||||
if not len(item_dict):
|
||||
frappe.msgprint(_("""All items have already been transferred for this Production Order."""))
|
||||
frappe.msgprint(_("""All items have already been transferred for this Work Order."""))
|
||||
|
||||
return item_dict
|
||||
|
||||
def get_pro_order_required_items(self):
|
||||
item_dict = frappe._dict()
|
||||
pro_order = frappe.get_doc("Production Order", self.production_order)
|
||||
pro_order = frappe.get_doc("Work Order", self.work_order)
|
||||
if not frappe.db.get_value("Warehouse", pro_order.wip_warehouse, "is_group"):
|
||||
wip_warehouse = pro_order.wip_warehouse
|
||||
else:
|
||||
@ -908,32 +909,32 @@ def move_sample_to_retention_warehouse(company, items):
|
||||
return stock_entry.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_production_order_details(production_order):
|
||||
production_order = frappe.get_doc("Production Order", production_order)
|
||||
pending_qty_to_produce = flt(production_order.qty) - flt(production_order.produced_qty)
|
||||
def get_work_order_details(work_order):
|
||||
work_order = frappe.get_doc("Work Order", work_order)
|
||||
pending_qty_to_produce = flt(work_order.qty) - flt(work_order.produced_qty)
|
||||
|
||||
return {
|
||||
"from_bom": 1,
|
||||
"bom_no": production_order.bom_no,
|
||||
"use_multi_level_bom": production_order.use_multi_level_bom,
|
||||
"wip_warehouse": production_order.wip_warehouse,
|
||||
"fg_warehouse": production_order.fg_warehouse,
|
||||
"bom_no": work_order.bom_no,
|
||||
"use_multi_level_bom": work_order.use_multi_level_bom,
|
||||
"wip_warehouse": work_order.wip_warehouse,
|
||||
"fg_warehouse": work_order.fg_warehouse,
|
||||
"fg_completed_qty": pending_qty_to_produce,
|
||||
"additional_costs": get_additional_costs(production_order, fg_qty=pending_qty_to_produce)
|
||||
"additional_costs": get_additional_costs(work_order, fg_qty=pending_qty_to_produce)
|
||||
}
|
||||
|
||||
def get_additional_costs(production_order=None, bom_no=None, fg_qty=None):
|
||||
def get_additional_costs(work_order=None, bom_no=None, fg_qty=None):
|
||||
additional_costs = []
|
||||
operating_cost_per_unit = get_operating_cost_per_unit(production_order, bom_no)
|
||||
operating_cost_per_unit = get_operating_cost_per_unit(work_order, bom_no)
|
||||
if operating_cost_per_unit:
|
||||
additional_costs.append({
|
||||
"description": "Operating Cost as per Production Order / BOM",
|
||||
"description": "Operating Cost as per Work Order / BOM",
|
||||
"amount": operating_cost_per_unit * flt(fg_qty)
|
||||
})
|
||||
|
||||
if production_order and production_order.additional_operating_cost and production_order.qty:
|
||||
if work_order and work_order.additional_operating_cost and work_order.qty:
|
||||
additional_operating_cost_per_unit = \
|
||||
flt(production_order.additional_operating_cost) / flt(production_order.qty)
|
||||
flt(work_order.additional_operating_cost) / flt(work_order.qty)
|
||||
|
||||
additional_costs.append({
|
||||
"description": "Additional Operating Cost",
|
||||
@ -942,19 +943,19 @@ def get_additional_costs(production_order=None, bom_no=None, fg_qty=None):
|
||||
|
||||
return additional_costs
|
||||
|
||||
def get_operating_cost_per_unit(production_order=None, bom_no=None):
|
||||
def get_operating_cost_per_unit(work_order=None, bom_no=None):
|
||||
operating_cost_per_unit = 0
|
||||
if production_order:
|
||||
if work_order:
|
||||
if not bom_no:
|
||||
bom_no = production_order.bom_no
|
||||
bom_no = work_order.bom_no
|
||||
|
||||
for d in production_order.get("operations"):
|
||||
for d in work_order.get("operations"):
|
||||
if flt(d.completed_qty):
|
||||
operating_cost_per_unit += flt(d.actual_operating_cost) / flt(d.completed_qty)
|
||||
elif production_order.qty:
|
||||
operating_cost_per_unit += flt(d.planned_operating_cost) / flt(production_order.qty)
|
||||
elif work_order.qty:
|
||||
operating_cost_per_unit += flt(d.planned_operating_cost) / flt(work_order.qty)
|
||||
|
||||
# Get operating cost from BOM if not found in production_order.
|
||||
# Get operating cost from BOM if not found in work_order.
|
||||
if not operating_cost_per_unit and bom_no:
|
||||
bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1)
|
||||
if bom.quantity:
|
||||
|
@ -1,6 +1,6 @@
|
||||
frappe.listview_settings['Stock Entry'] = {
|
||||
add_fields: ["`tabStock Entry`.`from_warehouse`", "`tabStock Entry`.`to_warehouse`",
|
||||
"`tabStock Entry`.`purpose`", "`tabStock Entry`.`production_order`", "`tabStock Entry`.`bom_no`"],
|
||||
"`tabStock Entry`.`purpose`", "`tabStock Entry`.`work_order`", "`tabStock Entry`.`bom_no`"],
|
||||
column_render: {
|
||||
"from_warehouse": function(doc) {
|
||||
var html = "";
|
||||
|
@ -538,14 +538,14 @@ class TestStockEntry(unittest.TestCase):
|
||||
self.assertRaises(StockFreezeError, se.submit)
|
||||
frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0)
|
||||
|
||||
def test_production_order(self):
|
||||
from erpnext.manufacturing.doctype.production_order.production_order \
|
||||
def test_work_order(self):
|
||||
from erpnext.manufacturing.doctype.work_order.work_order \
|
||||
import make_stock_entry as _make_stock_entry
|
||||
bom_no, bom_operation_cost = frappe.db.get_value("BOM", {"item": "_Test FG Item 2",
|
||||
"is_default": 1, "docstatus": 1}, ["name", "operating_cost"])
|
||||
|
||||
production_order = frappe.new_doc("Production Order")
|
||||
production_order.update({
|
||||
work_order = frappe.new_doc("Work Order")
|
||||
work_order.update({
|
||||
"company": "_Test Company",
|
||||
"fg_warehouse": "_Test Warehouse 1 - _TC",
|
||||
"production_item": "_Test FG Item 2",
|
||||
@ -555,13 +555,13 @@ class TestStockEntry(unittest.TestCase):
|
||||
"wip_warehouse": "_Test Warehouse - _TC",
|
||||
"additional_operating_cost": 1000
|
||||
})
|
||||
production_order.insert()
|
||||
production_order.submit()
|
||||
work_order.insert()
|
||||
work_order.submit()
|
||||
|
||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
|
||||
make_stock_entry(item_code="_Test Item 2", target="_Test Warehouse - _TC", qty=50, basic_rate=20)
|
||||
|
||||
stock_entry = _make_stock_entry(production_order.name, "Manufacture", 1)
|
||||
stock_entry = _make_stock_entry(work_order.name, "Manufacture", 1)
|
||||
|
||||
rm_cost = 0
|
||||
for d in stock_entry.get("items"):
|
||||
@ -569,15 +569,15 @@ class TestStockEntry(unittest.TestCase):
|
||||
rm_cost += flt(d.amount)
|
||||
fg_cost = filter(lambda x: x.item_code=="_Test FG Item 2", stock_entry.get("items"))[0].amount
|
||||
self.assertEqual(fg_cost,
|
||||
flt(rm_cost + bom_operation_cost + production_order.additional_operating_cost, 2))
|
||||
flt(rm_cost + bom_operation_cost + work_order.additional_operating_cost, 2))
|
||||
|
||||
|
||||
def test_variant_production_order(self):
|
||||
def test_variant_work_order(self):
|
||||
bom_no = frappe.db.get_value("BOM", {"item": "_Test Variant Item",
|
||||
"is_default": 1, "docstatus": 1})
|
||||
|
||||
production_order = frappe.new_doc("Production Order")
|
||||
production_order.update({
|
||||
work_order = frappe.new_doc("Work Order")
|
||||
work_order.update({
|
||||
"company": "_Test Company",
|
||||
"fg_warehouse": "_Test Warehouse 1 - _TC",
|
||||
"production_item": "_Test Variant Item-S",
|
||||
@ -586,12 +586,12 @@ class TestStockEntry(unittest.TestCase):
|
||||
"stock_uom": "_Test UOM",
|
||||
"wip_warehouse": "_Test Warehouse - _TC"
|
||||
})
|
||||
production_order.insert()
|
||||
production_order.submit()
|
||||
work_order.insert()
|
||||
work_order.submit()
|
||||
|
||||
from erpnext.manufacturing.doctype.production_order.production_order import make_stock_entry
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry
|
||||
|
||||
stock_entry = frappe.get_doc(make_stock_entry(production_order.name, "Manufacture", 1))
|
||||
stock_entry = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 1))
|
||||
stock_entry.insert()
|
||||
self.assertTrue("_Test Variant Item-S" in [d.item_code for d in stock_entry.items])
|
||||
|
||||
|
@ -131,7 +131,7 @@ def get_ordered_qty(item_code, warehouse):
|
||||
|
||||
def get_planned_qty(item_code, warehouse):
|
||||
planned_qty = frappe.db.sql("""
|
||||
select sum(qty - produced_qty) from `tabProduction Order`
|
||||
select sum(qty - produced_qty) from `tabWork Order`
|
||||
where production_item = %s and fg_warehouse = %s and status not in ("Stopped", "Completed")
|
||||
and docstatus=1 and qty > produced_qty""", (item_code, warehouse))
|
||||
|
||||
|
@ -40,7 +40,7 @@ erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js
|
||||
erpnext/hr/doctype/leave_allocation/test_leave_allocation.js
|
||||
erpnext/hr/doctype/leave_application/test_leave_application.js
|
||||
erpnext/stock/doctype/warehouse/test_warehouse.js
|
||||
erpnext/manufacturing/doctype/production_order/test_production_order.js #long
|
||||
erpnext/manufacturing/doctype/work_order/test_work_order.js #long
|
||||
erpnext/accounts/page/pos/test_pos.js
|
||||
erpnext/selling/page/point_of_sale/tests/test_point_of_sale.js
|
||||
erpnext/selling/doctype/product_bundle/test_product_bundle.js
|
||||
|
Loading…
x
Reference in New Issue
Block a user