Merge pull request #19441 from rohitwaghchaure/capacity_planning_for_workstataion_feature
fix: capacity planning back
This commit is contained in:
commit
36b60dbbef
File diff suppressed because it is too large
Load Diff
@ -4,10 +4,16 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import datetime
|
||||
from frappe import _
|
||||
from frappe.utils import flt, time_diff_in_hours, get_datetime
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import (flt, cint, time_diff_in_hours, get_datetime, getdate,
|
||||
get_time, add_to_date, time_diff, add_days, get_datetime_str)
|
||||
|
||||
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
|
||||
|
||||
class OverlapError(frappe.ValidationError): pass
|
||||
|
||||
class JobCard(Document):
|
||||
def validate(self):
|
||||
@ -26,7 +32,7 @@ class JobCard(Document):
|
||||
data = self.get_overlap_for(d)
|
||||
if data:
|
||||
frappe.throw(_("Row {0}: From Time and To Time of {1} is overlapping with {2}")
|
||||
.format(d.idx, self.name, data.name))
|
||||
.format(d.idx, self.name, data.name), OverlapError)
|
||||
|
||||
if d.from_time and d.to_time:
|
||||
d.time_in_mins = time_diff_in_hours(d.to_time, d.from_time) * 60
|
||||
@ -35,27 +41,120 @@ class JobCard(Document):
|
||||
if d.completed_qty:
|
||||
self.total_completed_qty += d.completed_qty
|
||||
|
||||
def get_overlap_for(self, args):
|
||||
existing = frappe.db.sql("""select jc.name as name from
|
||||
def get_overlap_for(self, args, check_next_available_slot=False):
|
||||
production_capacity = 1
|
||||
|
||||
if self.workstation:
|
||||
production_capacity = frappe.get_cached_value("Workstation",
|
||||
self.workstation, 'production_capacity') or 1
|
||||
validate_overlap_for = " and jc.workstation = %(workstation)s "
|
||||
|
||||
if self.employee:
|
||||
# override capacity for employee
|
||||
production_capacity = 1
|
||||
validate_overlap_for = " and jc.employee = %(employee)s "
|
||||
|
||||
extra_cond = ''
|
||||
if check_next_available_slot:
|
||||
extra_cond = " or (%(from_time)s <= jctl.from_time and %(to_time)s <= jctl.to_time)"
|
||||
|
||||
existing = frappe.db.sql("""select jc.name as name, jctl.to_time from
|
||||
`tabJob Card Time Log` jctl, `tabJob Card` jc where jctl.parent = jc.name and
|
||||
(
|
||||
(%(from_time)s > jctl.from_time and %(from_time)s < jctl.to_time) or
|
||||
(%(to_time)s > jctl.from_time and %(to_time)s < jctl.to_time) or
|
||||
(%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time))
|
||||
and jctl.name!=%(name)s
|
||||
and jc.name!=%(parent)s
|
||||
and jc.docstatus < 2
|
||||
and jc.employee = %(employee)s """,
|
||||
(%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time) {0}
|
||||
)
|
||||
and jctl.name != %(name)s and jc.name != %(parent)s and jc.docstatus < 2 {1}
|
||||
order by jctl.to_time desc limit 1""".format(extra_cond, validate_overlap_for),
|
||||
{
|
||||
"from_time": args.from_time,
|
||||
"to_time": args.to_time,
|
||||
"name": args.name or "No Name",
|
||||
"parent": args.parent or "No Name",
|
||||
"employee": self.employee
|
||||
"employee": self.employee,
|
||||
"workstation": self.workstation
|
||||
}, as_dict=True)
|
||||
|
||||
if existing and production_capacity > len(existing):
|
||||
return
|
||||
|
||||
return existing[0] if existing else None
|
||||
|
||||
def schedule_time_logs(self, row):
|
||||
row.remaining_time_in_mins = row.time_in_mins
|
||||
while row.remaining_time_in_mins > 0:
|
||||
args = frappe._dict({
|
||||
"from_time": row.planned_start_time,
|
||||
"to_time": row.planned_end_time
|
||||
})
|
||||
|
||||
self.validate_overlap_for_workstation(args, row)
|
||||
self.check_workstation_time(row)
|
||||
|
||||
def validate_overlap_for_workstation(self, args, row):
|
||||
# get the last record based on the to time from the job card
|
||||
data = self.get_overlap_for(args, check_next_available_slot=True)
|
||||
if data:
|
||||
row.planned_start_time = get_datetime(data.to_time + get_mins_between_operations())
|
||||
|
||||
def check_workstation_time(self, row):
|
||||
workstation_doc = frappe.get_cached_doc("Workstation", self.workstation)
|
||||
if (not workstation_doc.working_hours or
|
||||
cint(frappe.db.get_single_value("Manufacturing Settings", "allow_overtime"))):
|
||||
row.remaining_time_in_mins -= time_diff_in_minutes(row.planned_end_time,
|
||||
row.planned_start_time)
|
||||
|
||||
self.update_time_logs(row)
|
||||
return
|
||||
|
||||
start_date = getdate(row.planned_start_time)
|
||||
start_time = get_time(row.planned_start_time)
|
||||
|
||||
new_start_date = workstation_doc.validate_workstation_holiday(start_date)
|
||||
|
||||
if new_start_date != start_date:
|
||||
row.planned_start_time = datetime.datetime.combine(new_start_date, start_time)
|
||||
start_date = new_start_date
|
||||
|
||||
total_idx = len(workstation_doc.working_hours)
|
||||
|
||||
for i, time_slot in enumerate(workstation_doc.working_hours):
|
||||
workstation_start_time = datetime.datetime.combine(start_date, get_time(time_slot.start_time))
|
||||
workstation_end_time = datetime.datetime.combine(start_date, get_time(time_slot.end_time))
|
||||
|
||||
if (get_datetime(row.planned_start_time) >= workstation_start_time and
|
||||
get_datetime(row.planned_start_time) <= workstation_end_time):
|
||||
time_in_mins = time_diff_in_minutes(workstation_end_time, row.planned_start_time)
|
||||
|
||||
# If remaining time fit in workstation time logs else split hours as per workstation time
|
||||
if time_in_mins > row.remaining_time_in_mins:
|
||||
row.planned_end_time = add_to_date(row.planned_start_time,
|
||||
minutes=row.remaining_time_in_mins)
|
||||
row.remaining_time_in_mins = 0
|
||||
else:
|
||||
row.planned_end_time = add_to_date(row.planned_start_time, minutes=time_in_mins)
|
||||
row.remaining_time_in_mins -= time_in_mins
|
||||
|
||||
self.update_time_logs(row)
|
||||
|
||||
if total_idx != (i+1) and row.remaining_time_in_mins > 0:
|
||||
row.planned_start_time = datetime.datetime.combine(start_date,
|
||||
get_time(workstation_doc.working_hours[i+1].start_time))
|
||||
|
||||
if row.remaining_time_in_mins > 0:
|
||||
start_date = add_days(start_date, 1)
|
||||
row.planned_start_time = datetime.datetime.combine(start_date,
|
||||
get_time(workstation_doc.working_hours[0].start_time))
|
||||
|
||||
def update_time_logs(self, row):
|
||||
self.append("time_logs", {
|
||||
"from_time": row.planned_start_time,
|
||||
"to_time": row.planned_end_time,
|
||||
"completed_qty": 0,
|
||||
"time_in_mins": time_diff_in_minutes(row.planned_end_time, row.planned_start_time),
|
||||
})
|
||||
|
||||
def get_required_items(self):
|
||||
if not self.get('work_order'):
|
||||
return
|
||||
@ -251,3 +350,6 @@ def make_stock_entry(source_name, target_doc=None):
|
||||
}, target_doc, set_missing_values)
|
||||
|
||||
return doclist
|
||||
|
||||
def time_diff_in_minutes(string_ed_date, string_st_date):
|
||||
return time_diff(string_ed_date, string_st_date).total_seconds() / 60
|
@ -1,585 +1,178 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2014-11-27 14:12:07.542534",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"editable_grid": 0,
|
||||
"engine": "InnoDB",
|
||||
"creation": "2014-11-27 14:12:07.542534",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"raw_materials_consumption_section",
|
||||
"material_consumption",
|
||||
"column_break_3",
|
||||
"backflush_raw_materials_based_on",
|
||||
"capacity_planning",
|
||||
"disable_capacity_planning",
|
||||
"allow_overtime",
|
||||
"allow_production_on_holidays",
|
||||
"column_break_5",
|
||||
"capacity_planning_for_days",
|
||||
"mins_between_operations",
|
||||
"section_break_6",
|
||||
"default_wip_warehouse",
|
||||
"default_fg_warehouse",
|
||||
"column_break_11",
|
||||
"default_scrap_warehouse",
|
||||
"over_production_for_sales_and_work_order_section",
|
||||
"overproduction_percentage_for_sales_order",
|
||||
"column_break_16",
|
||||
"overproduction_percentage_for_work_order",
|
||||
"other_settings_section",
|
||||
"update_bom_costs_automatically"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "capacity_planning",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Capacity Planning",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "capacity_planning",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Capacity Planning"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Disables creation of time logs against Work Orders. Operations shall not be tracked against Work Order",
|
||||
"fieldname": "disable_capacity_planning",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Disable Capacity Planning and Time Tracking",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "0",
|
||||
"depends_on": "eval:!doc.disable_capacity_planning",
|
||||
"description": "Plan time logs outside Workstation Working Hours.",
|
||||
"fieldname": "allow_overtime",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow Overtime"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Plan time logs outside Workstation Working Hours.",
|
||||
"fieldname": "allow_overtime",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Allow Overtime",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "0",
|
||||
"depends_on": "eval:!doc.disable_capacity_planning",
|
||||
"fieldname": "allow_production_on_holidays",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Allow Production on Holidays"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fieldname": "allow_production_on_holidays",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Allow Production on Holidays",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "30",
|
||||
"depends_on": "eval:!doc.disable_capacity_planning",
|
||||
"description": "Try planning operations for X days in advance.",
|
||||
"fieldname": "capacity_planning_for_days",
|
||||
"fieldtype": "Int",
|
||||
"label": "Capacity Planning For (Days)"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "30",
|
||||
"description": "Try planning operations for X days in advance.",
|
||||
"fieldname": "capacity_planning_for_days",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Capacity Planning For (Days)",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"depends_on": "eval:!doc.disable_capacity_planning",
|
||||
"description": "Default 10 mins",
|
||||
"fieldname": "mins_between_operations",
|
||||
"fieldtype": "Int",
|
||||
"label": "Time Between Operations (in mins)"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Default 10 mins",
|
||||
"fieldname": "mins_between_operations",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Time Between Operations (in mins)",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Default Warehouses for Production"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "overproduction_percentage_for_sales_order",
|
||||
"fieldtype": "Percent",
|
||||
"label": "Overproduction Percentage For Sales Order"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "overproduction_percentage_for_sales_order",
|
||||
"fieldtype": "Percent",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Overproduction Percentage For Sales Order",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "overproduction_percentage_for_work_order",
|
||||
"fieldtype": "Percent",
|
||||
"label": "Overproduction Percentage For Work Order"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "overproduction_percentage_for_work_order",
|
||||
"fieldtype": "Percent",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Overproduction Percentage For Work Order",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "BOM",
|
||||
"fieldname": "backflush_raw_materials_based_on",
|
||||
"fieldtype": "Select",
|
||||
"label": "Backflush Raw Materials Based On",
|
||||
"options": "BOM\nMaterial Transferred for Manufacture"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "BOM",
|
||||
"fieldname": "backflush_raw_materials_based_on",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Backflush Raw Materials Based On",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "BOM\nMaterial Transferred for Manufacture",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "0",
|
||||
"description": "Allow multiple Material Consumption against a Work Order",
|
||||
"fieldname": "material_consumption",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow Multiple Material Consumption"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Allow multiple Material Consumption against a Work Order",
|
||||
"fieldname": "material_consumption",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Allow Multiple Material Consumption",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "0",
|
||||
"description": "Update BOM cost automatically via Scheduler, based on latest valuation rate / price list rate / last purchase rate of raw materials.",
|
||||
"fieldname": "update_bom_costs_automatically",
|
||||
"fieldtype": "Check",
|
||||
"label": "Update BOM Cost Automatically"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Update BOM cost automatically via Scheduler, based on latest valuation rate / price list rate / last purchase rate of raw materials.",
|
||||
"fieldname": "update_bom_costs_automatically",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Update BOM Cost Automatically",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "default_wip_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default Work In Progress Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "default_wip_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Default Work In Progress Warehouse",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Warehouse",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "default_fg_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default Finished Goods Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "default_fg_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Default Finished Goods Warehouse",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Warehouse",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"default": "0",
|
||||
"fieldname": "disable_capacity_planning",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable Capacity Planning"
|
||||
},
|
||||
{
|
||||
"fieldname": "default_scrap_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default Scrap Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"fieldname": "over_production_for_sales_and_work_order_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Over Production for Sales and Work Order"
|
||||
},
|
||||
{
|
||||
"fieldname": "raw_materials_consumption_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Raw Materials Consumption"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_16",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "other_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Other Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-wrench",
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2018-05-28 00:46:25.310621",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Manufacturing Settings",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"icon": "icon-wrench",
|
||||
"issingle": 1,
|
||||
"modified": "2019-11-26 13:10:45.569341",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Manufacturing Settings",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "Manufacturing Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"read": 1,
|
||||
"role": "Manufacturing Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -5,10 +5,10 @@
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
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_months, cint, today
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||
from erpnext.manufacturing.doctype.work_order.work_order \
|
||||
import make_stock_entry, ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import (make_stock_entry,
|
||||
ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError, CapacityError)
|
||||
from erpnext.stock.doctype.stock_entry import test_stock_entry
|
||||
from erpnext.stock.utils import get_bin
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
@ -307,14 +307,50 @@ class TestWorkOrder(unittest.TestCase):
|
||||
{'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
|
||||
|
||||
if data:
|
||||
frappe.db.set_value("Manufacturing Settings",
|
||||
None, "disable_capacity_planning", 0)
|
||||
|
||||
bom, bom_item = data
|
||||
|
||||
bom_doc = frappe.get_doc('BOM', bom)
|
||||
work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom)
|
||||
self.assertTrue(work_order.planned_end_date)
|
||||
|
||||
job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name})
|
||||
self.assertEqual(len(job_cards), len(bom_doc.operations))
|
||||
|
||||
def test_capcity_planning(self):
|
||||
frappe.db.set_value("Manufacturing Settings", None, {
|
||||
"disable_capacity_planning": 0,
|
||||
"capacity_planning_for_days": 1
|
||||
})
|
||||
|
||||
data = frappe.get_cached_value('BOM', {'docstatus': 1, 'item': '_Test FG Item 2',
|
||||
'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
|
||||
|
||||
if data:
|
||||
bom, bom_item = data
|
||||
|
||||
planned_start_date = add_months(today(), months=-1)
|
||||
work_order = make_wo_order_test_record(item=bom_item,
|
||||
qty=10, bom_no=bom, planned_start_date=planned_start_date)
|
||||
|
||||
work_order1 = make_wo_order_test_record(item=bom_item,
|
||||
qty=30, bom_no=bom, planned_start_date=planned_start_date, do_not_submit=1)
|
||||
|
||||
self.assertRaises(CapacityError, work_order1.submit)
|
||||
|
||||
frappe.db.set_value("Manufacturing Settings", None, {
|
||||
"capacity_planning_for_days": 30
|
||||
})
|
||||
|
||||
work_order1.reload()
|
||||
work_order1.submit()
|
||||
self.assertTrue(work_order1.docstatus, 1)
|
||||
|
||||
work_order1.cancel()
|
||||
work_order.cancel()
|
||||
|
||||
def test_work_order_with_non_transfer_item(self):
|
||||
items = {'Finished Good Transfer Item': 1, '_Test FG Item': 1, '_Test FG Item 1': 0}
|
||||
for item, allow_transfer in items.items():
|
||||
@ -371,14 +407,12 @@ def make_wo_order_test_record(**args):
|
||||
wo_order.skip_transfer=1
|
||||
wo_order.get_items_and_operations_from_bom()
|
||||
wo_order.sales_order = args.sales_order or None
|
||||
wo_order.planned_start_date = args.planned_start_date or now()
|
||||
|
||||
if args.source_warehouse:
|
||||
for item in wo_order.get("required_items"):
|
||||
item.source_warehouse = args.source_warehouse
|
||||
|
||||
if args.planned_start_date:
|
||||
wo_order.planned_start_date = args.planned_start_date
|
||||
|
||||
if not args.do_not_save:
|
||||
wo_order.insert()
|
||||
|
||||
|
@ -551,6 +551,7 @@ erpnext.work_order = {
|
||||
if (!r.exe) {
|
||||
frm.set_value("wip_warehouse", r.message.wip_warehouse);
|
||||
frm.set_value("fg_warehouse", r.message.fg_warehouse);
|
||||
frm.set_value("scrap_warehouse", r.message.scrap_warehouse);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -12,7 +12,7 @@ from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from erpnext.stock.doctype.item.item import validate_end_of_life
|
||||
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError
|
||||
from erpnext.projects.doctype.timesheet.timesheet import OverlapError
|
||||
from erpnext.manufacturing.doctype.job_card.job_card import OverlapError
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs
|
||||
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
|
||||
from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
|
||||
@ -22,6 +22,7 @@ from erpnext.utilities.transaction_base import validate_uom_is_integer
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
class OverProductionError(frappe.ValidationError): pass
|
||||
class CapacityError(frappe.ValidationError): pass
|
||||
class StockOverProductionError(frappe.ValidationError): pass
|
||||
class OperationTooLongError(frappe.ValidationError): pass
|
||||
class ItemHasVariantError(frappe.ValidationError): pass
|
||||
@ -260,12 +261,50 @@ class WorkOrder(Document):
|
||||
self.update_reserved_qty_for_production()
|
||||
|
||||
def create_job_card(self):
|
||||
for row in self.operations:
|
||||
manufacturing_settings_doc = frappe.get_doc("Manufacturing Settings")
|
||||
|
||||
enable_capacity_planning = not cint(manufacturing_settings_doc.disable_capacity_planning)
|
||||
plan_days = cint(manufacturing_settings_doc.capacity_planning_for_days) or 30
|
||||
|
||||
for i, row in enumerate(self.operations):
|
||||
self.set_operation_start_end_time(i, row)
|
||||
|
||||
if not row.workstation:
|
||||
frappe.throw(_("Row {0}: select the workstation against the operation {1}")
|
||||
.format(row.idx, row.operation))
|
||||
|
||||
create_job_card(self, row, auto_create=True)
|
||||
original_start_time = row.planned_start_time
|
||||
job_card_doc = create_job_card(self, row,
|
||||
enable_capacity_planning=enable_capacity_planning, auto_create=True)
|
||||
|
||||
if enable_capacity_planning and job_card_doc:
|
||||
row.planned_start_time = job_card_doc.time_logs[0].from_time
|
||||
row.planned_end_time = job_card_doc.time_logs[-1].to_time
|
||||
|
||||
if date_diff(row.planned_start_time, original_start_time) > plan_days:
|
||||
frappe.throw(_("Unable to find the time slot in the next {0} days for the operation {1}.")
|
||||
.format(plan_days, row.operation), CapacityError)
|
||||
|
||||
row.db_update()
|
||||
|
||||
planned_end_date = self.operations and self.operations[-1].planned_end_time
|
||||
if planned_end_date:
|
||||
self.db_set("planned_end_date", planned_end_date)
|
||||
|
||||
def set_operation_start_end_time(self, idx, row):
|
||||
"""Set start and end time for given operation. If first operation, set start as
|
||||
`planned_start_date`, else add time diff to end time of earlier operation."""
|
||||
if idx==0:
|
||||
# first operation at planned_start date
|
||||
row.planned_start_time = self.planned_start_date
|
||||
else:
|
||||
row.planned_start_time = get_datetime(self.operations[idx-1].planned_end_time)\
|
||||
+ get_mins_between_operations()
|
||||
|
||||
row.planned_end_time = get_datetime(row.planned_start_time) + relativedelta(minutes = row.time_in_mins)
|
||||
|
||||
if row.planned_start_time == row.planned_end_time:
|
||||
frappe.throw(_("Capacity Planning Error, planned start time can not be same as end time"))
|
||||
|
||||
def validate_cancel(self):
|
||||
if self.status == "Stopped":
|
||||
@ -327,9 +366,8 @@ class WorkOrder(Document):
|
||||
"""Fetch operations from BOM and set in 'Work Order'"""
|
||||
self.set('operations', [])
|
||||
|
||||
if not self.bom_no \
|
||||
or cint(frappe.db.get_single_value("Manufacturing Settings", "disable_capacity_planning")):
|
||||
return
|
||||
if not self.bom_no:
|
||||
return
|
||||
|
||||
if self.use_multi_level_bom:
|
||||
bom_list = frappe.get_doc("BOM", self.bom_no).traverse_tree()
|
||||
@ -681,11 +719,13 @@ def make_stock_entry(work_order_id, purpose, qty=None):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_default_warehouse():
|
||||
wip_warehouse = frappe.db.get_single_value("Manufacturing Settings",
|
||||
"default_wip_warehouse")
|
||||
fg_warehouse = frappe.db.get_single_value("Manufacturing Settings",
|
||||
"default_fg_warehouse")
|
||||
return {"wip_warehouse": wip_warehouse, "fg_warehouse": fg_warehouse}
|
||||
doc = frappe.get_cached_doc("Manufacturing Settings")
|
||||
|
||||
return {
|
||||
"wip_warehouse": doc.default_wip_warehouse,
|
||||
"fg_warehouse": doc.default_fg_warehouse,
|
||||
"scrap_warehouse": doc.default_scrap_warehouse
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def stop_unstop(work_order, status):
|
||||
@ -721,7 +761,7 @@ def make_job_card(work_order, operation, workstation, qty=0):
|
||||
if row:
|
||||
return create_job_card(work_order, row, qty)
|
||||
|
||||
def create_job_card(work_order, row, qty=0, auto_create=False):
|
||||
def create_job_card(work_order, row, qty=0, enable_capacity_planning=False, auto_create=False):
|
||||
doc = frappe.new_doc("Job Card")
|
||||
doc.update({
|
||||
'work_order': work_order.name,
|
||||
@ -741,6 +781,9 @@ def create_job_card(work_order, row, qty=0, auto_create=False):
|
||||
|
||||
if auto_create:
|
||||
doc.flags.ignore_mandatory = True
|
||||
if enable_capacity_planning:
|
||||
doc.schedule_time_logs(row)
|
||||
|
||||
doc.insert()
|
||||
frappe.msgprint(_("Job card {0} created").format(doc.name))
|
||||
|
||||
|
@ -1,466 +1,159 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:workstation_name",
|
||||
"beta": 0,
|
||||
"creation": "2013-01-10 16:34:17",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:workstation_name",
|
||||
"creation": "2013-01-10 16:34:17",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"field_order": [
|
||||
"workstation_name",
|
||||
"production_capacity",
|
||||
"column_break_3",
|
||||
"over_heads",
|
||||
"hour_rate_electricity",
|
||||
"hour_rate_consumable",
|
||||
"column_break_11",
|
||||
"hour_rate_rent",
|
||||
"hour_rate_labour",
|
||||
"hour_rate",
|
||||
"working_hours_section",
|
||||
"holiday_list",
|
||||
"working_hours",
|
||||
"workstaion_description",
|
||||
"description"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fieldname": "description_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "workstation_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Workstation Name",
|
||||
"oldfieldname": "workstation_name",
|
||||
"oldfieldtype": "Data",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "workstation_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Workstation Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "workstation_name",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Text",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Description",
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Text",
|
||||
"width": "300px"
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "over_heads",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Operating Costs",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldtype": "Section Break",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "over_heads",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Operating Costs",
|
||||
"oldfieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "per hour",
|
||||
"fieldname": "hour_rate_electricity",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Electricity Cost",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "hour_rate_electricity",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"bold": 1,
|
||||
"description": "per hour",
|
||||
"fieldname": "hour_rate_electricity",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Electricity Cost",
|
||||
"oldfieldname": "hour_rate_electricity",
|
||||
"oldfieldtype": "Currency"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "per hour",
|
||||
"fieldname": "hour_rate_consumable",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Consumable Cost",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "hour_rate_consumable",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"bold": 1,
|
||||
"description": "per hour",
|
||||
"fieldname": "hour_rate_consumable",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Consumable Cost",
|
||||
"oldfieldname": "hour_rate_consumable",
|
||||
"oldfieldtype": "Currency"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "per hour",
|
||||
"fieldname": "hour_rate_rent",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Rent Cost",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "hour_rate_rent",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"bold": 1,
|
||||
"description": "per hour",
|
||||
"fieldname": "hour_rate_rent",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Rent Cost",
|
||||
"oldfieldname": "hour_rate_rent",
|
||||
"oldfieldtype": "Currency"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Wages per hour",
|
||||
"fieldname": "hour_rate_labour",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Wages",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "hour_rate_labour",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"bold": 1,
|
||||
"description": "Wages per hour",
|
||||
"fieldname": "hour_rate_labour",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Wages",
|
||||
"oldfieldname": "hour_rate_labour",
|
||||
"oldfieldtype": "Currency"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "per hour",
|
||||
"fieldname": "hour_rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Net Hour Rate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "hour_rate",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"description": "per hour",
|
||||
"fieldname": "hour_rate",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Net Hour Rate",
|
||||
"oldfieldname": "hour_rate",
|
||||
"oldfieldtype": "Currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "working_hours_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Working Hours",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "working_hours_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Working Hours"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "working_hours",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Working Hours",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Workstation Working Hour",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "working_hours",
|
||||
"fieldtype": "Table",
|
||||
"label": "Working Hours",
|
||||
"options": "Workstation Working Hour"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fieldname": "holiday_list",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Holiday List",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Holiday List",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
"fieldname": "holiday_list",
|
||||
"fieldtype": "Link",
|
||||
"label": "Holiday List",
|
||||
"options": "Holiday List"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "production_capacity",
|
||||
"fieldtype": "Int",
|
||||
"label": "Production Capacity",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "workstaion_description",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Description"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-wrench",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-07-18 22:28:50.163219",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Workstation",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"icon": "icon-wrench",
|
||||
"idx": 1,
|
||||
"modified": "2019-11-26 12:39:19.742052",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Workstation",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing User",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1
|
||||
}
|
@ -4,7 +4,9 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt, cint, getdate, formatdate, comma_and, time_diff_in_seconds, to_timedelta
|
||||
from erpnext.support.doctype.issue.issue import get_holidays
|
||||
from frappe.utils import (flt, cint, getdate, formatdate,
|
||||
comma_and, time_diff_in_seconds, to_timedelta, add_days)
|
||||
from frappe.model.document import Document
|
||||
from dateutil.parser import parse
|
||||
|
||||
@ -43,6 +45,17 @@ class Workstation(Document):
|
||||
where parent = %s and workstation = %s""",
|
||||
(self.hour_rate, bom_no[0], self.name))
|
||||
|
||||
def validate_workstation_holiday(self, schedule_date, skip_holiday_list_check=False):
|
||||
if not skip_holiday_list_check and (not self.holiday_list or
|
||||
cint(frappe.db.get_single_value("Manufacturing Settings", "allow_production_on_holidays"))):
|
||||
return schedule_date
|
||||
|
||||
if schedule_date in tuple(get_holidays(self.holiday_list)):
|
||||
schedule_date = add_days(schedule_date, 1)
|
||||
self.validate_workstation_holiday(schedule_date, skip_holiday_list_check=True)
|
||||
|
||||
return schedule_date
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_default_holiday_list():
|
||||
return frappe.get_cached_value('Company', frappe.defaults.get_user_default("Company"), "default_holiday_list")
|
||||
|
@ -6,8 +6,12 @@ def get_data():
|
||||
'fieldname': 'workstation',
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Manufacture'),
|
||||
'items': ['BOM', 'Routing', 'Work Order', 'Job Card', 'Operation', 'Timesheet']
|
||||
'label': _('Master'),
|
||||
'items': ['BOM', 'Routing', 'Operation']
|
||||
},
|
||||
{
|
||||
'label': _('Transaction'),
|
||||
'items': ['Work Order', 'Job Card', 'Timesheet']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -646,4 +646,5 @@ erpnext.patches.v12_0.set_payment_entry_status
|
||||
erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields
|
||||
erpnext.patches.v12_0.set_default_for_add_taxes_from_item_tax_template
|
||||
erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger
|
||||
erpnext.patches.v12_0.update_price_or_product_discount
|
||||
erpnext.patches.v12_0.update_price_or_product_discount
|
||||
erpnext.patches.v12_0.set_production_capacity_in_workstation
|
@ -0,0 +1,8 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("manufacturing", "doctype", "workstation")
|
||||
|
||||
frappe.db.sql(""" UPDATE `tabWorkstation`
|
||||
SET production_capacity = 1 """)
|
Loading…
Reference in New Issue
Block a user