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