Merge pull request #19441 from rohitwaghchaure/capacity_planning_for_workstataion_feature

fix: capacity planning back
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index eda59ab..6133b1c 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -350,13 +350,13 @@
 	if dt in ["Sales Order", "Purchase Order"]:
 		grand_total = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
 
-	if dt in ["Sales Invoice", "Purchase Invoice"]:
+	elif dt in ["Sales Invoice", "Purchase Invoice"]:
 		if ref_doc.party_account_currency == ref_doc.currency:
 			grand_total = flt(ref_doc.outstanding_amount)
 		else:
 			grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
 
-	if dt == "Fees":
+	elif dt == "Fees":
 		grand_total = ref_doc.outstanding_amount
 
 	if grand_total > 0 :
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
index 8955830..b607c0f 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
@@ -36,7 +36,7 @@
 			self.filters.report_date) or {}
 
 		for party, party_dict in iteritems(self.party_total):
-			if party_dict.outstanding <= 0:
+			if party_dict.outstanding == 0:
 				continue
 
 			row = frappe._dict()
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 1f8b663..4e0dd6f 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -415,9 +415,10 @@
 		return gl_dict
 
 	def validate_qty_is_not_zero(self):
-		for item in self.items:
-			if not item.qty:
-				frappe.throw(_("Item quantity can not be zero"))
+		if self.doctype != "Purchase Receipt":
+			for item in self.items:
+				if not item.qty:
+					frappe.throw(_("Item quantity can not be zero"))
 
 	def validate_account_currency(self, account, account_currency=None):
 		valid_currency = [self.company_currency]
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.py b/erpnext/crm/doctype/email_campaign/email_campaign.py
index 3050d05..00a4bd1 100644
--- a/erpnext/crm/doctype/email_campaign/email_campaign.py
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.py
@@ -41,7 +41,8 @@
 		email_campaign_exists = frappe.db.exists("Email Campaign", {
 			"campaign_name": self.campaign_name,
 			"recipient": self.recipient,
-			"status": ("in", ["In Progress", "Scheduled"])
+			"status": ("in", ["In Progress", "Scheduled"]),
+			"name": ("!=", self.name)
 		})
 		if email_campaign_exists:
 			frappe.throw(_("The Campaign '{0}' already exists for the {1} '{2}'").format(self.campaign_name, self.email_campaign_for, self.recipient))
@@ -78,7 +79,7 @@
 	comm = make(
 		doctype = "Email Campaign",
 		name = email_campaign.name,
-		subject = email_template.get("subject"),
+		subject = frappe.render_template(email_template.get("subject"), context),
 		content = frappe.render_template(email_template.get("response"), context),
 		sender = sender,
 		recipients = recipient,
diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py
index 9af5e22..76825ce 100644
--- a/erpnext/education/doctype/student/student.py
+++ b/erpnext/education/doctype/student/student.py
@@ -40,7 +40,7 @@
 			frappe.throw(_("Student {0} exist against student applicant {1}").format(student[0][0], self.student_applicant))
 
 	def after_insert(self):
-		if not frappe.get_single('Education Settings').user_creation_skip:
+		if not frappe.get_single('Education Settings').get('user_creation_skip'):
 			self.create_student_user()
 
 	def create_student_user(self):
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index 703ec06..2f88e1e 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -4,7 +4,7 @@
 from __future__ import unicode_literals
 import frappe
 
-from frappe.utils import getdate, validate_email_address, today, add_years, format_datetime
+from frappe.utils import getdate, validate_email_address, today, add_years, format_datetime, cstr
 from frappe.model.naming import set_name_by_naming_series
 from frappe import throw, _, scrub
 from frappe.permissions import add_user_permission, remove_user_permission, \
@@ -218,8 +218,8 @@
 
 	def reset_employee_emails_cache(self):
 		prev_doc = self.get_doc_before_save() or {}
-		cell_number = self.get('cell_number')
-		prev_number = prev_doc.get('cell_number')
+		cell_number = cstr(self.get('cell_number'))
+		prev_number = cstr(prev_doc.get('cell_number'))
 		if (cell_number != prev_number or
 			self.get('user_id') != prev_doc.get('user_id')):
 			frappe.cache().hdel('employees_with_number', cell_number)
diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.js b/erpnext/hr/doctype/payroll_entry/payroll_entry.js
index adc0671..d25eb6d 100644
--- a/erpnext/hr/doctype/payroll_entry/payroll_entry.js
+++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.js
@@ -31,7 +31,11 @@
 			}
 			if ((frm.doc.employees || []).length) {
 				frm.page.set_primary_action(__('Create Salary Slips'), () => {
-					frm.save('Submit');
+					frm.save('Submit').then(()=>{
+						frm.page.clear_primary_action();
+						frm.refresh();
+						frm.events.refresh(frm);
+					});
 				});
 			}
 		}
diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
index be016ad..f7b407b 100644
--- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
+++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
@@ -4,6 +4,7 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
+from frappe.utils.data import comma_and
 
 def execute(filters=None):
 #	if not filters: filters = {}
