<commit_before># -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe, json
from frappe import msgprint, _
from six import string_types, iteritems

from frappe.model.document import Document
from frappe.utils import cstr, flt, cint, nowdate, add_days, comma_and, now_datetime, ceil
from frappe.utils.csvutils import build_csv_response
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_children
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults

class ProductionPlan(Document):
	def validate(self):
		self.calculate_total_planned_qty()
		self.set_status()

	def validate_data(self):
		for d in self.get('po_items'):
			if not d.bom_no:
				frappe.throw(_("Please select BOM for Item in Row {0}".format(d.idx)))
			else:
				validate_bom_no(d.item_code, d.bom_no)

			if not flt(d.planned_qty):
				frappe.throw(_("Please enter Planned Qty for Item {0} at row {1}").format(d.item_code, d.idx))

	def get_open_sales_orders(self):
		""" Pull sales orders  which are pending to deliver based on criteria selected"""
		open_so = get_sales_orders(self)

		if open_so:
			self.add_so_in_table(open_so)
		else:
			frappe.msgprint(_("Sales orders are not available for production"))

	def add_so_in_table(self, open_so):
		""" Add sales orders in the table"""
		self.set('sales_orders', [])

		for data in open_so:
			self.append('sales_orders', {
				'sales_order': data.name,
				'sales_order_date': data.transaction_date,
				'customer': data.customer,
				'grand_total': data.grand_total
			})

	def get_pending_material_requests(self):
		""" Pull Material Requests that are pending based on criteria selected"""
		mr_filter = item_filter = ""
		if self.from_date:
			mr_filter += " and mr.transaction_date >= %(from_date)s"
		if self.to_date:
			mr_filter += " and mr.transaction_date <= %(to_date)s"
		if self.warehouse:
			mr_filter += " and mr_item.warehouse = %(warehouse)s"

		if self.item_code:
			item_filter += " and mr_item.item_code = %(item)s"

		pending_mr = frappe.db.sql("""
			select distinct mr.name, mr.transaction_date
			from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
			where mr_item.parent = mr.name
				and mr.material_request_type = "Manufacture"
				and mr.docstatus = 1 and mr.company = %(company)s
				and mr_item.qty > ifnull(mr_item.ordered_qty,0) {0} {1}
				and (exists (select name from `tabBOM` bom where bom.item=mr_item.item_code
					and bom.is_active = 1))
			""".format(mr_filter, item_filter), {
				"from_date": self.from_date,
				"to_date": self.to_date,
				"warehouse": self.warehouse,
				"item": self.item_code,
				"company": self.company
			}, as_dict=1)

		self.add_mr_in_table(pending_mr)

	def add_mr_in_table(self, pending_mr):
		""" Add Material Requests in the table"""
		self.set('material_requests', [])

		for data in pending_mr:
			self.append('material_requests', {
				'material_request': data.name,
				'material_request_date': data.transaction_date
			})

	def get_items(self):
		if self.get_items_from == "Sales Order":
			self.get_so_items()
		elif self.get_items_from == "Material Request":
			self.get_mr_items()

	def get_so_items(self):
		so_list = [d.sales_order for d in self.get("sales_orders", []) if d.sales_order]
		if not so_list:
			msgprint(_("Please enter Sales Orders in the above table"))
			return []

		item_condition = ""
		if self.item_code:
			item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))

		items = frappe.db.sql("""select distinct parent, item_code, warehouse,
			(qty - work_order_qty) * conversion_factor as pending_qty, name
			from `tabSales Order Item` so_item
			where parent in (%s) and docstatus = 1 and qty > work_order_qty
			and exists (select name from `tabBOM` bom where bom.item=so_item.item_code
					and bom.is_active = 1) %s""" % \
			(", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1)

		if self.item_code:
			item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))

		packed_items = frappe.db.sql("""select distinct pi.parent, pi.item_code, pi.warehouse as warehouse,
			(((so_item.qty - so_item.work_order_qty) * pi.qty) / so_item.qty)
				as pending_qty, pi.parent_item, so_item.name
			from `tabSales Order Item` so_item, `tabPacked Item` pi
			where so_item.parent = pi.parent and so_item.docstatus = 1
			and pi.parent_item = so_item.item_code
			and so_item.parent in (%s) and so_item.qty > so_item.work_order_qty
			and exists (select name from `tabBOM` bom where bom.item=pi.item_code
					and bom.is_active = 1) %s""" % \
			(", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1)

		self.add_items(items + packed_items)
		self.calculate_total_planned_qty()

	def get_mr_items(self):
		mr_list = [d.material_request for d in self.get("material_requests", []) if d.material_request]
		if not mr_list:
			msgprint(_("Please enter Material Requests in the above table"))
			return []

		item_condition = ""
		if self.item_code:
			item_condition = " and mr_item.item_code ={0}".format(frappe.db.escape(self.item_code))

		items = frappe.db.sql("""select distinct parent, name, item_code, warehouse,
			(qty - ordered_qty) as pending_qty
			from `tabMaterial Request Item` mr_item
			where parent in (%s) and docstatus = 1 and qty > ordered_qty
			and exists (select name from `tabBOM` bom where bom.item=mr_item.item_code
				and bom.is_active = 1) %s""" % \
			(", ".join(["%s"] * len(mr_list)), item_condition), tuple(mr_list), as_dict=1)

		self.add_items(items)
		self.calculate_total_planned_qty()

	def add_items(self, items):
		self.set('po_items', [])
		for data in items:
			item_details = get_item_details(data.item_code)
			pi = self.append('po_items', {
				'include_exploded_items': 1,
				'warehouse': data.warehouse,
				'item_code': data.item_code,
				'description': item_details and item_details.description or '',
				'stock_uom': item_details and item_details.stock_uom or '',
				'bom_no': item_details and item_details.bom_no or '',
				'planned_qty': data.pending_qty,
				'pending_qty': data.pending_qty,
				'planned_start_date': now_datetime(),
				'product_bundle_item': data.parent_item
			})

			if self.get_items_from == "Sales Order":
				pi.sales_order = data.parent
				pi.sales_order_item = data.name

			elif self.get_items_from == "Material Request":
				pi.material_request = data.parent
				pi.material_request_item = data.name

	def calculate_total_planned_qty(self):
		self.total_planned_qty = 0
		for d in self.po_items:
			self.total_planned_qty += flt(d.planned_qty)

	def calculate_total_produced_qty(self):
		self.total_produced_qty = 0
		for d in self.po_items:
			self.total_produced_qty += flt(d.produced_qty)

		self.db_set("total_produced_qty", self.total_produced_qty, update_modified=False)

	def update_produced_qty(self, produced_qty, production_plan_item):
		for data in self.po_items:
			if data.name == production_plan_item:
				data.produced_qty = produced_qty
				data.db_update()

		self.calculate_total_produced_qty()
		self.set_status()
		self.db_set('status', self.status)

	def on_cancel(self):
		self.db_set('status', 'Cancelled')
		self.delete_draft_work_order()

	def delete_draft_work_order(self):
		for d in frappe.get_all('Work Order', fields = ["name"],
			filters = {'docstatus': 0, 'production_plan': ("=", self.name)}):
			frappe.delete_doc('Work Order', d.name)

	def set_status(self):
		self.status = {
			0: 'Draft',
			1: 'Submitted',
			2: 'Cancelled'
		}.get(self.docstatus)

		if self.total_produced_qty > 0:
			self.status = "In Process"
			if self.total_produced_qty == self.total_planned_qty:<commit_msg>fix: update the pending qty in production plan on completion of work order (#19180)<commit_after># -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe, json
from frappe import msgprint, _
from six import string_types, iteritems

from frappe.model.document import Document
from frappe.utils import cstr, flt, cint, nowdate, add_days, comma_and, now_datetime, ceil
from frappe.utils.csvutils import build_csv_response
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_children
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults

class ProductionPlan(Document):
	def validate(self):
		self.calculate_total_planned_qty()
		self.set_status()

	def validate_data(self):
		for d in self.get('po_items'):
			if not d.bom_no:
				frappe.throw(_("Please select BOM for Item in Row {0}".format(d.idx)))
			else:
				validate_bom_no(d.item_code, d.bom_no)

			if not flt(d.planned_qty):
				frappe.throw(_("Please enter Planned Qty for Item {0} at row {1}").format(d.item_code, d.idx))

	def get_open_sales_orders(self):
		""" Pull sales orders  which are pending to deliver based on criteria selected"""
		open_so = get_sales_orders(self)

		if open_so:
			self.add_so_in_table(open_so)
		else:
			frappe.msgprint(_("Sales orders are not available for production"))

	def add_so_in_table(self, open_so):
		""" Add sales orders in the table"""
		self.set('sales_orders', [])

		for data in open_so:
			self.append('sales_orders', {
				'sales_order': data.name,
				'sales_order_date': data.transaction_date,
				'customer': data.customer,
				'grand_total': data.grand_total
			})

	def get_pending_material_requests(self):
		""" Pull Material Requests that are pending based on criteria selected"""
		mr_filter = item_filter = ""
		if self.from_date:
			mr_filter += " and mr.transaction_date >= %(from_date)s"
		if self.to_date:
			mr_filter += " and mr.transaction_date <= %(to_date)s"
		if self.warehouse:
			mr_filter += " and mr_item.warehouse = %(warehouse)s"

		if self.item_code:
			item_filter += " and mr_item.item_code = %(item)s"

		pending_mr = frappe.db.sql("""
			select distinct mr.name, mr.transaction_date
			from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
			where mr_item.parent = mr.name
				and mr.material_request_type = "Manufacture"
				and mr.docstatus = 1 and mr.company = %(company)s
				and mr_item.qty > ifnull(mr_item.ordered_qty,0) {0} {1}
				and (exists (select name from `tabBOM` bom where bom.item=mr_item.item_code
					and bom.is_active = 1))
			""".format(mr_filter, item_filter), {
				"from_date": self.from_date,
				"to_date": self.to_date,
				"warehouse": self.warehouse,
				"item": self.item_code,
				"company": self.company
			}, as_dict=1)

		self.add_mr_in_table(pending_mr)

	def add_mr_in_table(self, pending_mr):
		""" Add Material Requests in the table"""
		self.set('material_requests', [])

		for data in pending_mr:
			self.append('material_requests', {
				'material_request': data.name,
				'material_request_date': data.transaction_date
			})

	def get_items(self):
		if self.get_items_from == "Sales Order":
			self.get_so_items()
		elif self.get_items_from == "Material Request":
			self.get_mr_items()

	def get_so_items(self):
		so_list = [d.sales_order for d in self.get("sales_orders", []) if d.sales_order]
		if not so_list:
			msgprint(_("Please enter Sales Orders in the above table"))
			return []

		item_condition = ""
		if self.item_code:
			item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))

		items = frappe.db.sql("""select distinct parent, item_code, warehouse,
			(qty - work_order_qty) * conversion_factor as pending_qty, name
			from `tabSales Order Item` so_item
			where parent in (%s) and docstatus = 1 and qty > work_order_qty
			and exists (select name from `tabBOM` bom where bom.item=so_item.item_code
					and bom.is_active = 1) %s""" % \
			(", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1)

		if self.item_code:
			item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))

		packed_items = frappe.db.sql("""select distinct pi.parent, pi.item_code, pi.warehouse as warehouse,
			(((so_item.qty - so_item.work_order_qty) * pi.qty) / so_item.qty)
				as pending_qty, pi.parent_item, so_item.name
			from `tabSales Order Item` so_item, `tabPacked Item` pi
			where so_item.parent = pi.parent and so_item.docstatus = 1
			and pi.parent_item = so_item.item_code
			and so_item.parent in (%s) and so_item.qty > so_item.work_order_qty
			and exists (select name from `tabBOM` bom where bom.item=pi.item_code
					and bom.is_active = 1) %s""" % \
			(", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1)

		self.add_items(items + packed_items)
		self.calculate_total_planned_qty()

	def get_mr_items(self):
		mr_list = [d.material_request for d in self.get("material_requests", []) if d.material_request]
		if not mr_list:
			msgprint(_("Please enter Material Requests in the above table"))
			return []

		item_condition = ""
		if self.item_code:
			item_condition = " and mr_item.item_code ={0}".format(frappe.db.escape(self.item_code))

		items = frappe.db.sql("""select distinct parent, name, item_code, warehouse,
			(qty - ordered_qty) as pending_qty
			from `tabMaterial Request Item` mr_item
			where parent in (%s) and docstatus = 1 and qty > ordered_qty
			and exists (select name from `tabBOM` bom where bom.item=mr_item.item_code
				and bom.is_active = 1) %s""" % \
			(", ".join(["%s"] * len(mr_list)), item_condition), tuple(mr_list), as_dict=1)

		self.add_items(items)
		self.calculate_total_planned_qty()

	def add_items(self, items):
		self.set('po_items', [])
		for data in items:
			item_details = get_item_details(data.item_code)
			pi = self.append('po_items', {
				'include_exploded_items': 1,
				'warehouse': data.warehouse,
				'item_code': data.item_code,
				'description': item_details and item_details.description or '',
				'stock_uom': item_details and item_details.stock_uom or '',
				'bom_no': item_details and item_details.bom_no or '',
				'planned_qty': data.pending_qty,
				'pending_qty': data.pending_qty,
				'planned_start_date': now_datetime(),
				'product_bundle_item': data.parent_item
			})

			if self.get_items_from == "Sales Order":
				pi.sales_order = data.parent
				pi.sales_order_item = data.name

			elif self.get_items_from == "Material Request":
				pi.material_request = data.parent
				pi.material_request_item = data.name

	def calculate_total_planned_qty(self):
		self.total_planned_qty = 0
		for d in self.po_items:
			self.total_planned_qty += flt(d.planned_qty)

	def calculate_total_produced_qty(self):
		self.total_produced_qty = 0
		for d in self.po_items:
			self.total_produced_qty += flt(d.produced_qty)

		self.db_set("total_produced_qty", self.total_produced_qty, update_modified=False)

	def update_produced_qty(self, produced_qty, production_plan_item):
		for data in self.po_items:
			if data.name == production_plan_item:
				data.produced_qty = produced_qty
				data.pending_qty = data.planned_qty - data.produced_qty
				data.db_update()

		self.calculate_total_produced_qty()
		self.set_status()
		self.db_set('status', self.status)

	def on_cancel(self):
		self.db_set('status', 'Cancelled')
		self.delete_draft_work_order()

	def delete_draft_work_order(self):
		for d in frappe.get_all('Work Order', fields = ["name"],
			filters = {'docstatus': 0, 'production_plan': ("=", self.name)}):
			frappe.delete_doc('Work Order', d.name)

	def set_status(self):
		self.status = {
			0: 'Draft',
			1: 'Submitted',
			2: 'Cancelled'
		}.get(self.docstatus)

		if self.total_produced_qty > 0:
			self.status = "In Process"
			if self.total_produced_qty == self.total_planned_qty:
{'fraction_non_alphanumeric': 0.1223530885853903, 'fraction_numerical': 0.004573361734118531, 'mean_word_length': 4.394727948631294, 'pattern_counts': {'":': 18, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7150151', 'n_tokens_mistral': 6460, 'n_tokens_neox': 5701, 'n_words': 1289}