Merge branch 'develop' into duration-control
This commit is contained in:
commit
b70321cc50
79 changed files with 2475 additions and 764 deletions
|
|
@ -256,6 +256,15 @@ def migrate(context, rebuild_website=False, skip_failing=False):
|
|||
print("Compiling Python Files...")
|
||||
compileall.compile_dir('../apps', quiet=1, rx=re.compile('.*node_modules.*'))
|
||||
|
||||
@click.command('migrate-to')
|
||||
@click.argument('frappe_provider')
|
||||
@pass_context
|
||||
def migrate_to(context, frappe_provider):
|
||||
"Migrates site to the specified provider"
|
||||
from frappe.integrations.frappe_providers import migrate_to
|
||||
for site in context.sites:
|
||||
migrate_to(site, frappe_provider)
|
||||
|
||||
@click.command('run-patch')
|
||||
@click.argument('module')
|
||||
@pass_context
|
||||
|
|
@ -322,18 +331,19 @@ def use(site, sites_path='.'):
|
|||
|
||||
@click.command('backup')
|
||||
@click.option('--with-files', default=False, is_flag=True, help="Take backup with files")
|
||||
@click.option('--verbose', default=False, is_flag=True)
|
||||
@pass_context
|
||||
def backup(context, with_files=False, backup_path_db=None, backup_path_files=None,
|
||||
backup_path_private_files=None, quiet=False):
|
||||
backup_path_private_files=None, quiet=False, verbose=False):
|
||||
"Backup"
|
||||
from frappe.utils.backups import scheduled_backup
|
||||
verbose = context.verbose
|
||||
verbose = verbose or context.verbose
|
||||
exit_code = 0
|
||||
for site in context.sites:
|
||||
try:
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
odb = scheduled_backup(ignore_files=not with_files, backup_path_db=backup_path_db, backup_path_files=backup_path_files, backup_path_private_files=backup_path_private_files, force=True)
|
||||
odb = scheduled_backup(ignore_files=not with_files, backup_path_db=backup_path_db, backup_path_files=backup_path_files, backup_path_private_files=backup_path_private_files, force=True, verbose=verbose)
|
||||
except Exception as e:
|
||||
if verbose:
|
||||
print("Backup failed for {0}. Database or site_config.json may be corrupted".format(site))
|
||||
|
|
@ -342,10 +352,12 @@ def backup(context, with_files=False, backup_path_db=None, backup_path_files=Non
|
|||
|
||||
if verbose:
|
||||
from frappe.utils import now
|
||||
print("database backup taken -", odb.backup_path_db, "- on", now())
|
||||
summary_title = "Backup Summary at {0}".format(now())
|
||||
print(summary_title + "\n" + "-" * len(summary_title))
|
||||
print("Database backup:", odb.backup_path_db)
|
||||
if with_files:
|
||||
print("files backup taken -", odb.backup_path_files, "- on", now())
|
||||
print("private files backup taken -", odb.backup_path_private_files, "- on", now())
|
||||
print("Public files: ", odb.backup_path_files)
|
||||
print("Private files: ", odb.backup_path_private_files)
|
||||
|
||||
frappe.destroy()
|
||||
sys.exit(exit_code)
|
||||
|
|
@ -559,6 +571,7 @@ commands = [
|
|||
install_app,
|
||||
list_apps,
|
||||
migrate,
|
||||
migrate_to,
|
||||
new_site,
|
||||
reinstall,
|
||||
reload_doc,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from frappe import _
|
|||
|
||||
def get_data():
|
||||
return [
|
||||
{
|
||||
{
|
||||
"label": _("Form Customization"),
|
||||
"icon": "fa fa-glass",
|
||||
"items": [
|
||||
|
|
@ -57,9 +57,9 @@ def get_data():
|
|||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"label": _("Custom Tags"),
|
||||
"name": "Tag Category",
|
||||
"description": _("Add your own Tag Categories")
|
||||
"label": _("Package"),
|
||||
"name": "Package",
|
||||
"description": _("Import and Export Packages.")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
"report_hide",
|
||||
"remember_last_selected_value",
|
||||
"ignore_xss_filter",
|
||||
"hide_border",
|
||||
"property_depends_on_section",
|
||||
"mandatory_depends_on",
|
||||
"column_break_38",
|
||||
|
|
@ -464,12 +465,19 @@
|
|||
"fieldname": "show_seconds",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Seconds"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.fieldtype=='Section Break'",
|
||||
"fieldname": "hide_border",
|
||||
"fieldtype": "Check",
|
||||
"label": "Hide Border"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-06 09:06:25.224411",
|
||||
"modified": "2020-05-15 09:06:25.224411",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocField",
|
||||
|
|
|
|||
0
frappe/core/doctype/installed_application/__init__.py
Normal file
0
frappe/core/doctype/installed_application/__init__.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2020-05-11 17:44:54.674657",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"app_name",
|
||||
"app_version",
|
||||
"git_branch"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "git_branch",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Git Branch",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "app_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Application Name",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "app_version",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Application Version",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-12 10:09:49.148087",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Installed Application",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class InstalledApplication(Document):
|
||||
pass
|
||||
0
frappe/core/doctype/installed_applications/__init__.py
Normal file
0
frappe/core/doctype/installed_applications/__init__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2020, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Installed Applications', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2020-05-11 17:45:41.587750",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"installed_applications"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "installed_applications",
|
||||
"fieldtype": "Table",
|
||||
"label": "Installed Applications",
|
||||
"options": "Installed Application",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-12 10:09:14.310622",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Installed Applications",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class InstalledApplications(Document):
|
||||
def update_versions(self):
|
||||
self.delete_key("installed_applications")
|
||||
for app in frappe.utils.get_installed_apps_info():
|
||||
self.append("installed_applications", {
|
||||
"app_name": app.get("app_name"),
|
||||
"app_version": app.get("version"),
|
||||
"git_branch": app.get("branch")
|
||||
})
|
||||
self.save()
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestInstalledApplications(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -6,7 +6,7 @@ frappe.provide('frappe.dashboards.chart_sources');
|
|||
|
||||
|
||||
frappe.pages['dashboard'].on_page_load = function(wrapper) {
|
||||
var page = frappe.ui.make_app_page({
|
||||
frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: __("Dashboard"),
|
||||
single_column: true
|
||||
|
|
@ -21,7 +21,7 @@ frappe.pages['dashboard'].on_page_load = function(wrapper) {
|
|||
class Dashboard {
|
||||
constructor(wrapper) {
|
||||
this.wrapper = $(wrapper);
|
||||
$(`<div class="dashboard">
|
||||
$(`<div class="dashboard" style="overflow-y: hidden">
|
||||
<div class="dashboard-graph"></div>
|
||||
</div>`).appendTo(this.wrapper.find(".page-content").empty());
|
||||
this.container = this.wrapper.find(".dashboard-graph");
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
"allow_in_quick_entry",
|
||||
"ignore_xss_filter",
|
||||
"translatable",
|
||||
"hide_border",
|
||||
"description",
|
||||
"permlevel",
|
||||
"width",
|
||||
|
|
@ -57,379 +58,386 @@
|
|||
],
|
||||
"fields": [
|
||||
{
|
||||
"bold": 1,
|
||||
"fieldname": "dt",
|
||||
"fieldtype": "Link",
|
||||
"in_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Document",
|
||||
"oldfieldname": "dt",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "DocType",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
"bold": 1,
|
||||
"fieldname": "dt",
|
||||
"fieldtype": "Link",
|
||||
"in_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Document",
|
||||
"oldfieldname": "dt",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "DocType",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"fieldname": "label",
|
||||
"fieldtype": "Data",
|
||||
"in_filter": 1,
|
||||
"label": "Label",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "label",
|
||||
"oldfieldtype": "Data"
|
||||
"bold": 1,
|
||||
"fieldname": "label",
|
||||
"fieldtype": "Data",
|
||||
"in_filter": 1,
|
||||
"label": "Label",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "label",
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"fieldname": "label_help",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Label Help",
|
||||
"oldfieldtype": "HTML"
|
||||
"fieldname": "label_help",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Label Help",
|
||||
"oldfieldtype": "HTML"
|
||||
},
|
||||
{
|
||||
"fieldname": "fieldname",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Fieldname",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "fieldname",
|
||||
"oldfieldtype": "Data",
|
||||
"read_only": 1
|
||||
"fieldname": "fieldname",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Fieldname",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "fieldname",
|
||||
"oldfieldtype": "Data",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"description": "Select the label after which you want to insert new field.",
|
||||
"fieldname": "insert_after",
|
||||
"fieldtype": "Select",
|
||||
"label": "Insert After",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "insert_after",
|
||||
"oldfieldtype": "Select"
|
||||
"description": "Select the label after which you want to insert new field.",
|
||||
"fieldname": "insert_after",
|
||||
"fieldtype": "Select",
|
||||
"label": "Insert After",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "insert_after",
|
||||
"oldfieldtype": "Select"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_6",
|
||||
"fieldtype": "Column Break"
|
||||
"fieldname": "column_break_6",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"default": "Data",
|
||||
"fieldname": "fieldtype",
|
||||
"fieldtype": "Select",
|
||||
"in_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Field Type",
|
||||
"oldfieldname": "fieldtype",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nGeolocation\nHTML\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature",
|
||||
"reqd": 1
|
||||
"bold": 1,
|
||||
"default": "Data",
|
||||
"fieldname": "fieldtype",
|
||||
"fieldtype": "Select",
|
||||
"in_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Field Type",
|
||||
"oldfieldname": "fieldtype",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nGeolocation\nHTML\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"fieldname": "options",
|
||||
"fieldtype": "Small Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Options",
|
||||
"oldfieldname": "options",
|
||||
"oldfieldtype": "Text"
|
||||
"fieldname": "options",
|
||||
"fieldtype": "Small Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Options",
|
||||
"oldfieldname": "options",
|
||||
"oldfieldtype": "Text"
|
||||
},
|
||||
{
|
||||
"fieldname": "fetch_from",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Fetch From"
|
||||
"fieldname": "fetch_from",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Fetch From"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.",
|
||||
"fieldname": "fetch_if_empty",
|
||||
"fieldtype": "Check",
|
||||
"label": "Fetch If Empty"
|
||||
"default": "0",
|
||||
"description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.",
|
||||
"fieldname": "fetch_if_empty",
|
||||
"fieldtype": "Check",
|
||||
"label": "Fetch If Empty"
|
||||
},
|
||||
{
|
||||
"fieldname": "options_help",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Options Help",
|
||||
"oldfieldtype": "HTML"
|
||||
"fieldname": "options_help",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Options Help",
|
||||
"oldfieldtype": "HTML"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_11",
|
||||
"fieldtype": "Section Break"
|
||||
"fieldname": "section_break_11",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.fieldtype==\"Section Break\"",
|
||||
"fieldname": "collapsible",
|
||||
"fieldtype": "Check",
|
||||
"label": "Collapsible"
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.fieldtype==\"Section Break\"",
|
||||
"fieldname": "collapsible",
|
||||
"fieldtype": "Check",
|
||||
"label": "Collapsible"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.fieldtype==\"Section Break\"",
|
||||
"fieldname": "collapsible_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Collapsible Depends On"
|
||||
"depends_on": "eval:doc.fieldtype==\"Section Break\"",
|
||||
"fieldname": "collapsible_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Collapsible Depends On"
|
||||
},
|
||||
{
|
||||
"fieldname": "default",
|
||||
"fieldtype": "Text",
|
||||
"label": "Default Value",
|
||||
"oldfieldname": "default",
|
||||
"oldfieldtype": "Text"
|
||||
"fieldname": "default",
|
||||
"fieldtype": "Text",
|
||||
"label": "Default Value",
|
||||
"oldfieldname": "default",
|
||||
"oldfieldtype": "Text"
|
||||
},
|
||||
{
|
||||
"fieldname": "depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Depends On",
|
||||
"length": 255
|
||||
"fieldname": "depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Depends On",
|
||||
"length": 255
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text",
|
||||
"label": "Field Description",
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Text",
|
||||
"print_width": "300px",
|
||||
"width": "300px"
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text",
|
||||
"label": "Field Description",
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Text",
|
||||
"print_width": "300px",
|
||||
"width": "300px"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "permlevel",
|
||||
"fieldtype": "Int",
|
||||
"label": "Permission Level",
|
||||
"oldfieldname": "permlevel",
|
||||
"oldfieldtype": "Int"
|
||||
"default": "0",
|
||||
"fieldname": "permlevel",
|
||||
"fieldtype": "Int",
|
||||
"label": "Permission Level",
|
||||
"oldfieldname": "permlevel",
|
||||
"oldfieldtype": "Int"
|
||||
},
|
||||
{
|
||||
"fieldname": "width",
|
||||
"fieldtype": "Data",
|
||||
"label": "Width",
|
||||
"oldfieldname": "width",
|
||||
"oldfieldtype": "Data"
|
||||
"fieldname": "width",
|
||||
"fieldtype": "Data",
|
||||
"label": "Width",
|
||||
"oldfieldname": "width",
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"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": "properties",
|
||||
"fieldtype": "Column Break",
|
||||
"oldfieldtype": "Column Break",
|
||||
"print_width": "50%",
|
||||
"width": "50%"
|
||||
"fieldname": "properties",
|
||||
"fieldtype": "Column Break",
|
||||
"oldfieldtype": "Column Break",
|
||||
"print_width": "50%",
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "reqd",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Is Mandatory Field",
|
||||
"oldfieldname": "reqd",
|
||||
"oldfieldtype": "Check"
|
||||
"default": "0",
|
||||
"fieldname": "reqd",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Is Mandatory Field",
|
||||
"oldfieldname": "reqd",
|
||||
"oldfieldtype": "Check"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "unique",
|
||||
"fieldtype": "Check",
|
||||
"label": "Unique"
|
||||
"default": "0",
|
||||
"fieldname": "unique",
|
||||
"fieldtype": "Check",
|
||||
"label": "Unique"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "read_only",
|
||||
"fieldtype": "Check",
|
||||
"label": "Read Only"
|
||||
"default": "0",
|
||||
"fieldname": "read_only",
|
||||
"fieldtype": "Check",
|
||||
"label": "Read Only"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.fieldtype===\"Link\"",
|
||||
"fieldname": "ignore_user_permissions",
|
||||
"fieldtype": "Check",
|
||||
"label": "Ignore User Permissions"
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.fieldtype===\"Link\"",
|
||||
"fieldname": "ignore_user_permissions",
|
||||
"fieldtype": "Check",
|
||||
"label": "Ignore User Permissions"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "hidden",
|
||||
"fieldtype": "Check",
|
||||
"label": "Hidden"
|
||||
"default": "0",
|
||||
"fieldname": "hidden",
|
||||
"fieldtype": "Check",
|
||||
"label": "Hidden"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "print_hide",
|
||||
"fieldtype": "Check",
|
||||
"label": "Print Hide",
|
||||
"oldfieldname": "print_hide",
|
||||
"oldfieldtype": "Check"
|
||||
"default": "0",
|
||||
"fieldname": "print_hide",
|
||||
"fieldtype": "Check",
|
||||
"label": "Print Hide",
|
||||
"oldfieldname": "print_hide",
|
||||
"oldfieldtype": "Check"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"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",
|
||||
"hidden": 1,
|
||||
"label": "Print Width",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1
|
||||
"fieldname": "print_width",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Print Width",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "no_copy",
|
||||
"fieldtype": "Check",
|
||||
"label": "No Copy",
|
||||
"oldfieldname": "no_copy",
|
||||
"oldfieldtype": "Check"
|
||||
"default": "0",
|
||||
"fieldname": "no_copy",
|
||||
"fieldtype": "Check",
|
||||
"label": "No Copy",
|
||||
"oldfieldname": "no_copy",
|
||||
"oldfieldtype": "Check"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "allow_on_submit",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow on Submit",
|
||||
"oldfieldname": "allow_on_submit",
|
||||
"oldfieldtype": "Check"
|
||||
"default": "0",
|
||||
"fieldname": "allow_on_submit",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow on Submit",
|
||||
"oldfieldname": "allow_on_submit",
|
||||
"oldfieldtype": "Check"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "in_list_view",
|
||||
"fieldtype": "Check",
|
||||
"label": "In List View"
|
||||
"default": "0",
|
||||
"fieldname": "in_list_view",
|
||||
"fieldtype": "Check",
|
||||
"label": "In List View"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "in_standard_filter",
|
||||
"fieldtype": "Check",
|
||||
"label": "In Standard Filter"
|
||||
"default": "0",
|
||||
"fieldname": "in_standard_filter",
|
||||
"fieldtype": "Check",
|
||||
"label": "In Standard 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:([\"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",
|
||||
"fieldname": "bold",
|
||||
"fieldtype": "Check",
|
||||
"label": "Bold"
|
||||
"default": "0",
|
||||
"fieldname": "bold",
|
||||
"fieldtype": "Check",
|
||||
"label": "Bold"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "report_hide",
|
||||
"fieldtype": "Check",
|
||||
"label": "Report Hide",
|
||||
"oldfieldname": "report_hide",
|
||||
"oldfieldtype": "Check"
|
||||
"default": "0",
|
||||
"fieldname": "report_hide",
|
||||
"fieldtype": "Check",
|
||||
"label": "Report Hide",
|
||||
"oldfieldname": "report_hide",
|
||||
"oldfieldtype": "Check"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "search_index",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Index",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1
|
||||
"default": "0",
|
||||
"fieldname": "search_index",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Index",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Don't HTML 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"
|
||||
"default": "0",
|
||||
"description": "Don't HTML 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"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)",
|
||||
"fieldname": "translatable",
|
||||
"fieldtype": "Check",
|
||||
"label": "Translatable"
|
||||
"default": "1",
|
||||
"depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)",
|
||||
"fieldname": "translatable",
|
||||
"fieldtype": "Check",
|
||||
"label": "Translatable"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"fieldname": "mandatory_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Mandatory Depends On",
|
||||
"length": 255
|
||||
"fieldname": "mandatory_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Mandatory Depends On",
|
||||
"length": 255
|
||||
},
|
||||
{
|
||||
"fieldname": "read_only_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Read Only Depends On",
|
||||
"length": 255
|
||||
"fieldname": "read_only_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Read Only Depends On",
|
||||
"length": 255
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "allow_in_quick_entry",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow in Quick Entry"
|
||||
"default": "0",
|
||||
"fieldname": "allow_in_quick_entry",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow in Quick Entry"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "in_preview",
|
||||
"fieldtype": "Check",
|
||||
"label": "In Preview"
|
||||
"default": "0",
|
||||
"fieldname": "in_preview",
|
||||
"fieldtype": "Check",
|
||||
"label": "In Preview"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.fieldtype === \"Duration\";",
|
||||
"fieldname": "show_seconds",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Seconds",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.fieldtype === \"Duration\";",
|
||||
"fieldname": "show_seconds",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Seconds",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.fieldtype === \"Duration\";",
|
||||
"fieldname": "show_days",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Days",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.fieldtype === \"Duration\";",
|
||||
"fieldname": "show_days",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Days",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.fieldtype=='Section Break'",
|
||||
"fieldname": "hide_border",
|
||||
"fieldtype": "Check",
|
||||
"label": "Hide Border"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-glass",
|
||||
"idx": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-14 23:43:00.123572",
|
||||
"modified": "2020-05-15 23:43:00.123572",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Custom Field",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Administrator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Administrator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "dt,label,fieldtype,options",
|
||||
|
|
|
|||
0
frappe/custom/doctype/custom_link/__init__.py
Normal file
0
frappe/custom/doctype/custom_link/__init__.py
Normal file
20
frappe/custom/doctype/custom_link/custom_link.js
Normal file
20
frappe/custom/doctype/custom_link/custom_link.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) 2020, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Custom Link', {
|
||||
refresh: function(frm) {
|
||||
frm.set_query("document_type", function () {
|
||||
return {
|
||||
filters: {
|
||||
custom: 0,
|
||||
istable: 0,
|
||||
module: ['not in', ["Email", "Core", "Custom", "Event Streaming", "Social", "Data Migration", "Geo", "Desk"]]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.add_custom_button(__('Go to {0} List', [frm.doc.document_type]), function() {
|
||||
frappe.set_route('List', frm.doc.document_type);
|
||||
});
|
||||
}
|
||||
});
|
||||
52
frappe/custom/doctype/custom_link/custom_link.json
Normal file
52
frappe/custom/doctype/custom_link/custom_link.json
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"actions": [],
|
||||
"autoname": "field:document_type",
|
||||
"creation": "2020-04-08 15:16:44.342509",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"document_type",
|
||||
"links"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "document_type",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Document Type",
|
||||
"options": "DocType",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "links",
|
||||
"fieldtype": "Table",
|
||||
"label": "Links",
|
||||
"options": "DocType Link"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-04-08 16:42:59.402671",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Custom Link",
|
||||
"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",
|
||||
"track_changes": 1
|
||||
}
|
||||
10
frappe/custom/doctype/custom_link/custom_link.py
Normal file
10
frappe/custom/doctype/custom_link/custom_link.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class CustomLink(Document):
|
||||
pass
|
||||
10
frappe/custom/doctype/custom_link/test_custom_link.py
Normal file
10
frappe/custom/doctype/custom_link/test_custom_link.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestCustomLink(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -76,7 +76,8 @@ docfield_properties = {
|
|||
'remember_last_selected_value': 'Check',
|
||||
'allow_bulk_edit': 'Check',
|
||||
'auto_repeat': 'Link',
|
||||
'allow_in_quick_entry': 'Check'
|
||||
'allow_in_quick_entry': 'Check',
|
||||
'hide_border': 'Check'
|
||||
}
|
||||
|
||||
allowed_fieldtype_change = (('Currency', 'Float', 'Percent'), ('Small Text', 'Data'),
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
"allow_on_submit",
|
||||
"report_hide",
|
||||
"remember_last_selected_value",
|
||||
"hide_border",
|
||||
"property_depends_on_section",
|
||||
"mandatory_depends_on",
|
||||
"column_break_33",
|
||||
|
|
@ -59,361 +60,368 @@
|
|||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "label_and_type",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Label and Type"
|
||||
"fieldname": "label_and_type",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Label and Type"
|
||||
},
|
||||
{
|
||||
"fieldname": "label",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Label",
|
||||
"oldfieldname": "label",
|
||||
"oldfieldtype": "Data",
|
||||
"search_index": 1
|
||||
"fieldname": "label",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Label",
|
||||
"oldfieldname": "label",
|
||||
"oldfieldtype": "Data",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"default": "Data",
|
||||
"fieldname": "fieldtype",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Type",
|
||||
"oldfieldname": "fieldtype",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
"default": "Data",
|
||||
"fieldname": "fieldtype",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Type",
|
||||
"oldfieldname": "fieldtype",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "fieldname",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Name",
|
||||
"oldfieldname": "fieldname",
|
||||
"oldfieldtype": "Data",
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
"fieldname": "fieldname",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Name",
|
||||
"oldfieldname": "fieldname",
|
||||
"oldfieldtype": "Data",
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:!in_list([\"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)",
|
||||
"fieldname": "reqd",
|
||||
"fieldtype": "Check",
|
||||
"label": "Mandatory",
|
||||
"oldfieldname": "reqd",
|
||||
"oldfieldtype": "Check",
|
||||
"print_width": "50px",
|
||||
"width": "50px"
|
||||
"default": "0",
|
||||
"depends_on": "eval:!in_list([\"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)",
|
||||
"fieldname": "reqd",
|
||||
"fieldtype": "Check",
|
||||
"label": "Mandatory",
|
||||
"oldfieldname": "reqd",
|
||||
"oldfieldtype": "Check",
|
||||
"print_width": "50px",
|
||||
"width": "50px"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "unique",
|
||||
"fieldtype": "Check",
|
||||
"label": "Unique"
|
||||
"default": "0",
|
||||
"fieldname": "unique",
|
||||
"fieldtype": "Check",
|
||||
"label": "Unique"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "in_list_view",
|
||||
"fieldtype": "Check",
|
||||
"label": "In List View"
|
||||
"default": "0",
|
||||
"fieldname": "in_list_view",
|
||||
"fieldtype": "Check",
|
||||
"label": "In List View"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "in_standard_filter",
|
||||
"fieldtype": "Check",
|
||||
"label": "In Standard Filter"
|
||||
"default": "0",
|
||||
"fieldname": "in_standard_filter",
|
||||
"fieldtype": "Check",
|
||||
"label": "In Standard 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:([\"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",
|
||||
"fieldname": "bold",
|
||||
"fieldtype": "Check",
|
||||
"label": "Bold"
|
||||
"default": "0",
|
||||
"fieldname": "bold",
|
||||
"fieldtype": "Check",
|
||||
"label": "Bold"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)",
|
||||
"fieldname": "translatable",
|
||||
"fieldtype": "Check",
|
||||
"label": "Translatable"
|
||||
"default": "1",
|
||||
"depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)",
|
||||
"fieldname": "translatable",
|
||||
"fieldtype": "Check",
|
||||
"label": "Translatable"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_7",
|
||||
"fieldtype": "Column Break"
|
||||
"fieldname": "column_break_7",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image'], doc.fieldtype)",
|
||||
"fieldname": "length",
|
||||
"fieldtype": "Int",
|
||||
"label": "Length"
|
||||
"depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image'], doc.fieldtype)",
|
||||
"fieldname": "length",
|
||||
"fieldtype": "Int",
|
||||
"label": "Length"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"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": "fetch_from",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Fetch From"
|
||||
"fieldname": "fetch_from",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Fetch From"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.",
|
||||
"fieldname": "fetch_if_empty",
|
||||
"fieldtype": "Check",
|
||||
"label": "Fetch If Empty"
|
||||
"default": "0",
|
||||
"description": "If checked, this field will be not overwritten based on Fetch From if a value already exists.",
|
||||
"fieldname": "fetch_if_empty",
|
||||
"fieldtype": "Check",
|
||||
"label": "Fetch If Empty"
|
||||
},
|
||||
{
|
||||
"fieldname": "permissions",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Permissions"
|
||||
"fieldname": "permissions",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Permissions"
|
||||
},
|
||||
{
|
||||
"description": "This field will appear only if the fieldname defined here has value OR the rules are true (examples): \nmyfield\neval:doc.myfield=='My Value'\neval:doc.age>18",
|
||||
"fieldname": "depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Depends On",
|
||||
"oldfieldname": "depends_on",
|
||||
"oldfieldtype": "Data",
|
||||
"options": "JS"
|
||||
"description": "This field will appear only if the fieldname defined here has value OR the rules are true (examples): \nmyfield\neval:doc.myfield=='My Value'\neval:doc.age>18",
|
||||
"fieldname": "depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Depends On",
|
||||
"oldfieldname": "depends_on",
|
||||
"oldfieldtype": "Data",
|
||||
"options": "JS"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "permlevel",
|
||||
"fieldtype": "Int",
|
||||
"in_list_view": 1,
|
||||
"label": "Perm Level",
|
||||
"oldfieldname": "permlevel",
|
||||
"oldfieldtype": "Int"
|
||||
"default": "0",
|
||||
"fieldname": "permlevel",
|
||||
"fieldtype": "Int",
|
||||
"in_list_view": 1,
|
||||
"label": "Perm Level",
|
||||
"oldfieldname": "permlevel",
|
||||
"oldfieldtype": "Int"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "hidden",
|
||||
"fieldtype": "Check",
|
||||
"label": "Hidden",
|
||||
"oldfieldname": "hidden",
|
||||
"oldfieldtype": "Check",
|
||||
"print_width": "50px",
|
||||
"width": "50px"
|
||||
"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"
|
||||
"default": "0",
|
||||
"fieldname": "read_only",
|
||||
"fieldtype": "Check",
|
||||
"label": "Read Only"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.fieldtype==\"Section Break\"",
|
||||
"fieldname": "collapsible",
|
||||
"fieldtype": "Check",
|
||||
"label": "Collapsible"
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.fieldtype==\"Section Break\"",
|
||||
"fieldname": "collapsible",
|
||||
"fieldtype": "Check",
|
||||
"label": "Collapsible"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval: doc.fieldtype == \"Table\"",
|
||||
"fieldname": "allow_bulk_edit",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow Bulk Edit"
|
||||
"default": "0",
|
||||
"depends_on": "eval: doc.fieldtype == \"Table\"",
|
||||
"fieldname": "allow_bulk_edit",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow Bulk Edit"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.fieldtype==\"Section Break\"",
|
||||
"fieldname": "collapsible_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Collapsible Depends On",
|
||||
"options": "JS"
|
||||
"depends_on": "eval:doc.fieldtype==\"Section Break\"",
|
||||
"fieldname": "collapsible_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Collapsible Depends On",
|
||||
"options": "JS"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_14",
|
||||
"fieldtype": "Column Break"
|
||||
"fieldname": "column_break_14",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "ignore_user_permissions",
|
||||
"fieldtype": "Check",
|
||||
"label": "Ignore User Permissions"
|
||||
"default": "0",
|
||||
"fieldname": "ignore_user_permissions",
|
||||
"fieldtype": "Check",
|
||||
"label": "Ignore User Permissions"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "allow_on_submit",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow on Submit",
|
||||
"oldfieldname": "allow_on_submit",
|
||||
"oldfieldtype": "Check"
|
||||
"default": "0",
|
||||
"fieldname": "allow_on_submit",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow on Submit",
|
||||
"oldfieldname": "allow_on_submit",
|
||||
"oldfieldtype": "Check"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "report_hide",
|
||||
"fieldtype": "Check",
|
||||
"label": "Report Hide",
|
||||
"oldfieldname": "report_hide",
|
||||
"oldfieldtype": "Check"
|
||||
"default": "0",
|
||||
"fieldname": "report_hide",
|
||||
"fieldtype": "Check",
|
||||
"label": "Report Hide",
|
||||
"oldfieldname": "report_hide",
|
||||
"oldfieldtype": "Check"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:(doc.fieldtype == 'Link')",
|
||||
"fieldname": "remember_last_selected_value",
|
||||
"fieldtype": "Check",
|
||||
"label": "Remember Last Selected Value"
|
||||
"default": "0",
|
||||
"depends_on": "eval:(doc.fieldtype == 'Link')",
|
||||
"fieldname": "remember_last_selected_value",
|
||||
"fieldtype": "Check",
|
||||
"label": "Remember Last Selected Value"
|
||||
},
|
||||
{
|
||||
"fieldname": "display",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Display"
|
||||
"fieldname": "display",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Display"
|
||||
},
|
||||
{
|
||||
"fieldname": "default",
|
||||
"fieldtype": "Text",
|
||||
"label": "Default",
|
||||
"oldfieldname": "default",
|
||||
"oldfieldtype": "Text"
|
||||
"fieldname": "default",
|
||||
"fieldtype": "Text",
|
||||
"label": "Default",
|
||||
"oldfieldname": "default",
|
||||
"oldfieldtype": "Text"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "in_filter",
|
||||
"fieldtype": "Check",
|
||||
"label": "In Filter",
|
||||
"oldfieldname": "in_filter",
|
||||
"oldfieldtype": "Check",
|
||||
"print_width": "50px",
|
||||
"width": "50px"
|
||||
"default": "0",
|
||||
"fieldname": "in_filter",
|
||||
"fieldtype": "Check",
|
||||
"label": "In Filter",
|
||||
"oldfieldname": "in_filter",
|
||||
"oldfieldtype": "Check",
|
||||
"print_width": "50px",
|
||||
"width": "50px"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_21",
|
||||
"fieldtype": "Column Break"
|
||||
"fieldname": "column_break_21",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text",
|
||||
"label": "Description",
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Text",
|
||||
"print_width": "300px",
|
||||
"width": "300px"
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text",
|
||||
"label": "Description",
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Text",
|
||||
"print_width": "300px",
|
||||
"width": "300px"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "print_hide",
|
||||
"fieldtype": "Check",
|
||||
"label": "Print Hide",
|
||||
"oldfieldname": "print_hide",
|
||||
"oldfieldtype": "Check"
|
||||
"default": "0",
|
||||
"fieldname": "print_hide",
|
||||
"fieldtype": "Check",
|
||||
"label": "Print Hide",
|
||||
"oldfieldname": "print_hide",
|
||||
"oldfieldtype": "Check"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"description": "Print Width of the field, if the field is a column in a table",
|
||||
"fieldname": "print_width",
|
||||
"fieldtype": "Data",
|
||||
"label": "Print Width",
|
||||
"print_width": "50px",
|
||||
"width": "50px"
|
||||
"description": "Print Width of the field, if the field is a column in a table",
|
||||
"fieldname": "print_width",
|
||||
"fieldtype": "Data",
|
||||
"label": "Print Width",
|
||||
"print_width": "50px",
|
||||
"width": "50px"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:cur_frm.doc.istable",
|
||||
"description": "Number of columns for a field in a Grid (Total Columns in a grid should be less than 11)",
|
||||
"fieldname": "columns",
|
||||
"fieldtype": "Int",
|
||||
"label": "Columns"
|
||||
"depends_on": "eval:cur_frm.doc.istable",
|
||||
"description": "Number of columns for a field in a Grid (Total Columns in a grid should be less than 11)",
|
||||
"fieldname": "columns",
|
||||
"fieldtype": "Int",
|
||||
"label": "Columns"
|
||||
},
|
||||
{
|
||||
"fieldname": "width",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Width",
|
||||
"oldfieldname": "width",
|
||||
"oldfieldtype": "Data",
|
||||
"print_width": "50px",
|
||||
"width": "50px"
|
||||
"fieldname": "width",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Width",
|
||||
"oldfieldname": "width",
|
||||
"oldfieldtype": "Data",
|
||||
"print_width": "50px",
|
||||
"width": "50px"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_custom_field",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Is Custom Field",
|
||||
"read_only": 1
|
||||
"default": "0",
|
||||
"fieldname": "is_custom_field",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Is Custom Field",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "allow_in_quick_entry",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow in Quick Entry"
|
||||
"default": "0",
|
||||
"fieldname": "allow_in_quick_entry",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow in Quick Entry"
|
||||
},
|
||||
{
|
||||
"fieldname": "property_depends_on_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Property Depends On"
|
||||
"fieldname": "property_depends_on_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Property Depends On"
|
||||
},
|
||||
{
|
||||
"fieldname": "mandatory_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Mandatory Depends On",
|
||||
"options": "JS"
|
||||
"fieldname": "mandatory_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Mandatory Depends On",
|
||||
"options": "JS"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_33",
|
||||
"fieldtype": "Column Break"
|
||||
"fieldname": "column_break_33",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "read_only_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Read Only Depends On",
|
||||
"options": "JS"
|
||||
"fieldname": "read_only_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Read Only Depends On",
|
||||
"options": "JS"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "in_preview",
|
||||
"fieldtype": "Check",
|
||||
"label": "In Preview"
|
||||
"default": "0",
|
||||
"fieldname": "in_preview",
|
||||
"fieldtype": "Check",
|
||||
"label": "In Preview"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.fieldtype === \"Duration\";",
|
||||
"fieldname": "show_seconds",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Seconds",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.fieldtype === \"Duration\";",
|
||||
"fieldname": "show_seconds",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Seconds",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.fieldtype === \"Duration\";",
|
||||
"fieldname": "show_days",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Days",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.fieldtype === \"Duration\";",
|
||||
"fieldname": "show_days",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Days",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.fieldtype=='Section Break'",
|
||||
"fieldname": "hide_border",
|
||||
"fieldtype": "Check",
|
||||
"label": "Hide Border"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-14 23:45:46.810869",
|
||||
"modified": "2020-05-15 23:45:46.810869",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Customize Form Field",
|
||||
|
|
|
|||
0
frappe/custom/doctype/package_document_type/__init__.py
Normal file
0
frappe/custom/doctype/package_document_type/__init__.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2020-05-14 16:45:47.196395",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"document_type",
|
||||
"column_break_2",
|
||||
"attachments",
|
||||
"overwrite",
|
||||
"section_break_4",
|
||||
"filters_json"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "document_type",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Document Type",
|
||||
"options": "DocType",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "attachments",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Include Attachments"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "overwrite",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Overwrite"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_4",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "filters_json",
|
||||
"fieldtype": "Code",
|
||||
"label": "Filters",
|
||||
"options": "JSON"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-14 16:45:47.196395",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Package Document Type",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class PackageDocumentType(Document):
|
||||
pass
|
||||
0
frappe/custom/doctype/package_publish_target/__init__.py
Normal file
0
frappe/custom/doctype/package_publish_target/__init__.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2020-05-13 16:04:32.724663",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"instance_url",
|
||||
"username",
|
||||
"password"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "instance_url",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Site URL",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "username",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Username",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "password",
|
||||
"fieldtype": "Password",
|
||||
"in_list_view": 1,
|
||||
"label": "Password",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-15 17:35:16.282235",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Package Publish Target",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class PackagePublishTarget(Document):
|
||||
pass
|
||||
0
frappe/custom/doctype/package_publish_tool/__init__.py
Normal file
0
frappe/custom/doctype/package_publish_tool/__init__.py
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright (c) 2020, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Package Publish Tool', {
|
||||
refresh: function(frm) {
|
||||
frm.set_query("document_type", "package_details", function () {
|
||||
return {
|
||||
filters: {
|
||||
"istable": 0,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frappe.realtime.on("package", (data) => {
|
||||
frm.dashboard.show_progress(data.prefix, data.progress / data.total * 100, __("{0}", [data.message]));
|
||||
if ((data.progress+1) != data.total) {
|
||||
frm.dashboard.show_progress(data.prefix, data.progress / data.total * 100, __("{0}", [data.message]));
|
||||
} else {
|
||||
frm.dashboard.hide_progress();
|
||||
}
|
||||
});
|
||||
|
||||
frm.trigger("show_instructions");
|
||||
frm.trigger("last_deployed_on");
|
||||
frm.trigger("set_dirty_trigger");
|
||||
frm.trigger("set_deploy_primary_action");
|
||||
},
|
||||
last_deployed_on: function(frm) {
|
||||
if (frm.doc.last_deployed_on) {
|
||||
frm.trigger("show_indicator");
|
||||
}
|
||||
},
|
||||
show_indicator: function(frm) {
|
||||
let pretty_date = frappe.datetime.prettyDate(frm.doc.last_deployed_on);
|
||||
frm.page.set_indicator(__("Last published {0}", [pretty_date]), "blue");
|
||||
},
|
||||
set_dirty_trigger: function(frm) {
|
||||
$(frm.wrapper).on("dirty", function() {
|
||||
frm.page.set_primary_action(__('Save'), () => frm.save());
|
||||
});
|
||||
},
|
||||
set_deploy_primary_action: function(frm) {
|
||||
if (frm.doc.package_details.length && frm.doc.instances.length) {
|
||||
frm.page.set_primary_action(__("Publish"), function () {
|
||||
frappe.show_alert({
|
||||
message: __("Publishing documents..."),
|
||||
indicator: "green"
|
||||
});
|
||||
|
||||
frappe.call({
|
||||
method: "frappe.custom.doctype.package_publish_tool.package_publish_tool.deploy_package",
|
||||
callback: function() {
|
||||
frm.reload_doc();
|
||||
frappe.msgprint(__("Documents have been published."));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
show_instructions: function(frm) {
|
||||
let field = frm.get_field("html_info");
|
||||
field.html(`
|
||||
<p class="text-muted text-medium">
|
||||
Package Publish Tool let's you copy documents from your site to any other remote site.
|
||||
Follow the steps below to publish.
|
||||
</p>
|
||||
<ol class="text-muted small">
|
||||
<li>Add Document Types that you want to copy from the table below. You can also add filters by expanding the row.</li>
|
||||
<li>Add the Sites URL where you want to copy these documents, and enter the Username and Password.</li>
|
||||
<li>Click on Save. Now, you can click on Publish and the documents will be copied.</li>
|
||||
</ol>
|
||||
`);
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Package Document Type', {
|
||||
form_render: function (frm, cdt, cdn) {
|
||||
function _show_filters(filters, table) {
|
||||
table.find('tbody').empty();
|
||||
|
||||
if (filters.length > 0) {
|
||||
filters.forEach(filter => {
|
||||
const filter_row =
|
||||
$(`<tr>
|
||||
<td>${filter[1]}</td>
|
||||
<td>${filter[2] || ""}</td>
|
||||
<td>${filter[3]}</td>
|
||||
</tr>`);
|
||||
|
||||
table.find('tbody').append(filter_row);
|
||||
});
|
||||
} else {
|
||||
const filter_row = $(`<tr><td colspan="3" class="text-muted text-center">
|
||||
${__("Click to Set Filters")}</td></tr>`);
|
||||
table.find('tbody').append(filter_row);
|
||||
}
|
||||
}
|
||||
|
||||
let row = frappe.get_doc(cdt, cdn);
|
||||
|
||||
let wrapper = $(`[data-fieldname="filters_json"]`).empty();
|
||||
let table = $(`<table class="table table-bordered" style="cursor:pointer; margin:0px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 33%">${__('Filter')}</th>
|
||||
<th style="width: 33%">${__('Condition')}</th>
|
||||
<th>${__('Value')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>`).appendTo(wrapper);
|
||||
$(`<p class="text-muted small">${__("Click table to edit")}</p>`).appendTo(wrapper);
|
||||
|
||||
let filters = JSON.parse(row.filters_json || '[]');
|
||||
_show_filters(filters, table);
|
||||
|
||||
table.on('click', () => {
|
||||
if (!row.document_type) {
|
||||
frappe.msgprint(__("Select Document Type."));
|
||||
return;
|
||||
}
|
||||
|
||||
frappe.model.with_doctype(row.document_type, function() {
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: __('Set Filters'),
|
||||
fields: [
|
||||
{
|
||||
fieldtype: 'HTML',
|
||||
label: 'Filters',
|
||||
fieldname: 'filter_area',
|
||||
}
|
||||
],
|
||||
primary_action: function() {
|
||||
let values = filter_group.get_filters();
|
||||
let flt = [];
|
||||
if (values) {
|
||||
values.forEach(function(value) {
|
||||
flt.push([value[0], value[1], value[2], value[3]]);
|
||||
});
|
||||
}
|
||||
row.filters_json = JSON.stringify(flt);
|
||||
_show_filters(flt, table);
|
||||
dialog.hide();
|
||||
},
|
||||
primary_action_label: "Set"
|
||||
});
|
||||
|
||||
let filter_group = new frappe.ui.FilterGroup({
|
||||
parent: dialog.get_field('filter_area').$wrapper,
|
||||
doctype: row.document_type,
|
||||
on_change: () => {},
|
||||
});
|
||||
filter_group.add_filters_to_filter_group(filters);
|
||||
dialog.show();
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2020-05-13 15:54:38.082657",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"html_info",
|
||||
"sb_00",
|
||||
"package_details",
|
||||
"sb_01",
|
||||
"instances",
|
||||
"last_deployed_on"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"description": "Click on the row for accessing filters.",
|
||||
"fieldname": "package_details",
|
||||
"fieldtype": "Table",
|
||||
"label": "Document Types",
|
||||
"options": "Package Document Type",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "instances",
|
||||
"fieldtype": "Table",
|
||||
"label": "Sites",
|
||||
"options": "Package Publish Target",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "html_info",
|
||||
"fieldtype": "HTML"
|
||||
},
|
||||
{
|
||||
"fieldname": "last_deployed_on",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 1,
|
||||
"label": "Last Deployed On",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "sb_00",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "sb_01",
|
||||
"fieldtype": "Section Break"
|
||||
}
|
||||
],
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-15 17:31:37.060199",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Package Publish Tool",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "All",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import json
|
||||
import datetime
|
||||
import base64
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.file_manager import save_file, get_file
|
||||
from frappe import _
|
||||
from six import string_types
|
||||
from frappe.frappeclient import FrappeClient
|
||||
from frappe.utils import get_datetime_str, get_datetime
|
||||
from frappe.utils.password import get_decrypted_password
|
||||
|
||||
class PackagePublishTool(Document):
|
||||
pass
|
||||
|
||||
@frappe.whitelist()
|
||||
def deploy_package():
|
||||
package, doc = export_package()
|
||||
|
||||
file_name = "Package-" + get_datetime_str(get_datetime())
|
||||
|
||||
length = len(doc.instances)
|
||||
for idx, instance in enumerate(doc.instances):
|
||||
frappe.publish_realtime("package", {"progress": idx, "total": length, "message": instance.instance_url, "prefix": _("Deploying")},
|
||||
user=frappe.session.user)
|
||||
|
||||
install_package_to_remote(package, instance)
|
||||
|
||||
frappe.db.set_value("Package Publish Tool", "Package Publish Tool", "last_deployed_on", frappe.utils.now_datetime())
|
||||
|
||||
def install_package_to_remote(package, instance):
|
||||
try:
|
||||
connection = FrappeClient(instance.instance_url, instance.username, get_decrypted_password(instance.doctype, instance.name))
|
||||
except Exception:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.throw(_("Couldn't connect to site {0}. Please check Error Logs.").format(instance.instance_url))
|
||||
|
||||
try:
|
||||
connection.post_request({
|
||||
"cmd": "frappe.custom.doctype.package_publish_tool.package_publish_tool.import_package",
|
||||
"package": json.dumps(package)
|
||||
})
|
||||
except Exception:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.throw(_("Error while installing package to site {0}. Please check Error Logs.").format(instance.instance_url))
|
||||
|
||||
@frappe.whitelist()
|
||||
def export_package():
|
||||
"""Export package as JSON."""
|
||||
package_doc = frappe.get_single("Package Publish Tool")
|
||||
package = []
|
||||
|
||||
for doctype in package_doc.package_details:
|
||||
filters = []
|
||||
|
||||
if doctype.get("filters_json"):
|
||||
filters = json.loads(doctype.get("filters_json"))
|
||||
|
||||
docs = frappe.get_all(doctype.get("document_type"), filters=filters)
|
||||
length = len(docs)
|
||||
|
||||
for idx, doc in enumerate(docs):
|
||||
frappe.publish_realtime("package", {
|
||||
"progress":idx, "total":length,
|
||||
"message":doctype.get("document_type"),
|
||||
"prefix": _("Exporting")
|
||||
},
|
||||
user=frappe.session.user)
|
||||
|
||||
document = frappe.get_doc(doctype.get("document_type"), doc.name).as_dict()
|
||||
attachments = []
|
||||
|
||||
if doctype.attachments:
|
||||
filters = {
|
||||
"attached_to_doctype": document.get("doctype"),
|
||||
"attached_to_name": document.get("name")
|
||||
}
|
||||
|
||||
for f in frappe.get_list("File", filters=filters):
|
||||
fname, fcontents = get_file(f.name)
|
||||
attachments.append({
|
||||
"fname": fname,
|
||||
"content": base64.b64encode(fcontents).decode('ascii')
|
||||
})
|
||||
|
||||
document.update({
|
||||
"__attachments": attachments,
|
||||
"__overwrite": True if doctype.overwrite else False
|
||||
})
|
||||
|
||||
package.append(document)
|
||||
|
||||
return post_process(package), package_doc
|
||||
|
||||
@frappe.whitelist()
|
||||
def import_package(package=None):
|
||||
"""Import package from JSON."""
|
||||
if isinstance(package, string_types):
|
||||
package = json.loads(package)
|
||||
|
||||
for doc in package:
|
||||
modified = doc.pop("modified")
|
||||
overwrite = doc.pop("__overwrite")
|
||||
attachments = doc.pop("__attachments")
|
||||
exists = frappe.db.exists(doc.get("doctype"), doc.get("name"))
|
||||
|
||||
if not exists:
|
||||
d = frappe.get_doc(doc).insert(ignore_permissions=True, ignore_if_duplicate=True)
|
||||
if attachments:
|
||||
add_attachment(attachments, d)
|
||||
else:
|
||||
docname = doc.pop("name")
|
||||
document = frappe.get_doc(doc.get("doctype"), docname)
|
||||
|
||||
if overwrite:
|
||||
update_document(document, doc, attachments)
|
||||
|
||||
else:
|
||||
if frappe.utils.get_datetime(document.modified) < frappe.utils.get_datetime(modified):
|
||||
update_document(document, doc, attachments)
|
||||
|
||||
def update_document(document, doc, attachments):
|
||||
document.update(doc)
|
||||
document.save()
|
||||
if attachments:
|
||||
add_attachment(attachments, document)
|
||||
|
||||
def add_attachment(attachments, doc):
|
||||
for attachment in attachments:
|
||||
save_file(attachment.get("fname"), base64.b64decode(attachment.get("content")), doc.get("doctype"), doc.get("name"))
|
||||
|
||||
def post_process(package):
|
||||
"""Remove the keys from Document and Child Document. Convert datetime, date, time to str."""
|
||||
del_keys = ('modified_by', 'creation', 'owner', 'idx', 'docstatus')
|
||||
child_del_keys = ('modified_by', 'creation', 'owner', 'idx', 'docstatus', 'name')
|
||||
|
||||
for doc in package:
|
||||
for key in del_keys:
|
||||
if key in doc:
|
||||
del doc[key]
|
||||
|
||||
for key, value in doc.items():
|
||||
stringified_value = get_stringified_value(value)
|
||||
if stringified_value:
|
||||
doc[key] = stringified_value
|
||||
|
||||
if not isinstance(value, list):
|
||||
continue
|
||||
|
||||
for child in value:
|
||||
for child_key in child_del_keys:
|
||||
if child_key in child:
|
||||
del child[child_key]
|
||||
|
||||
for child_key, child_value in child.items():
|
||||
stringified_value = get_stringified_value(child_value)
|
||||
if stringified_value:
|
||||
child[child_key] = stringified_value
|
||||
|
||||
return package
|
||||
|
||||
def get_stringified_value(value):
|
||||
if isinstance(value, datetime.datetime):
|
||||
return frappe.utils.get_datetime_str(value)
|
||||
|
||||
if isinstance(value, datetime.date):
|
||||
return frappe.utils.get_date_str(value)
|
||||
|
||||
if isinstance(value, datetime.timedelta):
|
||||
return frappe.utils.get_time_str(value)
|
||||
|
||||
return None
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestPackagePublishTool(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -63,6 +63,7 @@ CREATE TABLE `tabDocField` (
|
|||
`precision` varchar(255) DEFAULT NULL,
|
||||
`length` int(11) NOT NULL DEFAULT 0,
|
||||
`translatable` int(1) NOT NULL DEFAULT 0,
|
||||
`hide_border` int(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`name`),
|
||||
KEY `parent` (`parent`),
|
||||
KEY `label` (`label`),
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ CREATE TABLE "tabDocField" (
|
|||
"precision" varchar(255) DEFAULT NULL,
|
||||
"length" bigint NOT NULL DEFAULT 0,
|
||||
"translatable" smallint NOT NULL DEFAULT 0,
|
||||
"hide_border" smallint NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY ("name")
|
||||
) ;
|
||||
|
||||
|
|
|
|||
|
|
@ -127,6 +127,8 @@ class Workspace:
|
|||
return name in self.allowed_reports
|
||||
if item_type == "help":
|
||||
return True
|
||||
if item_type == "dashboard":
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
|
@ -272,6 +274,8 @@ class Workspace:
|
|||
for doc in self.onboarding_doc.get_steps():
|
||||
step = doc.as_dict().copy()
|
||||
step.label = _(doc.title)
|
||||
if step.action == "Create Entry":
|
||||
step.is_submittable = frappe.db.get_value("DocType", step.reference_document, 'is_submittable', cache=True)
|
||||
steps.append(step)
|
||||
|
||||
return steps
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
"dashboard_name",
|
||||
"is_default",
|
||||
"charts",
|
||||
"chart_options",
|
||||
"cards"
|
||||
],
|
||||
"fields": [
|
||||
|
|
@ -33,6 +34,13 @@
|
|||
"options": "Dashboard Chart Link",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"description": "Set Default Options for all charts on this Dashboard (Ex: \"colors\": [\"#d1d8dd\", \"#ff5858\"])",
|
||||
"fieldname": "chart_options",
|
||||
"fieldtype": "Code",
|
||||
"label": "Chart Options",
|
||||
"options": "JSON"
|
||||
},
|
||||
{
|
||||
"fieldname": "cards",
|
||||
"fieldtype": "Table",
|
||||
|
|
@ -41,7 +49,7 @@
|
|||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-04-19 17:44:36.237163",
|
||||
"modified": "2020-04-29 13:26:37.362482",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Dashboard",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
from __future__ import unicode_literals
|
||||
from frappe.model.document import Document
|
||||
import frappe
|
||||
from frappe import _
|
||||
import json
|
||||
|
||||
class Dashboard(Document):
|
||||
def on_update(self):
|
||||
|
|
@ -13,13 +15,29 @@ class Dashboard(Document):
|
|||
frappe.db.sql('''update
|
||||
tabDashboard set is_default = 0 where name != %s''', self.name)
|
||||
|
||||
def validate(self):
|
||||
self.validate_custom_options()
|
||||
|
||||
def validate_custom_options(self):
|
||||
if self.chart_options:
|
||||
try:
|
||||
json.loads(self.chart_options)
|
||||
except ValueError as error:
|
||||
frappe.throw(_("Invalid json added in the custom options: {0}").format(error))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_permitted_charts(dashboard_name):
|
||||
permitted_charts = []
|
||||
dashboard = frappe.get_doc('Dashboard', dashboard_name)
|
||||
for chart in dashboard.charts:
|
||||
if frappe.has_permission('Dashboard Chart', doc=chart.chart):
|
||||
permitted_charts.append(chart)
|
||||
chart_dict = frappe._dict()
|
||||
chart_dict.update(chart.as_dict())
|
||||
|
||||
if dashboard.get('chart_options'):
|
||||
chart_dict.custom_options = dashboard.get('chart_options')
|
||||
permitted_charts.append(chart_dict)
|
||||
|
||||
return permitted_charts
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
});
|
||||
|
||||
frm.set_df_property("filters_section", "hidden", 1);
|
||||
frm.trigger('set_time_series');
|
||||
frm.set_query('document_type', function() {
|
||||
return {
|
||||
filters: {
|
||||
|
|
@ -57,6 +58,7 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
}
|
||||
});
|
||||
frm.trigger('update_options');
|
||||
frm.trigger('set_heatmap_year_options');
|
||||
if (frm.doc.report_name) {
|
||||
frm.trigger('set_chart_report_filters');
|
||||
}
|
||||
|
|
@ -70,7 +72,17 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
frm.trigger("show_filters");
|
||||
},
|
||||
|
||||
set_heatmap_year_options: function(frm) {
|
||||
if (frm.doc.type == 'Heatmap') {
|
||||
frappe.db.get_doc('System Settings').then(doc => {
|
||||
const creation_date = doc.creation;
|
||||
frm.set_df_property('heatmap_year', 'options', frappe.dashboard_utils.get_years_since_creation(creation_date));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
chart_type: function(frm) {
|
||||
frm.trigger('set_time_series');
|
||||
if (frm.doc.chart_type == 'Report') {
|
||||
frm.set_query('report_name', () => {
|
||||
return {
|
||||
|
|
@ -80,23 +92,25 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
// set timeseries based on chart type
|
||||
if (['Count', 'Average', 'Sum'].includes(frm.doc.chart_type)) {
|
||||
frm.set_value('timeseries', 1);
|
||||
} else {
|
||||
frm.set_value('timeseries', 0);
|
||||
}
|
||||
|
||||
if (frm.doc.chart_type == 'Group By') {
|
||||
frm.set_df_property('type', 'options', ['Line', 'Bar', 'Percentage', 'Pie']);
|
||||
frm.set_df_property('type', 'options', ['Line', 'Bar', 'Percentage', 'Pie', 'Donut']);
|
||||
} else {
|
||||
frm.set_df_property('type', 'options', ['Line', 'Bar']);
|
||||
frm.set_df_property('type', 'options', ['Line', 'Bar', 'Heatmap']);
|
||||
}
|
||||
|
||||
frm.set_value('document_type', '');
|
||||
}
|
||||
},
|
||||
|
||||
set_time_series: function(frm) {
|
||||
// set timeseries based on chart type
|
||||
if (['Count', 'Average', 'Sum'].includes(frm.doc.chart_type)) {
|
||||
frm.set_value('timeseries', 1);
|
||||
} else {
|
||||
frm.set_value('timeseries', 0);
|
||||
}
|
||||
},
|
||||
|
||||
document_type: function(frm) {
|
||||
// update `based_on` options based on date / datetime fields
|
||||
frm.set_value('source', '');
|
||||
|
|
@ -283,17 +297,7 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
});
|
||||
}
|
||||
} else if (frm.chart_filters.length) {
|
||||
fields = frm.chart_filters.filter(f => {
|
||||
if (f.on_change && !f.reqd) {
|
||||
return false;
|
||||
}
|
||||
if (f.get_query || f.get_data) {
|
||||
f.read_only = 1;
|
||||
}
|
||||
|
||||
return f.fieldname;
|
||||
});
|
||||
|
||||
fields = frm.chart_filters.filter(f => f.fieldname);
|
||||
fields.map( f => {
|
||||
if (filters[f.fieldname]) {
|
||||
let condition = '=';
|
||||
|
|
@ -353,10 +357,10 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
}
|
||||
|
||||
dialog.show();
|
||||
//Set query report object so that it can be used while fetching filter values in the report
|
||||
frappe.query_report = new frappe.views.QueryReport({'filters': dialog.fields_list});
|
||||
dialog.set_values(filters);
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -23,17 +23,18 @@
|
|||
"number_of_groups",
|
||||
"column_break_6",
|
||||
"is_public",
|
||||
"heatmap_year",
|
||||
"timespan",
|
||||
"from_date",
|
||||
"to_date",
|
||||
"time_interval",
|
||||
"timeseries",
|
||||
"type",
|
||||
"filters_section",
|
||||
"filters_json",
|
||||
"chart_options_section",
|
||||
"type",
|
||||
"column_break_2",
|
||||
"color",
|
||||
"column_break_2",
|
||||
"custom_options",
|
||||
"section_break_10",
|
||||
"last_synced_on"
|
||||
|
|
@ -85,14 +86,14 @@
|
|||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "timeseries",
|
||||
"depends_on": "eval: doc.timeseries && doc.type !== 'Heatmap'",
|
||||
"fieldname": "timespan",
|
||||
"fieldtype": "Select",
|
||||
"label": "Timespan",
|
||||
"options": "Last Year\nLast Quarter\nLast Month\nLast Week\nSelect Date Range"
|
||||
},
|
||||
{
|
||||
"depends_on": "timeseries",
|
||||
"depends_on": "eval: doc.timeseries && doc.type !== 'Heatmap'",
|
||||
"fieldname": "time_interval",
|
||||
"fieldtype": "Select",
|
||||
"label": "Time Interval",
|
||||
|
|
@ -100,7 +101,7 @@
|
|||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval: ['Count', 'Sum', 'Average'].includes(doc.chart_type)",
|
||||
"depends_on": "eval: !['Group By', 'Report'].includes(doc.chart_type)\n",
|
||||
"fieldname": "timeseries",
|
||||
"fieldtype": "Check",
|
||||
"label": "Time Series"
|
||||
|
|
@ -123,10 +124,11 @@
|
|||
"label": "Chart Options"
|
||||
},
|
||||
{
|
||||
"default": "Line",
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Select",
|
||||
"label": "Type",
|
||||
"options": "Line\nBar\nPercentage\nPie\nDonut",
|
||||
"options": "Line\nBar\nHeatmap",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
|
|
@ -134,7 +136,7 @@
|
|||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.chart_type !== 'Report'",
|
||||
"depends_on": "eval: doc.chart_type !== 'Report' && doc.type !== 'Heatmap'",
|
||||
"fieldname": "color",
|
||||
"fieldtype": "Color",
|
||||
"label": "Color"
|
||||
|
|
@ -217,7 +219,7 @@
|
|||
"options": "Dashboard Chart Field"
|
||||
},
|
||||
{
|
||||
"description": "Ex: \"colors\": [\"#d1d8dd\", \"#ff5858\"]",
|
||||
"description": "Ex: \"colors\": [\"#d1d8dd\", \"#ff5858\"] (the options set here will override the chart options set in the Dashboard)",
|
||||
"fieldname": "custom_options",
|
||||
"fieldtype": "Code",
|
||||
"label": "Custom Options"
|
||||
|
|
@ -228,10 +230,16 @@
|
|||
"fieldname": "is_public",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Public"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.type == 'Heatmap'",
|
||||
"fieldname": "heatmap_year",
|
||||
"fieldtype": "Select",
|
||||
"label": "Year"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-05-01 15:22:59.119341",
|
||||
"modified": "2020-05-01 19:45:01.669384",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Dashboard Chart",
|
||||
|
|
@ -275,4 +283,4 @@
|
|||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from frappe import _
|
|||
import datetime
|
||||
import json
|
||||
from frappe.utils.dashboard import cache_source, get_from_date_from_timespan
|
||||
from frappe.utils import nowdate, add_to_date, getdate, get_last_day, formatdate, get_datetime
|
||||
from frappe.utils import nowdate, add_to_date, getdate, get_last_day, formatdate, get_datetime, cint
|
||||
from frappe.model.naming import append_number_if_name_exists
|
||||
from frappe.boot import get_allowed_reports
|
||||
from frappe.model.document import Document
|
||||
|
|
@ -58,13 +58,13 @@ def has_permission(doc, ptype, user):
|
|||
@frappe.whitelist()
|
||||
@cache_source
|
||||
def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
|
||||
to_date = None, timespan = None, time_interval = None, refresh = None):
|
||||
to_date = None, timespan = None, time_interval = None, heatmap_year=None, refresh = None):
|
||||
if chart_name:
|
||||
chart = frappe.get_doc('Dashboard Chart', chart_name)
|
||||
else:
|
||||
chart = frappe._dict(frappe.parse_json(chart))
|
||||
|
||||
|
||||
heatmap_year = heatmap_year or chart.heatmap_year
|
||||
timespan = timespan or chart.timespan
|
||||
|
||||
if timespan == 'Select Date Range':
|
||||
|
|
@ -87,7 +87,10 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d
|
|||
if chart.chart_type == 'Group By':
|
||||
chart_config = get_group_by_chart_config(chart, filters)
|
||||
else:
|
||||
chart_config = get_chart_config(chart, filters, timespan, timegrain, from_date, to_date)
|
||||
if chart.type == 'Heatmap':
|
||||
chart_config = get_heatmap_chart_config(chart, filters, heatmap_year)
|
||||
else:
|
||||
chart_config = get_chart_config(chart, filters, timespan, timegrain, from_date, to_date)
|
||||
|
||||
return chart_config
|
||||
|
||||
|
|
@ -174,6 +177,41 @@ def get_chart_config(chart, filters, timespan, timegrain, from_date, to_date):
|
|||
|
||||
return chart_config
|
||||
|
||||
def get_heatmap_chart_config(chart, filters, heatmap_year):
|
||||
aggregate_function = get_aggregate_function(chart.chart_type)
|
||||
value_field = chart.value_based_on or '1'
|
||||
doctype = chart.document_type
|
||||
datefield = chart.based_on
|
||||
year = cint(heatmap_year) if heatmap_year else getdate(nowdate()).year
|
||||
year_start_date = datetime.date(year, 1, 1).strftime('%Y-%m-%d')
|
||||
next_year_start_date = datetime.date(year + 1, 1, 1).strftime('%Y-%m-%d')
|
||||
|
||||
filters.append([doctype, datefield, '>', "{date}".format(date=year_start_date), False])
|
||||
filters.append([doctype, datefield, '<', "{date}".format(date=next_year_start_date), False])
|
||||
|
||||
if frappe.db.db_type == 'mariadb':
|
||||
timestamp_field = 'unix_timestamp({datefield})'.format(datefield=datefield)
|
||||
else:
|
||||
timestamp_field = 'extract(epoch from timestamp {datefield})'.format(datefield=datefield)
|
||||
|
||||
data = dict(frappe.db.get_all(
|
||||
doctype,
|
||||
fields = [
|
||||
timestamp_field,
|
||||
'{aggregate_function}({value_field})'.format(aggregate_function=aggregate_function, value_field=value_field),
|
||||
],
|
||||
filters = filters,
|
||||
group_by = 'date({datefield})'.format(datefield=datefield),
|
||||
as_list = 1,
|
||||
order_by = '{datefield} asc'.format(datefield=datefield),
|
||||
ignore_ifnull = True
|
||||
))
|
||||
|
||||
chart_config = {
|
||||
'labels': [],
|
||||
'dataPoints': data,
|
||||
}
|
||||
return chart_config
|
||||
|
||||
def get_group_by_chart_config(chart, filters):
|
||||
|
||||
|
|
@ -397,11 +435,11 @@ class DashboardChart(Document):
|
|||
|
||||
def check_document_type(self):
|
||||
if frappe.get_meta(self.document_type).issingle:
|
||||
frappe.throw("You cannot create a dashboard chart from single DocTypes")
|
||||
frappe.throw(_("You cannot create a dashboard chart from single DocTypes"))
|
||||
|
||||
def validate_custom_options(self):
|
||||
if self.custom_options:
|
||||
try:
|
||||
json.loads(self.custom_options)
|
||||
except ValueError as error:
|
||||
frappe.throw("Invalid json added in the custom options: %s" % error)
|
||||
frappe.throw(_("Invalid json added in the custom options: {0}").format(error))
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ frappe.ui.form.on('Desk Page', {
|
|||
frm.set_df_property("extends", "read_only", true);
|
||||
}
|
||||
|
||||
if (frm.doc.for_user || frm.doc.is_standard) {
|
||||
if (frm.doc.for_user || (frm.doc.is_standard && !frappe.boot.developer_mode)) {
|
||||
frm.trigger('disable_form');
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Type",
|
||||
"options": "DocType\nReport\nPage",
|
||||
"options": "DocType\nReport\nPage\nDashboard",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
|
|
@ -88,7 +88,7 @@
|
|||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-13 19:26:34.229669",
|
||||
"modified": "2020-05-14 16:02:15.420993",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Desk Shortcut",
|
||||
|
|
|
|||
|
|
@ -15,11 +15,16 @@
|
|||
"action",
|
||||
"column_break_7",
|
||||
"reference_document",
|
||||
"show_full_form",
|
||||
"is_single",
|
||||
"reference_report",
|
||||
"report_reference_doctype",
|
||||
"report_type",
|
||||
"report_description",
|
||||
"path",
|
||||
"callback_title",
|
||||
"callback_message",
|
||||
"validate_action",
|
||||
"field",
|
||||
"value_to_validate",
|
||||
"video_url"
|
||||
|
|
@ -58,7 +63,7 @@
|
|||
"fieldname": "action",
|
||||
"fieldtype": "Select",
|
||||
"label": "Action",
|
||||
"options": "Create Entry\nUpdate Settings\nShow Form Tour\nView Report\nWatch Video",
|
||||
"options": "Create Entry\nUpdate Settings\nShow Form Tour\nView Report\nGo to Page\nWatch Video",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
|
|
@ -70,6 +75,7 @@
|
|||
"fieldname": "reference_document",
|
||||
"fieldtype": "Link",
|
||||
"label": "Reference Document",
|
||||
"mandatory_depends_on": "eval:doc.action == \"Create Entry\" || doc.action == \"Update Settings\" || doc.action == \"Create Entry\" || doc.action == \"Show Form Tour\"",
|
||||
"options": "DocType"
|
||||
},
|
||||
{
|
||||
|
|
@ -84,7 +90,8 @@
|
|||
"depends_on": "eval:doc.action == \"Watch Video\"",
|
||||
"fieldname": "video_url",
|
||||
"fieldtype": "Data",
|
||||
"label": "Video URL"
|
||||
"label": "Video URL",
|
||||
"mandatory_depends_on": "eval:doc.action == \"Watch Video\""
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.action == \"View Report\"",
|
||||
|
|
@ -102,17 +109,19 @@
|
|||
"label": "Is Skipped"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.action == \"Update Settings\"",
|
||||
"depends_on": "eval:doc.action == \"Update Settings\" && doc.validate_action",
|
||||
"fieldname": "field",
|
||||
"fieldtype": "Select",
|
||||
"label": "Field"
|
||||
"label": "Field",
|
||||
"mandatory_depends_on": "eval:doc.action == \"Update Settings\" && doc.validate_action"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.action == \"Update Settings\"",
|
||||
"depends_on": "eval:doc.action == \"Update Settings\" && doc.validate_action",
|
||||
"description": "Use % for any non empty value.",
|
||||
"fieldname": "value_to_validate",
|
||||
"fieldtype": "Data",
|
||||
"label": "Value to Validate"
|
||||
"label": "Value to Validate",
|
||||
"mandatory_depends_on": "eval:doc.action == \"Update Settings\" && doc.validate_action"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.action == \"View Report\"",
|
||||
|
|
@ -136,10 +145,46 @@
|
|||
"fieldname": "is_single",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Single"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.action == \"Go to Page\"",
|
||||
"description": "Example: #Tree/Account",
|
||||
"fieldname": "path",
|
||||
"fieldtype": "Data",
|
||||
"label": "Path",
|
||||
"mandatory_depends_on": "eval:doc.action == \"Go to Page\""
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.action == \"Go to Page\"",
|
||||
"fieldname": "callback_title",
|
||||
"fieldtype": "Data",
|
||||
"label": "Callback Title"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.action == \"Go to Page\"",
|
||||
"description": "This will be shown in a modal after routing",
|
||||
"fieldname": "callback_message",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Callback Message"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.action == \"Update Settings\"",
|
||||
"fieldname": "validate_action",
|
||||
"fieldtype": "Check",
|
||||
"label": "Validate Field"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.action == \"Create Entry\"",
|
||||
"description": "Show full form instead of a quick entry modal",
|
||||
"fieldname": "show_full_form",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Full Form?"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-05-11 13:24:05.457160",
|
||||
"modified": "2020-05-14 15:10:05.627706",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Onboarding Step",
|
||||
|
|
|
|||
|
|
@ -10,3 +10,7 @@ class OnboardingStep(Document):
|
|||
def before_export(self, doc):
|
||||
doc.is_complete = 0
|
||||
doc.is_skipped = 0
|
||||
|
||||
def validate(self):
|
||||
if self.action == "Go to Page":
|
||||
self.is_mandatory = 0
|
||||
|
|
|
|||
|
|
@ -212,7 +212,10 @@ def get_notification_config():
|
|||
def get_filters_for(doctype):
|
||||
'''get open filters for doctype'''
|
||||
config = get_notification_config()
|
||||
return config.get("for_doctype").get(doctype, {})
|
||||
doctype_config = config.get("for_doctype").get(doctype, {})
|
||||
filters = doctype_config if not isinstance(doctype_config, string_types) else None
|
||||
|
||||
return filters
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.read_only()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ def install():
|
|||
update_global_search_doctypes()
|
||||
setup_email_linking()
|
||||
sync_dashboards()
|
||||
add_unsubscribe()
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_genders():
|
||||
|
|
@ -37,3 +38,15 @@ def setup_email_linking():
|
|||
"email_id": "email_linking@example.com",
|
||||
})
|
||||
doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
|
||||
|
||||
def add_unsubscribe():
|
||||
email_unsubscribe = [
|
||||
{"email": "admin@example.com", "global_unsubscribe": 1},
|
||||
{"email": "guest@example.com", "global_unsubscribe": 1}
|
||||
]
|
||||
|
||||
for unsubscribe in email_unsubscribe:
|
||||
if not frappe.get_all("Email Unsubscribe", filters=unsubscribe):
|
||||
doc = frappe.new_doc("Email Unsubscribe")
|
||||
doc.update(unsubscribe)
|
||||
doc.insert(ignore_permissions=True)
|
||||
|
|
|
|||
|
|
@ -108,21 +108,6 @@ class UserProfile {
|
|||
});
|
||||
}
|
||||
|
||||
get_years_since_creation() {
|
||||
//Get years since user account created
|
||||
this.user_creation = frappe.boot.user.creation;
|
||||
let creation_year = this.get_year(this.user_creation);
|
||||
let current_year = this.get_year(frappe.datetime.now_date());
|
||||
let years_list = [];
|
||||
for (var year = current_year; year >= creation_year; year--) {
|
||||
years_list.push(year);
|
||||
}
|
||||
return years_list;
|
||||
}
|
||||
|
||||
get_year(date_str) {
|
||||
return date_str.substring(0, date_str.indexOf('-'));
|
||||
}
|
||||
|
||||
render_line_chart() {
|
||||
this.line_chart_filters = [['Energy Point Log', 'user', '=', this.user_id, false]];
|
||||
|
|
@ -246,8 +231,8 @@ class UserProfile {
|
|||
create_heatmap_chart_filters() {
|
||||
let filters = [
|
||||
{
|
||||
label: this.get_year(frappe.datetime.now_date()),
|
||||
options: this.get_years_since_creation(),
|
||||
label: frappe.dashboard_utils.get_year(frappe.datetime.now_date()),
|
||||
options: frappe.dashboard_utils.get_years_since_creation(frappe.boot.user.creation),
|
||||
action: (selected_item) => {
|
||||
this.update_heatmap_data(frappe.datetime.obj_to_str(selected_item));
|
||||
}
|
||||
|
|
|
|||
14
frappe/integrations/frappe_providers/__init__.py
Normal file
14
frappe/integrations/frappe_providers/__init__.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# imports - standard imports
|
||||
import sys
|
||||
|
||||
# imports - module imports
|
||||
from frappe.integrations.frappe_providers.frappecloud import frappecloud_migrator
|
||||
|
||||
|
||||
def migrate_to(local_site, frappe_provider):
|
||||
if frappe_provider in ("frappe.cloud", "frappecloud.com"):
|
||||
frappe_provider = "frappecloud.com"
|
||||
return frappecloud_migrator(local_site, frappe_provider)
|
||||
else:
|
||||
print("{} is not supported yet".format(frappe_provider))
|
||||
sys.exit(1)
|
||||
268
frappe/integrations/frappe_providers/frappecloud.py
Normal file
268
frappe/integrations/frappe_providers/frappecloud.py
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
# imports - standard imports
|
||||
import getpass
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
|
||||
# imports - third party imports
|
||||
import click
|
||||
from html2text import html2text
|
||||
import requests
|
||||
|
||||
# imports - module imports
|
||||
import frappe
|
||||
import frappe.utils.backups
|
||||
from frappe.utils import get_installed_apps_info
|
||||
from frappe.utils.commands import render_table, add_line_after
|
||||
|
||||
|
||||
def get_new_site_options():
|
||||
site_options_sc = session.post(options_url)
|
||||
|
||||
if site_options_sc.ok:
|
||||
site_options = site_options_sc.json()["message"]
|
||||
return site_options
|
||||
else:
|
||||
print("Couldn't retrive New site information: {}".format(site_options_sc.status_code))
|
||||
|
||||
|
||||
def is_valid_subdomain(subdomain):
|
||||
if len(subdomain) < 5:
|
||||
print("Subdomain too short. Use 5 or more characters")
|
||||
return False
|
||||
matched = re.match("^[a-z0-9][a-z0-9-]*[a-z0-9]$", subdomain)
|
||||
if matched:
|
||||
return True
|
||||
print("Subdomain contains invalid characters. Use lowercase characters, numbers and hyphens")
|
||||
|
||||
|
||||
def is_subdomain_available(subdomain):
|
||||
res = session.post(site_exists_url, {"subdomain": subdomain})
|
||||
if res.ok:
|
||||
available = not res.json()["message"]
|
||||
if not available:
|
||||
print("Subdomain already exists! Try another one")
|
||||
|
||||
return available
|
||||
|
||||
|
||||
def render_plan_table(plans_list):
|
||||
plans_table = []
|
||||
|
||||
# title row
|
||||
visible_headers = ["name", "cpu_time_per_day"]
|
||||
plans_table.append(["Plan", "CPU Time"])
|
||||
|
||||
# all rows
|
||||
for plan in plans_list:
|
||||
plan, cpu_time = [plan[header] for header in visible_headers]
|
||||
plans_table.append([plan, "{} hour{}/day".format(cpu_time, "" if cpu_time < 2 else "s")])
|
||||
|
||||
render_table(plans_table)
|
||||
|
||||
|
||||
@add_line_after
|
||||
def choose_plan(plans_list):
|
||||
print("{} plans available".format(len(plans_list)))
|
||||
available_plans = [plan["name"] for plan in plans_list]
|
||||
render_plan_table(plans_list)
|
||||
|
||||
while True:
|
||||
input_plan = click.prompt("Select Plan").strip()
|
||||
if input_plan in available_plans:
|
||||
print("{} Plan selected ✅".format(input_plan))
|
||||
return input_plan
|
||||
else:
|
||||
print("Invalid Selection ❌")
|
||||
|
||||
|
||||
@add_line_after
|
||||
def check_app_compat(available_group):
|
||||
is_compat = True
|
||||
incompatible_apps, filtered_apps, branch_msgs = [], [], []
|
||||
existing_group = [(app["app_name"], app["branch"]) for app in get_installed_apps_info()]
|
||||
print("Checking availability of existing app group")
|
||||
|
||||
for (app, branch) in existing_group:
|
||||
info = [ (a["name"], a["branch"]) for a in available_group["apps"] if a["scrubbed"] == app ]
|
||||
if info:
|
||||
app_title, available_branch = info[0]
|
||||
|
||||
if branch != available_branch:
|
||||
print("⚠️ App {}:{} => {}".format(app, branch, available_branch))
|
||||
branch_msgs.append([app, branch, available_branch])
|
||||
filtered_apps.append(app_title)
|
||||
is_compat = False
|
||||
|
||||
else:
|
||||
print("✅ App {}:{}".format(app, branch))
|
||||
filtered_apps.append(app_title)
|
||||
|
||||
else:
|
||||
incompatible_apps.append(app)
|
||||
print("❌ App {}:{}".format(app, branch))
|
||||
is_compat = False
|
||||
|
||||
start_msg = "\nSelecting this group will "
|
||||
incompatible_apps = ("\n\nDrop the following apps:\n" + "\n".join(incompatible_apps)) if incompatible_apps else ""
|
||||
branch_change = ("\n\nUpgrade the following apps:\n" + "\n".join(["{}: {} => {}".format(*x) for x in branch_msgs])) if branch_msgs else ""
|
||||
changes = (incompatible_apps + branch_change) or "be perfect for you :)"
|
||||
warning_message = start_msg + changes
|
||||
print(warning_message)
|
||||
|
||||
return is_compat, filtered_apps
|
||||
|
||||
|
||||
def render_group_table(app_groups):
|
||||
# title row
|
||||
app_groups_table = [["#", "App Group", "Apps"]]
|
||||
|
||||
# all rows
|
||||
for idx, app_group in enumerate(app_groups):
|
||||
apps_list = ", ".join(["{}:{}".format(app["scrubbed"], app["branch"]) for app in app_group["apps"]])
|
||||
row = [idx + 1, app_group["name"], apps_list]
|
||||
app_groups_table.append(row)
|
||||
|
||||
render_table(app_groups_table)
|
||||
|
||||
|
||||
@add_line_after
|
||||
def filter_apps(app_groups):
|
||||
render_group_table(app_groups)
|
||||
|
||||
while True:
|
||||
app_group_index = click.prompt("Select App Group Number", type=int) - 1
|
||||
try:
|
||||
if app_group_index == -1:
|
||||
raise IndexError
|
||||
selected_group = app_groups[app_group_index]
|
||||
except IndexError:
|
||||
print("Invalid Selection ❌")
|
||||
continue
|
||||
|
||||
is_compat, filtered_apps = check_app_compat(selected_group)
|
||||
|
||||
if is_compat or click.confirm("Continue anyway?"):
|
||||
print("App Group {} selected! ✅".format(selected_group["name"]))
|
||||
break
|
||||
|
||||
return selected_group["name"], filtered_apps
|
||||
|
||||
@add_line_after
|
||||
def create_session():
|
||||
# take user input from STDIN
|
||||
username = click.prompt("Username").strip()
|
||||
password = getpass.unix_getpass()
|
||||
|
||||
auth_credentials = {"usr": username, "pwd": password}
|
||||
|
||||
session = requests.Session()
|
||||
login_sc = session.post(login_url, auth_credentials)
|
||||
|
||||
if login_sc.ok:
|
||||
print("Authorization Successful! ✅")
|
||||
session.headers.update({"X-Press-Team": username})
|
||||
return session
|
||||
else:
|
||||
print("Authorization Failed with Error Code {}".format(login_sc.status_code))
|
||||
|
||||
|
||||
@add_line_after
|
||||
def get_subdomain(domain):
|
||||
while True:
|
||||
subdomain = click.prompt("Enter subdomain").strip()
|
||||
if is_valid_subdomain(subdomain) and is_subdomain_available(subdomain):
|
||||
print("Site Domain: {}.{}".format(subdomain, domain))
|
||||
return subdomain
|
||||
|
||||
|
||||
@add_line_after
|
||||
def upload_backup(local_site):
|
||||
# take backup
|
||||
files_session = {}
|
||||
print("Taking backup for site {}".format(local_site))
|
||||
odb = frappe.utils.backups.new_backup(ignore_files=False, force=True)
|
||||
|
||||
# upload files
|
||||
for x, (file_type, file_path) in enumerate([
|
||||
("database", odb.backup_path_db),
|
||||
("public", odb.backup_path_files),
|
||||
("private", odb.backup_path_private_files)
|
||||
]):
|
||||
file_upload_response = session.post(files_url, data={}, files={
|
||||
"file": open(file_path, "rb"),
|
||||
"is_private": 1,
|
||||
"folder": "Home",
|
||||
"method": "press.api.site.upload_backup",
|
||||
"type": file_type
|
||||
})
|
||||
print("Uploading files ({}/3)".format(x+1), end="\r")
|
||||
if file_upload_response.ok:
|
||||
files_session[file_type] = file_upload_response.json()["message"]
|
||||
else:
|
||||
print("Upload failed for: {}".format(file_path))
|
||||
|
||||
files_uploaded = { k: v["file_url"] for k, v in files_session.items() }
|
||||
print("Uploaded backup files! ✅")
|
||||
|
||||
return files_uploaded
|
||||
|
||||
|
||||
def frappecloud_migrator(local_site, remote_site):
|
||||
global login_url, upload_url, files_url, options_url, site_exists_url, session
|
||||
|
||||
login_url = "https://{}/api/method/login".format(remote_site)
|
||||
upload_url = "https://{}/api/method/press.api.site.new".format(remote_site)
|
||||
files_url = "https://{}/api/method/upload_file".format(remote_site)
|
||||
options_url = "https://{}/api/method/press.api.site.options_for_new".format(remote_site)
|
||||
site_exists_url = "https://{}/api/method/press.api.site.exists".format(remote_site)
|
||||
|
||||
print("Frappe Cloud credentials @ {}".format(remote_site))
|
||||
|
||||
# get credentials + auth user + start session
|
||||
session = create_session()
|
||||
|
||||
if session:
|
||||
# connect to site db
|
||||
frappe.init(site=local_site)
|
||||
frappe.connect()
|
||||
|
||||
# get new site options
|
||||
site_options = get_new_site_options()
|
||||
|
||||
# set preferences from site options
|
||||
subdomain = get_subdomain(site_options["domain"])
|
||||
plan = choose_plan(site_options["plans"])
|
||||
|
||||
app_groups = site_options["groups"]
|
||||
selected_group, filtered_apps = filter_apps(app_groups)
|
||||
files_uploaded = upload_backup(local_site)
|
||||
|
||||
# push to frappe_cloud
|
||||
payload = json.dumps({
|
||||
"site": {
|
||||
"apps": filtered_apps,
|
||||
"files": files_uploaded,
|
||||
"group": selected_group,
|
||||
"name": subdomain,
|
||||
"plan": plan
|
||||
}
|
||||
})
|
||||
|
||||
session.headers.update({"Content-Type": "application/json; charset=utf-8"})
|
||||
site_creation_request = session.post(upload_url, payload)
|
||||
frappe.destroy()
|
||||
|
||||
if site_creation_request.ok:
|
||||
site_url = site_creation_request.json()["message"]
|
||||
print("Your site {} is being migrated ✨".format(local_site))
|
||||
print("View your site dashboard at {}/dashboard/#/sites/{}".format(remote_site, site_url))
|
||||
print("Your site URL: {}".format(site_url))
|
||||
else:
|
||||
print("Request failed with error code {}".format(site_creation_request.status_code))
|
||||
reason = html2text(site_creation_request.text)
|
||||
print(reason)
|
||||
sys.exit(1)
|
||||
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
|
@ -5,11 +5,13 @@ from __future__ import unicode_literals
|
|||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import frappe
|
||||
import frappe.translate
|
||||
import frappe.modules.patch_handler
|
||||
import frappe.model.sync
|
||||
from frappe.utils.fixtures import sync_fixtures
|
||||
from frappe.utils.connections import check_connection
|
||||
from frappe.utils.dashboard import sync_dashboards
|
||||
from frappe.cache_manager import clear_global_cache
|
||||
from frappe.desk.notifications import clear_notifications
|
||||
|
|
@ -19,6 +21,7 @@ from frappe.modules.utils import sync_customizations
|
|||
from frappe.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs
|
||||
from frappe.utils import global_search
|
||||
|
||||
|
||||
def migrate(verbose=True, rebuild_website=False, skip_failing=False):
|
||||
'''Migrate all apps to the latest version, will:
|
||||
- run before migrate hooks
|
||||
|
|
@ -32,6 +35,19 @@ def migrate(verbose=True, rebuild_website=False, skip_failing=False):
|
|||
- run after migrate hooks
|
||||
'''
|
||||
|
||||
service_status = check_connection(redis_services=["redis_cache"])
|
||||
if False in service_status.values():
|
||||
for service in service_status:
|
||||
if not service_status.get(service, True):
|
||||
print("{} service is not running.".format(service))
|
||||
print("""Cannot run bench migrate without the services running.
|
||||
If you are running bench in development mode, make sure that bench is running:
|
||||
|
||||
$ bench start
|
||||
|
||||
Otherwise, check the server logs and ensure that all the required services are running.""")
|
||||
sys.exit(1)
|
||||
|
||||
touched_tables_file = frappe.get_site_path('touched_tables.json')
|
||||
if os.path.exists(touched_tables_file):
|
||||
os.remove(touched_tables_file)
|
||||
|
|
@ -67,6 +83,9 @@ def migrate(verbose=True, rebuild_website=False, skip_failing=False):
|
|||
# add static pages to global search
|
||||
global_search.update_global_search_for_all_web_pages()
|
||||
|
||||
# updating installed applications data
|
||||
frappe.get_single('Installed Applications').update_versions()
|
||||
|
||||
#run after_migrate hooks
|
||||
for app in frappe.get_installed_apps():
|
||||
for fn in frappe.get_hooks('after_migrate', app_name=app):
|
||||
|
|
|
|||
|
|
@ -443,34 +443,41 @@ class Meta(Document):
|
|||
|
||||
def add_doctype_links(self, data):
|
||||
'''add `links` child table in standard link dashboard format'''
|
||||
dashboard_links = []
|
||||
|
||||
if hasattr(self, 'links') and self.links:
|
||||
if not data.transactions:
|
||||
# init groups
|
||||
data.transactions = []
|
||||
data.non_standard_fieldnames = {}
|
||||
dashboard_links.extend(self.links)
|
||||
|
||||
for link in self.links:
|
||||
link.added = False
|
||||
for group in data.transactions:
|
||||
group = frappe._dict(group)
|
||||
# group found
|
||||
if link.group and group.label == link.group:
|
||||
if link.link_doctype not in group.get('items'):
|
||||
group.get('items').append(link.link_doctype)
|
||||
link.added = True
|
||||
if frappe.get_all("Custom Link", {"document_type": self.name}):
|
||||
dashboard_links.extend(frappe.get_doc("Custom Link", self.name).links)
|
||||
|
||||
if not link.added:
|
||||
# group not found, make a new group
|
||||
data.transactions.append(dict(
|
||||
label = link.group,
|
||||
items = [link.link_doctype]
|
||||
))
|
||||
if not data.transactions:
|
||||
# init groups
|
||||
data.transactions = []
|
||||
data.non_standard_fieldnames = {}
|
||||
|
||||
if link.link_fieldname != data.fieldname:
|
||||
if data.fieldname:
|
||||
data.non_standard_fieldnames[link.link_doctype] = link.link_fieldname
|
||||
else:
|
||||
data.fieldname = link.link_fieldname
|
||||
for link in dashboard_links:
|
||||
link.added = False
|
||||
for group in data.transactions:
|
||||
group = frappe._dict(group)
|
||||
# group found
|
||||
if link.group and group.label == link.group:
|
||||
if link.link_doctype not in group.get('items'):
|
||||
group.get('items').append(link.link_doctype)
|
||||
link.added = True
|
||||
|
||||
if not link.added:
|
||||
# group not found, make a new group
|
||||
data.transactions.append(dict(
|
||||
label = link.group,
|
||||
items = [link.link_doctype]
|
||||
))
|
||||
|
||||
if link.link_fieldname != data.fieldname:
|
||||
if data.fieldname:
|
||||
data.non_standard_fieldnames[link.link_doctype] = link.link_fieldname
|
||||
else:
|
||||
data.fieldname = link.link_fieldname
|
||||
|
||||
|
||||
def get_row_template(self):
|
||||
|
|
|
|||
|
|
@ -280,3 +280,4 @@ frappe.patches.v13_0.set_read_times
|
|||
frappe.patches.v13_0.remove_web_view
|
||||
frappe.patches.v13_0.remove_tailwind_from_page_builder
|
||||
frappe.patches.v13_0.rename_onboarding
|
||||
frappe.patches.v13_0.email_unsubscribe
|
||||
|
|
|
|||
13
frappe/patches/v13_0/email_unsubscribe.py
Normal file
13
frappe/patches/v13_0/email_unsubscribe.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import frappe
|
||||
|
||||
def execute():
|
||||
email_unsubscribe = [
|
||||
{"email": "admin@example.com", "global_unsubscribe": 1},
|
||||
{"email": "guest@example.com", "global_unsubscribe": 1}
|
||||
]
|
||||
|
||||
for unsubscribe in email_unsubscribe:
|
||||
if not frappe.get_all("Email Unsubscribe", filters=unsubscribe):
|
||||
doc = frappe.new_doc("Email Unsubscribe")
|
||||
doc.update(unsubscribe)
|
||||
doc.insert(ignore_permissions=True)
|
||||
|
|
@ -2259,14 +2259,19 @@ class extends Component {
|
|||
) : null,
|
||||
h("div","",
|
||||
h("div", { class: "panel-title" },
|
||||
h("div", { class: "cursor-pointer", onclick: () => { frappe.set_route(item.route) }},
|
||||
h("div", { class: "cursor-pointer", onclick: () => {
|
||||
frappe.session.user !== "Guest" ?
|
||||
frappe.set_route(item.route) : null;
|
||||
}},
|
||||
h(frappe.Chat.Widget.MediaProfile, { ...item })
|
||||
)
|
||||
)
|
||||
),
|
||||
h("div", { class: popper ? "col-xs-1" : "col-xs-3" },
|
||||
h("div", { class: popper ? "col-xs-2" : "col-xs-3" },
|
||||
h("div", { class: "text-right" },
|
||||
|
||||
frappe._.is_mobile() && h(frappe.components.Button, { class: "frappe-chat-close", onclick: props.toggle },
|
||||
h(frappe.components.Octicon, { type: "x" })
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -651,6 +651,12 @@ frappe.ui.form.Form = class FrappeForm {
|
|||
callback && callback();
|
||||
me.script_manager.trigger("on_submit")
|
||||
.then(() => resolve(me));
|
||||
if (frappe.route_hooks.after_submit) {
|
||||
let route_callback = frappe.route_hooks.after_submit;
|
||||
delete frappe.route_hooks.after_submit;
|
||||
|
||||
route_callback(me);
|
||||
}
|
||||
}
|
||||
}, btn, () => me.handle_save_fail(btn, on_error), resolve);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -599,12 +599,15 @@ frappe.ui.form.Section = Class.extend({
|
|||
if(this.df.cssClass) {
|
||||
this.wrapper.addClass(this.df.cssClass);
|
||||
}
|
||||
if (this.df.hide_border) {
|
||||
this.wrapper.toggleClass("hide-border", true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// for bc
|
||||
this.body = $('<div class="section-body">').appendTo(this.wrapper);
|
||||
},
|
||||
|
||||
make_head: function() {
|
||||
var me = this;
|
||||
if(!this.df.collapsible) {
|
||||
|
|
@ -663,9 +666,11 @@ frappe.ui.form.Section = Class.extend({
|
|||
}
|
||||
});
|
||||
},
|
||||
|
||||
is_collapsed() {
|
||||
return this.body.hasClass('hide');
|
||||
},
|
||||
|
||||
has_missing_mandatory: function() {
|
||||
var missing_mandatory = false;
|
||||
for (var j=0, l=this.fields_list.length; j < l; j++) {
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ frappe.ui.form.QuickEntryForm = Class.extend({
|
|||
});
|
||||
|
||||
this.register_primary_action();
|
||||
this.render_edit_in_full_page_link();
|
||||
!this.force && this.render_edit_in_full_page_link();
|
||||
// ctrl+enter to save
|
||||
this.dialog.wrapper.keydown(function(e) {
|
||||
if((e.ctrlKey || e.metaKey) && e.which==13) {
|
||||
|
|
@ -213,8 +213,15 @@ frappe.ui.form.QuickEntryForm = Class.extend({
|
|||
me.dialog.doc = r.message;
|
||||
if (frappe._from_link) {
|
||||
frappe.ui.form.update_calling_link(me.dialog.doc);
|
||||
} else {
|
||||
if (me.after_insert) {
|
||||
me.after_insert(me.dialog.doc);
|
||||
} else {
|
||||
me.open_form_if_not_list();
|
||||
}
|
||||
}
|
||||
cur_frm.reload_doc();
|
||||
|
||||
cur_frm && cur_frm.reload_doc();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -82,5 +82,21 @@ frappe.dashboard_utils = {
|
|||
).then(settings => {
|
||||
return settings;
|
||||
});
|
||||
},
|
||||
|
||||
get_years_since_creation(creation) {
|
||||
//Get years since user account created
|
||||
let creation_year = this.get_year(creation);
|
||||
let current_year = this.get_year(frappe.datetime.now_date());
|
||||
let years_list = [];
|
||||
for (var year = current_year; year >= creation_year; year--) {
|
||||
years_list.push(year);
|
||||
}
|
||||
return years_list;
|
||||
},
|
||||
|
||||
get_year(date_str) {
|
||||
return date_str.substring(0, date_str.indexOf('-'));
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -76,6 +76,7 @@ window.comment_when = function(datetime, mini) {
|
|||
+ prettyDate(datetime, mini) + '</span>';
|
||||
};
|
||||
frappe.datetime.comment_when = comment_when;
|
||||
frappe.datetime.prettyDate = prettyDate;
|
||||
|
||||
frappe.datetime.refresh_when = function() {
|
||||
if (jQuery) {
|
||||
|
|
|
|||
|
|
@ -250,7 +250,8 @@ Object.assign(frappe.utils, {
|
|||
regExp = /^\w+$/;
|
||||
break;
|
||||
case "email":
|
||||
regExp = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
|
||||
// from https://emailregex.com/
|
||||
regExp = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
break;
|
||||
case "url":
|
||||
regExp = /^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i;
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ class DesktopPage {
|
|||
|
||||
make_charts() {
|
||||
return frappe.dashboard_utils.get_dashboard_settings().then(settings => {
|
||||
let chart_config = settings.chart_config? JSON.parse(settings.chart_config): {};
|
||||
let chart_config = settings.chart_config ? JSON.parse(settings.chart_config): {};
|
||||
if (this.data.charts.items) {
|
||||
this.data.charts.items.map(chart => {
|
||||
chart.chart_settings = chart_config[chart.chart_name] || {};
|
||||
|
|
@ -306,6 +306,7 @@ class DesktopPage {
|
|||
container: this.page,
|
||||
type: "chart",
|
||||
columns: 1,
|
||||
hidden: Boolean(this.onboarding_widget),
|
||||
options: {
|
||||
allow_sorting: this.allow_customization,
|
||||
allow_create: this.allow_customization,
|
||||
|
|
|
|||
|
|
@ -330,8 +330,8 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
|
|||
|
||||
evaluate_depends_on_value(expression, filter_label) {
|
||||
let out = null;
|
||||
let filters = this.get_filter_values();
|
||||
if (filters) {
|
||||
let doc = this.get_filter_values();
|
||||
if (doc) {
|
||||
if (typeof expression === 'boolean') {
|
||||
out = expression;
|
||||
} else if (expression.substr(0, 5) == 'eval:') {
|
||||
|
|
@ -341,7 +341,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
|
|||
frappe.throw(__(`Invalid "depends_on" expression set in filter ${filter_label}`));
|
||||
}
|
||||
} else {
|
||||
var value = filters[expression];
|
||||
var value = doc[expression];
|
||||
if ($.isArray(value)) {
|
||||
out = !!value.length;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ frappe.report_utils = {
|
|||
|
||||
return {
|
||||
data: {
|
||||
labels: labels,
|
||||
labels: labels.length? labels: null,
|
||||
datasets: datasets
|
||||
},
|
||||
truncateLegends: 1,
|
||||
|
|
|
|||
|
|
@ -1020,7 +1020,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
|
|||
name: __('Totals Row'),
|
||||
content: totals[col.id],
|
||||
format: value => {
|
||||
return frappe.format(value, col.docfield, { always_show_decimals: true });
|
||||
return frappe.format(value, col.docfield, { always_show_decimals: true }, data[0]);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ export default class ChartWidget extends Widget {
|
|||
setup_container() {
|
||||
this.body.empty();
|
||||
|
||||
if (this.chart_doc.type == 'Heatmap') {
|
||||
this.setup_heatmap_container();
|
||||
}
|
||||
|
||||
this.loading = $(
|
||||
`<div class="chart-loading-state text-muted" style="height: ${this.height}px;">${__(
|
||||
"Loading..."
|
||||
|
|
@ -57,9 +61,16 @@ export default class ChartWidget extends Widget {
|
|||
this.chart_wrapper = $(`<div></div>`);
|
||||
this.chart_wrapper.appendTo(this.body);
|
||||
|
||||
this.$heatmap_legend = null;
|
||||
this.set_chart_title();
|
||||
}
|
||||
|
||||
setup_heatmap_container() {
|
||||
this.widget.addClass('heatmap-chart');
|
||||
this.widget.removeClass('full-width').addClass('full-width');
|
||||
this.width = 'Full';
|
||||
}
|
||||
|
||||
set_summary() {
|
||||
if (!this.$summary) {
|
||||
this.$summary = $(`<div class="report-summary"></div>`).hide();
|
||||
|
|
@ -104,54 +115,7 @@ export default class ChartWidget extends Widget {
|
|||
}
|
||||
|
||||
render_time_series_filters() {
|
||||
let filters = [
|
||||
{
|
||||
label: this.chart_settings.timespan || this.chart_doc.timespan,
|
||||
options: [
|
||||
"Select Date Range",
|
||||
"Last Year",
|
||||
"Last Quarter",
|
||||
"Last Month",
|
||||
"Last Week"
|
||||
],
|
||||
action: selected_item => {
|
||||
this.selected_timespan = selected_item;
|
||||
|
||||
if (this.selected_timespan === "Select Date Range") {
|
||||
this.render_date_range_fields();
|
||||
} else {
|
||||
this.selected_from_date = null;
|
||||
this.selected_to_date = null;
|
||||
if (this.date_field_wrapper) {
|
||||
this.date_field_wrapper.hide();
|
||||
|
||||
// Title maybe hidden becuase of date range fields
|
||||
// in half width chart
|
||||
this.title_field.show();
|
||||
this.head.css('flex-direction', "row");
|
||||
}
|
||||
|
||||
this.save_chart_config_for_user({
|
||||
'timespan': this.selected_timespan,
|
||||
'from_date': null,
|
||||
'to_date': null
|
||||
|
||||
});
|
||||
this.fetch_and_update_chart();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.chart_settings.time_interval || this.chart_doc.time_interval,
|
||||
options: ["Yearly", "Quarterly", "Monthly", "Weekly", "Daily"],
|
||||
action: selected_item => {
|
||||
this.selected_time_interval = selected_item;
|
||||
this.save_chart_config_for_user({'time_interval': this.selected_time_interval});
|
||||
this.fetch_and_update_chart();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
let filters = this.get_time_series_filters();
|
||||
frappe.dashboard_utils.render_chart_filters(
|
||||
filters,
|
||||
"chart-actions",
|
||||
|
|
@ -160,12 +124,77 @@ export default class ChartWidget extends Widget {
|
|||
);
|
||||
}
|
||||
|
||||
get_time_series_filters() {
|
||||
let filters;
|
||||
if (this.chart_doc.type == 'Heatmap') {
|
||||
filters = [{
|
||||
label: this.chart_settings.heatmap_year || this.chart_doc.heatmap_year,
|
||||
options: frappe.dashboard_utils.get_years_since_creation(frappe.boot.user.creation),
|
||||
action: selected_item => {
|
||||
this.selected_heatmap_year = selected_item;
|
||||
this.save_chart_config_for_user({'heatmap_year': this.selected_heatmap_year});
|
||||
this.fetch_and_update_chart();
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
filters = [
|
||||
{
|
||||
label: this.chart_settings.timespan || this.chart_doc.timespan,
|
||||
options: [
|
||||
"Select Date Range",
|
||||
"Last Year",
|
||||
"Last Quarter",
|
||||
"Last Month",
|
||||
"Last Week"
|
||||
],
|
||||
action: selected_item => {
|
||||
this.selected_timespan = selected_item;
|
||||
|
||||
if (this.selected_timespan === "Select Date Range") {
|
||||
this.render_date_range_fields();
|
||||
} else {
|
||||
this.selected_from_date = null;
|
||||
this.selected_to_date = null;
|
||||
if (this.date_field_wrapper) {
|
||||
this.date_field_wrapper.hide();
|
||||
|
||||
// Title maybe hidden becuase of date range fields
|
||||
// in half width chart
|
||||
this.title_field.show();
|
||||
this.head.css('flex-direction', "row");
|
||||
}
|
||||
|
||||
this.save_chart_config_for_user({
|
||||
'timespan': this.selected_timespan,
|
||||
'from_date': null,
|
||||
'to_date': null
|
||||
|
||||
});
|
||||
this.fetch_and_update_chart();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.chart_settings.time_interval || this.chart_doc.time_interval,
|
||||
options: ["Yearly", "Quarterly", "Monthly", "Weekly", "Daily"],
|
||||
action: selected_item => {
|
||||
this.selected_time_interval = selected_item;
|
||||
this.save_chart_config_for_user({'time_interval': this.selected_time_interval});
|
||||
this.fetch_and_update_chart();
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
fetch_and_update_chart() {
|
||||
this.args = {
|
||||
timespan: this.selected_timespan || this.chart_settings.timespan,
|
||||
time_interval: this.selected_time_interval || this.chart_settings.time_interval,
|
||||
from_date: this.selected_from_date || this.chart_settings.from_date,
|
||||
to_date: this.selected_to_date || this.chart_settings.to_date
|
||||
to_date: this.selected_to_date || this.chart_settings.to_date,
|
||||
heatmap_year: this.selected_heatmap_year || this.chart_settings.heatmap_year,
|
||||
};
|
||||
|
||||
this.fetch(this.filters, true, this.args).then(data => {
|
||||
|
|
@ -274,7 +303,7 @@ export default class ChartWidget extends Widget {
|
|||
},
|
||||
{
|
||||
label: __("Reset Chart"),
|
||||
action: "action-list",
|
||||
action: "action-reset",
|
||||
handler: () => {
|
||||
this.reset_chart();
|
||||
delete this.dashboard_chart;
|
||||
|
|
@ -332,15 +361,12 @@ export default class ChartWidget extends Widget {
|
|||
}
|
||||
];
|
||||
} else {
|
||||
fields = filters.filter(f => {
|
||||
if (f.on_change && !f.reqd) {
|
||||
return false;
|
||||
}
|
||||
if (f.get_query || f.get_data) {
|
||||
f.read_only = 1;
|
||||
}
|
||||
return f.fieldname;
|
||||
});
|
||||
fields = filters
|
||||
.filter(df => df.fieldname)
|
||||
.map(df => {
|
||||
Object.assign(df, df.dashboard_config || {});
|
||||
return df;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
fields = [
|
||||
|
|
@ -384,6 +410,8 @@ export default class ChartWidget extends Widget {
|
|||
}
|
||||
|
||||
dialog.show();
|
||||
//Set query report object so that it can be used while fetching filter values in the report
|
||||
frappe.query_report = new frappe.views.QueryReport({'filters': dialog.fields_list});
|
||||
dialog.set_values(this.filters);
|
||||
}
|
||||
|
||||
|
|
@ -391,6 +419,9 @@ export default class ChartWidget extends Widget {
|
|||
this.save_chart_config_for_user(null, 1);
|
||||
this.chart_settings = {};
|
||||
this.filters = null;
|
||||
this.selected_time_interval = null;
|
||||
this.selected_timespan = null;
|
||||
this.selected_heatmap_year = null;
|
||||
}
|
||||
|
||||
save_chart_config_for_user(config, reset=0) {
|
||||
|
|
@ -458,58 +489,25 @@ export default class ChartWidget extends Widget {
|
|||
time_interval: args && args.time_interval ? args.time_interval : null,
|
||||
timespan: args && args.timespan ? args.timespan : null,
|
||||
from_date: args && args.from_date ? args.from_date : null,
|
||||
to_date: args && args.to_date ? args.to_date : null
|
||||
to_date: args && args.to_date ? args.to_date : null,
|
||||
heatmap_year: args && args.heatmap_year ? args.heatmap_year : null,
|
||||
};
|
||||
}
|
||||
return frappe.xcall(method, args);
|
||||
}
|
||||
|
||||
render() {
|
||||
const chart_type_map = {
|
||||
Line: "line",
|
||||
Bar: "bar",
|
||||
Percentage: "percentage",
|
||||
Pie: "pie",
|
||||
Donut: "donut"
|
||||
};
|
||||
|
||||
let colors = [];
|
||||
|
||||
if (this.chart_doc.y_axis.length) {
|
||||
this.chart_doc.y_axis.map(field => {
|
||||
colors.push(field.color);
|
||||
});
|
||||
} else if (["Line", "Bar"].includes(this.chart_doc.type)) {
|
||||
colors = [this.chart_doc.color || []];
|
||||
}
|
||||
|
||||
if (!this.data || !this.data.labels.length || !Object.keys(this.data).length) {
|
||||
if (!this.data || !this.data.labels || !Object.keys(this.data).length) {
|
||||
this.chart_wrapper.hide();
|
||||
this.loading.hide();
|
||||
this.$summary.hide();
|
||||
this.$summary && this.$summary.hide();
|
||||
this.empty.show();
|
||||
} else {
|
||||
this.loading.hide();
|
||||
this.empty.hide();
|
||||
this.chart_wrapper.show();
|
||||
|
||||
let chart_args = {
|
||||
data: this.data,
|
||||
type: chart_type_map[this.chart_doc.type],
|
||||
colors: colors,
|
||||
height: this.height,
|
||||
axisOptions: {
|
||||
xIsSeries: this.chart_doc.timeseries,
|
||||
shortenYAxisNumbers: 1
|
||||
}
|
||||
};
|
||||
|
||||
if (this.chart_doc.custom_options) {
|
||||
let custom_options = JSON.parse(this.chart_doc.custom_options);
|
||||
for (let key in custom_options) {
|
||||
chart_args[key] = custom_options[key];
|
||||
}
|
||||
}
|
||||
const chart_args = this.get_chart_args();
|
||||
|
||||
if (!this.dashboard_chart) {
|
||||
this.dashboard_chart = new frappe.Chart(
|
||||
|
|
@ -519,7 +517,93 @@ export default class ChartWidget extends Widget {
|
|||
} else {
|
||||
this.dashboard_chart.update(this.data);
|
||||
}
|
||||
|
||||
this.width == "Full" && this.summary && this.set_summary();
|
||||
this.chart_doc.type == 'Heatmap' && this.render_heatmap_legend();
|
||||
}
|
||||
}
|
||||
|
||||
get_chart_args() {
|
||||
let colors = this.get_chart_colors();
|
||||
|
||||
const chart_type_map = {
|
||||
Line: "line",
|
||||
Bar: "bar",
|
||||
Percentage: "percentage",
|
||||
Pie: "pie",
|
||||
Donut: "donut",
|
||||
Heatmap: "heatmap"
|
||||
};
|
||||
|
||||
let chart_args = {
|
||||
data: this.data,
|
||||
type: chart_type_map[this.chart_doc.type],
|
||||
colors: colors,
|
||||
height: this.height,
|
||||
axisOptions: {
|
||||
xIsSeries: this.chart_doc.timeseries,
|
||||
shortenYAxisNumbers: 1
|
||||
}
|
||||
};
|
||||
|
||||
if (this.chart_doc.type == "Heatmap") {
|
||||
const heatmap_year = parseInt(this.selected_heatmap_year || this.chart_settings.heatmap_year || this.chart_doc.heatmap_year);
|
||||
chart_args.data.start = new Date(`${heatmap_year}-01-01`);
|
||||
chart_args.data.end = new Date(`${heatmap_year+1}-01-01`);
|
||||
}
|
||||
|
||||
let set_options = (options) => {
|
||||
let custom_options = JSON.parse(options);
|
||||
for (let key in custom_options) {
|
||||
chart_args[key] = custom_options[key];
|
||||
}
|
||||
};
|
||||
|
||||
if (this.custom_options) {
|
||||
set_options(this.custom_options);
|
||||
}
|
||||
|
||||
if (this.chart_doc.custom_options) {
|
||||
set_options(this.chart_doc.custom_options);
|
||||
}
|
||||
|
||||
return chart_args;
|
||||
}
|
||||
|
||||
get_chart_colors() {
|
||||
let colors = [];
|
||||
if (this.chart_doc.y_axis.length) {
|
||||
this.chart_doc.y_axis.map(field => {
|
||||
colors.push(field.color);
|
||||
});
|
||||
} else if (["Line", "Bar"].includes(this.chart_doc.type)) {
|
||||
colors = [this.chart_doc.color || "light-blue"];
|
||||
} else if (this.chart_doc.type == "Heatmap") {
|
||||
colors = [];
|
||||
}
|
||||
|
||||
return colors;
|
||||
}
|
||||
|
||||
render_heatmap_legend() {
|
||||
if (!this.$heatmap_legend && this.widget.width() > 991) {
|
||||
this.$heatmap_legend =
|
||||
$(`
|
||||
<div class="heatmap-legend">
|
||||
<ul class="legend-colors">
|
||||
<li style="background-color: #ebedf0"></li>
|
||||
<li style="background-color: #c6e48b"></li>
|
||||
<li style="background-color: #7bc96f"></li>
|
||||
<li style="background-color: #239a3b"></li>
|
||||
<li style="background-color: #196127"></li>
|
||||
</ul>
|
||||
<div class="legend-label">
|
||||
<div style="margin-bottom: 45px">${__("Less")}</div>
|
||||
<div>${__("More")}</div>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
this.body.append(this.$heatmap_legend);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -542,6 +626,10 @@ export default class ChartWidget extends Widget {
|
|||
let saved_filters = this.chart_settings.filters || null;
|
||||
this.filters =
|
||||
saved_filters || this.filters || JSON.parse(this.chart_doc.filters_json || "[]");
|
||||
|
||||
if (this.chart_doc.type == 'Heatmap' && !this.chart_doc.heatmap_year) {
|
||||
this.chart_doc.heatmap_year = frappe.dashboard_utils.get_year(frappe.datetime.now_date());
|
||||
}
|
||||
}
|
||||
|
||||
get_settings() {
|
||||
|
|
|
|||
|
|
@ -119,7 +119,8 @@ export default class NumberCardWidget extends Widget {
|
|||
get_formatted_number() {
|
||||
const based_on_df =
|
||||
frappe.meta.get_docfield(this.card_doc.document_type, this.card_doc.aggregate_function_based_on);
|
||||
const shortened_number = shorten_number(this.number);
|
||||
const default_country = frappe.sys_defaults.country;
|
||||
const shortened_number = shorten_number(this.number, default_country);
|
||||
let number_parts = shortened_number.split(' ');
|
||||
|
||||
const symbol = number_parts[1] || '';
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export default class OnboardingWidget extends Widget {
|
|||
|
||||
if (step.is_skipped) {
|
||||
status = "skipped";
|
||||
icon_class = "fa-times-circle-o";
|
||||
icon_class = "fa-check-circle-o";
|
||||
}
|
||||
|
||||
if (step.is_complete) {
|
||||
|
|
@ -56,10 +56,17 @@ export default class OnboardingWidget extends Widget {
|
|||
// Setup actions
|
||||
let actions = {
|
||||
"Watch Video": () => this.show_video(step),
|
||||
"Create Entry": () => this.show_quick_entry(step),
|
||||
"Create Entry": () => {
|
||||
if (step.show_full_form) {
|
||||
this.create_entry(step);
|
||||
} else {
|
||||
this.show_quick_entry(step);
|
||||
}
|
||||
},
|
||||
"Show Form Tour": () => this.show_form_tour(step),
|
||||
"Update Settings": () => this.update_settings(step),
|
||||
"View Report": () => this.open_report(step),
|
||||
"Go to Page": () => this.go_to_page(step),
|
||||
};
|
||||
|
||||
$step.find("#title").on("click", actions[step.action]);
|
||||
|
|
@ -68,6 +75,24 @@ export default class OnboardingWidget extends Widget {
|
|||
return $step;
|
||||
}
|
||||
|
||||
go_to_page(step) {
|
||||
frappe.set_route(step.path).then(() => {
|
||||
if (step.callback_message) {
|
||||
let msg_dialog = frappe.msgprint({
|
||||
message: __(step.callback_message),
|
||||
title: __(step.callback_title),
|
||||
primary_action: {
|
||||
action: () => {
|
||||
msg_dialog.hide();
|
||||
},
|
||||
label: () => __("Continue"),
|
||||
},
|
||||
wide: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
open_report(step) {
|
||||
let route = generate_route({
|
||||
name: step.reference_report,
|
||||
|
|
@ -75,10 +100,9 @@ export default class OnboardingWidget extends Widget {
|
|||
is_query_report: ["Query Report", "Script Report"].includes(
|
||||
step.report_type
|
||||
),
|
||||
doctype: step.report_reference_doctype
|
||||
doctype: step.report_reference_doctype,
|
||||
});
|
||||
|
||||
|
||||
let current_route = frappe.get_route();
|
||||
|
||||
frappe.set_route(route).then(() => {
|
||||
|
|
@ -133,7 +157,7 @@ export default class OnboardingWidget extends Widget {
|
|||
msg_dialog.hide();
|
||||
},
|
||||
label: () => __("Continue"),
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
@ -147,6 +171,7 @@ export default class OnboardingWidget extends Widget {
|
|||
frappe.route_hooks = {};
|
||||
frappe.route_hooks.after_load = (frm) => {
|
||||
frm.scroll_to_field(step.field);
|
||||
frm.doc.__unsaved = true;
|
||||
};
|
||||
|
||||
frappe.route_hooks.after_save = (frm) => {
|
||||
|
|
@ -204,6 +229,44 @@ export default class OnboardingWidget extends Widget {
|
|||
frappe.set_route("Form", step.reference_document);
|
||||
}
|
||||
|
||||
create_entry(step) {
|
||||
let current_route = frappe.get_route();
|
||||
|
||||
frappe.route_hooks = {};
|
||||
let callback = () => {
|
||||
frappe.msgprint({
|
||||
message: __("You're doing great, let's take you back to the onboarding page."),
|
||||
title: __("Good Work 🎉"),
|
||||
primary_action: {
|
||||
action: () => {
|
||||
frappe.set_route(current_route).then(() => {
|
||||
this.mark_complete(step);
|
||||
});
|
||||
},
|
||||
label: __("Continue"),
|
||||
},
|
||||
});
|
||||
|
||||
frappe.msg_dialog.custom_onhide = () => {
|
||||
this.mark_complete(step);
|
||||
};
|
||||
};
|
||||
|
||||
if (step.is_submittable) {
|
||||
frappe.route_hooks.after_save = () => {
|
||||
frappe.msgprint({
|
||||
message: __("Submit this document to complete this step."),
|
||||
title: __("Great")
|
||||
});
|
||||
};
|
||||
frappe.route_hooks.after_submit = callback;
|
||||
} else {
|
||||
frappe.route_hooks.after_save = callback;
|
||||
}
|
||||
|
||||
frappe.set_route(`Form/${step.reference_document}/New ${step.reference_document} 1`);
|
||||
}
|
||||
|
||||
show_quick_entry(step) {
|
||||
let current_route = frappe.get_route_str();
|
||||
frappe.ui.form.make_quick_entry(
|
||||
|
|
@ -221,7 +284,7 @@ export default class OnboardingWidget extends Widget {
|
|||
});
|
||||
},
|
||||
label: __("Continue"),
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
frappe.msg_dialog.custom_onhide = () => {
|
||||
|
|
@ -271,8 +334,10 @@ export default class OnboardingWidget extends Widget {
|
|||
update_step_status(step, status, value, callback) {
|
||||
let icon_class = {
|
||||
is_complete: "fa-check-circle-o",
|
||||
is_skipped: "fa-times-circle-o",
|
||||
is_skipped: "fa-check-circle-o",
|
||||
};
|
||||
// Clear any hooks
|
||||
frappe.route_hooks = {};
|
||||
|
||||
frappe
|
||||
.call("frappe.desk.desktop.update_onboarding_step", {
|
||||
|
|
@ -394,4 +459,4 @@ export default class OnboardingWidget extends Widget {
|
|||
});
|
||||
dismiss.appendTo(this.action_area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ function generate_route(item) {
|
|||
route = "List/" + item.doctype + "/Report/" + item.name;
|
||||
} else if (type === "page") {
|
||||
route = item.name;
|
||||
} else if (type === "dashboard") {
|
||||
route = "dashboard/" + item.name;
|
||||
}
|
||||
|
||||
route = "#" + route;
|
||||
|
|
@ -125,19 +127,44 @@ function go_to_list_with_filters(doctype, filters) {
|
|||
});
|
||||
}
|
||||
|
||||
function shorten_number(number) {
|
||||
function shorten_number(number, country) {
|
||||
country = country || '';
|
||||
const number_system = get_number_system(country);
|
||||
let x = Math.abs(Math.round(number));
|
||||
|
||||
switch (true) {
|
||||
case x >= 1.0e+12:
|
||||
return Math.round(number/1.0e+12) + " T";
|
||||
case x >= 1.0e+9:
|
||||
return Math.round(number/1.0e+9) + " B";
|
||||
case x >= 1.0e+6:
|
||||
return Math.round(number/1.0e+6) + " M";
|
||||
default:
|
||||
return number.toFixed();
|
||||
for (const map of number_system) {
|
||||
if (x >= map.divisor) {
|
||||
return Math.round(number/map.divisor) + ' ' + map.symbol;
|
||||
}
|
||||
}
|
||||
return number.toFixed();
|
||||
}
|
||||
|
||||
function get_number_system(country) {
|
||||
let number_system_map = {
|
||||
'India':
|
||||
[{
|
||||
divisor: 1.0e+7,
|
||||
symbol: 'Cr'
|
||||
},
|
||||
{
|
||||
divisor: 1.0e+5,
|
||||
symbol: 'Lakh'
|
||||
}],
|
||||
'':
|
||||
[{
|
||||
divisor: 1.0e+12,
|
||||
symbol: 'T'
|
||||
},
|
||||
{
|
||||
divisor: 1.0e+9,
|
||||
symbol: 'B'
|
||||
},
|
||||
{
|
||||
divisor: 1.0e+6,
|
||||
symbol: 'M'
|
||||
}]
|
||||
};
|
||||
return number_system_map[country];
|
||||
}
|
||||
|
||||
export { generate_route, generate_grid, build_summary_item, go_to_list_with_filters, shorten_number };
|
||||
|
|
@ -145,7 +145,7 @@ class ShortcutDialog extends WidgetDialog {
|
|||
fieldname: "type",
|
||||
label: "Type",
|
||||
reqd: 1,
|
||||
options: "DocType\nReport\nPage",
|
||||
options: "DocType\nReport\nPage\nDashboard",
|
||||
onchange: () => {
|
||||
if (this.dialog.get_value("type") == "DocType") {
|
||||
this.dialog.fields_dict.link_to.get_query = () => {
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ export default class WidgetGroup {
|
|||
</div>
|
||||
</div>`);
|
||||
this.widget_area = widget_area;
|
||||
if (this.hidden) this.widget_area.hide();
|
||||
this.title_area = widget_area.find(".widget-group-title");
|
||||
this.control_area = widget_area.find(".widget-group-control");
|
||||
this.body = widget_area.find(".widget-group-body");
|
||||
|
|
@ -96,7 +97,7 @@ export default class WidgetGroup {
|
|||
}
|
||||
|
||||
customize() {
|
||||
this.widget_area.show();
|
||||
if (!this.hidden) this.widget_area.show();
|
||||
this.widgets_list.forEach((wid) => {
|
||||
wid.customize(this.options);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -293,6 +293,75 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.dashboard-widget-box.heatmap-chart {
|
||||
min-height: 0px;
|
||||
height: 180px;
|
||||
|
||||
.widget-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.widget-control {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.frappe-chart .chart-legend {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chart-loading-state {
|
||||
height: 160px !important;
|
||||
}
|
||||
|
||||
.widget-body {
|
||||
display: flex;
|
||||
max-height: 100%;
|
||||
margin: auto;
|
||||
margin-top: -15px;
|
||||
|
||||
.chart-container {
|
||||
height: 100%;
|
||||
.frappe-chart {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.heatmap-legend {
|
||||
display: flex;
|
||||
margin: 45px 20px 0 20px;
|
||||
|
||||
.legend-colors {
|
||||
padding-left: 1;
|
||||
padding-left: 15px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
li {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.legend-label {
|
||||
color: #555b51;
|
||||
font-size: 11px;
|
||||
margin-left: 15px;
|
||||
line-height: 1.6em;
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
&.dashboard-widget-box.heatmap-chart {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.onboarding-widget-box {
|
||||
margin-bottom: 50px;
|
||||
margin-top: 10px;
|
||||
|
|
|
|||
|
|
@ -314,11 +314,20 @@ h6.uppercase, .h6.uppercase {
|
|||
}
|
||||
}
|
||||
|
||||
.form-section:not(:last-child),
|
||||
.hide-border {
|
||||
border-top: none !important;
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.form-section:not(:first-child) {
|
||||
border-top: 1px solid @border-color;
|
||||
}
|
||||
|
||||
.form-inner-toolbar {
|
||||
border-bottom: 1px solid @border-color;
|
||||
}
|
||||
|
||||
|
||||
.empty-section {
|
||||
display: none !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,8 @@ class BackupGenerator:
|
|||
cmd_string = """tar -cf %s %s""" % (backup_path, files_path)
|
||||
err, out = frappe.utils.execute_in_shell(cmd_string)
|
||||
|
||||
print('Backed up files', os.path.abspath(backup_path))
|
||||
if verbose:
|
||||
print('Backed up files', os.path.abspath(backup_path))
|
||||
|
||||
def take_dump(self):
|
||||
import frappe.utils
|
||||
|
|
@ -151,7 +152,6 @@ def get_backup():
|
|||
This function is executed when the user clicks on
|
||||
Toos > Download Backup
|
||||
"""
|
||||
#if verbose: print frappe.db.cur_db_name + " " + conf.db_password
|
||||
delete_temp_backups()
|
||||
odb = BackupGenerator(frappe.conf.db_name, frappe.conf.db_name,\
|
||||
frappe.conf.db_password, db_host = frappe.db.host)
|
||||
|
|
|
|||
42
frappe/utils/commands.py
Normal file
42
frappe/utils/commands.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import functools
|
||||
import requests
|
||||
from terminaltables import AsciiTable
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=1024)
|
||||
def get_first_party_apps():
|
||||
"""Get list of all apps under orgs: frappe. erpnext from GitHub"""
|
||||
apps = []
|
||||
for org in ["frappe", "erpnext"]:
|
||||
req = requests.get(f"https://api.github.com/users/{org}/repos", {"type": "sources", "per_page": 200})
|
||||
if req.ok:
|
||||
apps.extend([x["name"] for x in req.json()])
|
||||
return apps
|
||||
|
||||
|
||||
def render_table(data):
|
||||
print(AsciiTable(data).table)
|
||||
|
||||
|
||||
def add_line_after(function):
|
||||
"""Adds an extra line to STDOUT after the execution of a function this decorates"""
|
||||
def empty_line(*args, **kwargs):
|
||||
result = function(*args, **kwargs)
|
||||
print()
|
||||
return result
|
||||
return empty_line
|
||||
|
||||
|
||||
def log(message, colour=''):
|
||||
"""Coloured log outputs to STDOUT"""
|
||||
colours = {
|
||||
"nc": '\033[0m',
|
||||
"blue": '\033[94m',
|
||||
"green": '\033[92m',
|
||||
"yellow": '\033[93m',
|
||||
"red": '\033[91m',
|
||||
"silver": '\033[90m'
|
||||
}
|
||||
colour = colours.get(colour, "")
|
||||
end_line = '\033[0m'
|
||||
print(colour + message + end_line)
|
||||
44
frappe/utils/connections.py
Normal file
44
frappe/utils/connections.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import socket
|
||||
|
||||
from six.moves.urllib.parse import urlparse
|
||||
from frappe import get_conf
|
||||
|
||||
config = get_conf()
|
||||
REDIS_KEYS = ('redis_cache', 'redis_queue', 'redis_socketio')
|
||||
|
||||
|
||||
def is_open(ip, port, timeout=10):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(timeout)
|
||||
try:
|
||||
s.connect((ip, int(port)))
|
||||
s.shutdown(socket.SHUT_RDWR)
|
||||
return True
|
||||
except socket.error:
|
||||
return False
|
||||
finally:
|
||||
s.close()
|
||||
|
||||
|
||||
def check_database():
|
||||
db_type = config.get("db_type", "mariadb")
|
||||
db_host = config.get("db_host", "localhost")
|
||||
db_port = config.get("db_port", 3306 if db_type == "mariadb" else 5342)
|
||||
return {db_type: is_open(db_host, db_port)}
|
||||
|
||||
|
||||
def check_redis(redis_services=None):
|
||||
services = redis_services or REDIS_KEYS
|
||||
status = {}
|
||||
for conn in services:
|
||||
redis_url = urlparse(config.get(conn)).netloc
|
||||
redis_host, redis_port = redis_url.split(":")
|
||||
status[conn] = is_open(redis_host, redis_port)
|
||||
return status
|
||||
|
||||
|
||||
def check_connection(redis_services=None):
|
||||
service_status = {}
|
||||
service_status.update(check_database())
|
||||
service_status.update(check_redis(redis_services))
|
||||
return service_status
|
||||
|
|
@ -41,6 +41,7 @@ def generate_and_cache_results(args, function, cache_key, chart):
|
|||
to_date = args.to_date or None,
|
||||
time_interval = args.time_interval or None,
|
||||
timespan = args.timespan or None,
|
||||
heatmap_year = args.heatmap_year or None
|
||||
)
|
||||
except TypeError as e:
|
||||
if str(e) == "'NoneType' object is not iterable":
|
||||
|
|
|
|||
|
|
@ -213,6 +213,19 @@ def get_datetime_str(datetime_obj):
|
|||
datetime_obj = get_datetime(datetime_obj)
|
||||
return datetime_obj.strftime(DATETIME_FORMAT)
|
||||
|
||||
def get_date_str(date_obj):
|
||||
if isinstance(date_obj, string_types):
|
||||
date_obj = get_datetime(date_obj)
|
||||
return date_obj.strftime(DATE_FORMAT)
|
||||
|
||||
def get_time_str(timedelta_obj):
|
||||
if isinstance(timedelta_obj, string_types):
|
||||
timedelta_obj = to_timedelta(timedelta_obj)
|
||||
|
||||
hours, remainder = divmod(timedelta_obj.seconds, 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
return "{0}:{1}:{2}".format(hours, minutes, seconds)
|
||||
|
||||
def get_user_date_format():
|
||||
"""Get the current user date format. The result will be cached."""
|
||||
if getattr(frappe.local, "user_date_format", None) is None:
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ from frappe import _
|
|||
import frappe.sessions
|
||||
from frappe.utils import cstr
|
||||
import os, mimetypes, json
|
||||
import re
|
||||
|
||||
import six
|
||||
from bs4 import BeautifulSoup
|
||||
from six import iteritems
|
||||
from werkzeug.wrappers import Response
|
||||
from werkzeug.routing import Map, Rule, NotFound
|
||||
|
|
@ -128,12 +130,35 @@ def build_response(path, data, http_status_code, headers=None):
|
|||
response.headers["X-Page-Name"] = path.encode("ascii", errors="xmlcharrefreplace")
|
||||
response.headers["X-From-Cache"] = frappe.local.response.from_cache or False
|
||||
|
||||
add_preload_headers(response)
|
||||
if headers:
|
||||
for key, val in iteritems(headers):
|
||||
response.headers[key] = val.encode("ascii", errors="xmlcharrefreplace")
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def add_preload_headers(response):
|
||||
try:
|
||||
preload = []
|
||||
soup = BeautifulSoup(response.data, "lxml")
|
||||
for elem in soup.find_all('script', src=re.compile(".*")):
|
||||
preload.append(("script", elem.get("src")))
|
||||
|
||||
for elem in soup.find_all('link', rel="stylesheet"):
|
||||
preload.append(("style", elem.get("href")))
|
||||
|
||||
links = []
|
||||
for type, link in preload:
|
||||
links.append("</{}>; rel=preload; as={}".format(link.lstrip("/"), type))
|
||||
|
||||
if links:
|
||||
response.headers["Link"] = ",".join(links)
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def render_page_by_language(path):
|
||||
translated_languages = frappe.get_hooks("translated_languages_for_website")
|
||||
user_lang = guess_language(translated_languages)
|
||||
|
|
|
|||
|
|
@ -18,4 +18,4 @@
|
|||
"theme": "Standard",
|
||||
"theme_scss": "$enable-shadows: false;\n$enable-gradients: false;\n$enable-rounded: true;\n\n// Bootstrap Variable Overrides\n\n\n@import \"frappe/public/scss/website\";\n\n\n\n// Custom Theme\n",
|
||||
"theme_url": "/assets/css/standard_style.css"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ semantic-version==2.8.4
|
|||
six==1.14.0
|
||||
sqlparse==0.2.4
|
||||
stripe==2.40.0
|
||||
terminaltables==3.1.0
|
||||
unittest-xml-reporting==2.5.2
|
||||
urllib3==1.25.8
|
||||
watchdog==0.8.0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue