Merge branch 'develop' into job-card-excess-transfer
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 521432d..2005dac 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -425,7 +425,10 @@
 						status: ["!=", "Stopped"],
 						per_ordered: ["<", 100],
 						company: me.frm.doc.company
-					}
+					},
+					allow_child_item_selection: true,
+					child_fielname: "items",
+					child_columns: ["item_code", "qty"]
 				})
 			}, __("Get Items From"));
 
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index c7a5db5..12a09cd 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -433,12 +433,12 @@
  "image_field": "image",
  "links": [
   {
-   "group": "Item Group",
-   "link_doctype": "Supplier Item Group",
-   "link_fieldname": "supplier"
+   "group": "Allowed Items",
+   "link_doctype": "Party Specific Item",
+   "link_fieldname": "party"
   }
  ],
- "modified": "2021-08-27 18:02:44.314077",
+ "modified": "2021-09-06 17:37:56.522233",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier",
diff --git a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.json b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.json
deleted file mode 100644
index 1971458..0000000
--- a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.json
+++ /dev/null
@@ -1,77 +0,0 @@
-{
- "actions": [],
- "creation": "2021-05-07 18:16:40.621421",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "supplier",
-  "item_group"
- ],
- "fields": [
-  {
-   "fieldname": "supplier",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Supplier",
-   "options": "Supplier",
-   "reqd": 1
-  },
-  {
-   "fieldname": "item_group",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Item Group",
-   "options": "Item Group",
-   "reqd": 1
-  }
- ],
- "index_web_pages_for_search": 1,
- "links": [],
- "modified": "2021-05-19 13:48:16.742303",
- "modified_by": "Administrator",
- "module": "Buying",
- "name": "Supplier Item Group",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "System Manager",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Purchase User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Purchase Manager",
-   "share": 1,
-   "write": 1
-  }
- ],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py
deleted file mode 100644
index 6d71f7d..0000000
--- a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-
-class SupplierItemGroup(Document):
-	def validate(self):
-		exists = frappe.db.exists({
-			'doctype': 'Supplier Item Group',
-			'supplier': self.supplier,
-			'item_group': self.item_group
-		})
-		if exists:
-			frappe.throw(_("Item Group has already been linked to this supplier."))
diff --git a/erpnext/buying/doctype/supplier_item_group/test_supplier_item_group.py b/erpnext/buying/doctype/supplier_item_group/test_supplier_item_group.py
deleted file mode 100644
index 55ba85e..0000000
--- a/erpnext/buying/doctype/supplier_item_group/test_supplier_item_group.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-# import frappe
-import unittest
-
-
-class TestSupplierItemGroup(unittest.TestCase):
-	pass
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index ccd417b..9f28646 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -7,6 +7,7 @@
 from collections import defaultdict
 
 import frappe
+from frappe import scrub
 from frappe.desk.reportview import get_filters_cond, get_match_cond
 from frappe.utils import nowdate, unique
 
@@ -223,18 +224,29 @@
 		if not field in searchfields]
 	searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
 
-	if filters and isinstance(filters, dict) and filters.get('supplier'):
-		item_group_list = frappe.get_all('Supplier Item Group',
-			filters = {'supplier': filters.get('supplier')}, fields = ['item_group'])
+	if filters and isinstance(filters, dict):
+		if filters.get('customer') or filters.get('supplier'):
+			party = filters.get('customer') or filters.get('supplier')
+			item_rules_list = frappe.get_all('Party Specific Item',
+				filters = {'party': party}, fields = ['restrict_based_on', 'based_on_value'])
 
-		item_groups = []
-		for i in item_group_list:
-			item_groups.append(i.item_group)
+			filters_dict = {}
+			for rule in item_rules_list:
+				if rule['restrict_based_on'] == 'Item':
+					rule['restrict_based_on'] = 'name'
+				filters_dict[rule.restrict_based_on] = []
 
-		del filters['supplier']
+			for rule in item_rules_list:
+				filters_dict[rule.restrict_based_on].append(rule.based_on_value)
 
-		if item_groups:
-			filters['item_group'] = ['in', item_groups]
+			for filter in filters_dict:
+				filters[scrub(filter)] = ['in', filters_dict[filter]]
+
+			if filters.get('customer'):
+				del filters['customer']
+			else:
+				del filters['supplier']
+
 
 	description_cond = ''
 	if frappe.db.count('Item', cache=True) < 50000:
diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py
index ff2ed45..8e5952c 100644
--- a/erpnext/controllers/website_list_for_contact.py
+++ b/erpnext/controllers/website_list_for_contact.py
@@ -7,6 +7,7 @@
 
 import frappe
 from frappe import _
+from frappe.modules.utils import get_module_app
 from frappe.utils import flt, has_common
 from frappe.utils.user import is_website_user
 
@@ -21,8 +22,32 @@
 		"get_list": get_transaction_list
 	}
 
+def get_webform_list_context(module):
+	if get_module_app(module) != 'erpnext':
+		return
+	return {
+		"get_list": get_webform_transaction_list
+	}
 
