first commit
This commit is contained in:
parent
314954dd18
commit
461d6424f0
|
|
@ -0,0 +1,37 @@
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def boot_session_handler(bootinfo):
|
||||||
|
# Формируем данные для всех Doctype, где есть вкладки
|
||||||
|
all_doctypes = frappe.get_all("DocType", fields=["name"])
|
||||||
|
tab_hierarchy_data = {}
|
||||||
|
|
||||||
|
for doctype in all_doctypes:
|
||||||
|
meta = frappe.get_meta(doctype["name"])
|
||||||
|
|
||||||
|
# Проверяем, есть ли вкладки (Tab Break) в Doctype
|
||||||
|
if not any(field.fieldtype == "Tab Break" for field in meta.fields):
|
||||||
|
continue
|
||||||
|
|
||||||
|
tabs = []
|
||||||
|
subtabs = {}
|
||||||
|
|
||||||
|
for field in meta.fields:
|
||||||
|
if field.fieldtype == "Tab Break":
|
||||||
|
tab_name = field.label
|
||||||
|
js_parent = field.js_parent_subtab or None
|
||||||
|
|
||||||
|
if js_parent:
|
||||||
|
if js_parent not in subtabs:
|
||||||
|
subtabs[js_parent] = []
|
||||||
|
subtabs[js_parent].append(tab_name)
|
||||||
|
else:
|
||||||
|
tabs.append(tab_name)
|
||||||
|
|
||||||
|
# Добавляем данные для Doctype
|
||||||
|
tab_hierarchy_data[doctype["name"]] = {
|
||||||
|
"tabs": tabs,
|
||||||
|
"subtabs": subtabs
|
||||||
|
}
|
||||||
|
|
||||||
|
# Передаём данные на клиентскую сторону через bootinfo
|
||||||
|
bootinfo.tab_hierarchy = tab_hierarchy_data
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
# Удаляем кастомное поле 'parent' из Doctype 'DocField', если оно существует
|
||||||
|
field_name = "DocField-js_parent_subtab"
|
||||||
|
if frappe.db.exists("Custom Field", {"name": field_name}):
|
||||||
|
frappe.delete_doc("Custom Field", field_name, ignore_permissions=True)
|
||||||
|
print(f"Custom Field '{field_name}' было удалено.")
|
||||||
|
else:
|
||||||
|
print(f"Custom Field '{field_name}' не существует.")
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,592 @@
|
||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"autoname": "hash",
|
||||||
|
"creation": "2013-02-22 01:27:33",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "Setup",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"label_and_type",
|
||||||
|
"label",
|
||||||
|
"fieldtype",
|
||||||
|
"fieldname",
|
||||||
|
"precision",
|
||||||
|
"length",
|
||||||
|
"non_negative",
|
||||||
|
"hide_days",
|
||||||
|
"hide_seconds",
|
||||||
|
"reqd",
|
||||||
|
"is_virtual",
|
||||||
|
"search_index",
|
||||||
|
"column_break_18",
|
||||||
|
"options",
|
||||||
|
"sort_options",
|
||||||
|
"show_dashboard",
|
||||||
|
"link_filters",
|
||||||
|
"defaults_section",
|
||||||
|
"default",
|
||||||
|
"column_break_6",
|
||||||
|
"fetch_from",
|
||||||
|
"fetch_if_empty",
|
||||||
|
"visibility_section",
|
||||||
|
"hidden",
|
||||||
|
"bold",
|
||||||
|
"allow_in_quick_entry",
|
||||||
|
"translatable",
|
||||||
|
"print_hide",
|
||||||
|
"print_hide_if_no_value",
|
||||||
|
"report_hide",
|
||||||
|
"column_break_28",
|
||||||
|
"depends_on",
|
||||||
|
"collapsible",
|
||||||
|
"collapsible_depends_on",
|
||||||
|
"hide_border",
|
||||||
|
"list__search_settings_section",
|
||||||
|
"in_list_view",
|
||||||
|
"in_standard_filter",
|
||||||
|
"in_preview",
|
||||||
|
"column_break_35",
|
||||||
|
"in_filter",
|
||||||
|
"in_global_search",
|
||||||
|
"permissions",
|
||||||
|
"read_only",
|
||||||
|
"allow_on_submit",
|
||||||
|
"ignore_user_permissions",
|
||||||
|
"allow_bulk_edit",
|
||||||
|
"column_break_13",
|
||||||
|
"permlevel",
|
||||||
|
"ignore_xss_filter",
|
||||||
|
"constraints_section",
|
||||||
|
"unique",
|
||||||
|
"no_copy",
|
||||||
|
"set_only_once",
|
||||||
|
"remember_last_selected_value",
|
||||||
|
"column_break_38",
|
||||||
|
"mandatory_depends_on",
|
||||||
|
"read_only_depends_on",
|
||||||
|
"display",
|
||||||
|
"print_width",
|
||||||
|
"width",
|
||||||
|
"max_height",
|
||||||
|
"columns",
|
||||||
|
"column_break_22",
|
||||||
|
"description",
|
||||||
|
"documentation_url",
|
||||||
|
"oldfieldname",
|
||||||
|
"oldfieldtype"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "label_and_type",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bold": 1,
|
||||||
|
"fieldname": "label",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Label",
|
||||||
|
"oldfieldname": "label",
|
||||||
|
"oldfieldtype": "Data",
|
||||||
|
"print_width": "163",
|
||||||
|
"search_index": 1,
|
||||||
|
"width": "163"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bold": 1,
|
||||||
|
"default": "Data",
|
||||||
|
"fieldname": "fieldtype",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Type",
|
||||||
|
"oldfieldname": "fieldtype",
|
||||||
|
"oldfieldtype": "Select",
|
||||||
|
"options": "Autocomplete\nAttach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nJSON\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nPhone\nRead Only\nRating\nSection Break\nSelect\nSignature\nSmall Text\nTab Break\nTable\nTable MultiSelect\nText\nText Editor\nTime",
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 1,
|
||||||
|
"sort_options": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bold": 1,
|
||||||
|
"fieldname": "fieldname",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Name",
|
||||||
|
"oldfieldname": "fieldname",
|
||||||
|
"oldfieldtype": "Data",
|
||||||
|
"search_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:!in_list([\"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)",
|
||||||
|
"fieldname": "reqd",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Mandatory",
|
||||||
|
"oldfieldname": "reqd",
|
||||||
|
"oldfieldtype": "Check",
|
||||||
|
"print_width": "50px",
|
||||||
|
"width": "50px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)",
|
||||||
|
"description": "Set non-standard precision for a Float or Currency field",
|
||||||
|
"fieldname": "precision",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Precision",
|
||||||
|
"options": "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9",
|
||||||
|
"print_hide": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image', 'Int'], doc.fieldtype)",
|
||||||
|
"fieldname": "length",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Length"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "search_index",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Index",
|
||||||
|
"oldfieldname": "search_index",
|
||||||
|
"oldfieldtype": "Check",
|
||||||
|
"print_width": "50px",
|
||||||
|
"width": "50px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:!doc.is_virtual",
|
||||||
|
"fieldname": "in_list_view",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "In List View",
|
||||||
|
"print_width": "70px",
|
||||||
|
"width": "70px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "in_standard_filter",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "In List Filter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)",
|
||||||
|
"fieldname": "in_global_search",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "In Global Search"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:!in_list(['Table', 'Table MultiSelect'], doc.fieldtype);",
|
||||||
|
"fieldname": "in_preview",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "In Preview"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "allow_in_quick_entry",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Allow in Quick Entry"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "bold",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Bold"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)",
|
||||||
|
"fieldname": "translatable",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Translatable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.fieldtype===\"Section Break\"",
|
||||||
|
"fieldname": "collapsible",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Collapsible",
|
||||||
|
"length": 255
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.fieldtype==\"Section Break\" && doc.collapsible",
|
||||||
|
"fieldname": "collapsible_depends_on",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Collapsible Depends On (JS)",
|
||||||
|
"max_height": "3rem",
|
||||||
|
"options": "JS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_6",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "For Links, enter the DocType as range.\nFor Select, enter list of Options, each on a new line.",
|
||||||
|
"fieldname": "options",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Options",
|
||||||
|
"oldfieldname": "options",
|
||||||
|
"oldfieldtype": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "default",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"label": "Default",
|
||||||
|
"max_height": "3rem",
|
||||||
|
"oldfieldname": "default",
|
||||||
|
"oldfieldtype": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "fetch_from",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"label": "Fetch From"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "If unchecked, the value will always be re-fetched on save.",
|
||||||
|
"fieldname": "fetch_if_empty",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Fetch on Save if Empty"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "permissions",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Permissions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "depends_on",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Display Depends On (JS)",
|
||||||
|
"length": 255,
|
||||||
|
"max_height": "3rem",
|
||||||
|
"oldfieldname": "depends_on",
|
||||||
|
"oldfieldtype": "Data",
|
||||||
|
"options": "JS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "hidden",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Hidden",
|
||||||
|
"oldfieldname": "hidden",
|
||||||
|
"oldfieldtype": "Check",
|
||||||
|
"print_width": "50px",
|
||||||
|
"width": "50px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "read_only",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Read Only",
|
||||||
|
"print_width": "50px",
|
||||||
|
"width": "50px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "unique",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Unique"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "set_only_once",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Set only once"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval: doc.fieldtype == \"Table\"",
|
||||||
|
"fieldname": "allow_bulk_edit",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Allow Bulk Edit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_13",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:!in_list(['Section Break', 'Column Break', 'Tab Break'], doc.fieldtype)",
|
||||||
|
"fieldname": "permlevel",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Perm Level",
|
||||||
|
"oldfieldname": "permlevel",
|
||||||
|
"oldfieldtype": "Int",
|
||||||
|
"print_width": "50px",
|
||||||
|
"width": "50px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "ignore_user_permissions",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Ignore User Permissions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval: parent.is_submittable",
|
||||||
|
"fieldname": "allow_on_submit",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Allow on Submit",
|
||||||
|
"oldfieldname": "allow_on_submit",
|
||||||
|
"oldfieldtype": "Check",
|
||||||
|
"print_width": "50px",
|
||||||
|
"width": "50px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "report_hide",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Report Hide",
|
||||||
|
"oldfieldname": "report_hide",
|
||||||
|
"oldfieldtype": "Check",
|
||||||
|
"print_width": "50px",
|
||||||
|
"width": "50px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:(doc.fieldtype == 'Link')",
|
||||||
|
"fieldname": "remember_last_selected_value",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Remember Last Selected Value"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "Don't encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field",
|
||||||
|
"fieldname": "ignore_xss_filter",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Ignore XSS Filter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "display",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Display"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "in_filter",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "In Filter",
|
||||||
|
"oldfieldname": "in_filter",
|
||||||
|
"oldfieldtype": "Check",
|
||||||
|
"print_width": "50px",
|
||||||
|
"width": "50px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "no_copy",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "No Copy",
|
||||||
|
"oldfieldname": "no_copy",
|
||||||
|
"oldfieldtype": "Check",
|
||||||
|
"print_width": "50px",
|
||||||
|
"width": "50px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "print_hide",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Print Hide",
|
||||||
|
"oldfieldname": "print_hide",
|
||||||
|
"oldfieldtype": "Check",
|
||||||
|
"print_width": "50px",
|
||||||
|
"width": "50px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1",
|
||||||
|
"fieldname": "print_hide_if_no_value",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Print Hide If No Value"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "print_width",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Print Width",
|
||||||
|
"length": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "width",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Width",
|
||||||
|
"length": 10,
|
||||||
|
"oldfieldname": "width",
|
||||||
|
"oldfieldtype": "Data",
|
||||||
|
"print_width": "50px",
|
||||||
|
"width": "50px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Number of columns for a field in a List View or a Grid (Total Columns should be less than 11)",
|
||||||
|
"fieldname": "columns",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_22",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Description",
|
||||||
|
"oldfieldname": "description",
|
||||||
|
"oldfieldtype": "Text",
|
||||||
|
"print_width": "300px",
|
||||||
|
"width": "300px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "oldfieldname",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 1,
|
||||||
|
"oldfieldname": "oldfieldname",
|
||||||
|
"oldfieldtype": "Data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "oldfieldtype",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 1,
|
||||||
|
"oldfieldname": "oldfieldtype",
|
||||||
|
"oldfieldtype": "Data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "mandatory_depends_on",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Mandatory Depends On (JS)",
|
||||||
|
"max_height": "3rem",
|
||||||
|
"options": "JS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "read_only_depends_on",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Read Only Depends On (JS)",
|
||||||
|
"max_height": "3rem",
|
||||||
|
"options": "JS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_38",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.fieldtype=='Duration'",
|
||||||
|
"fieldname": "hide_days",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Hide Days"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.fieldtype=='Duration'",
|
||||||
|
"fieldname": "hide_seconds",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Hide Seconds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.fieldtype=='Section Break'",
|
||||||
|
"fieldname": "hide_border",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Hide Border"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:in_list([\"Int\", \"Float\", \"Currency\"], doc.fieldtype)",
|
||||||
|
"fieldname": "non_negative",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Non Negative"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_18",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "defaults_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Defaults",
|
||||||
|
"max_height": "2rem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "visibility_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Visibility"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_28",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "constraints_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Constraints"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "js_parent_subtab",
|
||||||
|
"label": "JS Parent Subtab",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"description": "Укажите имя родительской вкладки для этой вкладки."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "max_height",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Max Height",
|
||||||
|
"length": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "list__search_settings_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "List / Search Settings"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_35",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.fieldtype===\"Tab Break\"",
|
||||||
|
"fieldname": "show_dashboard",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Show Dashboard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "is_virtual",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Virtual"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:!in_list([\"Tab Break\", \"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)",
|
||||||
|
"fieldname": "documentation_url",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Documentation URL",
|
||||||
|
"options": "URL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval: doc.fieldtype === 'Select'",
|
||||||
|
"fieldname": "sort_options",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Sort Options"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "link_filters",
|
||||||
|
"fieldtype": "JSON",
|
||||||
|
"label": "Link Filters"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"idx": 1,
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2024-02-01 15:55:44.007917",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Core",
|
||||||
|
"name": "DocField",
|
||||||
|
"naming_rule": "Random",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "ASC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,32 @@
|
||||||
app_name = "custom_subtabs"
|
app_name = "custom_subtabs"
|
||||||
app_title = "Custom Subtabs"
|
app_title = "Custom Subtabs"
|
||||||
app_publisher = "Jey ERP"
|
app_publisher = "Jey Soft"
|
||||||
app_description = "Custom subtabs"
|
app_description = "custom subtubs for frappe doctypes"
|
||||||
app_email = "info@jeyerp.az"
|
app_email = "info@jeyerp.az"
|
||||||
app_license = "unlicense"
|
app_license = "mit"
|
||||||
|
|
||||||
|
# Apps
|
||||||
|
# ------------------
|
||||||
|
|
||||||
# required_apps = []
|
# required_apps = []
|
||||||
|
|
||||||
|
# Each item in the list will be shown as an app in the apps page
|
||||||
|
# add_to_apps_screen = [
|
||||||
|
# {
|
||||||
|
# "name": "custom_subtabs",
|
||||||
|
# "logo": "/assets/custom_subtabs/logo.png",
|
||||||
|
# "title": "Custom Subtabs",
|
||||||
|
# "route": "/custom_subtabs",
|
||||||
|
# "has_permission": "custom_subtabs.api.permission.has_app_permission"
|
||||||
|
# }
|
||||||
|
# ]
|
||||||
|
|
||||||
# Includes in <head>
|
# Includes in <head>
|
||||||
# ------------------
|
# ------------------
|
||||||
|
|
||||||
# include js, css files in header of desk.html
|
# include js, css files in header of desk.html
|
||||||
# app_include_css = "/assets/custom_subtabs/css/custom_subtabs.css"
|
app_include_css = "/assets/custom_subtabs/css/custom_subtabs.css"
|
||||||
# app_include_js = "/assets/custom_subtabs/js/custom_subtabs.js"
|
app_include_js = "/assets/custom_subtabs/js/custom_subtabs.js"
|
||||||
|
|
||||||
# include js, css files in header of web template
|
# include js, css files in header of web template
|
||||||
# web_include_css = "/assets/custom_subtabs/css/custom_subtabs.css"
|
# web_include_css = "/assets/custom_subtabs/css/custom_subtabs.css"
|
||||||
|
|
@ -227,3 +242,17 @@ app_license = "unlicense"
|
||||||
# "Logging DocType Name": 30 # days to retain logs
|
# "Logging DocType Name": 30 # days to retain logs
|
||||||
# }
|
# }
|
||||||
|
|
||||||
|
|
||||||
|
app_include_js = [
|
||||||
|
"/assets/custom_subtabs/js/custom_subtabs.js"
|
||||||
|
]
|
||||||
|
|
||||||
|
fixtures = [
|
||||||
|
{"doctype": "DocType", "filters": [["name", "in", ["DocField"]]]}
|
||||||
|
]
|
||||||
|
|
||||||
|
after_migrate = [
|
||||||
|
"custom_subtabs.patches.add_parent_field.execute"
|
||||||
|
]
|
||||||
|
|
||||||
|
boot_session = "custom_subtabs.custom_subtabs.boot_session_handler"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
from frappe.custom.doctype.custom_field.custom_field import CustomField
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
class CustomFieldOverride(CustomField):
|
||||||
|
def on_update(self):
|
||||||
|
super().on_update()
|
||||||
|
self.setup_nested_tabs()
|
||||||
|
|
||||||
|
def setup_nested_tabs(self):
|
||||||
|
"""Автоматически подчиняет вкладки, основываясь на поле `js_parent_subtab`."""
|
||||||
|
doctype_meta = frappe.get_meta(self.dt)
|
||||||
|
fields = doctype_meta.fields
|
||||||
|
|
||||||
|
parent_mapping = {}
|
||||||
|
|
||||||
|
# Создаем структуру родительских вкладок
|
||||||
|
for field in fields:
|
||||||
|
if field.fieldtype == "Tab Break" and field.js_parent_subtab:
|
||||||
|
parent_mapping[field.js_parent_subtab] = field.fieldname
|
||||||
|
|
||||||
|
# Устанавливаем подчинение
|
||||||
|
for field in fields:
|
||||||
|
if field.fieldtype == "Tab Break" and field.js_parent_subtab:
|
||||||
|
parent_name = field.js_parent_subtab
|
||||||
|
if parent_name in parent_mapping:
|
||||||
|
parent_fieldname = parent_mapping[parent_name]
|
||||||
|
field.parent_tab = parent_fieldname # Подчиняем вкладку
|
||||||
|
frappe.db.set_value("Custom Field", field.name, "parent_tab", parent_fieldname)
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
# Проверяем, существует ли такое свойство
|
||||||
|
if frappe.db.exists("Custom Field", {"fieldname": "js_parent_subtab", "dt": "DocField"}):
|
||||||
|
frappe.msgprint("Свойство 'js_parent_subtab' уже существует. Пропускаем создание.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Создаём новое свойство
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "Custom Field",
|
||||||
|
"dt": "DocField", # Указываем DocType, для которого добавляем
|
||||||
|
"fieldname": "js_parent_subtab",
|
||||||
|
"label": "JS Parent Subtab",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"insert_after": "reqd", # Вставляем после поля "Mandatory"
|
||||||
|
"description": "Укажите имя родительского таба"
|
||||||
|
}).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
frappe.msgprint("Поле 'js_parent_subtab' успешно добавлено в настройки вкладок.")
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
/* Убираем анимацию скрытия Bootstrap */
|
||||||
|
.tab-pane.fade:not(.show) {
|
||||||
|
opacity: 0 !important; /* Только убираем анимацию, не меняем display */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Скрываем все вкладки, кроме активной */
|
||||||
|
.tab-pane {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Показываем активную вкладку */
|
||||||
|
.tab-pane.active {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,161 @@
|
||||||
|
frappe.ui.form.Layout.prototype.setup_tab_events = function () {
|
||||||
|
console.log("Setting up tab events...");
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log("Initializing tabs...");
|
||||||
|
|
||||||
|
const tabs = this.wrapper.find("ul.form-tabs > li.nav-item");
|
||||||
|
console.log(`Found ${tabs.length} main tabs.`);
|
||||||
|
|
||||||
|
const doctype = this.frm.doctype;
|
||||||
|
const hierarchy = frappe.boot.tab_hierarchy[doctype];
|
||||||
|
|
||||||
|
if (!hierarchy) {
|
||||||
|
console.warn(`No tab hierarchy found for Doctype: ${doctype}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subtabs = hierarchy.subtabs || {};
|
||||||
|
|
||||||
|
// Process subtabs and parent tabs
|
||||||
|
tabs.each((index, tab) => {
|
||||||
|
const tabLabel = $(tab).text().trim();
|
||||||
|
if (subtabs[tabLabel]) {
|
||||||
|
console.log(`Processing subtabs for parent tab: '${tabLabel}'`);
|
||||||
|
|
||||||
|
let subtabContainer = $(tab).find(".sub-tab-container");
|
||||||
|
if (!subtabContainer.length) {
|
||||||
|
subtabContainer = $("<ul class='sub-tab-container nav'></ul>");
|
||||||
|
$(tab).append(subtabContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
subtabs[tabLabel].forEach((subtab) => {
|
||||||
|
const subtabElement = tabs.filter((_, el) => $(el).text().trim() === subtab);
|
||||||
|
|
||||||
|
if (subtabElement.length) {
|
||||||
|
const subtabClone = subtabElement.clone();
|
||||||
|
subtabClone.addClass("sub-tab").find("a").removeAttr("href");
|
||||||
|
subtabContainer.append(subtabClone);
|
||||||
|
|
||||||
|
// Удаляем лишние классы и стили у субтаба
|
||||||
|
subtabElement.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle main tab clicks
|
||||||
|
$(".nav-item").on("click", function () {
|
||||||
|
const clickedTab = $(this);
|
||||||
|
const tabId = clickedTab.find("a").attr("aria-controls");
|
||||||
|
|
||||||
|
console.log(`Activating main tab: '${clickedTab.text().trim()}' (ID: ${tabId})`);
|
||||||
|
|
||||||
|
// Hide all tabs
|
||||||
|
$(".form-tab-content > .tab-pane").each(function () {
|
||||||
|
const pane = $(this);
|
||||||
|
pane.removeClass("active show").css("display", "none").addClass("tab-pane fade");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Activate the clicked tab
|
||||||
|
const tabContent = document.querySelector(`#${tabId}`);
|
||||||
|
if (tabContent) {
|
||||||
|
tabContent.style.display = "block";
|
||||||
|
tabContent.classList.add("active", "show");
|
||||||
|
} else {
|
||||||
|
console.warn(`Tab content not found for ID: ${tabId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
clickedTab.addClass("active");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle subtab clicks
|
||||||
|
$(".sub-tab").on("click", function (event) {
|
||||||
|
event.stopPropagation(); // Предотвращаем срабатывание событий родительских вкладок
|
||||||
|
|
||||||
|
const clickedSubtab = $(this);
|
||||||
|
const subtabId = clickedSubtab.find("a").attr("aria-controls");
|
||||||
|
const subtabName = clickedSubtab.text().trim();
|
||||||
|
|
||||||
|
console.log(`Activating subtab: '${subtabName}' (ID: ${subtabId})`);
|
||||||
|
|
||||||
|
// Найти имя родительской вкладки через объект иерархии
|
||||||
|
let parentTabName = null;
|
||||||
|
Object.keys(subtabs).forEach((parent) => {
|
||||||
|
if (subtabs[parent].includes(subtabName)) {
|
||||||
|
parentTabName = parent;
|
||||||
|
console.log(`Found parent tab for '${subtabName}': '${parentTabName}'`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!parentTabName) {
|
||||||
|
console.warn(`Parent tab not found for subtab: '${subtabName}'`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получить ID родительской вкладки через href
|
||||||
|
const parentTabId = $(`ul.form-tabs a:contains('${parentTabName}')`).attr("aria-controls");
|
||||||
|
|
||||||
|
if (!parentTabId) {
|
||||||
|
console.warn(`Parent tab ID not found for: '${parentTabName}'`);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
console.log(`Parent tab ID for '${parentTabName}': ${parentTabId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработать родительскую вкладку
|
||||||
|
const parentTabContent = document.querySelector(`#${parentTabId}`);
|
||||||
|
if (parentTabContent) {
|
||||||
|
console.log(`Parent tab content found:`, parentTabContent);
|
||||||
|
|
||||||
|
// Убираем классы и стили, чтобы показать родительскую вкладку
|
||||||
|
parentTabContent.style.display = "block";
|
||||||
|
parentTabContent.classList.add("active", "show");
|
||||||
|
parentTabContent.classList.remove("tab-pane", "fade");
|
||||||
|
|
||||||
|
console.log(`Parent tab '${parentTabName}' is now visible.`);
|
||||||
|
} else {
|
||||||
|
console.warn(`Parent tab content not found for ID: ${parentTabId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Активировать текущую субвкладку
|
||||||
|
const subtabContent = document.querySelector(`#${subtabId}`);
|
||||||
|
if (subtabContent) {
|
||||||
|
console.log(`Subtab content found:`, subtabContent);
|
||||||
|
|
||||||
|
// Убираем классы и стили, чтобы показать субвкладку
|
||||||
|
subtabContent.style.display = "block";
|
||||||
|
subtabContent.classList.add("active", "show");
|
||||||
|
subtabContent.classList.remove("tab-pane", "fade");
|
||||||
|
|
||||||
|
console.log(`Subtab '${subtabName}' content activated.`);
|
||||||
|
} else {
|
||||||
|
console.warn(`Subtab content not found for ID: ${subtabId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
clickedSubtab.addClass("active");
|
||||||
|
|
||||||
|
// Скрыть все другие вкладки, кроме текущей субвкладки и родительской
|
||||||
|
$(".form-tab-content > .tab-pane").each(function () {
|
||||||
|
const pane = $(this);
|
||||||
|
if (pane.attr("id") !== subtabId && pane.attr("id") !== parentTabId) {
|
||||||
|
console.log(`Hiding tab content for ID: ${pane.attr("id")}`);
|
||||||
|
|
||||||
|
pane.removeClass("active show").css("display", "none").addClass("tab-pane fade");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset subtabs when switching to another main tab
|
||||||
|
$(".nav-item").on("click", function () {
|
||||||
|
$(".sub-tab").each(function () {
|
||||||
|
const subtabId = $(this).find("a").attr("aria-controls");
|
||||||
|
const subtabPane = document.querySelector(`#${subtabId}`);
|
||||||
|
if (subtabPane) {
|
||||||
|
subtabPane.style.display = "none";
|
||||||
|
subtabPane.classList.add("tab-pane", "fade");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 500); // Задержка для загрузки DOM
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def process_nested_tabs(doc, method):
|
||||||
|
"""
|
||||||
|
Обработчик для вложенных вкладок.
|
||||||
|
Автоматически связывает вкладки, если у них есть указанный `js_parent_subtab`.
|
||||||
|
"""
|
||||||
|
if not hasattr(doc, 'fields') or not isinstance(doc.fields, list):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Получаем все вкладки из полей
|
||||||
|
tabs = [field for field in doc.fields if field.get("fieldtype") == "Tab"]
|
||||||
|
tab_map = {tab.get("fieldname"): tab for tab in tabs}
|
||||||
|
|
||||||
|
# Проверяем каждую вкладку на наличие `js_parent_subtab`
|
||||||
|
for tab in tabs:
|
||||||
|
parent_name = tab.get("js_parent_subtab")
|
||||||
|
if parent_name and parent_name in tab_map:
|
||||||
|
parent_tab = tab_map[parent_name]
|
||||||
|
|
||||||
|
# Убедимся, что поле `sub_tabs` существует
|
||||||
|
if not parent_tab.get("sub_tabs"):
|
||||||
|
parent_tab["sub_tabs"] = []
|
||||||
|
|
||||||
|
# Добавляем текущую вкладку как дочернюю
|
||||||
|
parent_tab["sub_tabs"].append(tab)
|
||||||
|
|
||||||
|
# Обновляем поля и сохраняем документ
|
||||||
|
doc.fields = doc.fields # Переприсваиваем для безопасности
|
||||||
|
doc.save()
|
||||||
Loading…
Reference in New Issue