@@ -13,35 +14,36 @@
 	data = get_bom_stock(filters)
 	qty_to_make = filters.get("qty_to_make")
 
+	manufacture_details = get_manufacturer_records()
 	for row in data:
-		item_map = get_item_details(row.item_code)
 		reqd_qty = qty_to_make * row.actual_qty
 		last_pur_price = frappe.db.get_value("Item", row.item_code, "last_purchase_rate")
-		if row.to_build > 0:
-			diff_qty = row.to_build - reqd_qty
-			summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, row.to_build, reqd_qty, diff_qty, last_pur_price])
-		else:
-			diff_qty = 0 - reqd_qty
-			summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, "0.000", reqd_qty, diff_qty, last_pur_price])
 
+		summ_data.append(get_report_data(last_pur_price, reqd_qty, row, manufacture_details))
 	return columns, summ_data
 
+def get_report_data(last_pur_price, reqd_qty, row, manufacture_details):
+	to_build = row.to_build if row.to_build > 0 else 0
+	diff_qty = to_build - reqd_qty
+	return [row.item_code, row.description,
+		comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer', []), add_quotes=False),
+		comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer_part', []), add_quotes=False),
+		row.actual_qty, str(to_build),
+		reqd_qty, diff_qty, last_pur_price]
+
 def get_columns():
 	"""return columns"""
 	columns = [
 		_("Item") + ":Link/Item:100",
 		_("Description") + "::150",
-		_("Manufacturer") + "::100",
-		_("Manufacturer Part Number") + "::100",
+		_("Manufacturer") + "::250",
+		_("Manufacturer Part Number") + "::250",
 		_("Qty") + ":Float:50",
 		_("Stock Qty") + ":Float:100",
 		_("Reqd Qty")+ ":Float:100",
 		_("Diff Qty")+ ":Float:100",
 		_("Last Purchase Price")+ ":Float:100",
-
-
 	]
-
 	return columns
 
 def get_bom_stock(filters):
@@ -85,7 +87,12 @@
 
 			GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
 
-def get_item_details(item_code):
-		items = frappe.db.sql("""select it.item_group, it.item_name, it.stock_uom, it.name, it.brand, it.description, it.manufacturer_part_no, it.manufacturer from tabItem it where it.item_code = %s""", item_code, as_dict=1)
+def get_manufacturer_records():
+	details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no, parent"])
+	manufacture_details = frappe._dict()
+	for detail in details:
+		dic = manufacture_details.setdefault(detail.get('parent'), {})
+		dic.setdefault('manufacturer', []).append(detail.get('manufacturer'))
+		dic.setdefault('manufacturer_part', []).append(detail.get('manufacturer_part_no'))
 
-		return dict((d.name, d) for d in items)
+	return manufacture_details
\ No newline at end of file
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 85e8143..7dc58b5 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -202,7 +202,7 @@
 					}
 				}
 				// payment request
-				if(flt(doc.per_billed)==0) {
+				if(flt(doc.per_billed)<100) {
 					this.frm.add_custom_button(__('Payment Request'), () => this.make_payment_request(), __('Create'));
 					this.frm.add_custom_button(__('Payment'), () => this.make_payment_entry(), __('Create'));
 				}
diff --git a/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py b/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py
index 32711b2..056492a 100644
--- a/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py
+++ b/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py
@@ -7,7 +7,7 @@
 
 def execute(filters=None):
 	if not filters: filters = {}
-	
+
 	columns = get_columns()
 	iwq_map = get_item_warehouse_quantity_map()
 	item_map = get_item_details()
@@ -15,22 +15,23 @@
 	for sbom, warehouse in iwq_map.items():
 		total = 0
 		total_qty = 0
-		
+
 		for wh, item_qty in warehouse.items():
 			total += 1
-			row = [sbom, item_map.get(sbom).item_name, item_map.get(sbom).description, 
-					item_map.get(sbom).stock_uom, wh]
-			available_qty = item_qty
-			total_qty += flt(available_qty)
-			row += [available_qty]
-			
-			if available_qty:
-				data.append(row)
-				if (total == len(warehouse)):
-					row = ["", "", "Total", "", "", total_qty]
+			if item_map.get(sbom):
+				row = [sbom, item_map.get(sbom).item_name, item_map.get(sbom).description,
+						item_map.get(sbom).stock_uom, wh]
+				available_qty = item_qty
+				total_qty += flt(available_qty)
+				row += [available_qty]
+
+				if available_qty:
 					data.append(row)
+					if (total == len(warehouse)):
+						row = ["", "", "Total", "", "", total_qty]
+						data.append(row)
 	return columns, data
-		
+
 def get_columns():
 	columns = ["Item Code:Link/Item:100", "Item Name::100", "Description::120", \
 				"UOM:Link/UOM:80", "Warehouse:Link/Warehouse:100", "Quantity::100"]