-def get_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified"):
+def get_webform_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified"):
+	""" Get List of transactions for custom doctypes """
+	from frappe.www.list import get_list
+
+	if not filters:
+		filters = []
+
+	meta = frappe.get_meta(doctype)
+
+	for d in meta.fields:
+		if d.fieldtype == 'Link' and d.fieldname != 'amended_from':
+			allowed_docs = [d.name for d in get_transaction_list(doctype=d.options, custom=True)]
+			allowed_docs.append('')
+			filters.append((d.fieldname, 'in', allowed_docs))
+
+	return get_list(doctype, txt, filters, limit_start, limit_page_length, ignore_permissions=False,
+		fields=None, order_by="modified")
+
+def get_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified", custom=False):
 	user = frappe.session.user
 	ignore_permissions = False
 
@@ -46,7 +71,7 @@
 				filters.append(('customer', 'in', customers))
 		elif suppliers:
 			filters.append(('supplier', 'in', suppliers))
-		else:
+		elif not custom:
 			return []
 
 		if doctype == 'Request for Quotation':
@@ -56,9 +81,16 @@
 		# Since customers and supplier do not have direct access to internal doctypes
 		ignore_permissions = True
 
+		if not customers and not suppliers and custom:
+			ignore_permissions = False
+			filters = []
+
 	transactions = get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length,
 		fields='name', ignore_permissions=ignore_permissions, order_by='modified desc')
 
+	if custom:
+		return transactions
+
 	return post_process(doctype, transactions)
 
 def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length=20,
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 5b6e1ee..be05e35 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -62,6 +62,7 @@
 # website
 update_website_context = ["erpnext.shopping_cart.utils.update_website_context", "erpnext.education.doctype.education_settings.education_settings.update_website_context"]
 my_account_context = "erpnext.shopping_cart.utils.update_my_account_context"
+webform_list_context = "erpnext.controllers.website_list_for_contact.get_webform_list_context"
 
 calendars = ["Task", "Work Order", "Leave Application", "Sales Order", "Holiday List", "Course Schedule"]
 
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js
index fa4b06a..7d1c7cb 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.js
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.js
@@ -73,7 +73,7 @@
 					frm.trigger('make_return_entry');
 				}, __('Create'));
 			} else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")) {
