Merge branch 'develop' into fix-note-2

This commit is contained in:
Suraj Shetty 2023-05-16 18:10:07 +05:30 committed by GitHub
commit d5b0c5ab90
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 296 additions and 68 deletions

View file

@ -32,7 +32,7 @@ context("Form Builder", () => {
cy.click_modal_primary_button("Change");
cy.get(".page-title .title-text").should("have.text", "Form Builder: Web Form Field");
cy.get(".page-title .title-text").should("have.text", "Web Form Field");
});
it("Save without change, check form dirty and reset changes", () => {

View file

@ -1,6 +1,8 @@
# Copyright (c) 2015, Frappe Technologies and contributors
# License: MIT. See LICENSE
from typing import Optional
from jinja2 import TemplateSyntaxError
import frappe
@ -100,32 +102,26 @@ def get_preferred_address(doctype, name, preferred_key="is_primary_address"):
@frappe.whitelist()
def get_default_address(doctype, name, sort_key="is_primary_address"):
def get_default_address(
doctype: str, name: str, sort_key: str = "is_primary_address"
) -> str | None:
"""Returns default Address name for the given doctype, name"""
if sort_key not in ["is_shipping_address", "is_primary_address"]:
return None
out = frappe.db.sql(
""" SELECT
addr.name, addr.%s
FROM
`tabAddress` addr, `tabDynamic Link` dl
WHERE
dl.parent = addr.name and dl.link_doctype = %s and
dl.link_name = %s and ifnull(addr.disabled, 0) = 0
"""
% (sort_key, "%s", "%s"),
(doctype, name),
as_dict=True,
addresses = frappe.get_all(
"Address",
filters=[
["Dynamic Link", "link_doctype", "=", doctype],
["Dynamic Link", "link_name", "=", name],
["disabled", "=", 0],
],
pluck="name",
order_by=f"{sort_key} DESC",
limit=1,
)
if out:
for contact in out:
if contact.get(sort_key):
return contact.name
return out[0].name
else:
return None
return addresses[0] if addresses else None
@frappe.whitelist()

View file

@ -21,10 +21,7 @@ frappe.ui.form.on("DocType", {
frm.toggle_enable("beta", 0);
}
!frm.is_new() &&
frm.add_custom_button(__("Try new form builder", [__(frm.doc.name)]), () => {
frappe.set_route("form-builder", frm.doc.name);
});
render_form_builder_message(frm);
if (!frm.is_new() && !frm.doc.istable) {
if (frm.doc.issingle) {
@ -118,4 +115,31 @@ frappe.ui.form.on("DocField", {
},
});
function render_form_builder_message(frm) {
$(frm.fields_dict["try_form_builder_html"].wrapper).empty();
if (!frm.is_new() && frm.fields_dict["try_form_builder_html"]) {
let title = __("Use Form Builder to visually edit your form layout");
let msg = __(
"You can drag and drop fields to create your form layout, add tabs, sections and columns to organize your form and update field properties all from one screen."
);
let message = `
<div class="flex form-message blue p-3">
<div class="mr-3"><img style="border-radius: var(--border-radius-md)" width="360" src="/assets/frappe/images/form-builder.gif"></div>
<div>
<p style="font-size: var(--text-lg)">${title}</p>
<p>${msg}</p>
<div>
<a class="btn btn-primary btn-sm" href="/app/form-builder/${frm.doc.name}">
${__("Form Builder")} ${frappe.utils.icon("right", "xs")}
</a>
</div>
</div>
</div>
`;
$(frm.fields_dict["try_form_builder_html"].wrapper).html(message);
}
}
extend_cscript(cur_frm.cscript, new frappe.model.DocTypeController({ frm: cur_frm }));

View file

@ -26,6 +26,7 @@
"is_virtual",
"queue_in_background",
"fields_section_break",
"try_form_builder_html",
"fields",
"sb1",
"naming_rule",
@ -630,6 +631,11 @@
"fieldname": "is_calendar_and_gantt",
"fieldtype": "Check",
"label": "Is Calendar and Gantt"
},
{
"fieldname": "try_form_builder_html",
"fieldtype": "HTML",
"label": "Try Form Builder HTML"
}
],
"icon": "fa fa-bolt",
@ -712,7 +718,7 @@
"link_fieldname": "reference_doctype"
}
],
"modified": "2023-03-23 16:15:51.067267",
"modified": "2023-05-15 14:07:51.526257",
"modified_by": "Administrator",
"module": "Core",
"name": "DocType",

View file

@ -638,7 +638,9 @@ class File(Document):
def create_attachment_record(self):
icon = ' <i class="fa fa-lock text-warning"></i>' if self.is_private else ""
file_url = quote(frappe.safe_encode(self.file_url)) if self.file_url else self.file_name
file_url = (
quote(frappe.safe_encode(self.file_url), safe="/:") if self.file_url else self.file_name
)
file_name = self.file_name or self.file_url
self.add_comment_in_reference_doc(

View file

@ -124,14 +124,14 @@
"depends_on": "enable_rate_limit",
"fieldname": "rate_limit_count",
"fieldtype": "Int",
"label": "Limit"
"label": "Request Limit"
},
{
"default": "86400",
"depends_on": "enable_rate_limit",
"fieldname": "rate_limit_seconds",
"fieldtype": "Int",
"label": "Seconds"
"label": "Time Window (Seconds)"
}
],
"index_web_pages_for_search": 1,
@ -141,7 +141,7 @@
"link_fieldname": "server_script"
}
],
"modified": "2023-05-12 20:54:54.365266",
"modified": "2023-05-16 11:03:58.282680",
"modified_by": "Administrator",
"module": "Core",
"name": "Server Script",

View file

@ -436,8 +436,8 @@ class TestUser(FrappeTestCase):
getdoc("User", "Administrator")
doc = frappe.response.docs[0]
self.assertListEqual(
doc.get("__onload").get("all_modules", []),
[m.get("module_name") for m in get_modules_from_all_apps()],
sorted(doc.get("__onload").get("all_modules", [])),
sorted(m.get("module_name") for m in get_modules_from_all_apps()),
)
def test_reset_password_link_expiry(self):

View file

@ -115,14 +115,7 @@ frappe.ui.form.on("Customize Form", {
frm.page.set_title(__("Customize Form - {0}", [frm.doc.doc_type]));
frappe.customize_form.set_primary_action(frm);
if (!frm.is_new()) {
frm.add_custom_button(
__("Try new form builder", [__(frm.doc.doc_type)]),
() => {
frappe.set_route("form-builder", frm.doc.doc_type, "customize");
}
);
}
render_form_builder_message(frm);
frm.add_custom_button(
__("Go to {0} List", [__(frm.doc.doc_type)]),
@ -426,4 +419,31 @@ frappe.customize_form.clear_locals_and_refresh = function (frm) {
frm.refresh();
};
function render_form_builder_message(frm) {
$(frm.fields_dict["try_form_builder_html"].wrapper).empty();
if (!frm.is_new() && frm.fields_dict["try_form_builder_html"]) {
let title = __("Use Form Builder to visually customize your form layout");
let msg = __(
"You can drag and drop fields to create your form layout, add tabs, sections and columns to organize your form and update field properties all from one screen."
);
let message = `
<div class="flex form-message blue p-3">
<div class="mr-3"><img style="border-radius: var(--border-radius-md)" width="275" src="/assets/frappe/images/form-builder.gif"></div>
<div>
<p style="font-size: var(--text-lg)">${title}</p>
<p>${msg}</p>
<div>
<a class="btn btn-primary btn-sm" href="/app/form-builder/${frm.doc.doc_type}/customize">
${__("Form Builder")} ${frappe.utils.icon("right", "xs")}
</a>
</div>
</div>
</div>
`;
$(frm.fields_dict["try_form_builder_html"].wrapper).html(message);
}
}
extend_cscript(cur_frm.cscript, new frappe.model.DocTypeController({ frm: cur_frm }));

View file

@ -22,6 +22,7 @@
"allow_import",
"queue_in_background",
"fields_section_break",
"try_form_builder_html",
"fields",
"naming_section",
"naming_rule",
@ -366,6 +367,11 @@
"fieldname": "is_calendar_and_gantt",
"fieldtype": "Check",
"label": "Is Calendar and Gantt"
},
{
"fieldname": "try_form_builder_html",
"fieldtype": "HTML",
"label": "Try Form Builder HTML"
}
],
"hide_toolbar": 1,
@ -374,7 +380,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2022-10-30 23:39:49.628093",
"modified": "2023-05-15 16:03:19.872532",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customize Form",

View file

@ -18,6 +18,7 @@ frappe.pages["form-builder"].on_page_show = function (wrapper) {
function load_form_builder(wrapper) {
let route = frappe.get_route();
route = route.filter((a) => a);
if (route.length > 1) {
let doctype = route[1];
let is_customize_form = route[2] === "customize";
@ -42,5 +43,140 @@ function load_form_builder(wrapper) {
customize: is_customize_form,
});
});
} else {
let d = new frappe.ui.Dialog({
title: __("Select DocType"),
fields: [
{
label: __("Select DocType"),
fieldname: "doctype",
fieldtype: "Link",
options: "DocType",
only_select: 1,
},
{
label: __("Customize"),
fieldname: "customize",
fieldtype: "Check",
},
],
primary_action_label: __("Edit"),
primary_action({ doctype, customize }) {
if (customize) {
frappe.model.with_doctype(doctype).then(() => {
let meta = frappe.get_meta(doctype);
if (in_list(frappe.model.core_doctypes_list, this.doctype))
frappe.throw(__("Core DocTypes cannot be customized."));
if (meta.issingle)
frappe.throw(__("Single DocTypes cannot be customized."));
if (meta.custom)
frappe.throw(
__(
"Only standard DocTypes are allowed to be customized from Customize Form."
)
);
frappe.set_route("form-builder", doctype, "customize");
});
} else {
frappe.set_route("form-builder", doctype);
}
},
secondary_action_label: __("Create New DocType"),
secondary_action() {
d.hide();
let new_d = new frappe.ui.Dialog({
title: __("Create New DocType"),
fields: [
{
label: __("DocType Name"),
fieldname: "doctype_name",
fieldtype: "Data",
reqd: 1,
},
{ fieldtype: "Column Break" },
{
label: __("Module"),
fieldname: "module",
fieldtype: "Link",
options: "Module Def",
reqd: 1,
},
{ fieldtype: "Section Break" },
{
label: __("Is Submittable"),
fieldname: "is_submittable",
fieldtype: "Check",
description: __(
"Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended."
),
depends_on: "eval:!doc.istable && !doc.issingle",
},
{
label: __("Is Child Table"),
fieldname: "istable",
fieldtype: "Check",
description: __("Child Tables are shown as a Grid in other DocTypes"),
depends_on: "eval:!doc.is_submittable && !doc.issingle",
},
{
label: __("Editable Grid"),
fieldname: "editable_grid",
fieldtype: "Check",
depends_on: "istable",
default: 1,
},
{
label: __("Is Single"),
fieldname: "issingle",
fieldtype: "Check",
description: __(
"Single Types have only one record no tables associated. Values are stored in tabSingles"
),
depends_on: "eval:!doc.istable && !doc.is_submittable",
},
{
label: __("Custom?"),
fieldname: "custom",
fieldtype: "Check",
},
],
primary_action_label: __("Create & Continue"),
primary_action(values) {
if (!values.istable) values.editable_grid = 0;
frappe.db
.insert({
doctype: "DocType",
name: values.doctype_name,
module: values.module,
istable: values.istable,
editable_grid: values.editable_grid,
issingle: values.issingle,
custom: values.custom,
is_submittable: values.is_submittable,
fields: [
{
label: "Title",
fieldname: "title",
fieldtype: "Data",
},
],
})
.then((doc) => {
frappe.set_route("form-builder", doc.name);
});
},
secondary_action_label: __("Back"),
secondary_action() {
new_d.hide();
d.show();
},
});
new_d.show();
},
});
d.show();
}
}

View file

@ -298,7 +298,9 @@ def export_query():
if isinstance(visible_idx, str):
visible_idx = json.loads(visible_idx)
data = run(report_name, form_params.filters, custom_columns=custom_columns)
data = run(
report_name, form_params.filters, custom_columns=custom_columns, are_default_filters=False
)
data = frappe._dict(data)
if not data.columns:
frappe.respond_as_web_page(

View file

@ -93,7 +93,7 @@ frappe.ui.form.on("Auto Email Report", {
wrapper
);
var filters = JSON.parse(frm.doc.filters || "{}");
var filters = {};
let report_filters;
@ -102,8 +102,19 @@ frappe.ui.form.on("Auto Email Report", {
frappe.query_reports[frm.doc.reference_report] &&
frappe.query_reports[frm.doc.reference_report].filters
) {
if (frm.doc.filters) {
filters = JSON.parse(frm.doc.filters);
} else {
frappe.db.get_value("Report", frm.doc.report, "json", (r) => {
if (r && r.json) {
filters = JSON.parse(r.json).filters || {};
}
});
}
report_filters = frappe.query_reports[frm.doc.reference_report].filters;
} else {
filters = JSON.parse(frm.doc.filters || "{}");
report_filters = frappe.query_reports[frm.doc.report].filters;
}

View file

@ -328,6 +328,13 @@ def check_if_doc_is_dynamically_linked(doc, method="Delete"):
):
reference_doctype = refdoc.parenttype if meta.istable else df.parent
reference_docname = refdoc.parent if meta.istable else refdoc.name
if reference_doctype in frappe.get_hooks("ignore_links_on_delete") or (
reference_doctype in ignore_linked_doctypes and method == "Cancel"
):
# don't check for communication and todo!
continue
at_position = f"at Row: {refdoc.idx}" if meta.istable else ""
raise_link_exists_exception(doc, reference_doctype, reference_docname, at_position)

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

View file

@ -1,8 +1,8 @@
<script setup>
import Sidebar from "./Sidebar.vue";
import Tabs from "./Tabs.vue";
import Sidebar from "./components/Sidebar.vue"
import Tabs from "./components/Tabs.vue";
import { computed, onMounted, watch, ref } from "vue";
import { useStore } from "../store";
import { useStore } from "./store";
import { onClickOutside, useMagicKeys, whenever } from "@vueuse/core";
let store = useStore();
@ -35,8 +35,8 @@ function setup_change_doctype_dialog() {
default: store.doctype || null
},
{
label: __("For Customize Form"),
fieldname: "for_customize_form",
label: __("Customize"),
fieldname: "customize",
fieldtype: "Check",
default: store.is_customize_form
}
@ -44,7 +44,7 @@ function setup_change_doctype_dialog() {
primary_action_label: __("Change"),
primary_action({ doctype }) {
dialog.hide();
let customize = dialog.get_value("for_customize_form") ? "customize" : "";
let customize = dialog.get_value("customize") ? "customize" : "";
frappe.set_route("form-builder", doctype, customize);
}
});

View file

@ -1,7 +1,7 @@
import { createApp, watchEffect } from "vue";
import { createPinia } from "pinia";
import { useStore } from "./store";
import FormBuilderComponent from "./components/FormBuilder.vue";
import FormBuilderComponent from "./FormBuilder.vue";
import { registerGlobalComponents } from "./globals.js";
class FormBuilder {
@ -17,7 +17,7 @@ class FormBuilder {
init(refresh) {
// set page title
this.page.set_title(__("Form Builder: {0}", [this.doctype]));
this.page.set_title(__(this.doctype));
this.setup_page_actions();
!refresh && this.setup_app();

View file

@ -101,6 +101,7 @@ export const useStore = defineStore("form-builder-store", () => {
});
setup_undo_redo();
setup_breadcrumbs();
}
let undo_redo_keyboard_event = onKeyDown(true, (e) => {
@ -120,6 +121,23 @@ export const useStore = defineStore("form-builder-store", () => {
undo_redo_keyboard_event;
}
function setup_breadcrumbs() {
let breadcrumbs = `
<li><a href="/app/doctype">${__("DocType")}</a></li>
<li><a href="/app/doctype/${doctype.value}">${__(doctype.value)}</a></li>
`;
if (is_customize_form.value) {
breadcrumbs = `
<li><a href="/app/customize-form?doc_type=${doctype.value}">
${__("Customize Form")}
</a></li>
`;
}
breadcrumbs += `<li class="disabled"><a href="#">${__("Form Builder")}</a></li>`;
frappe.breadcrumbs.clear();
frappe.breadcrumbs.$breadcrumbs.append(breadcrumbs);
}
function reset_changes() {
fetch();
}

View file

@ -57,8 +57,8 @@ frappe.ui.form.ControlDateRange = class ControlDateRange extends frappe.ui.form.
}
format_for_input(value1, value2) {
if (value1 && value2) {
value1 = frappe.datetime.str_to_user(value1);
value2 = frappe.datetime.str_to_user(value2);
value1 = frappe.datetime.str_to_user(value1, false, true);
value2 = frappe.datetime.str_to_user(value2, false, true);
return __("{0} to {1}", [value1, value2]);
}
return "";

View file

@ -36,11 +36,13 @@ frappe.workflow = {
frappe.workflow.setup(doc.doctype);
return frappe.xcall("frappe.model.workflow.get_transitions", { doc: doc });
},
get_document_state: function (doctype, state) {
get_document_state_roles: function (doctype, state) {
frappe.workflow.setup(doctype);
return frappe.get_children(frappe.workflow.workflows[doctype], "states", {
state: state,
})[0];
let workflow_states =
frappe.get_children(frappe.workflow.workflows[doctype], "states", { state: state }) ||
[];
let allow_edit_list = workflow_states.map((d) => d.allow_edit);
return allow_edit_list;
},
is_self_approval_enabled: function (doctype) {
return frappe.workflow.workflows[doctype].allow_self_approval;
@ -55,14 +57,13 @@ frappe.workflow = {
var state =
doc[state_fieldname] || frappe.workflow.get_default_state(doctype, doc.docstatus);
var allow_edit = state
? frappe.workflow.get_document_state(doctype, state) &&
frappe.workflow.get_document_state(doctype, state).allow_edit
let allow_edit_roles = state
? frappe.workflow.get_document_state_roles(doctype, state)
: null;
if (!frappe.user_roles.includes(allow_edit)) {
return true;
}
let has_common_role = frappe.user_roles.some((role) =>
allow_edit_roles.includes(role)
);
return !has_common_role;
}
return false;
},

View file

@ -411,10 +411,9 @@ export default class OnboardingWidget extends Widget {
});
};
} else {
frappe.msgprint({
message: __("Let us continue with the onboarding"),
title: __("Document Saved"),
});
frappe.show_alert(
__("Document Saved") + "<br>" + __("Let us continue with the onboarding")
);
this.mark_complete(step);
}
},