diff --git a/erpnext/edi/__init__.py b/erpnext/edi/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/edi/doctype/__init__.py b/erpnext/edi/doctype/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/edi/doctype/code_list/__init__.py b/erpnext/edi/doctype/code_list/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/edi/doctype/code_list/code_list.js b/erpnext/edi/doctype/code_list/code_list.js new file mode 100644 index 00000000000..f8b9a2003fd --- /dev/null +++ b/erpnext/edi/doctype/code_list/code_list.js @@ -0,0 +1,51 @@ +// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Code List", { + refresh: (frm) => { + if (!frm.doc.__islocal) { + frm.add_custom_button(__("Import Genericode File"), function () { + erpnext.edi.import_genericode(frm); + }); + } + }, + setup: (frm) => { + frm.savetrash = () => { + frm.validate_form_action("Delete"); + frappe.confirm( + __( + "Are you sure you want to delete {0}?
This action will also delete all associated Common Code documents.
", + [frm.docname.bold()] + ), + function () { + return frappe.call({ + method: "frappe.client.delete", + args: { + doctype: frm.doctype, + name: frm.docname, + }, + freeze: true, + freeze_message: __("Deleting {0} and all associated Common Code documents...", [ + frm.docname, + ]), + callback: function (r) { + if (!r.exc) { + frappe.utils.play_sound("delete"); + frappe.model.clear_doc(frm.doctype, frm.docname); + window.history.back(); + } + }, + }); + } + ); + }; + + frm.set_query("default_common_code", function (doc) { + return { + filters: { + code_list: doc.name, + }, + }; + }); + }, +}); diff --git a/erpnext/edi/doctype/code_list/code_list.json b/erpnext/edi/doctype/code_list/code_list.json new file mode 100644 index 00000000000..ffcc2f2b605 --- /dev/null +++ b/erpnext/edi/doctype/code_list/code_list.json @@ -0,0 +1,112 @@ +{ + "actions": [], + "allow_copy": 1, + "allow_rename": 1, + "autoname": "prompt", + "creation": "2024-09-29 06:55:03.920375", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "title", + "canonical_uri", + "url", + "default_common_code", + "column_break_nkls", + "version", + "publisher", + "publisher_id", + "section_break_npxp", + "description" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title" + }, + { + "fieldname": "publisher", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "Publisher" + }, + { + "columns": 1, + "fieldname": "version", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Version" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description" + }, + { + "fieldname": "canonical_uri", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Canonical URI" + }, + { + "fieldname": "column_break_nkls", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_npxp", + "fieldtype": "Section Break" + }, + { + "fieldname": "publisher_id", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "Publisher ID" + }, + { + "fieldname": "url", + "fieldtype": "Data", + "label": "URL", + "options": "URL" + }, + { + "description": "This value shall be used when no matching Common Code for a record is found.", + "fieldname": "default_common_code", + "fieldtype": "Link", + "label": "Default Common Code", + "options": "Common Code" + } + ], + "index_web_pages_for_search": 1, + "links": [ + { + "link_doctype": "Common Code", + "link_fieldname": "code_list" + } + ], + "modified": "2024-11-16 17:01:40.260293", + "modified_by": "Administrator", + "module": "EDI", + "name": "Code List", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "search_fields": "description", + "show_title_field_in_link": 1, + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "title_field": "title" +} \ No newline at end of file diff --git a/erpnext/edi/doctype/code_list/code_list.py b/erpnext/edi/doctype/code_list/code_list.py new file mode 100644 index 00000000000..8957c6565b9 --- /dev/null +++ b/erpnext/edi/doctype/code_list/code_list.py @@ -0,0 +1,125 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from typing import TYPE_CHECKING + +import frappe +from frappe.model.document import Document + +if TYPE_CHECKING: + from lxml.etree import Element + + +class CodeList(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + canonical_uri: DF.Data | None + default_common_code: DF.Link | None + description: DF.SmallText | None + publisher: DF.Data | None + publisher_id: DF.Data | None + title: DF.Data | None + url: DF.Data | None + version: DF.Data | None + # end: auto-generated types + + def on_trash(self): + if not frappe.flags.in_bulk_delete: + self.__delete_linked_docs() + + def __delete_linked_docs(self): + self.db_set("default_common_code", None) + + linked_docs = frappe.get_all( + "Common Code", + filters={"code_list": self.name}, + fields=["name"], + ) + + for doc in linked_docs: + frappe.delete_doc("Common Code", doc.name) + + def get_codes_for(self, doctype: str, name: str) -> tuple[str]: + """Get the applicable codes for a doctype and name""" + return get_codes_for(self.name, doctype, name) + + def get_docnames_for(self, doctype: str, code: str) -> tuple[str]: + """Get the mapped docnames for a doctype and code""" + return get_docnames_for(self.name, doctype, code) + + def get_default_code(self) -> str | None: + """Get the default common code for this code list""" + return ( + frappe.db.get_value("Common Code", self.default_common_code, "common_code") + if self.default_common_code + else None + ) + + def from_genericode(self, root: "Element"): + """Extract Code List details from genericode XML""" + self.title = root.find(".//Identification/ShortName").text + self.version = root.find(".//Identification/Version").text + self.canonical_uri = root.find(".//CanonicalUri").text + # optionals + self.description = getattr(root.find(".//Identification/LongName"), "text", None) + self.publisher = getattr(root.find(".//Identification/Agency/ShortName"), "text", None) + if not self.publisher: + self.publisher = getattr(root.find(".//Identification/Agency/LongName"), "text", None) + self.publisher_id = getattr(root.find(".//Identification/Agency/Identifier"), "text", None) + self.url = getattr(root.find(".//Identification/LocationUri"), "text", None) + + +def get_codes_for(code_list: str, doctype: str, name: str) -> tuple[str]: + """Return the common code for a given record""" + CommonCode = frappe.qb.DocType("Common Code") + DynamicLink = frappe.qb.DocType("Dynamic Link") + + codes = ( + frappe.qb.from_(CommonCode) + .join(DynamicLink) + .on((CommonCode.name == DynamicLink.parent) & (DynamicLink.parenttype == "Common Code")) + .select(CommonCode.common_code) + .where( + (DynamicLink.link_doctype == doctype) + & (DynamicLink.link_name == name) + & (CommonCode.code_list == code_list) + ) + .distinct() + .orderby(CommonCode.common_code) + ).run() + + return tuple(c[0] for c in codes) if codes else () + + +def get_docnames_for(code_list: str, doctype: str, code: str) -> tuple[str]: + """Return the record name for a given common code""" + CommonCode = frappe.qb.DocType("Common Code") + DynamicLink = frappe.qb.DocType("Dynamic Link") + + docnames = ( + frappe.qb.from_(CommonCode) + .join(DynamicLink) + .on((CommonCode.name == DynamicLink.parent) & (DynamicLink.parenttype == "Common Code")) + .select(DynamicLink.link_name) + .where( + (DynamicLink.link_doctype == doctype) + & (CommonCode.common_code == code) + & (CommonCode.code_list == code_list) + ) + .distinct() + .orderby(DynamicLink.idx) + ).run() + + return tuple(d[0] for d in docnames) if docnames else () + + +def get_default_code(code_list: str) -> str | None: + """Return the default common code for a given code list""" + code_id = frappe.db.get_value("Code List", code_list, "default_common_code") + return frappe.db.get_value("Common Code", code_id, "common_code") if code_id else None diff --git a/erpnext/edi/doctype/code_list/code_list_import.js b/erpnext/edi/doctype/code_list/code_list_import.js new file mode 100644 index 00000000000..4a33f3e2fe6 --- /dev/null +++ b/erpnext/edi/doctype/code_list/code_list_import.js @@ -0,0 +1,218 @@ +frappe.provide("erpnext.edi"); + +erpnext.edi.import_genericode = function (listview_or_form) { + let doctype = "Code List"; + let docname = undefined; + if (listview_or_form.doc !== undefined) { + docname = listview_or_form.doc.name; + } + new frappe.ui.FileUploader({ + method: "erpnext.edi.doctype.code_list.code_list_import.import_genericode", + doctype: doctype, + docname: docname, + allow_toggle_private: false, + allow_take_photo: false, + on_success: function (_file_doc, r) { + listview_or_form.refresh(); + show_column_selection_dialog(r.message); + }, + }); +}; + +function show_column_selection_dialog(context) { + let title_description = __("If there is no title column, use the code column for the title."); + let default_title = get_default(context.columns, ["name", "Name", "code-name", "scheme-name"]); + let fields = [ + { + fieldtype: "HTML", + fieldname: "code_list_info", + options: `| ${__("Title")} | `; + if (code_column) html += `${__("Code")} | `; + if (description_column) html += `${__("Description")} | `; + + // Add headers for filterable columns + for (let column in context.filterable_columns) { + if (dialog.get_value(`filter_${column}`)) { + html += `${__(column)} | `; + } + } + + html += "
|---|---|---|---|
| ${truncate(title)} | `; + } + if (code_column) { + let code = context.example_values[code_column][i] || ""; + html += `${truncate(code)} | `; + } + if (description_column) { + let description = context.example_values[description_column][i] || ""; + html += `${truncate(description)} | `; + } + + // Add values for filterable columns + for (let column in context.filterable_columns) { + if (dialog.get_value(`filter_${column}`)) { + let value = context.example_values[column][i] || ""; + html += `${truncate(value)} | `; + } + } + + html += "
{e!s}", title=_("Fetching Error"))
+
+ if file_url := frappe.local.uploaded_file_url:
+ file_path = frappe.utils.file_manager.get_file_path(file_url)
+ with open(file_path.encode(), mode="rb") as f:
+ content = f.read()
+
+ # Parse the xml content
+ parser = etree.XMLParser(remove_blank_text=True)
+ try:
+ root = etree.fromstring(content, parser=parser)
+ except Exception as e:
+ frappe.throw(f"{e!s}", title=_("Parsing Error"))
+
+ # Extract the name (CanonicalVersionUri) from the parsed XML
+ name = root.find(".//CanonicalVersionUri").text
+ docname = docname or name
+
+ if frappe.db.exists(doctype, docname):
+ code_list = frappe.get_doc(doctype, docname)
+ if code_list.name != name:
+ frappe.throw(_("The uploaded file does not match the selected Code List."))
+ else:
+ # Create a new Code List document with the extracted name
+ code_list = frappe.new_doc(doctype)
+ code_list.name = name
+
+ code_list.from_genericode(root)
+ code_list.save()
+
+ # Attach the file and provide a recoverable identifier
+ file_doc = frappe.get_doc(
+ {
+ "doctype": "File",
+ "attached_to_doctype": "Code List",
+ "attached_to_name": code_list.name,
+ "folder": "Home/Attachments",
+ "file_name": frappe.local.uploaded_filename,
+ "file_url": frappe.local.uploaded_file_url,
+ "is_private": 1,
+ "content": content,
+ }
+ ).save()
+
+ # Get available columns and example values
+ columns, example_values, filterable_columns = get_genericode_columns_and_examples(root)
+
+ return {
+ "code_list": code_list.name,
+ "code_list_title": code_list.title,
+ "file": file_doc.name,
+ "columns": columns,
+ "example_values": example_values,
+ "filterable_columns": filterable_columns,
+ }
+
+
+@frappe.whitelist()
+def process_genericode_import(
+ code_list_name: str,
+ file_name: str,
+ code_column: str,
+ title_column: str | None = None,
+ description_column: str | None = None,
+ filters: str | None = None,
+):
+ from erpnext.edi.doctype.common_code.common_code import import_genericode
+
+ column_map = {"code": code_column, "title": title_column, "description": description_column}
+
+ return import_genericode(code_list_name, file_name, column_map, json.loads(filters) if filters else None)
+
+
+def get_genericode_columns_and_examples(root):
+ columns = []
+ example_values = {}
+ filterable_columns = {}
+
+ # Get column names
+ for column in root.findall(".//Column"):
+ column_id = column.get("Id")
+ columns.append(column_id)
+ example_values[column_id] = []
+ filterable_columns[column_id] = set()
+
+ # Get all values and count unique occurrences
+ for row in root.findall(".//SimpleCodeList/Row"):
+ for value in row.findall("Value"):
+ column_id = value.get("ColumnRef")
+ if column_id not in columns:
+ # Handle undeclared column
+ columns.append(column_id)
+ example_values[column_id] = []
+ filterable_columns[column_id] = set()
+
+ simple_value = value.find("./SimpleValue")
+ if simple_value is None:
+ continue
+
+ filterable_columns[column_id].add(simple_value.text)
+
+ # Get example values (up to 3) and filter columns with cardinality <= 5
+ for row in root.findall(".//SimpleCodeList/Row")[:3]:
+ for value in row.findall("Value"):
+ column_id = value.get("ColumnRef")
+ simple_value = value.find("./SimpleValue")
+ if simple_value is None:
+ continue
+
+ example_values[column_id].append(simple_value.text)
+
+ filterable_columns = {k: list(v) for k, v in filterable_columns.items() if len(v) <= 5}
+
+ return columns, example_values, filterable_columns
diff --git a/erpnext/edi/doctype/code_list/code_list_list.js b/erpnext/edi/doctype/code_list/code_list_list.js
new file mode 100644
index 00000000000..08125de2903
--- /dev/null
+++ b/erpnext/edi/doctype/code_list/code_list_list.js
@@ -0,0 +1,8 @@
+frappe.listview_settings["Code List"] = {
+ onload: function (listview) {
+ listview.page.add_inner_button(__("Import Genericode File"), function () {
+ erpnext.edi.import_genericode(listview);
+ });
+ },
+ hide_name_column: true,
+};
diff --git a/erpnext/edi/doctype/code_list/test_code_list.py b/erpnext/edi/doctype/code_list/test_code_list.py
new file mode 100644
index 00000000000..d37b1ee8f5a
--- /dev/null
+++ b/erpnext/edi/doctype/code_list/test_code_list.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestCodeList(FrappeTestCase):
+ pass
diff --git a/erpnext/edi/doctype/common_code/__init__.py b/erpnext/edi/doctype/common_code/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/edi/doctype/common_code/common_code.js b/erpnext/edi/doctype/common_code/common_code.js
new file mode 100644
index 00000000000..646d5c85b74
--- /dev/null
+++ b/erpnext/edi/doctype/common_code/common_code.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+// frappe.ui.form.on("Common Code", {
+// refresh(frm) {
+
+// },
+// });
diff --git a/erpnext/edi/doctype/common_code/common_code.json b/erpnext/edi/doctype/common_code/common_code.json
new file mode 100644
index 00000000000..b2cb43fa575
--- /dev/null
+++ b/erpnext/edi/doctype/common_code/common_code.json
@@ -0,0 +1,103 @@
+{
+ "actions": [],
+ "autoname": "hash",
+ "creation": "2024-09-29 07:01:18.133067",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "code_list",
+ "title",
+ "common_code",
+ "description",
+ "column_break_wxsw",
+ "additional_data",
+ "section_break_rhgh",
+ "applies_to"
+ ],
+ "fields": [
+ {
+ "fieldname": "code_list",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Code List",
+ "options": "Code List",
+ "reqd": 1,
+ "search_index": 1
+ },
+ {
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Title",
+ "length": 300,
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_wxsw",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_rhgh",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "applies_to",
+ "fieldtype": "Table",
+ "label": "Applies To",
+ "options": "Dynamic Link"
+ },
+ {
+ "fieldname": "common_code",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Common Code",
+ "length": 300,
+ "reqd": 1,
+ "search_index": 1
+ },
+ {
+ "fieldname": "additional_data",
+ "fieldtype": "Code",
+ "label": "Additional Data",
+ "max_height": "190px",
+ "read_only": 1
+ },
+ {
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "in_list_view": 1,
+ "label": "Description",
+ "max_height": "60px"
+ }
+ ],
+ "links": [],
+ "modified": "2024-11-06 07:46:17.175687",
+ "modified_by": "Administrator",
+ "module": "EDI",
+ "name": "Common Code",
+ "naming_rule": "Random",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "search_fields": "common_code,description",
+ "show_title_field_in_link": 1,
+ "sort_field": "creation",
+ "sort_order": "DESC",
+ "states": [],
+ "title_field": "title"
+}
\ No newline at end of file
diff --git a/erpnext/edi/doctype/common_code/common_code.py b/erpnext/edi/doctype/common_code/common_code.py
new file mode 100644
index 00000000000..d558b2d282f
--- /dev/null
+++ b/erpnext/edi/doctype/common_code/common_code.py
@@ -0,0 +1,114 @@
+# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import hashlib
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils.data import get_link_to_form
+from lxml import etree
+
+
+class CommonCode(Document):
+ # begin: auto-generated types
+ # This code is auto-generated. Do not modify anything in this block.
+
+ from typing import TYPE_CHECKING
+
+ if TYPE_CHECKING:
+ from frappe.core.doctype.dynamic_link.dynamic_link import DynamicLink
+ from frappe.types import DF
+
+ additional_data: DF.Code | None
+ applies_to: DF.Table[DynamicLink]
+ code_list: DF.Link
+ common_code: DF.Data
+ description: DF.SmallText | None
+ title: DF.Data
+ # end: auto-generated types
+
+ def validate(self):
+ self.validate_distinct_references()
+
+ def validate_distinct_references(self):
+ """Ensure no two Common Codes of the same Code List are linked to the same document."""
+ for link in self.applies_to:
+ existing_links = frappe.get_all(
+ "Common Code",
+ filters=[
+ ["name", "!=", self.name],
+ ["code_list", "=", self.code_list],
+ ["Dynamic Link", "link_doctype", "=", link.link_doctype],
+ ["Dynamic Link", "link_name", "=", link.link_name],
+ ],
+ fields=["name", "common_code"],
+ )
+
+ if existing_links:
+ existing_link = existing_links[0]
+ frappe.throw(
+ _("{0} {1} is already linked to Common Code {2}.").format(
+ link.link_doctype,
+ link.link_name,
+ get_link_to_form("Common Code", existing_link["name"], existing_link["common_code"]),
+ )
+ )
+
+ def from_genericode(self, column_map: dict, xml_element: "etree.Element"):
+ """Populate the Common Code document from a genericode XML element
+
+ Args:
+ column_map (dict): A mapping of column names to XML column references. Keys: code, title, description
+ code (etree.Element): The XML element representing a code in the genericode file
+ """
+ title_column = column_map.get("title")
+ code_column = column_map["code"]
+ description_column = column_map.get("description")
+
+ self.common_code = xml_element.find(f"./Value[@ColumnRef='{code_column}']/SimpleValue").text
+
+ if title_column:
+ simple_value_title = xml_element.find(f"./Value[@ColumnRef='{title_column}']/SimpleValue")
+ self.title = simple_value_title.text if simple_value_title is not None else self.common_code
+
+ if description_column:
+ simple_value_descr = xml_element.find(f"./Value[@ColumnRef='{description_column}']/SimpleValue")
+ self.description = simple_value_descr.text if simple_value_descr is not None else None
+
+ self.additional_data = etree.tostring(xml_element, encoding="unicode", pretty_print=True)
+
+
+def simple_hash(input_string, length=6):
+ return hashlib.blake2b(input_string.encode(), digest_size=length // 2).hexdigest()
+
+
+def import_genericode(code_list: str, file_name: str, column_map: dict, filters: dict | None = None):
+ """Import genericode file and create Common Code entries"""
+ file_path = frappe.utils.file_manager.get_file_path(file_name)
+ parser = etree.XMLParser(remove_blank_text=True)
+ tree = etree.parse(file_path, parser=parser)
+ root = tree.getroot()
+
+ # Construct the XPath expression
+ xpath_expr = ".//SimpleCodeList/Row"
+ filter_conditions = [
+ f"Value[@ColumnRef='{column_ref}']/SimpleValue='{value}'" for column_ref, value in filters.items()
+ ]
+ if filter_conditions:
+ xpath_expr += "[" + " and ".join(filter_conditions) + "]"
+
+ elements = root.xpath(xpath_expr)
+ total_elements = len(elements)
+ for i, xml_element in enumerate(elements, start=1):
+ common_code: "CommonCode" = frappe.new_doc("Common Code")
+ common_code.code_list = code_list
+ common_code.from_genericode(column_map, xml_element)
+ common_code.save()
+ frappe.publish_progress(i / total_elements * 100, title=_("Importing Common Codes"))
+
+ return total_elements
+
+
+def on_doctype_update():
+ frappe.db.add_index("Common Code", ["code_list", "common_code"])
diff --git a/erpnext/edi/doctype/common_code/common_code_list.js b/erpnext/edi/doctype/common_code/common_code_list.js
new file mode 100644
index 00000000000..de1b665b161
--- /dev/null
+++ b/erpnext/edi/doctype/common_code/common_code_list.js
@@ -0,0 +1,8 @@
+frappe.listview_settings["Common Code"] = {
+ onload: function (listview) {
+ listview.page.add_inner_button(__("Import Genericode File"), function () {
+ erpnext.edi.import_genericode(listview);
+ });
+ },
+ hide_name_column: true,
+};
diff --git a/erpnext/edi/doctype/common_code/test_common_code.py b/erpnext/edi/doctype/common_code/test_common_code.py
new file mode 100644
index 00000000000..e9c67b2cc82
--- /dev/null
+++ b/erpnext/edi/doctype/common_code/test_common_code.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestCommonCode(FrappeTestCase):
+ pass
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 30121e5f2cb..882adec4d51 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -35,6 +35,14 @@ doctype_js = {
"Newsletter": "public/js/newsletter.js",
"Contact": "public/js/contact.js",
}
+doctype_list_js = {
+ "Code List": [
+ "edi/doctype/code_list/code_list_import.js",
+ ],
+ "Common Code": [
+ "edi/doctype/code_list/code_list_import.js",
+ ],
+}
override_doctype_class = {"Address": "erpnext.accounts.custom.address.ERPNextAddress"}
diff --git a/erpnext/modules.txt b/erpnext/modules.txt
index c53cdf467d2..b8b12e90fb0 100644
--- a/erpnext/modules.txt
+++ b/erpnext/modules.txt
@@ -18,3 +18,4 @@ Communication
Telephony
Bulk Transaction
Subcontracting
+EDI
\ No newline at end of file