From 3a5f5d5cd069d908fb584b8c2e3dfea362f048c3 Mon Sep 17 00:00:00 2001 From: Mostafa Fekry Date: Thu, 10 Mar 2022 16:05:08 +0200 Subject: [PATCH 1/9] fix: e_commerce_settings.js Update get meta from Item to Website Item To allow Website Item Fields to display meta fields --- .../doctype/e_commerce_settings/e_commerce_settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js index 6302d260e0a..1084a75630e 100644 --- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js +++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js @@ -24,7 +24,7 @@ frappe.ui.form.on("E Commerce Settings", { ); } - frappe.model.with_doctype("Item", () => { + frappe.model.with_doctype("Website Item", () => { const web_item_meta = frappe.get_meta('Website Item'); const valid_fields = web_item_meta.fields.filter( From 86c5f4db85ae2c8273574e67b97b6ccd7b564351 Mon Sep 17 00:00:00 2001 From: Mostafa Fekry Date: Thu, 10 Mar 2022 16:10:39 +0200 Subject: [PATCH 2/9] fix: e_commerce_settings.py Update get meta from Item to Website Item doctype to check validate_field_filters --- .../doctype/e_commerce_settings/e_commerce_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py index 1110eb1accd..584dca170f4 100644 --- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py +++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py @@ -35,7 +35,7 @@ class ECommerceSettings(Document): if not (self.enable_field_filters and self.filter_fields): return - item_meta = frappe.get_meta("Item") + item_meta = frappe.get_meta("Website Item") valid_fields = [df.fieldname for df in item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"]] for f in self.filter_fields: From bed9e09153e7869021e2dcb0949b6e48b65928f9 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 18 Apr 2022 18:01:48 +0530 Subject: [PATCH 3/9] fix: Query filter fields from Website Item instead of Item master - tweak `filters.py` to correctly query filter field values from Website Item - Use Website Item for filter field options in Settings and Item Group Field Filter table --- .../e_commerce_settings/e_commerce_settings.js | 4 ++-- .../e_commerce/product_data_engine/filters.py | 16 +++++++++++----- erpnext/setup/doctype/item_group/item_group.js | 10 +++++----- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js index 1084a75630e..a8966b07a79 100644 --- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js +++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js @@ -31,10 +31,10 @@ frappe.ui.form.on("E Commerce Settings", { df => ["Link", "Table MultiSelect"].includes(df.fieldtype) && !df.hidden ).map(df => ({ label: df.label, value: df.fieldname })); - frm.fields_dict.filter_fields.grid.update_docfield_property( + frm.get_field("filter_fields").grid.update_docfield_property( 'fieldname', 'fieldtype', 'Select' ); - frm.fields_dict.filter_fields.grid.update_docfield_property( + frm.get_field("filter_fields").grid.update_docfield_property( 'fieldname', 'options', valid_fields ); }); diff --git a/erpnext/e_commerce/product_data_engine/filters.py b/erpnext/e_commerce/product_data_engine/filters.py index 3ff1ab726c1..73d51f6281c 100644 --- a/erpnext/e_commerce/product_data_engine/filters.py +++ b/erpnext/e_commerce/product_data_engine/filters.py @@ -22,12 +22,14 @@ class ProductFiltersBuilder: fields, filter_data = [], [] filter_fields = [row.fieldname for row in self.doc.filter_fields] # fields in settings - # filter valid field filters i.e. those that exist in Item - item_meta = frappe.get_meta("Item", cached=True) - fields = [item_meta.get_field(field) for field in filter_fields if item_meta.has_field(field)] + # filter valid field filters i.e. those that exist in Website Item + web_item_meta = frappe.get_meta("Website Item", cached=True) + fields = [ + web_item_meta.get_field(field) for field in filter_fields if web_item_meta.has_field(field) + ] for df in fields: - item_filters, item_or_filters = {"published_in_website": 1}, [] + item_filters, item_or_filters = {"published": 1}, [] link_doctype_values = self.get_filtered_link_doctype_records(df) if df.fieldtype == "Link": @@ -50,9 +52,13 @@ class ProductFiltersBuilder: ] ) + # exclude variants if mentioned in settings + if frappe.db.get_single_value("E Commerce Settings", "hide_variants"): + item_filters["variant_of"] = ["is", "not set"] + # Get link field values attached to published items item_values = frappe.get_all( - "Item", + "Website Item", fields=[df.fieldname], filters=item_filters, or_filters=item_or_filters, diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js index f570c2faec6..cf96dc1a7d6 100644 --- a/erpnext/setup/doctype/item_group/item_group.js +++ b/erpnext/setup/doctype/item_group/item_group.js @@ -72,17 +72,17 @@ frappe.ui.form.on("Item Group", { }); } - frappe.model.with_doctype('Item', () => { - const item_meta = frappe.get_meta('Item'); + frappe.model.with_doctype('Website Item', () => { + const web_item_meta = frappe.get_meta('Website Item'); - const valid_fields = item_meta.fields.filter( + const valid_fields = web_item_meta.fields.filter( df => ['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden ).map(df => ({ label: df.label, value: df.fieldname })); - frm.fields_dict.filter_fields.grid.update_docfield_property( + frm.get_field("filter_fields").grid.update_docfield_property( 'fieldname', 'fieldtype', 'Select' ); - frm.fields_dict.filter_fields.grid.update_docfield_property( + frm.get_field("filter_fields").grid.update_docfield_property( 'fieldname', 'options', valid_fields ); }); From 34437a83df710e77090a21115fcea9c88fea5bc8 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 18 Apr 2022 18:50:58 +0530 Subject: [PATCH 4/9] fix: Validate field filter wrt to Website Item & re-use validation in Item Group --- .../e_commerce_settings.py | 19 ++++++++++--------- .../setup/doctype/item_group/item_group.py | 2 ++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py index a0003436efc..bd7ac9cdb7a 100644 --- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py +++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py @@ -26,7 +26,7 @@ class ECommerceSettings(Document): self.is_redisearch_loaded = is_search_module_loaded() def validate(self): - self.validate_field_filters() + self.validate_field_filters(self.filter_fields, self.enable_field_filters) self.validate_attribute_filters() self.validate_checkout() self.validate_search_index_fields() @@ -50,21 +50,22 @@ class ECommerceSettings(Document): define_autocomplete_dictionary() create_website_items_index() - def validate_field_filters(self): - if not (self.enable_field_filters and self.filter_fields): + @staticmethod + def validate_field_filters(filter_fields, enable_field_filters): + if not (enable_field_filters and filter_fields): return - item_meta = frappe.get_meta("Website Item") + web_item_meta = frappe.get_meta("Website Item") valid_fields = [ - df.fieldname for df in item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"] + df.fieldname for df in web_item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"] ] - for f in self.filter_fields: - if f.fieldname not in valid_fields: + for row in filter_fields: + if row.fieldname not in valid_fields: frappe.throw( _( - "Filter Fields Row #{0}: Fieldname {1} must be of type 'Link' or 'Table MultiSelect'" - ).format(f.idx, f.fieldname) + "Filter Fields Row #{0}: Fieldname {1} must be of type 'Link' or 'Table MultiSelect'" + ).format(row.idx, frappe.bold(row.fieldname)) ) def validate_attribute_filters(self): diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 890b18c37a9..769b2d88085 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -12,6 +12,7 @@ from frappe.website.render import clear_cache from frappe.website.website_generator import WebsiteGenerator from six.moves.urllib.parse import quote +from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import ECommerceSettings from erpnext.e_commerce.product_data_engine.filters import ProductFiltersBuilder @@ -36,6 +37,7 @@ class ItemGroup(NestedSet, WebsiteGenerator): self.make_route() self.validate_item_group_defaults() + ECommerceSettings.validate_field_filters(self.filter_fields, enable_field_filters=True) def on_update(self): NestedSet.on_update(self) From 9506dbe43385f7e80b9d772a29de304814c9b51b Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 18 Apr 2022 21:38:22 +0530 Subject: [PATCH 5/9] chore: Patch to copy custom fields (field filters) from Item to Website Item --- erpnext/patches.txt | 1 + ...py_custom_field_filters_to_website_item.py | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b2d0871a17c..5d836fc2d72 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -359,3 +359,4 @@ erpnext.patches.v13_0.enable_ksa_vat_docs #1 erpnext.patches.v13_0.create_gst_custom_fields_in_quotation erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances erpnext.patches.v13_0.set_return_against_in_pos_invoice_references +erpnext.patches.v13_0.copy_custom_field_filters_to_website_item diff --git a/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py b/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py new file mode 100644 index 00000000000..5f2125144fe --- /dev/null +++ b/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py @@ -0,0 +1,54 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_field + + +def execute(): + "Add Field Filters, that are not standard fields in Website Item, as Custom Fields." + settings = frappe.get_doc("E Commerce Settings") + + if not (settings.filter_fields or settings.field_filters): + return + + item_meta = frappe.get_meta("Item") + valid_item_fields = [ + df.fieldname for df in item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"] + ] + + web_item_meta = frappe.get_meta("Website Item") + valid_web_item_fields = [ + df.fieldname for df in web_item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"] + ] + + for row in settings.filter_fields: + # skip if illegal field + if row.fieldname not in valid_item_fields: + continue + + # if Item field is not in Website Item, add it as a custom field + if row.fieldname not in valid_web_item_fields: + df = item_meta.get_field(row.fieldname) + create_custom_field( + "Website Item", + dict( + owner="Administrator", + fieldname=df.fieldname, + label=df.label, + fieldtype=df.fieldtype, + options=df.options, + description=df.description, + read_only=df.read_only, + no_copy=df.no_copy, + insert_after="on_backorder", + ), + ) + + # map field values + frappe.db.sql( + """ + UPDATE `tabWebsite Item` wi, `tabItem` i + SET wi.{0} = i.{0} + WHERE wi.item_code = i.item_code + """.format( + row.fieldname + ) + ) From e76220e819bceed03f8fc0a5618f583a8c172faf Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 20 Apr 2022 12:27:04 +0530 Subject: [PATCH 6/9] fix: Mistyped variable name in patch --- .../patches/v13_0/copy_custom_field_filters_to_website_item.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py b/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py index 5f2125144fe..3e7e52a401d 100644 --- a/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py +++ b/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py @@ -6,7 +6,7 @@ def execute(): "Add Field Filters, that are not standard fields in Website Item, as Custom Fields." settings = frappe.get_doc("E Commerce Settings") - if not (settings.filter_fields or settings.field_filters): + if not (settings.enable_field_filters or settings.filter_fields): return item_meta = frappe.get_meta("Item") From dc2f6945475e53bb4dfb20edd1d4cf6c2c940671 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 20 Apr 2022 17:34:51 +0530 Subject: [PATCH 7/9] fix: Handle Multiselect field mapping separately - Map Multiselect child table to Website Item (copy rows) --- erpnext/patches.txt | 2 +- ...py_custom_field_filters_to_website_item.py | 56 ++++++++++++++++--- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 5d836fc2d72..841c59b78a2 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -359,4 +359,4 @@ erpnext.patches.v13_0.enable_ksa_vat_docs #1 erpnext.patches.v13_0.create_gst_custom_fields_in_quotation erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances erpnext.patches.v13_0.set_return_against_in_pos_invoice_references -erpnext.patches.v13_0.copy_custom_field_filters_to_website_item +erpnext.patches.v13_0.copy_custom_field_filters_to_website_item \ No newline at end of file diff --git a/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py b/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py index 3e7e52a401d..e8d0b593e6f 100644 --- a/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py +++ b/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py @@ -4,6 +4,43 @@ from frappe.custom.doctype.custom_field.custom_field import create_custom_field def execute(): "Add Field Filters, that are not standard fields in Website Item, as Custom Fields." + + def move_table_multiselect_data(docfield): + "Copy child table data (Table Multiselect) from Item to Website Item for a docfield." + table_multiselect_data = get_table_multiselect_data(docfield) + field = docfield.fieldname + + for row in table_multiselect_data: + # add copied multiselect data rows in Website Item + web_item = frappe.db.get_value("Website Item", {"item_code": row.parent}) + web_item_doc = frappe.get_doc("Website Item", web_item) + + child_doc = frappe.new_doc(docfield.options, web_item_doc, field) + + for field in ["name", "creation", "modified", "idx"]: + row[field] = None + + child_doc.update(row) + + child_doc.parenttype = "Website Item" + child_doc.parent = web_item + + child_doc.insert() + + def get_table_multiselect_data(docfield): + child_table = frappe.qb.DocType(docfield.options) + item = frappe.qb.DocType("Item") + + table_multiselect_data = ( # query table data for field + frappe.qb.from_(child_table) + .join(item) + .on(item.item_code == child_table.parent) + .select(child_table.star) + .where((child_table.parentfield == docfield.fieldname) & (item.published_in_website == 1)) + ).run(as_dict=True) + + return table_multiselect_data + settings = frappe.get_doc("E Commerce Settings") if not (settings.enable_field_filters or settings.filter_fields): @@ -43,12 +80,15 @@ def execute(): ) # map field values - frappe.db.sql( - """ - UPDATE `tabWebsite Item` wi, `tabItem` i - SET wi.{0} = i.{0} - WHERE wi.item_code = i.item_code - """.format( - row.fieldname + if df.fieldtype == "Table MultiSelect": + move_table_multiselect_data(df) + else: + frappe.db.sql( # nosemgrep + """ + UPDATE `tabWebsite Item` wi, `tabItem` i + SET wi.{0} = i.{0} + WHERE wi.item_code = i.item_code + """.format( + row.fieldname + ) ) - ) From ba635145da5e4069da564764f8c853ed2622d402 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 20 Apr 2022 18:50:47 +0530 Subject: [PATCH 8/9] test: Field filter validation and Custom field as field filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Test to block Item fields (which aren’t in Website Item) in E Commerce Settings as filters - Removed unnecessary function and setup in E Commerce Settings test - Removed commented useless test - Test to check custom field as filter --- .../test_e_commerce_settings.py | 44 +++++++---------- .../test_product_data_engine.py | 48 +++++++++++++++++++ 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py b/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py index b159135012e..2c3428190e8 100644 --- a/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py +++ b/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt import unittest @@ -11,42 +11,32 @@ from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import ( class TestECommerceSettings(unittest.TestCase): - def setUp(self): - frappe.db.sql("""delete from `tabSingles` where doctype="Shipping Cart Settings" """) - - def get_cart_settings(self): - return frappe.get_doc({"doctype": "E Commerce Settings", "company": "_Test Company"}) - - # NOTE: Exchangrate API has all enabled currencies that ERPNext supports. - # We aren't checking just currency exchange record anymore - # while validating price list currency exchange rate to that of company. - # The API is being used to fetch the rate which again almost always - # gives back a valid value (for valid currencies). - # This makes the test obsolete. - # Commenting because im not sure if there's a better test we can write - - # def test_exchange_rate_exists(self): - # frappe.db.sql("""delete from `tabCurrency Exchange`""") - - # cart_settings = self.get_cart_settings() - # cart_settings.price_list = "_Test Price List Rest of the World" - # self.assertRaises(ShoppingCartSetupError, cart_settings.validate_price_list_exchange_rate) - - # from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records as \ - # currency_exchange_records - # frappe.get_doc(currency_exchange_records[0]).insert() - # cart_settings.validate_price_list_exchange_rate() + def tearDown(self): + frappe.db.rollback() def test_tax_rule_validation(self): frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0") - cart_settings = self.get_cart_settings() + cart_settings = frappe.get_doc("E Commerce Settings") cart_settings.enabled = 1 if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1}, "name"): self.assertRaises(ShoppingCartSetupError, cart_settings.validate_tax_rule) frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 1") + def test_invalid_filter_fields(self): + "Check if Item fields are blocked in E Commerce Settings filter fields." + from frappe.custom.doctype.custom_field.custom_field import create_custom_field + + create_custom_field( + "Item", + dict(owner="Administrator", fieldname="test_data", label="Test", fieldtype="Data"), + ) + settings = frappe.get_doc("E Commerce Settings") + settings.append("filter_fields", {"fieldname": "test_data"}) + + self.assertRaises(frappe.ValidationError, settings.save) + def setup_e_commerce_settings(values_dict): "Accepts a dict of values that updates E Commerce Settings." diff --git a/erpnext/e_commerce/product_data_engine/test_product_data_engine.py b/erpnext/e_commerce/product_data_engine/test_product_data_engine.py index ab958d14866..c3b6ed5da25 100644 --- a/erpnext/e_commerce/product_data_engine/test_product_data_engine.py +++ b/erpnext/e_commerce/product_data_engine/test_product_data_engine.py @@ -277,6 +277,54 @@ class TestProductDataEngine(unittest.TestCase): # tear down setup_e_commerce_settings({"enable_attribute_filters": 1, "hide_variants": 0}) + def test_custom_field_as_filter(self): + "Test if custom field functions as filter correctly." + from frappe.custom.doctype.custom_field.custom_field import create_custom_field + + create_custom_field( + "Website Item", + dict( + owner="Administrator", + fieldname="supplier", + label="Supplier", + fieldtype="Link", + options="Supplier", + insert_after="on_backorder", + ), + ) + + frappe.db.set_value( + "Website Item", {"item_code": "Test 11I Laptop"}, "supplier", "_Test Supplier" + ) + frappe.db.set_value( + "Website Item", {"item_code": "Test 12I Laptop"}, "supplier", "_Test Supplier 1" + ) + + settings = frappe.get_doc("E Commerce Settings") + settings.append("filter_fields", {"fieldname": "supplier"}) + settings.save() + + filter_engine = ProductFiltersBuilder() + field_filters = filter_engine.get_field_filters() + custom_filter = field_filters[1] + filter_values = custom_filter[1] + + self.assertEqual(custom_filter[0].options, "Supplier") + self.assertEqual(len(filter_values), 2) + self.assertIn("_Test Supplier", filter_values) + + # test if custom filter works in query + field_filters = {"supplier": "_Test Supplier 1"} + engine = ProductQuery() + result = engine.query( + attributes={}, fields=field_filters, search_term=None, start=0, item_group=None + ) + items = result.get("items") + + # check if only 'Raw Material' are fetched in the right order + self.assertEqual(len(items), 1) + self.assertEqual(items[0].get("item_code"), "Test 12I Laptop") + def create_variant_web_item(): "Create Variant and Template Website Items." From f58d3b4d3dc5de06ea85a24e5ebe8f778a192195 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 Apr 2022 12:29:30 +0530 Subject: [PATCH 9/9] test: setup e commerce settings before running invalid filtrs test --- .../doctype/e_commerce_settings/test_e_commerce_settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py b/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py index 2c3428190e8..9372f80252f 100644 --- a/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py +++ b/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py @@ -28,6 +28,8 @@ class TestECommerceSettings(unittest.TestCase): "Check if Item fields are blocked in E Commerce Settings filter fields." from frappe.custom.doctype.custom_field.custom_field import create_custom_field + setup_e_commerce_settings({"enable_field_filters": 1}) + create_custom_field( "Item", dict(owner="Administrator", fieldname="test_data", label="Test", fieldtype="Data"),