-				frm.add_custom_button(__("Deduction from salary"), function() {
+				frm.add_custom_button(__("Deduction from Salary"), function() {
 					frm.events.make_deduction_via_additional_salary(frm);
 				}, __('Create'));
 			}
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json
index ea25aa7..0475453 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.json
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.json
@@ -170,7 +170,7 @@
    "default": "0",
    "fieldname": "repay_unclaimed_amount_from_salary",
    "fieldtype": "Check",
-   "label": "Repay unclaimed amount from salary"
+   "label": "Repay Unclaimed Amount from Salary"
   },
   {
    "depends_on": "eval:cur_frm.doc.employee",
@@ -200,10 +200,11 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 22:31:53.746659",
+ "modified": "2021-09-11 18:38:38.617478",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee Advance",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py
index 87d42d3..8d90bcc 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.py
@@ -172,7 +172,10 @@
 @frappe.whitelist()
 def create_return_through_additional_salary(doc):
 	import json
-	doc = frappe._dict(json.loads(doc))
+
+	if isinstance(doc, str):
+		doc = frappe._dict(json.loads(doc))
+
 	additional_salary = frappe.new_doc('Additional Salary')
 	additional_salary.employee = doc.employee
 	additional_salary.currency = doc.currency
diff --git a/erpnext/hr/doctype/employee_advance/test_employee_advance.py b/erpnext/hr/doctype/employee_advance/test_employee_advance.py
index f8e5f53..c439d45 100644
--- a/erpnext/hr/doctype/employee_advance/test_employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/test_employee_advance.py
@@ -12,8 +12,11 @@
 from erpnext.hr.doctype.employee.test_employee import make_employee
 from erpnext.hr.doctype.employee_advance.employee_advance import (
 	EmployeeAdvanceOverPayment,
+	create_return_through_additional_salary,
 	make_bank_entry,
 )
+from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component
+from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
 
 
 class TestEmployeeAdvance(unittest.TestCase):
@@ -33,6 +36,46 @@
 		journal_entry1 = make_payment_entry(advance)
 		self.assertRaises(EmployeeAdvanceOverPayment, journal_entry1.submit)
 
+	def test_repay_unclaimed_amount_from_salary(self):
+		employee_name = make_employee("_T@employe.advance")
+		advance = make_employee_advance(employee_name, {"repay_unclaimed_amount_from_salary": 1})
+
+		args = {"type": "Deduction"}
+		create_salary_component("Advance Salary - Deduction", **args)
+		make_salary_structure("Test Additional Salary for Advance Return", "Monthly", employee=employee_name)
+
+		# additional salary for 700 first
+		advance.reload()
+		additional_salary = create_return_through_additional_salary(advance)
+		additional_salary.salary_component = "Advance Salary - Deduction"
+		additional_salary.payroll_date = nowdate()
+		additional_salary.amount = 700
+		additional_salary.insert()
+		additional_salary.submit()
+
+		advance.reload()
+		self.assertEqual(advance.return_amount, 700)
+
+		# additional salary for remaining 300
+		additional_salary = create_return_through_additional_salary(advance)
+		additional_salary.salary_component = "Advance Salary - Deduction"
+		additional_salary.payroll_date = nowdate()
+		additional_salary.amount = 300
+		additional_salary.insert()
+		additional_salary.submit()
+
+		advance.reload()
+		self.assertEqual(advance.return_amount, 1000)
+
+		# update advance return amount on additional salary cancellation
+		additional_salary.cancel()
+		advance.reload()
+		self.assertEqual(advance.return_amount, 700)
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+
 def make_payment_entry(advance):
 	journal_entry = frappe.get_doc(make_bank_entry("Employee Advance", advance.name))
 	journal_entry.cheque_no = "123123"
@@ -41,7 +84,7 @@
 
 	return journal_entry
 
-def make_employee_advance(employee_name):
+def make_employee_advance(employee_name, args=None):
 	doc = frappe.new_doc("Employee Advance")
 	doc.employee = employee_name
 	doc.company  = "_Test company"
@@ -51,6 +94,10 @@
 	doc.advance_amount = 1000
 	doc.posting_date = nowdate()
 	doc.advance_account = "_Test Employee Advance - _TC"
+
+	if args:
+		doc.update(args)
+
 	doc.insert()
 	doc.submit()
 
diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py
index deec644..b6f4cad 100644
--- a/erpnext/hr/utils.py
+++ b/erpnext/hr/utils.py
@@ -32,7 +32,10 @@
 def update_employee(employee, details, date=None, cancel=False):
 	internal_work_history = {}
 	for item in details:
-		fieldtype = frappe.get_meta("Employee").get_field(item.fieldname).fieldtype
+		field = frappe.get_meta("Employee").get_field(item.fieldname)
+		if not field:
+			continue
+		fieldtype = field.fieldtype
 		new_data = item.new if not cancel else item.current
 		if fieldtype == "Date" and new_data:
 			new_data = getdate(new_data)
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 15b196f..3bc40a1 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -304,5 +304,6 @@
 erpnext.patches.v13_0.validate_options_for_data_field
 erpnext.patches.v13_0.create_gst_payment_entry_fields
 erpnext.patches.v14_0.delete_shopify_doctypes
+erpnext.patches.v13_0.replace_supplier_item_group_with_party_specific_item
 erpnext.patches.v13_0.update_dates_in_tax_withholding_category
 erpnext.patches.v14_0.update_opportunity_currency_fields
diff --git a/erpnext/patches/v13_0/replace_supplier_item_group_with_party_specific_item.py b/erpnext/patches/v13_0/replace_supplier_item_group_with_party_specific_item.py
new file mode 100644
index 0000000..ba96fdd
--- /dev/null
+++ b/erpnext/patches/v13_0/replace_supplier_item_group_with_party_specific_item.py
@@ -0,0 +1,17 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+
+
+def execute():
+	if frappe.db.table_exists('Supplier Item Group'):
+		frappe.reload_doc("selling", "doctype", "party_specific_item")
+		sig = frappe.db.get_all("Supplier Item Group", fields=["name", "supplier", "item_group"])
+		for item in sig:
+			psi = frappe.new_doc("Party Specific Item")
+			psi.party_type = "Supplier"
+			psi.party = item.supplier
+			psi.restrict_based_on = "Item Group"
+			psi.based_on_value = item.item_group
+			psi.insert()
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py
index ed10f2b..7c0a8ea 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py
@@ -14,12 +14,11 @@
 
 class AdditionalSalary(Document):
 	def on_submit(self):
-		if self.ref_doctype == "Employee Advance" and self.ref_docname:
-			frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", self.amount)
-
+		self.update_return_amount_in_employee_advance()
 		self.update_employee_referral()
 
 	def on_cancel(self):
+		self.update_return_amount_in_employee_advance()
 		self.update_employee_referral(cancel=True)
 
 	def validate(self):
@@ -98,6 +97,17 @@
 				frappe.throw(_("Additional Salary for referral bonus can only be created against Employee Referral with status {0}").format(
 					frappe.bold("Accepted")))
 
+	def update_return_amount_in_employee_advance(self):
+		if self.ref_doctype == "Employee Advance" and self.ref_docname:
+			return_amount = frappe.db.get_value("Employee Advance", self.ref_docname, "return_amount")
+
+			if self.docstatus == 2:
+				return_amount -= self.amount
+			else:
+				return_amount += self.amount
+
+			frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", return_amount)
+
 	def update_employee_referral(self, cancel=False):
 		if self.ref_doctype == "Employee Referral":
 			status = "Unpaid" if cancel else "Paid"
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index 8c48345..d113e7e 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -487,7 +487,7 @@
 			self.calculate_component_amounts("deductions")
 
 		self.set_loan_repayment()
-		self.set_component_amounts_based_on_payment_days()
+		self.set_precision_for_component_amounts()
 		self.set_net_pay()
 
 	def set_net_pay(self):
@@ -709,6 +709,17 @@
 
 		component_row.amount = amount
 
+		self.update_component_amount_based_on_payment_days(component_row)
+
+	def update_component_amount_based_on_payment_days(self, component_row):
+		joining_date, relieving_date = self.get_joining_and_relieving_dates()
+		component_row.amount = self.get_amount_based_on_payment_days(component_row, joining_date, relieving_date)[0]
+
+	def set_precision_for_component_amounts(self):
+		for component_type in ("earnings", "deductions"):
+			for component_row in self.get(component_type):
+				component_row.amount = flt(component_row.amount, component_row.precision("amount"))
+
 	def calculate_variable_based_on_taxable_salary(self, tax_component, payroll_period):
 		if not payroll_period:
 			frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}.")
