Merge pull request #27281 from DeeMysterio/party-specific-items
feat: link items to supplier / customer
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/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/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/sales_common.js b/erpnext/selling/sales_common.js
index 6a09109..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}
}
});
}