@@ -866,14 +877,7 @@
 		return total_tax_paid
 
 	def get_taxable_earnings(self, allow_tax_exemption=False, based_on_payment_days=0):
-		joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
-			["date_of_joining", "relieving_date"])
-
-		if not relieving_date:
-			relieving_date = getdate(self.end_date)
-
-		if not joining_date:
-			frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
+		joining_date, relieving_date = self.get_joining_and_relieving_dates()
 
 		taxable_earnings = 0
 		additional_income = 0
@@ -884,7 +888,10 @@
 			if based_on_payment_days:
 				amount, additional_amount = self.get_amount_based_on_payment_days(earning, joining_date, relieving_date)
 			else:
-				amount, additional_amount = earning.amount, earning.additional_amount
+				if earning.additional_amount:
+					amount, additional_amount = earning.amount, earning.additional_amount
+				else:
+					amount, additional_amount = earning.default_amount, earning.additional_amount
 
 			if earning.is_tax_applicable:
 				if additional_amount:
@@ -1055,7 +1062,7 @@
 				total += amount
 		return total
 
-	def set_component_amounts_based_on_payment_days(self):
+	def get_joining_and_relieving_dates(self):
 		joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
 			["date_of_joining", "relieving_date"])
 
@@ -1065,9 +1072,7 @@
 		if not joining_date:
 			frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
 
-		for component_type in ("earnings", "deductions"):
-			for d in self.get(component_type):
-				d.amount = flt(self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0], d.precision("amount"))
+		return joining_date, relieving_date
 
 	def set_loan_repayment(self):
 		self.total_loan_repayment = 0
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index 480daa2..bff36a4 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -17,6 +17,7 @@
 	getdate,
 	nowdate,
 )
+from frappe.utils.make_random import get_random
 
 import erpnext
 from erpnext.accounts.utils import get_fiscal_year
@@ -134,6 +135,65 @@
 
 		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
 
+	def test_component_amount_dependent_on_another_payment_days_based_component(self):
+		from erpnext.hr.doctype.attendance.attendance import mark_attendance
+		from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
+			create_salary_structure_assignment,
+		)
+
+		no_of_days = self.get_no_of_days()
+		# Payroll based on attendance
+		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance")
+
+		salary_structure = make_salary_structure_for_payment_days_based_component_dependency()
+		employee = make_employee("test_payment_days_based_component@salary.com", company="_Test Company")
+
+		# base = 50000
+		create_salary_structure_assignment(employee, salary_structure.name, company="_Test Company", currency="INR")
+
+		# mark employee absent for a day since this case works fine if payment days are equal to working days
+		month_start_date = get_first_day(nowdate())
+		month_end_date = get_last_day(nowdate())
+
+		first_sunday = frappe.db.sql("""
+			select holiday_date from `tabHoliday`
+			where parent = 'Salary Slip Test Holiday List'
+				and holiday_date between %s and %s
+			order by holiday_date
+		""", (month_start_date, month_end_date))[0][0]
+
+		mark_attendance(employee, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # counted as absent
+
+		# make salary slip and assert payment days
+		ss = make_salary_slip_for_payment_days_dependency_test("test_payment_days_based_component@salary.com", salary_structure.name)
+		self.assertEqual(ss.absent_days, 1)
+
+		days_in_month = no_of_days[0]
+		no_of_holidays = no_of_days[1]
+
+		self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 1)
+
+		ss.reload()
+		payment_days_based_comp_amount = 0
+		for component in ss.earnings:
+			if component.salary_component == "HRA - Payment Days":
+				payment_days_based_comp_amount = flt(component.amount, component.precision("amount"))
+				break
+
+		# check if the dependent component is calculated using the amount updated after payment days
+		actual_amount = 0
+		precision = 0
+		for component in ss.deductions:
+			if component.salary_component == "P - Employee Provident Fund":
+				precision = component.precision("amount")
+				actual_amount = flt(component.amount, precision)
+				break
+
+		expected_amount = flt((flt(ss.gross_pay) - payment_days_based_comp_amount) * 0.12, precision)
+
+		self.assertEqual(actual_amount, expected_amount)
+		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
+
 	def test_salary_slip_with_holidays_included(self):
 		no_of_days = self.get_no_of_days()
 		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1)
@@ -864,3 +924,91 @@
 		holiday_list = holiday_list.name
 
 	return holiday_list
+
+def make_salary_structure_for_payment_days_based_component_dependency():
+	earnings = [
+		{
+			"salary_component": "Basic Salary - Payment Days",
+			"abbr": "P_BS",
+			"type": "Earning",
+			"formula": "base",
+			"amount_based_on_formula": 1
+		},
+		{
+			"salary_component": "HRA - Payment Days",
+			"abbr": "P_HRA",
+			"type": "Earning",
+			"depends_on_payment_days": 1,
+			"amount_based_on_formula": 1,
+			"formula": "base * 0.20"
+		}
+	]
+
+	make_salary_component(earnings, False, company_list=["_Test Company"])
+
+	deductions = [
+		{
+			"salary_component": "P - Professional Tax",
+			"abbr": "P_PT",
+			"type": "Deduction",
+			"depends_on_payment_days": 1,
+			"amount": 200.00
+		},
+		{
+			"salary_component": "P - Employee Provident Fund",
+			"abbr": "P_EPF",
+			"type": "Deduction",
+			"exempted_from_income_tax": 1,
+			"amount_based_on_formula": 1,
+			"depends_on_payment_days": 0,
+			"formula": "(gross_pay - P_HRA) * 0.12"
+		}
+	]
+
+	make_salary_component(deductions, False, company_list=["_Test Company"])
+
+	salary_structure = "Salary Structure with PF"
+	if frappe.db.exists("Salary Structure", salary_structure):
+		frappe.db.delete("Salary Structure", salary_structure)
+
+	details = {
+		"doctype": "Salary Structure",
+		"name": salary_structure,
+		"company": "_Test Company",
+		"payroll_frequency": "Monthly",
+		"payment_account": get_random("Account", filters={"account_currency": "INR"}),
+		"currency": "INR"
+	}
+
+	salary_structure_doc = frappe.get_doc(details)
+
+	for entry in earnings:
+		salary_structure_doc.append("earnings", entry)
+
+	for entry in deductions:
+		salary_structure_doc.append("deductions", entry)
+
+	salary_structure_doc.insert()
+	salary_structure_doc.submit()
+
+	return salary_structure_doc
+
+def make_salary_slip_for_payment_days_dependency_test(employee, salary_structure):
+	employee = frappe.db.get_value("Employee", {
+			"user_id": employee
+		},
+		["name", "company", "employee_name"],
+		as_dict=True)
+
+	salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": employee})})
+
+	if not salary_slip_name:
+		salary_slip = make_salary_slip(salary_structure, employee=employee.name)
+		salary_slip.employee_name = employee.employee_name
+		salary_slip.payroll_frequency = "Monthly"
+		salary_slip.posting_date = nowdate()
+		salary_slip.insert()
+	else:
+		salary_slip = frappe.get_doc("Salary Slip", salary_slip_name)
+
+	return salary_slip
\ No newline at end of file
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index e0610eb..2a74d60 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -709,6 +709,9 @@
 			setters: opts.setters,
 			get_query: opts.get_query,
 			add_filters_group: 1,
+			allow_child_item_selection: opts.allow_child_item_selection,
+			child_fieldname: opts.child_fielname,
+			child_columns: opts.child_columns,
 			action: function(selections, args) {
 				let values = selections;
 				if(values.length === 0){
@@ -716,7 +719,7 @@
 					return;
 				}
 				opts.source_name = values;
-				opts.setters = args;
+				opts.args = args;
 				d.dialog.hide();
 				_map();
 			},
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 5913b84..e811435 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -20,7 +20,6 @@
   "tax_withholding_category",
   "default_bank_account",
   "lead_name",
-  "prospect",
   "opportunity_name",
   "image",
   "column_break0",
@@ -214,7 +213,8 @@
    "fieldtype": "Link",
    "ignore_user_permissions": 1,
    "label": "Represents Company",
-   "options": "Company"
+   "options": "Company",
+   "unique": 1
   },
   {
    "depends_on": "represents_company",
@@ -498,14 +498,6 @@
    "options": "Tax Withholding Category"
   },
   {
-   "fieldname": "prospect",
-   "fieldtype": "Link",
-   "label": "Prospect",
-   "no_copy": 1,
-   "options": "Prospect",
-   "print_hide": 1
-  },
-  {
    "fieldname": "opportunity_name",
    "fieldtype": "Link",
    "label": "From Opportunity",
@@ -518,8 +510,14 @@
  "idx": 363,
  "image_field": "image",
  "index_web_pages_for_search": 1,
- "links": [],
- "modified": "2021-08-25 18:56:09.929905",
+ "links": [
+  {
+   "group": "Allowed Items",
+   "link_doctype": "Party Specific Item",
+   "link_fieldname": "party"
+  }
+ ],
+ "modified": "2021-09-06 17:38:54.196663",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Customer",
diff --git a/erpnext/buying/doctype/supplier_item_group/__init__.py b/erpnext/selling/doctype/party_specific_item/__init__.py
similarity index 100%
rename from erpnext/buying/doctype/supplier_item_group/__init__.py
rename to erpnext/selling/doctype/party_specific_item/__init__.py
diff --git a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.js b/erpnext/selling/doctype/party_specific_item/party_specific_item.js
similarity index 78%
rename from erpnext/buying/doctype/supplier_item_group/supplier_item_group.js
rename to erpnext/selling/doctype/party_specific_item/party_specific_item.js
index f7da90d..077b936 100644
--- a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.js
+++ b/erpnext/selling/doctype/party_specific_item/party_specific_item.js
@@ -1,7 +1,7 @@
 // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
-frappe.ui.form.on('Supplier Item Group', {
+frappe.ui.form.on('Party Specific Item', {
 	// refresh: function(frm) {
 
 	// }
diff --git a/erpnext/selling/doctype/party_specific_item/party_specific_item.json b/erpnext/selling/doctype/party_specific_item/party_specific_item.json
new file mode 100644
index 0000000..32b5d47
--- /dev/null
+++ b/erpnext/selling/doctype/party_specific_item/party_specific_item.json
@@ -0,0 +1,77 @@
+{
+ "actions": [],
+ "creation": "2021-08-27 19:28:07.559978",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "party_type",
+  "party",
+  "column_break_3",
+  "restrict_based_on",
+  "based_on_value"
+ ],
+ "fields": [
+  {
+   "fieldname": "party_type",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Party Type",
+   "options": "Customer\nSupplier",
+   "reqd": 1
+  },
+  {
+   "fieldname": "party",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "label": "Party Name",
+   "options": "party_type",
+   "reqd": 1
+  },
+  {
+   "fieldname": "restrict_based_on",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Restrict Items Based On",
+   "options": "Item\nItem Group\nBrand",
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "based_on_value",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "label": "Based On Value",
+   "options": "restrict_based_on",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-09-14 13:27:58.612334",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Party Specific Item",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "party",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/selling/doctype/party_specific_item/party_specific_item.py b/erpnext/selling/doctype/party_specific_item/party_specific_item.py
new file mode 100644
index 0000000..a408af5
--- /dev/null
+++ b/erpnext/selling/doctype/party_specific_item/party_specific_item.py
@@ -0,0 +1,19 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+
+
+class PartySpecificItem(Document):
+	def validate(self):
+		exists = frappe.db.exists({
+			'doctype': 'Party Specific Item',
+			'party_type': self.party_type,
+			'party': self.party,
+			'restrict_based_on': self.restrict_based_on,
+			'based_on': self.based_on_value,
+		})
+		if exists:
+			frappe.throw(_("This item filter has already been applied for the {0}").format(self.party_type))
diff --git a/erpnext/selling/doctype/party_specific_item/test_party_specific_item.py b/erpnext/selling/doctype/party_specific_item/test_party_specific_item.py
new file mode 100644
index 0000000..874a364
--- /dev/null
+++ b/erpnext/selling/doctype/party_specific_item/test_party_specific_item.py
@@ -0,0 +1,38 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import unittest
+
+import frappe
+
+from erpnext.controllers.queries import item_query
+
+test_dependencies = ['Item', 'Customer', 'Supplier']
+
+def create_party_specific_item(**args):
+	psi = frappe.new_doc("Party Specific Item")
+	psi.party_type = args.get('party_type')
+	psi.party = args.get('party')
+	psi.restrict_based_on = args.get('restrict_based_on')
+	psi.based_on_value = args.get('based_on_value')
+	psi.insert()
+
+class TestPartySpecificItem(unittest.TestCase):
+	def setUp(self):
+		self.customer = frappe.get_last_doc("Customer")
+		self.supplier = frappe.get_last_doc("Supplier")
+		self.item = frappe.get_last_doc("Item")
+
+	def test_item_query_for_customer(self):
+		create_party_specific_item(party_type='Customer', party=self.customer.name, restrict_based_on='Item', based_on_value=self.item.name)
+		filters = {'is_sales_item': 1, 'customer': self.customer.name}
+		items = item_query(doctype= 'Item', txt= '', searchfield= 'name', start= 0, page_len= 20,filters=filters, as_dict= False)
+		for item in items:
+			self.assertEqual(item[0], self.item.name)
+
+	def test_item_query_for_supplier(self):
+		create_party_specific_item(party_type='Supplier', party=self.supplier.name, restrict_based_on='Item Group', based_on_value=self.item.item_group)
+		filters = {'supplier': self.supplier.name, 'is_purchase_item': 1}
+		items = item_query(doctype= 'Item', txt= '', searchfield= 'name', start= 0, page_len= 20,filters=filters, as_dict= False)
+		for item in items:
+			self.assertEqual(item[2], self.item.item_group)
diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
index 805c3d8..5c4d8b6 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
@@ -73,7 +73,7 @@
 			`tabSales Order` so,
 			`tabSales Order Item` soi
 		LEFT JOIN `tabSales Invoice Item` sii
-			ON sii.so_detail = soi.name
+			ON sii.so_detail = soi.name and sii.docstatus = 1
 		WHERE
 			soi.parent = so.name
 			and so.status not in ('Stopped', 'Closed', 'On Hold')
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index 955ef5e..ddd4c4e 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -63,7 +63,7 @@
 			this.frm.set_query("item_code", "items", function() {
 				return {
 					query: "erpnext.controllers.queries.item_query",
-					filters: {'is_sales_item': 1}
+					filters: {'is_sales_item': 1, 'customer': cur_frm.doc.customer}
 				}
 			});
 		}
@@ -247,7 +247,12 @@
 		var editable_price_list_rate = cint(frappe.defaults.get_default("editable_price_list_rate"));
 
 		if(df && editable_price_list_rate) {
-			df.read_only = 0;
+			const parent_field = frappe.meta.get_parentfield(this.frm.doc.doctype, this.frm.doc.doctype + " Item");
+			if (!this.frm.fields_dict[parent_field]) return;
+
+			this.frm.fields_dict[parent_field].grid.update_docfield_property(
+				'price_list_rate', 'read_only', 0
+			);
 		}
 	}
 
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index ddf3e66..b26c6a4 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -99,7 +99,7 @@
 		filter_engine = ProductFiltersBuilder(self.name)
 
 		context.field_filters = filter_engine.get_field_filters()
-		context.attribute_filters = filter_engine.get_attribute_fitlers()
+		context.attribute_filters = filter_engine.get_attribute_filters()
 
 		context.update({
 			"parents": get_parent_item_groups(self.parent_item_group),
diff --git a/erpnext/shopping_cart/filters.py b/erpnext/shopping_cart/filters.py
index aaeff0f..4787ae5 100644
--- a/erpnext/shopping_cart/filters.py
+++ b/erpnext/shopping_cart/filters.py
@@ -4,7 +4,6 @@
 from __future__ import unicode_literals
 
 import frappe
-from frappe import _dict
 
 
 class ProductFiltersBuilder:
@@ -57,37 +56,31 @@
 
 		return filter_data
 
-	def get_attribute_fitlers(self):
+	def get_attribute_filters(self):
 		attributes = [row.attribute for row in self.doc.filter_attributes]
-		attribute_docs = [
-			frappe.get_doc('Item Attribute', attribute) for attribute in attributes
-		]
 
-		valid_attributes = []
+		if not attributes:
+			return []
 
-		for attr_doc in attribute_docs:
-			selected_attributes = []
-			for attr in attr_doc.item_attribute_values:
-				or_filters = []
-				filters= [
-					["Item Variant Attribute", "attribute", "=", attr.parent],
-					["Item Variant Attribute", "attribute_value", "=", attr.attribute_value]
-				]
-				if self.item_group:
-					or_filters.extend([
-						["item_group", "=", self.item_group],
-						["Website Item Group", "item_group", "=", self.item_group]
-					])
+		result = frappe.db.sql(
+			"""
+			select
+				distinct attribute, attribute_value
+			from
+				`tabItem Variant Attribute`
+			where
+				attribute in %(attributes)s
+				and attribute_value is not null
+		""",
+			{"attributes": attributes},
+			as_dict=1,
+		)
 
-				if frappe.db.get_all("Item", filters, or_filters=or_filters, limit=1):
-					selected_attributes.append(attr)
+		attribute_value_map = {}
+		for d in result:
+			attribute_value_map.setdefault(d.attribute, []).append(d.attribute_value)
 
-			if selected_attributes:
-				valid_attributes.append(
-					_dict(
-						item_attribute_values=selected_attributes,
-						name=attr_doc.name
-					)
-				)
-
-		return valid_attributes
+		out = []
+		for name, values in attribute_value_map.items():
+			out.append(frappe._dict(name=name, item_attribute_values=values))
+		return out
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 9eb4721..2569c04 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -6,10 +6,13 @@
 
 from __future__ import unicode_literals
 
+import json
+
 import frappe
 from frappe import _, msgprint
 from frappe.model.mapper import get_mapped_doc
 from frappe.utils import cstr, flt, get_link_to_form, getdate, new_line_sep, nowdate
+from six import string_types
 
 from erpnext.buying.utils import check_on_hold_or_closed_status, validate_for_items
 from erpnext.controllers.buying_controller import BuyingController
@@ -269,7 +272,10 @@
 	material_request.update_status(status)
 
 @frappe.whitelist()
-def make_purchase_order(source_name, target_doc=None):
+def make_purchase_order(source_name, target_doc=None, args={}):
+
+	if isinstance(args, string_types):
+		args = json.loads(args)
 
 	def postprocess(source, target_doc):
 		if frappe.flags.args and frappe.flags.args.default_supplier:
@@ -284,7 +290,10 @@
 		set_missing_values(source, target_doc)
 
 	def select_item(d):
-		return d.ordered_qty < d.stock_qty
+		filtered_items = args.get('filtered_children', [])
+		child_filter = d.name in filtered_items if filtered_items else True
+
+		return d.ordered_qty < d.stock_qty and child_filter
 
 	doclist = get_mapped_doc("Material Request", source_name, 	{
 		"Material Request": {
diff --git a/erpnext/tests/test_webform.py b/erpnext/tests/test_webform.py
new file mode 100644
index 0000000..19255db
--- /dev/null
+++ b/erpnext/tests/test_webform.py
@@ -0,0 +1,138 @@
+import unittest
+
+import frappe
+
+from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+
+
+class TestWebsite(unittest.TestCase):
+	def test_permission_for_custom_doctype(self):
+		create_user('Supplier 1', 'supplier1@gmail.com')
+		create_user('Supplier 2', 'supplier2@gmail.com')
+		create_supplier_with_contact('Supplier1', 'All Supplier Groups', 'Supplier 1', 'supplier1@gmail.com')
+		create_supplier_with_contact('Supplier2', 'All Supplier Groups', 'Supplier 2', 'supplier2@gmail.com')
+		po1 = create_purchase_order(supplier='Supplier1')
+		po2 = create_purchase_order(supplier='Supplier2')
+
+		create_custom_doctype()
+		create_webform()
+		create_order_assignment(supplier='Supplier1', po = po1.name)
+		create_order_assignment(supplier='Supplier2', po = po2.name)
+
+		frappe.set_user("Administrator")
+		# checking if data consist of all order assignment of Supplier1 and Supplier2
+		self.assertTrue('Supplier1' and 'Supplier2' in [data.supplier for data in get_data()])
+
+		frappe.set_user("supplier1@gmail.com")
+		# checking if data only consist of order assignment of Supplier1
+		self.assertTrue('Supplier1' in [data.supplier for data in get_data()])
+		self.assertFalse([data.supplier for data in get_data() if data.supplier != 'Supplier1'])
+
+		frappe.set_user("supplier2@gmail.com")
+		# checking if data only consist of order assignment of Supplier2
+		self.assertTrue('Supplier2' in [data.supplier for data in get_data()])
+		self.assertFalse([data.supplier for data in get_data() if data.supplier != 'Supplier2'])
+
+		frappe.set_user("Administrator")
+
+def get_data():
+	webform_list_contexts = frappe.get_hooks('webform_list_context')
+	if webform_list_contexts:
+		context = frappe._dict(frappe.get_attr(webform_list_contexts[0])('Buying') or {})
+	kwargs = dict(doctype='Order Assignment', order_by = 'modified desc')
+	return context.get_list(**kwargs)
+
+def create_user(name, email):
+	frappe.get_doc({
+		'doctype': 'User',
+		'send_welcome_email': 0,
+		'user_type': 'Website User',
+		'first_name': name,
+		'email': email,
+		'roles': [{"doctype": "Has Role", "role": "Supplier"}]
+	}).insert(ignore_if_duplicate = True)
+
+def create_supplier_with_contact(name, group, contact_name, contact_email):
+	supplier = frappe.get_doc({
+		'doctype': 'Supplier',
+		'supplier_name': name,
+		'supplier_group': group
+	}).insert(ignore_if_duplicate = True)
+
+	if not frappe.db.exists('Contact', contact_name+'-1-'+name):
+		new_contact = frappe.new_doc("Contact")
+		new_contact.first_name = contact_name
+		new_contact.is_primary_contact = True,
+		new_contact.append('links', {
+			"link_doctype": "Supplier",
+			"link_name": supplier.name
+		})
+		new_contact.append('email_ids', {
+			"email_id": contact_email,
+			"is_primary": 1
+		})
+
+		new_contact.insert(ignore_mandatory=True)
+
+def create_custom_doctype():
+	frappe.get_doc({
+		'doctype': 'DocType',
+		'name': 'Order Assignment',
+		'module': 'Buying',
+		'custom': 1,
+		'autoname': 'field:po',
+		'fields': [
+			{'label': 'PO', 'fieldname': 'po', 'fieldtype': 'Link', 'options': 'Purchase Order'},
+			{'label': 'Supplier', 'fieldname': 'supplier', 'fieldtype': 'Data', "fetch_from": "po.supplier"}
+		],
+		'permissions': [
+			{
+				"create": 1,
+				"delete": 1,
+				"email": 1,
+				"export": 1,
+				"print": 1,
+				"read": 1,
+				"report": 1,
+				"role": "System Manager",
+				"share": 1,
+				"write": 1
+			},
+			{
+				"read": 1,
+				"role": "Supplier"
+			}
+		]
+	}).insert(ignore_if_duplicate = True)
+
+def create_webform():
+	frappe.get_doc({
+		'doctype': 'Web Form',
+		'module': 'Buying',
+		'title': 'SO Schedule',
+		'route': 'so-schedule',
+		'doc_type': 'Order Assignment',
+		'web_form_fields': [
+			{
+				'doctype': 'Web Form Field',
+				'fieldname': 'po',
+				'fieldtype': 'Link',
+				'options': 'Purchase Order',
+				'label': 'PO'
+			},
+			{
+				'doctype': 'Web Form Field',
+				'fieldname': 'supplier',
+				'fieldtype': 'Data',
+				'label': 'Supplier'
+			}
+		]
+
+	}).insert(ignore_if_duplicate = True)
+
+def create_order_assignment(supplier, po):
+	frappe.get_doc({
+		'doctype': 'Order Assignment',
+		'po': po,
+		'supplier': supplier,
+	}).insert(ignore_if_duplicate = True)
\ No newline at end of file
diff --git a/erpnext/www/all-products/index.html b/erpnext/www/all-products/index.html
index 7c18ecc..a7838ee 100644
--- a/erpnext/www/all-products/index.html
+++ b/erpnext/www/all-products/index.html
@@ -98,14 +98,14 @@
 					<div class="filter-options">
 						{% for attr_value in attribute.item_attribute_values %}
 						<div class="checkbox">
-							<label data-value="{{ value }}">
+							<label>
 								<input type="checkbox"
 									class="product-filter attribute-filter"
-									id="{{attr_value.name}}"
+									id="{{attr_value}}"
 									data-attribute-name="{{ attribute.name }}"
-									data-attribute-value="{{ attr_value.attribute_value }}"
+									data-attribute-value="{{ attr_value }}"
 									{% if attr_value.checked %} checked {% endif %}>
-									<span class="label-area">{{ attr_value.attribute_value }}</span>
+									<span class="label-area">{{ attr_value }}</span>
 							</label>
 						</div>
 						{% endfor %}
diff --git a/erpnext/www/all-products/index.py b/erpnext/www/all-products/index.py
index 335c104..df5258b 100644
--- a/erpnext/www/all-products/index.py
+++ b/erpnext/www/all-products/index.py
@@ -27,7 +27,7 @@
 	filter_engine = ProductFiltersBuilder()
 
 	context.field_filters = filter_engine.get_field_filters()
-	context.attribute_filters = filter_engine.get_attribute_fitlers()
+	context.attribute_filters = filter_engine.get_attribute_filters()
 
 	context.product_settings = product_settings
 	context.body_class = "product-page"