Merge branch 'develop' into disable_change_log
This commit is contained in:
commit
dff8d92b69
33 changed files with 451 additions and 533 deletions
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"autoname": "format:AL-{#####}",
|
||||
"actions": [],
|
||||
"autoname": "hash",
|
||||
"creation": "2019-07-25 15:44:44.955496",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
|
|
@ -127,10 +128,12 @@
|
|||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"modified": "2019-08-05 19:00:13.839471",
|
||||
"links": [],
|
||||
"modified": "2022-05-03 09:34:19.337551",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Access Log",
|
||||
"naming_rule": "Random",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
|
|
@ -146,5 +149,6 @@
|
|||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_seen": 1
|
||||
}
|
||||
|
|
@ -1018,12 +1018,13 @@ def validate_fields(meta):
|
|||
validate_column_name(fieldname)
|
||||
|
||||
def check_invalid_fieldnames(docname, fieldname):
|
||||
invalid_fields = ("doctype",)
|
||||
if fieldname in invalid_fields:
|
||||
if fieldname in Document._reserved_keywords:
|
||||
frappe.throw(
|
||||
_("{0}: Fieldname cannot be one of {1}").format(
|
||||
docname, ", ".join(frappe.bold(d) for d in invalid_fields)
|
||||
)
|
||||
_("{0}: fieldname cannot be set to reserved keyword {1}").format(
|
||||
frappe.bold(docname),
|
||||
frappe.bold(fieldname),
|
||||
),
|
||||
title=_("Invalid Fieldname"),
|
||||
)
|
||||
|
||||
def check_unique_fieldname(docname, fieldname):
|
||||
|
|
|
|||
|
|
@ -45,6 +45,13 @@
|
|||
"new_password": "Eastern_43A1W",
|
||||
"enabled": 1
|
||||
},
|
||||
{
|
||||
"doctype": "User",
|
||||
"email": "test'5@example.com",
|
||||
"first_name": "_Test'5",
|
||||
"new_password": "Eastern_43A1W",
|
||||
"enabled": 1
|
||||
},
|
||||
{
|
||||
"doctype": "User",
|
||||
"email": "testperm@example.com",
|
||||
|
|
|
|||
|
|
@ -42,12 +42,10 @@ def save_to_db():
|
|||
record_count += 1
|
||||
insert_record(record, doctype)
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
|
||||
def insert_record(record: Union[Dict, "Document"], doctype: str):
|
||||
setattr(record, "doctype", doctype)
|
||||
try:
|
||||
record.update({"doctype": doctype})
|
||||
frappe.get_doc(record).insert()
|
||||
except Exception as e:
|
||||
frappe.logger().error(f"Error while inserting deferred {doctype} record: {e}")
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ def get_permission_query_conditions(user):
|
|||
if not user:
|
||||
user = frappe.session.user
|
||||
|
||||
return """(`tabDashboard Settings`.name = '{user}')""".format(user=user)
|
||||
return """(`tabDashboard Settings`.name = {user})""".format(user=frappe.db.escape(user))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
|
|||
|
|
@ -34,7 +34,9 @@ def get_permission_query_conditions(user):
|
|||
if user == "Administrator":
|
||||
return ""
|
||||
|
||||
return """(`tabKanban Board`.private=0 or `tabKanban Board`.owner='{user}')""".format(user=user)
|
||||
return """(`tabKanban Board`.private=0 or `tabKanban Board`.owner={user})""".format(
|
||||
user=frappe.db.escape(user)
|
||||
)
|
||||
|
||||
|
||||
def has_permission(doc, ptype, user):
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ def get_permission_query_conditions(user):
|
|||
if user == "Administrator":
|
||||
return ""
|
||||
|
||||
return """(`tabNote`.public=1 or `tabNote`.owner="{user}")""".format(user=user)
|
||||
return """(`tabNote`.public=1 or `tabNote`.owner={user})""".format(user=frappe.db.escape(user))
|
||||
|
||||
|
||||
def has_permission(doc, ptype, user):
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ def get_permission_query_conditions(for_user):
|
|||
if for_user == "Administrator":
|
||||
return
|
||||
|
||||
return """(`tabNotification Log`.for_user = '{user}')""".format(user=for_user)
|
||||
return """(`tabNotification Log`.for_user = {user})""".format(user=frappe.db.escape(for_user))
|
||||
|
||||
|
||||
def get_title(doctype, docname, title_field=None):
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ def get_permission_query_conditions(user):
|
|||
if "System Manager" in roles:
|
||||
return """(`tabNotification Settings`.name != 'Administrator')"""
|
||||
|
||||
return """(`tabNotification Settings`.name = '{user}')""".format(user=user)
|
||||
return """(`tabNotification Settings`.name = {user})""".format(user=frappe.db.escape(user))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ from frappe.config import get_modules_from_all_apps_for_user
|
|||
from frappe.model.document import Document
|
||||
from frappe.model.naming import append_number_if_name_exists
|
||||
from frappe.modules.export_file import export_to_files
|
||||
from frappe.query_builder import Criterion
|
||||
from frappe.query_builder.utils import DocType
|
||||
from frappe.utils import cint
|
||||
|
||||
|
||||
|
|
@ -190,36 +192,18 @@ def get_cards_for_user(doctype, txt, searchfield, start, page_len, filters):
|
|||
if not frappe.db.exists("DocType", doctype):
|
||||
return
|
||||
|
||||
numberCard = DocType("Number Card")
|
||||
|
||||
if txt:
|
||||
for field in searchfields:
|
||||
search_conditions.append(
|
||||
"`tab{doctype}`.`{field}` like %(txt)s".format(field=field, doctype=doctype, txt=txt)
|
||||
)
|
||||
search_conditions = [numberCard[field].like("%{txt}%".format(txt=txt)) for field in searchfields]
|
||||
|
||||
search_conditions = " or ".join(search_conditions)
|
||||
condition_query = frappe.db.query.build_conditions(doctype, filters)
|
||||
|
||||
search_conditions = "and (" + search_conditions + ")" if search_conditions else ""
|
||||
conditions, values = frappe.db.build_conditions(filters)
|
||||
values["txt"] = "%" + txt + "%"
|
||||
|
||||
return frappe.db.sql(
|
||||
"""select
|
||||
`tabNumber Card`.name, `tabNumber Card`.label, `tabNumber Card`.document_type
|
||||
from
|
||||
`tabNumber Card`
|
||||
where
|
||||
{conditions} and
|
||||
(`tabNumber Card`.owner = '{user}' or
|
||||
`tabNumber Card`.is_public = 1)
|
||||
{search_conditions}
|
||||
""".format(
|
||||
filters=filters,
|
||||
user=frappe.session.user,
|
||||
search_conditions=search_conditions,
|
||||
conditions=conditions,
|
||||
),
|
||||
values,
|
||||
)
|
||||
return (
|
||||
condition_query.select(numberCard.name, numberCard.label, numberCard.document_type)
|
||||
.where((numberCard.owner == frappe.session.user) | (numberCard.is_public == 1))
|
||||
.where(Criterion.any(search_conditions))
|
||||
).run()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
|
|||
|
|
@ -55,12 +55,6 @@ class FormMeta(Meta):
|
|||
super(FormMeta, self).__init__(doctype)
|
||||
self.load_assets()
|
||||
|
||||
def set(self, key, value, *args, **kwargs):
|
||||
if key in ASSET_KEYS:
|
||||
self.__dict__[key] = value
|
||||
else:
|
||||
super(FormMeta, self).set(key, value, *args, **kwargs)
|
||||
|
||||
def load_assets(self):
|
||||
if self.get("__assets_loaded", False):
|
||||
return
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ frappe.pages['setup-wizard'].on_page_load = function (wrapper) {
|
|||
}
|
||||
frappe.wizard = new frappe.setup.SetupWizard(wizard_settings);
|
||||
frappe.setup.run_event("after_load");
|
||||
// frappe.wizard.values = test_values_edu;
|
||||
let route = frappe.get_route();
|
||||
if (route) {
|
||||
frappe.wizard.show_slide(route[1]);
|
||||
|
|
@ -145,7 +144,7 @@ frappe.setup.SetupWizard = class SetupWizard extends frappe.ui.Slides {
|
|||
|
||||
refresh_slides() {
|
||||
// For Translations, etc.
|
||||
if (this.in_refresh_slides || !this.current_slide.set_values()) {
|
||||
if (this.in_refresh_slides || !this.current_slide.set_values(true)) {
|
||||
return;
|
||||
}
|
||||
this.in_refresh_slides = true;
|
||||
|
|
@ -344,63 +343,40 @@ frappe.setup.slides_settings = [
|
|||
// Welcome (language) slide
|
||||
name: "welcome",
|
||||
title: __("Hello!"),
|
||||
icon: "fa fa-world",
|
||||
// help: __("Let's prepare the system for first use."),
|
||||
|
||||
fields: [
|
||||
{
|
||||
fieldname: "language", label: __("Your Language"),
|
||||
fieldtype: "Select", reqd: 1
|
||||
}
|
||||
],
|
||||
|
||||
onload: function (slide) {
|
||||
this.setup_fields(slide);
|
||||
let browser_language = frappe.setup.utils.get_language_name_from_code(navigator.language);
|
||||
let language_field = slide.get_field("language");
|
||||
|
||||
language_field.set_input(browser_language || "English");
|
||||
|
||||
if (!frappe.setup._from_load_messages) {
|
||||
language_field.$input.trigger("change");
|
||||
}
|
||||
delete frappe.setup._from_load_messages;
|
||||
moment.locale("en");
|
||||
},
|
||||
|
||||
setup_fields: function (slide) {
|
||||
frappe.setup.utils.setup_language_field(slide);
|
||||
frappe.setup.utils.bind_language_events(slide);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Region slide
|
||||
name: 'region',
|
||||
title: __("Select Your Region"),
|
||||
icon: "fa fa-flag",
|
||||
// help: __("Select your Country, Time Zone and Currency"),
|
||||
fields: [
|
||||
{
|
||||
fieldname: "country", label: __("Your Country"), reqd: 1,
|
||||
fieldname: "language",
|
||||
label: __("Your Language"),
|
||||
fieldtype: "Autocomplete",
|
||||
placeholder: __('Select Country')
|
||||
placeholder: __('Select Language'),
|
||||
default: "English",
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "country",
|
||||
label: __("Your Country"),
|
||||
fieldtype: "Autocomplete",
|
||||
placeholder: __('Select Country'),
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldtype: "Section Break"
|
||||
},
|
||||
{ fieldtype: "Section Break" },
|
||||
{
|
||||
fieldname: "timezone",
|
||||
label: __("Time Zone"),
|
||||
placeholder: __('Select Time Zone'),
|
||||
reqd: 1,
|
||||
fieldtype: "Select",
|
||||
reqd: 1,
|
||||
},
|
||||
{ fieldtype: "Column Break" },
|
||||
{
|
||||
fieldname: "currency",
|
||||
label: __("Currency"),
|
||||
placeholder: __('Select Currency'),
|
||||
reqd: 1,
|
||||
fieldtype: "Select",
|
||||
reqd: 1,
|
||||
}
|
||||
],
|
||||
|
||||
|
|
@ -410,14 +386,26 @@ frappe.setup.slides_settings = [
|
|||
} else {
|
||||
frappe.setup.utils.load_regional_data(slide, this.setup_fields);
|
||||
}
|
||||
if (!slide.get_value("language")) {
|
||||
let session_language = frappe.setup.utils.get_language_name_from_code(frappe.boot.lang || navigator.language) || "English";
|
||||
let language_field = slide.get_field("language");
|
||||
|
||||
language_field.set_input(session_language);
|
||||
if (!frappe.setup._from_load_messages) {
|
||||
language_field.$input.trigger("change");
|
||||
}
|
||||
delete frappe.setup._from_load_messages;
|
||||
moment.locale("en");
|
||||
}
|
||||
frappe.setup.utils.bind_region_events(slide);
|
||||
frappe.setup.utils.bind_language_events(slide);
|
||||
},
|
||||
|
||||
setup_fields: function (slide) {
|
||||
frappe.setup.utils.setup_region_fields(slide);
|
||||
frappe.setup.utils.bind_region_events(slide);
|
||||
}
|
||||
frappe.setup.utils.setup_language_field(slide);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Profile slide
|
||||
name: 'user',
|
||||
|
|
@ -438,7 +426,7 @@ frappe.setup.slides_settings = [
|
|||
},
|
||||
{ "fieldname": "password", "label": __("Password"), "fieldtype": "Password" }
|
||||
],
|
||||
// help: __('The first user will become the System Manager (you can change this later).'),
|
||||
|
||||
onload: function (slide) {
|
||||
if (frappe.session.user !== "Administrator") {
|
||||
slide.form.fields_dict.email.$wrapper.toggle(false);
|
||||
|
|
@ -516,7 +504,7 @@ frappe.setup.utils = {
|
|||
setup_language_field: function (slide) {
|
||||
var language_field = slide.get_field("language");
|
||||
language_field.df.options = frappe.setup.data.lang.languages;
|
||||
language_field.refresh();
|
||||
language_field.set_options();
|
||||
},
|
||||
|
||||
setup_region_fields: function (slide) {
|
||||
|
|
@ -555,9 +543,7 @@ frappe.setup.utils = {
|
|||
}
|
||||
|
||||
slide.get_field("currency").set_input(frappe.wizard.values.currency);
|
||||
|
||||
slide.get_field("timezone").set_input(frappe.wizard.values.timezone);
|
||||
|
||||
},
|
||||
|
||||
bind_language_events: function (slide) {
|
||||
|
|
|
|||
|
|
@ -134,10 +134,11 @@ frappe.ui.form.on("Email Account", {
|
|||
|
||||
show_gmail_message_for_less_secure_apps: function(frm) {
|
||||
frm.dashboard.clear_headline();
|
||||
let msg = __("GMail will only work if you enable 2-step authentication and use app-specific password.");
|
||||
let cta = __("Read the step by step guide here.");
|
||||
msg += ` <a target="_blank" href="https://docs.erpnext.com/docs/v13/user/manual/en/setting-up/email/email_account_setup_with_gmail">${cta}</a>`;
|
||||
if (frm.doc.service==="GMail") {
|
||||
frm.dashboard.set_headline_alert('Gmail will only work if you allow access for less secure \
|
||||
apps in Gmail settings. <a target="_blank" \
|
||||
href="https://support.google.com/accounts/answer/6010255?hl=en">Read this for details</a>');
|
||||
frm.dashboard.set_headline_alert(msg);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ class BraintreeSettings(Document):
|
|||
self.data = frappe._dict(data)
|
||||
|
||||
try:
|
||||
self.integration_request = create_request_log(self.data, "Host", "Braintree")
|
||||
self.integration_request = create_request_log(self.data, service_name="Braintree")
|
||||
return self.create_charge_on_braintree()
|
||||
|
||||
except Exception:
|
||||
|
|
|
|||
|
|
@ -1,334 +1,154 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2016-08-04 04:58:40.457416",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"actions": [],
|
||||
"creation": "2022-03-28 12:25:29.929952",
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"request_id",
|
||||
"integration_request_service",
|
||||
"is_remote_request",
|
||||
"column_break_5",
|
||||
"request_description",
|
||||
"status",
|
||||
"section_break_8",
|
||||
"url",
|
||||
"request_headers",
|
||||
"data",
|
||||
"response_section",
|
||||
"output",
|
||||
"error",
|
||||
"reference_section",
|
||||
"reference_doctype",
|
||||
"column_break_16",
|
||||
"reference_docname"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "integration_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Integration Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nHost\nRemote\nSubscription Notification",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "integration_request_service",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Integration Request Service",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Service",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Queued",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nQueued\nAuthorized\nCompleted\nCancelled\nFailed",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "data",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Data",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Request Data",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "output",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Output",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "error",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Error",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "reference_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reference Document Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "reference_docname",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reference Docname",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"label": "Reference Document Name",
|
||||
"options": "reference_doctype",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_remote_request",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Remote Request?",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "request_description",
|
||||
"fieldtype": "Data",
|
||||
"label": "Request Description",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "request_id",
|
||||
"fieldtype": "Data",
|
||||
"label": "Request ID",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "url",
|
||||
"fieldtype": "Data",
|
||||
"label": "URL",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "response_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Response"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.reference_doctype",
|
||||
"fieldname": "reference_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Reference"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_16",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "request_headers",
|
||||
"fieldtype": "Code",
|
||||
"label": "Request Headers",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 1,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-09-05 14:22:27.664645",
|
||||
"links": [],
|
||||
"modified": "2022-04-07 11:32:27.557548",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Integrations",
|
||||
"name": "Integration Request",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
"share": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "integration_request_service",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -182,9 +182,8 @@ class PayPalSettings(Document):
|
|||
kwargs.update(
|
||||
{"token": response.get("TOKEN")[0], "correlation_id": response.get("CORRELATIONID")[0]}
|
||||
)
|
||||
self.integration_request = create_request_log(
|
||||
kwargs, "Remote", "PayPal", response.get("TOKEN")[0]
|
||||
)
|
||||
|
||||
create_request_log(kwargs, service_name="PayPal", name=kwargs["token"])
|
||||
|
||||
return return_url.format(kwargs["token"])
|
||||
|
||||
|
|
@ -463,7 +462,8 @@ def ipn_handler():
|
|||
{
|
||||
"data": json.dumps(frappe.local.form_dict),
|
||||
"doctype": "Integration Request",
|
||||
"integration_type": "Subscription Notification",
|
||||
"request_description": "Subscription Notification",
|
||||
"is_remote_request": 1,
|
||||
"status": "Queued",
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class PaytmSettings(Document):
|
|||
def get_payment_url(self, **kwargs):
|
||||
"""Return payment url with several params"""
|
||||
# create unique order id by making it equal to the integration request
|
||||
integration_request = create_request_log(kwargs, "Host", "Paytm")
|
||||
integration_request = create_request_log(kwargs, service_name="Paytm")
|
||||
kwargs.update(dict(order_id=integration_request.name))
|
||||
|
||||
return get_url("./integrations/paytm_checkout?{0}".format(urlencode(kwargs)))
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ class RazorpaySettings(Document):
|
|||
headers={"content-type": "application/json"},
|
||||
)
|
||||
if not resp.get("id"):
|
||||
frappe.log_error(str(resp), "Razorpay Failed while creating subscription")
|
||||
frappe.log_error(message=str(resp), title="Razorpay Failed while creating subscription")
|
||||
except:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
# failed
|
||||
|
|
@ -179,7 +179,7 @@ class RazorpaySettings(Document):
|
|||
frappe.flags.status = "created"
|
||||
return kwargs
|
||||
else:
|
||||
frappe.log_error(str(resp), "Razorpay Failed while creating subscription")
|
||||
frappe.log_error(message=str(resp), title="Razorpay Failed while creating subscription")
|
||||
|
||||
except:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
|
|
@ -196,7 +196,7 @@ class RazorpaySettings(Document):
|
|||
return kwargs
|
||||
|
||||
def get_payment_url(self, **kwargs):
|
||||
integration_request = create_request_log(kwargs, "Host", "Razorpay")
|
||||
integration_request = create_request_log(kwargs, service_name="Razorpay")
|
||||
return get_url("./integrations/razorpay_checkout?token={0}".format(integration_request.name))
|
||||
|
||||
def create_order(self, **kwargs):
|
||||
|
|
@ -206,7 +206,7 @@ class RazorpaySettings(Document):
|
|||
kwargs["amount"] *= 100
|
||||
|
||||
# Create integration log
|
||||
integration_request = create_request_log(kwargs, "Host", "Razorpay")
|
||||
integration_request = create_request_log(kwargs, service_name="Razorpay")
|
||||
|
||||
# Setup payment options
|
||||
payment_options = {
|
||||
|
|
@ -281,7 +281,7 @@ class RazorpaySettings(Document):
|
|||
self.flags.status_changed_to = "Verified"
|
||||
|
||||
else:
|
||||
frappe.log_error(str(resp), "Razorpay Payment not authorized")
|
||||
frappe.log_error(message=str(resp), title="Razorpay Payment not authorized")
|
||||
|
||||
except:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
|
|
@ -490,7 +490,8 @@ def razorpay_subscription_callback():
|
|||
{
|
||||
"data": json.dumps(frappe.local.form_dict),
|
||||
"doctype": "Integration Request",
|
||||
"integration_type": "Subscription Notification",
|
||||
"request_description": "Subscription Notification",
|
||||
"is_remote_request": 1,
|
||||
"status": "Queued",
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ class StripeSettings(Document):
|
|||
stripe.default_http_client = stripe.http_client.RequestsClient()
|
||||
|
||||
try:
|
||||
self.integration_request = create_request_log(self.data, "Host", "Stripe")
|
||||
self.integration_request = create_request_log(self.data, service_name="Stripe")
|
||||
return self.create_charge_on_stripe()
|
||||
|
||||
except Exception:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"actions": [],
|
||||
"autoname": "WEBHOOK-REQ-.#####",
|
||||
"autoname": "hash",
|
||||
"creation": "2021-05-24 21:35:59.104776",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
|
|
@ -56,10 +56,11 @@
|
|||
"in_create": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-05-26 23:57:58.495261",
|
||||
"modified": "2022-05-03 09:33:49.240777",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Integrations",
|
||||
"name": "Webhook Request Log",
|
||||
"naming_rule": "Random",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
|
|
@ -77,5 +78,6 @@
|
|||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -42,22 +42,45 @@ def make_put_request(url, **kwargs):
|
|||
return make_request("PUT", url, **kwargs)
|
||||
|
||||
|
||||
def create_request_log(data, integration_type, service_name, name=None, error=None):
|
||||
if isinstance(data, str):
|
||||
data = json.loads(data)
|
||||
def create_request_log(
|
||||
data,
|
||||
integration_type=None,
|
||||
service_name=None,
|
||||
name=None,
|
||||
error=None,
|
||||
request_headers=None,
|
||||
output=None,
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
DEPRECATED: The parameter integration_type will be removed in the next major release.
|
||||
Use is_remote_request instead.
|
||||
"""
|
||||
if integration_type == "Remote":
|
||||
kwargs["is_remote_request"] = 1
|
||||
|
||||
if isinstance(error, str):
|
||||
error = json.loads(error)
|
||||
elif integration_type == "Subscription Notification":
|
||||
kwargs["request_description"] = integration_type
|
||||
|
||||
reference_doctype = reference_docname = None
|
||||
if "reference_doctype" not in kwargs:
|
||||
if isinstance(data, str):
|
||||
data = json.loads(data)
|
||||
|
||||
reference_doctype = data.get("reference_doctype")
|
||||
reference_docname = data.get("reference_docname")
|
||||
|
||||
integration_request = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Integration Request",
|
||||
"integration_type": integration_type,
|
||||
"integration_request_service": service_name,
|
||||
"reference_doctype": data.get("reference_doctype"),
|
||||
"reference_docname": data.get("reference_docname"),
|
||||
"error": json.dumps(error, default=json_handler),
|
||||
"data": json.dumps(data, default=json_handler),
|
||||
"request_headers": get_json(request_headers),
|
||||
"data": get_json(data),
|
||||
"output": get_json(output),
|
||||
"error": get_json(error),
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_docname": reference_docname,
|
||||
**kwargs,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -70,6 +93,10 @@ def create_request_log(data, integration_type, service_name, name=None, error=No
|
|||
return integration_request
|
||||
|
||||
|
||||
def get_json(obj):
|
||||
return obj if isinstance(obj, str) else frappe.as_json(obj, indent=1)
|
||||
|
||||
|
||||
def get_payment_gateway_controller(payment_gateway):
|
||||
"""Return payment gateway controller"""
|
||||
gateway = frappe.get_doc("Payment Gateway", payment_gateway)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import json
|
|||
from typing import Dict, List
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe import _, _dict
|
||||
from frappe.model import (
|
||||
child_table_fields,
|
||||
datetime_fields,
|
||||
|
|
@ -23,7 +23,16 @@ from frappe.utils.html_utils import unescape_html
|
|||
|
||||
max_positive_value = {"smallint": 2**15, "int": 2**31, "bigint": 2**63}
|
||||
|
||||
DOCTYPES_FOR_DOCTYPE = ("DocType", "DocField", "DocPerm", "DocType Action", "DocType Link")
|
||||
DOCTYPE_TABLE_FIELDS = [
|
||||
_dict(fieldname="fields", options="DocField"),
|
||||
_dict(fieldname="permissions", options="DocPerm"),
|
||||
_dict(fieldname="actions", options="DocType Action"),
|
||||
_dict(fieldname="links", options="DocType Link"),
|
||||
_dict(fieldname="states", options="DocType State"),
|
||||
]
|
||||
|
||||
TABLE_DOCTYPES_FOR_DOCTYPE = {df["fieldname"]: df["options"] for df in DOCTYPE_TABLE_FIELDS}
|
||||
DOCTYPES_FOR_DOCTYPE = {"DocType", *TABLE_DOCTYPES_FOR_DOCTYPE.values()}
|
||||
|
||||
|
||||
def get_controller(doctype):
|
||||
|
|
@ -78,12 +87,28 @@ def get_controller(doctype):
|
|||
|
||||
|
||||
class BaseDocument(object):
|
||||
ignore_in_setter = ("doctype", "_meta", "meta", "_table_fields", "_valid_columns")
|
||||
_reserved_keywords = {
|
||||
"doctype",
|
||||
"meta",
|
||||
"_meta",
|
||||
"flags",
|
||||
"_table_fields",
|
||||
"_valid_columns",
|
||||
"_table_fieldnames",
|
||||
"_reserved_keywords",
|
||||
"dont_update_if_missing",
|
||||
}
|
||||
|
||||
def __init__(self, d):
|
||||
if d.get("doctype"):
|
||||
self.doctype = d["doctype"]
|
||||
|
||||
self._table_fieldnames = (
|
||||
d["_table_fieldnames"] # from cache
|
||||
if "_table_fieldnames" in d
|
||||
else {df.fieldname for df in self._get_table_fields()}
|
||||
)
|
||||
|
||||
self.update(d)
|
||||
self.dont_update_if_missing = []
|
||||
|
||||
|
|
@ -92,10 +117,10 @@ class BaseDocument(object):
|
|||
|
||||
@property
|
||||
def meta(self):
|
||||
if not getattr(self, "_meta", None):
|
||||
self._meta = frappe.get_meta(self.doctype)
|
||||
if not (meta := getattr(self, "_meta", None)):
|
||||
self._meta = meta = frappe.get_meta(self.doctype)
|
||||
|
||||
return self._meta
|
||||
return meta
|
||||
|
||||
def __getstate__(self):
|
||||
self._meta = None
|
||||
|
|
@ -144,17 +169,12 @@ class BaseDocument(object):
|
|||
|
||||
if filters:
|
||||
if isinstance(filters, dict):
|
||||
value = _filter(self.__dict__.get(key, []), filters, limit=limit)
|
||||
else:
|
||||
default = filters
|
||||
filters = None
|
||||
value = self.__dict__.get(key, default)
|
||||
else:
|
||||
value = self.__dict__.get(key, default)
|
||||
return _filter(self.__dict__.get(key, []), filters, limit=limit)
|
||||
|
||||
if value is None and key in (d.fieldname for d in self.meta.get_table_fields()):
|
||||
value = []
|
||||
self.set(key, value)
|
||||
# perhaps you wanted to set a default instead
|
||||
default = filters
|
||||
|
||||
value = self.__dict__.get(key, default)
|
||||
|
||||
if limit and isinstance(value, (list, tuple)) and len(value) > limit:
|
||||
value = value[:limit]
|
||||
|
|
@ -165,14 +185,19 @@ class BaseDocument(object):
|
|||
return self.get(key, filters=filters, limit=1)[0]
|
||||
|
||||
def set(self, key, value, as_value=False):
|
||||
if key in self.ignore_in_setter:
|
||||
if key in self._reserved_keywords:
|
||||
return
|
||||
|
||||
if isinstance(value, list) and not as_value:
|
||||
if not as_value and key in self._table_fieldnames:
|
||||
self.__dict__[key] = []
|
||||
self.extend(key, value)
|
||||
else:
|
||||
self.__dict__[key] = value
|
||||
|
||||
# if value is falsy, just init to an empty list
|
||||
if value:
|
||||
self.extend(key, value)
|
||||
|
||||
return
|
||||
|
||||
self.__dict__[key] = value
|
||||
|
||||
def delete_key(self, key):
|
||||
if key in self.__dict__:
|
||||
|
|
@ -190,41 +215,27 @@ class BaseDocument(object):
|
|||
"""
|
||||
if value is None:
|
||||
value = {}
|
||||
if isinstance(value, (dict, BaseDocument)):
|
||||
if not self.__dict__.get(key):
|
||||
self.__dict__[key] = []
|
||||
|
||||
value = self._init_child(value, key)
|
||||
self.__dict__[key].append(value)
|
||||
if (table := self.__dict__.get(key)) is None:
|
||||
self.__dict__[key] = table = []
|
||||
|
||||
# reference parent document
|
||||
value.parent_doc = self
|
||||
value = self._init_child(value, key)
|
||||
table.append(value)
|
||||
|
||||
return value
|
||||
else:
|
||||
# reference parent document
|
||||
value.parent_doc = self
|
||||
|
||||
# metaclasses may have arbitrary lists
|
||||
# which we can ignore
|
||||
if getattr(self, "_metaclass", None) or self.__class__.__name__ in (
|
||||
"Meta",
|
||||
"FormMeta",
|
||||
"DocField",
|
||||
):
|
||||
return value
|
||||
|
||||
raise ValueError(
|
||||
'Document for field "{0}" attached to child table of "{1}" must be a dict or BaseDocument, not {2} ({3})'.format(
|
||||
key, self.name, str(type(value))[1:-1], value
|
||||
)
|
||||
)
|
||||
return value
|
||||
|
||||
def extend(self, key, value):
|
||||
if isinstance(value, list):
|
||||
for v in value:
|
||||
self.append(key, v)
|
||||
else:
|
||||
try:
|
||||
value = iter(value)
|
||||
except TypeError:
|
||||
raise ValueError
|
||||
|
||||
for v in value:
|
||||
self.append(key, v)
|
||||
|
||||
def remove(self, doc):
|
||||
# Usage: from the parent doc, pass the child table doc
|
||||
# to remove that child doc from the child table, thus removing it from the parent doc
|
||||
|
|
@ -232,15 +243,12 @@ class BaseDocument(object):
|
|||
self.get(doc.parentfield).remove(doc)
|
||||
|
||||
def _init_child(self, value, key):
|
||||
if not self.doctype:
|
||||
return value
|
||||
|
||||
if not isinstance(value, BaseDocument):
|
||||
value["doctype"] = self.get_table_field_doctype(key)
|
||||
if not value["doctype"]:
|
||||
if not (doctype := self.get_table_field_doctype(key)):
|
||||
raise AttributeError(key)
|
||||
|
||||
value = get_controller(value["doctype"])(value)
|
||||
value["doctype"] = doctype
|
||||
value = get_controller(doctype)(value)
|
||||
|
||||
value.parent = self.name
|
||||
value.parenttype = self.doctype
|
||||
|
|
@ -250,17 +258,35 @@ class BaseDocument(object):
|
|||
value.docstatus = DocStatus.draft()
|
||||
|
||||
if not getattr(value, "idx", None):
|
||||
value.idx = len(self.get(key) or []) + 1
|
||||
if table := getattr(self, key, None):
|
||||
value.idx = len(table) + 1
|
||||
else:
|
||||
value.idx = 1
|
||||
|
||||
if not getattr(value, "name", None):
|
||||
value.__dict__["__islocal"] = 1
|
||||
|
||||
return value
|
||||
|
||||
def _get_table_fields(self):
|
||||
"""
|
||||
To get table fields during Document init
|
||||
Meta.get_table_fields goes into recursion for special doctypes
|
||||
"""
|
||||
|
||||
if self.doctype == "DocType":
|
||||
return DOCTYPE_TABLE_FIELDS
|
||||
|
||||
# child tables don't have child tables
|
||||
if self.doctype in DOCTYPES_FOR_DOCTYPE or getattr(self, "parentfield", None):
|
||||
return ()
|
||||
|
||||
return self.meta.get_table_fields()
|
||||
|
||||
def get_valid_dict(
|
||||
self, sanitize=True, convert_dates_to_str=False, ignore_nulls=False, ignore_virtual=False
|
||||
) -> Dict:
|
||||
d = frappe._dict()
|
||||
d = _dict()
|
||||
for fieldname in self.meta.get_valid_columns():
|
||||
# column is valid, we can use getattr
|
||||
d[fieldname] = getattr(self, fieldname, None)
|
||||
|
|
@ -316,6 +342,16 @@ class BaseDocument(object):
|
|||
|
||||
return d
|
||||
|
||||
def init_child_tables(self):
|
||||
"""
|
||||
This is needed so that one can loop over child table properties
|
||||
without worrying about whether or not they have values
|
||||
"""
|
||||
|
||||
for fieldname in self._table_fieldnames:
|
||||
if self.__dict__.get(fieldname) is None:
|
||||
self.__dict__[fieldname] = []
|
||||
|
||||
def init_valid_columns(self):
|
||||
for key in default_fields:
|
||||
if key not in self.__dict__:
|
||||
|
|
@ -365,9 +401,9 @@ class BaseDocument(object):
|
|||
doc = self.get_valid_dict(convert_dates_to_str=convert_dates_to_str, ignore_nulls=no_nulls)
|
||||
doc["doctype"] = self.doctype
|
||||
|
||||
for df in self.meta.get_table_fields():
|
||||
children = self.get(df.fieldname) or []
|
||||
doc[df.fieldname] = [
|
||||
for fieldname in self._table_fieldnames:
|
||||
children = self.get(fieldname) or []
|
||||
doc[fieldname] = [
|
||||
d.as_dict(
|
||||
convert_dates_to_str=convert_dates_to_str,
|
||||
no_nulls=no_nulls,
|
||||
|
|
@ -407,10 +443,9 @@ class BaseDocument(object):
|
|||
try:
|
||||
return self.meta.get_field(fieldname).options
|
||||
except AttributeError:
|
||||
if self.doctype == "DocType":
|
||||
return dict(links="DocType Link", actions="DocType Action", states="DocType State").get(
|
||||
fieldname
|
||||
)
|
||||
if self.doctype == "DocType" and (table_doctype := TABLE_DOCTYPES_FOR_DOCTYPE.get(fieldname)):
|
||||
return table_doctype
|
||||
|
||||
raise
|
||||
|
||||
def get_parentfield_of_doctype(self, doctype):
|
||||
|
|
@ -519,8 +554,8 @@ class BaseDocument(object):
|
|||
"""Raw update parent + children
|
||||
DOES NOT VALIDATE AND CALL TRIGGERS"""
|
||||
self.db_update()
|
||||
for df in self.meta.get_table_fields():
|
||||
for doc in self.get(df.fieldname):
|
||||
for fieldname in self._table_fieldnames:
|
||||
for doc in self.get(fieldname):
|
||||
doc.db_update()
|
||||
|
||||
def show_unique_validation_message(self, e):
|
||||
|
|
@ -632,7 +667,7 @@ class BaseDocument(object):
|
|||
if self.meta.istable:
|
||||
for fieldname in ("parent", "parenttype"):
|
||||
if not self.get(fieldname):
|
||||
missing.append((fieldname, get_msg(frappe._dict(label=fieldname))))
|
||||
missing.append((fieldname, get_msg(_dict(label=fieldname))))
|
||||
|
||||
return missing
|
||||
|
||||
|
|
@ -679,7 +714,7 @@ class BaseDocument(object):
|
|||
if not frappe.get_meta(doctype).get("is_virtual"):
|
||||
if not fields_to_fetch:
|
||||
# cache a single value type
|
||||
values = frappe._dict(name=frappe.db.get_value(doctype, docname, "name", cache=True))
|
||||
values = _dict(name=frappe.db.get_value(doctype, docname, "name", cache=True))
|
||||
else:
|
||||
values_to_fetch = ["name"] + [_df.fetch_from.split(".")[-1] for _df in fields_to_fetch]
|
||||
|
||||
|
|
@ -1009,10 +1044,10 @@ class BaseDocument(object):
|
|||
cache_key = parentfield or "main"
|
||||
|
||||
if not hasattr(self, "_precision"):
|
||||
self._precision = frappe._dict()
|
||||
self._precision = _dict()
|
||||
|
||||
if cache_key not in self._precision:
|
||||
self._precision[cache_key] = frappe._dict()
|
||||
self._precision[cache_key] = _dict()
|
||||
|
||||
if fieldname not in self._precision[cache_key]:
|
||||
self._precision[cache_key][fieldname] = None
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ class Document(BaseDocument):
|
|||
if kwargs:
|
||||
# init base document
|
||||
super(Document, self).__init__(kwargs)
|
||||
self.init_child_tables()
|
||||
self.init_valid_columns()
|
||||
|
||||
else:
|
||||
|
|
@ -150,25 +151,19 @@ class Document(BaseDocument):
|
|||
|
||||
super(Document, self).__init__(d)
|
||||
|
||||
if self.name == "DocType" and self.doctype == "DocType":
|
||||
from frappe.model.meta import DOCTYPE_TABLE_FIELDS
|
||||
|
||||
table_fields = DOCTYPE_TABLE_FIELDS
|
||||
else:
|
||||
table_fields = self.meta.get_table_fields()
|
||||
|
||||
for df in table_fields:
|
||||
children = frappe.db.get_values(
|
||||
df.options,
|
||||
{"parent": self.name, "parenttype": self.doctype, "parentfield": df.fieldname},
|
||||
"*",
|
||||
as_dict=True,
|
||||
order_by="idx asc",
|
||||
for df in self._get_table_fields():
|
||||
children = (
|
||||
frappe.db.get_values(
|
||||
df.options,
|
||||
{"parent": self.name, "parenttype": self.doctype, "parentfield": df.fieldname},
|
||||
"*",
|
||||
as_dict=True,
|
||||
order_by="idx asc",
|
||||
)
|
||||
or []
|
||||
)
|
||||
if children:
|
||||
self.set(df.fieldname, children)
|
||||
else:
|
||||
self.set(df.fieldname, [])
|
||||
|
||||
self.set(df.fieldname, children)
|
||||
|
||||
# sometimes __setup__ can depend on child values, hence calling again at the end
|
||||
if hasattr(self, "__setup__"):
|
||||
|
|
@ -897,8 +892,7 @@ class Document(BaseDocument):
|
|||
if parenttype and df.options != parenttype:
|
||||
continue
|
||||
|
||||
value = self.get(df.fieldname)
|
||||
if isinstance(value, list):
|
||||
if value := self.get(df.fieldname):
|
||||
children.extend(value)
|
||||
|
||||
return children
|
||||
|
|
|
|||
|
|
@ -30,7 +30,11 @@ from frappe.model import (
|
|||
optional_fields,
|
||||
table_fields,
|
||||
)
|
||||
from frappe.model.base_document import BaseDocument
|
||||
from frappe.model.base_document import (
|
||||
DOCTYPE_TABLE_FIELDS,
|
||||
TABLE_DOCTYPES_FOR_DOCTYPE,
|
||||
BaseDocument,
|
||||
)
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.workflow import get_workflow_name
|
||||
from frappe.modules import load_doctype_module
|
||||
|
|
@ -148,9 +152,9 @@ class Meta(Document):
|
|||
out[key] = value
|
||||
|
||||
# set empty lists for unset table fields
|
||||
for table_field in DOCTYPE_TABLE_FIELDS:
|
||||
if out.get(table_field.fieldname) is None:
|
||||
out[table_field.fieldname] = []
|
||||
for fieldname in TABLE_DOCTYPES_FOR_DOCTYPE.keys():
|
||||
if out.get(fieldname) is None:
|
||||
out[fieldname] = []
|
||||
|
||||
return out
|
||||
|
||||
|
|
@ -225,13 +229,7 @@ class Meta(Document):
|
|||
return self._valid_columns
|
||||
|
||||
def get_table_field_doctype(self, fieldname):
|
||||
return {
|
||||
"fields": "DocField",
|
||||
"permissions": "DocPerm",
|
||||
"actions": "DocType Action",
|
||||
"links": "DocType Link",
|
||||
"states": "DocType State",
|
||||
}.get(fieldname)
|
||||
return TABLE_DOCTYPES_FOR_DOCTYPE.get(fieldname)
|
||||
|
||||
def get_field(self, fieldname):
|
||||
"""Return docfield from meta"""
|
||||
|
|
@ -642,14 +640,6 @@ class Meta(Document):
|
|||
return self.has_field("lft") and self.has_field("rgt")
|
||||
|
||||
|
||||
DOCTYPE_TABLE_FIELDS = [
|
||||
frappe._dict({"fieldname": "fields", "options": "DocField"}),
|
||||
frappe._dict({"fieldname": "permissions", "options": "DocPerm"}),
|
||||
frappe._dict({"fieldname": "actions", "options": "DocType Action"}),
|
||||
frappe._dict({"fieldname": "links", "options": "DocType Link"}),
|
||||
frappe._dict({"fieldname": "states", "options": "DocType State"}),
|
||||
]
|
||||
|
||||
#######
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ from frappe.model.dynamic_links import get_dynamic_link_map
|
|||
from frappe.model.naming import validate_name
|
||||
from frappe.model.utils.user_settings import sync_user_settings, update_user_settings_data
|
||||
from frappe.query_builder import Field
|
||||
from frappe.query_builder.utils import DocType, Table
|
||||
from frappe.utils.data import sbool
|
||||
from frappe.utils.password import rename_password
|
||||
from frappe.utils.scheduler import is_scheduler_inactive
|
||||
|
|
@ -198,7 +197,7 @@ def rename_doc(
|
|||
rename_password(doctype, old, new)
|
||||
|
||||
# update user_permissions
|
||||
DefaultValue = DocType("DefaultValue")
|
||||
DefaultValue = frappe.qb.DocType("DefaultValue")
|
||||
frappe.qb.update(DefaultValue).set(DefaultValue.defvalue, new).where(
|
||||
(DefaultValue.parenttype == "User Permission")
|
||||
& (DefaultValue.defkey == doctype)
|
||||
|
|
@ -267,7 +266,7 @@ def update_user_settings(old: str, new: str, link_fields: List[Dict]) -> None:
|
|||
|
||||
# find the user settings for the linked doctypes
|
||||
linked_doctypes = {d.parent for d in link_fields if not d.issingle}
|
||||
UserSettings = Table("__UserSettings")
|
||||
UserSettings = frappe.qb.Table("__UserSettings")
|
||||
|
||||
user_settings_details = (
|
||||
frappe.qb.from_(UserSettings)
|
||||
|
|
@ -299,7 +298,7 @@ def update_customizations(old: str, new: str) -> None:
|
|||
|
||||
def update_attachments(doctype: str, old: str, new: str) -> None:
|
||||
if doctype != "DocType":
|
||||
File = DocType("File")
|
||||
File = frappe.qb.DocType("File")
|
||||
|
||||
frappe.qb.update(File).set(File.attached_to_name, new).where(
|
||||
(File.attached_to_name == old) & (File.attached_to_doctype == doctype)
|
||||
|
|
@ -307,7 +306,7 @@ def update_attachments(doctype: str, old: str, new: str) -> None:
|
|||
|
||||
|
||||
def rename_versions(doctype: str, old: str, new: str) -> None:
|
||||
Version = DocType("Version")
|
||||
Version = frappe.qb.DocType("Version")
|
||||
|
||||
frappe.qb.update(Version).set(Version.docname, new).where(
|
||||
(Version.docname == old) & (Version.ref_doctype == doctype)
|
||||
|
|
@ -315,7 +314,7 @@ def rename_versions(doctype: str, old: str, new: str) -> None:
|
|||
|
||||
|
||||
def rename_eps_records(doctype: str, old: str, new: str) -> None:
|
||||
EPL = DocType("Energy Point Log")
|
||||
EPL = frappe.qb.DocType("Energy Point Log")
|
||||
|
||||
frappe.qb.update(EPL).set(EPL.reference_name, new).where(
|
||||
(EPL.reference_doctype == doctype) & (EPL.reference_name == old)
|
||||
|
|
@ -455,10 +454,10 @@ def get_link_fields(doctype: str) -> List[Dict]:
|
|||
frappe.flags.link_fields = {}
|
||||
|
||||
if doctype not in frappe.flags.link_fields:
|
||||
dt = DocType("DocType")
|
||||
df = DocType("DocField")
|
||||
cf = DocType("Custom Field")
|
||||
ps = DocType("Property Setter")
|
||||
dt = frappe.qb.DocType("DocType")
|
||||
df = frappe.qb.DocType("DocField")
|
||||
cf = frappe.qb.DocType("Custom Field")
|
||||
ps = frappe.qb.DocType("Property Setter")
|
||||
|
||||
st_issingle = frappe.qb.from_(dt).select(dt.issingle).where(dt.name == df.parent).as_("issingle")
|
||||
standard_fields = (
|
||||
|
|
@ -492,8 +491,8 @@ def get_link_fields(doctype: str) -> List[Dict]:
|
|||
|
||||
|
||||
def update_options_for_fieldtype(fieldtype: str, old: str, new: str) -> None:
|
||||
CustomField = DocType("Custom Field")
|
||||
PropertySetter = DocType("Property Setter")
|
||||
CustomField = frappe.qb.DocType("Custom Field")
|
||||
PropertySetter = frappe.qb.DocType("Property Setter")
|
||||
|
||||
if frappe.conf.developer_mode:
|
||||
for name in frappe.get_all("DocField", filters={"options": old}, pluck="parent"):
|
||||
|
|
@ -506,7 +505,7 @@ def update_options_for_fieldtype(fieldtype: str, old: str, new: str) -> None:
|
|||
if save:
|
||||
doctype.save()
|
||||
else:
|
||||
DocField = DocType("DocField")
|
||||
DocField = frappe.qb.DocType("DocField")
|
||||
frappe.qb.update(DocField).set(DocField.options, new).where(
|
||||
(DocField.fieldtype == fieldtype) & (DocField.options == old)
|
||||
).run()
|
||||
|
|
@ -525,10 +524,10 @@ def get_select_fields(old: str, new: str) -> List[Dict]:
|
|||
get select type fields where doctype's name is hardcoded as
|
||||
new line separated list
|
||||
"""
|
||||
df = DocType("DocField")
|
||||
dt = DocType("DocType")
|
||||
cf = DocType("Custom Field")
|
||||
ps = DocType("Property Setter")
|
||||
df = frappe.qb.DocType("DocField")
|
||||
dt = frappe.qb.DocType("DocType")
|
||||
cf = frappe.qb.DocType("Custom Field")
|
||||
ps = frappe.qb.DocType("Property Setter")
|
||||
|
||||
# get link fields from tabDocField
|
||||
st_issingle = frappe.qb.from_(dt).select(dt.issingle).where(dt.name == df.parent).as_("issingle")
|
||||
|
|
@ -570,9 +569,9 @@ def get_select_fields(old: str, new: str) -> List[Dict]:
|
|||
def update_select_field_values(old: str, new: str):
|
||||
from frappe.query_builder.functions import Replace
|
||||
|
||||
DocField = DocType("DocField")
|
||||
CustomField = DocType("Custom Field")
|
||||
PropertySetter = DocType("Property Setter")
|
||||
DocField = frappe.qb.DocType("DocField")
|
||||
CustomField = frappe.qb.DocType("Custom Field")
|
||||
PropertySetter = frappe.qb.DocType("Property Setter")
|
||||
|
||||
frappe.qb.update(DocField).set(DocField.options, Replace(DocField.options, old, new)).where(
|
||||
(DocField.fieldtype == "Select")
|
||||
|
|
@ -623,12 +622,12 @@ def update_parenttype_values(old: str, new: str):
|
|||
child_doctypes = set(list(d["options"] for d in child_doctypes) + property_setter_child_doctypes)
|
||||
|
||||
for doctype in child_doctypes:
|
||||
Table = DocType(doctype)
|
||||
frappe.qb.update(Table).set(Table.parenttype, new).where(Table.parenttype == old).run()
|
||||
table = frappe.qb.DocType(doctype)
|
||||
frappe.qb.update(table).set(table.parenttype, new).where(table.parenttype == old).run()
|
||||
|
||||
|
||||
def rename_dynamic_links(doctype: str, old: str, new: str):
|
||||
Singles = DocType("Singles")
|
||||
Singles = frappe.qb.DocType("Singles")
|
||||
for df in get_dynamic_link_map().get(doctype, []):
|
||||
# dynamic link in single, just one value to check
|
||||
if frappe.get_meta(df.parent).issingle:
|
||||
|
|
|
|||
17
frappe/patches/v14_0/update_integration_request.py
Normal file
17
frappe/patches/v14_0/update_integration_request.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
doctype = "Integration Request"
|
||||
frappe.db.set_value(
|
||||
doctype,
|
||||
{"integration_type": "Remote", "integration_request_service": ("!=", "PayPal")},
|
||||
"is_remote_request",
|
||||
1,
|
||||
)
|
||||
frappe.db.set_value(
|
||||
doctype,
|
||||
{"integration_type": "Subscription Notification"},
|
||||
"request_description",
|
||||
"Subscription Notification",
|
||||
)
|
||||
|
|
@ -105,8 +105,8 @@ frappe.ui.Slide = class Slide {
|
|||
});
|
||||
}
|
||||
|
||||
set_values() {
|
||||
this.values = this.form.get_values();
|
||||
set_values(ignore_errors) {
|
||||
this.values = this.form.get_values(ignore_errors);
|
||||
if (this.values === null) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -309,6 +309,8 @@ frappe.ui.Slides = class Slides {
|
|||
// Can be called by a slide to update states
|
||||
this.$slide_progress.empty();
|
||||
|
||||
if (this.slides.length <= 1) return
|
||||
|
||||
this.slides.map((slide, id) => {
|
||||
let $dot = $(`<div class="slide-step">
|
||||
<div class="slide-step-indicator"></div>
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ frappe.ui.SortSelector = class SortSelector {
|
|||
// bold, mandatory and fields that are available in list view
|
||||
meta.fields.forEach(function(df) {
|
||||
if (
|
||||
(df.mandatory || df.bold || df.in_list_view)
|
||||
(df.mandatory || df.bold || df.in_list_view || df.reqd)
|
||||
&& frappe.model.is_value_type(df.fieldtype)
|
||||
&& frappe.perm.has_perm(me.doctype, df.permlevel, "read")
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from frappe.model.base_document import BaseDocument
|
|||
|
||||
class TestBaseDocument(unittest.TestCase):
|
||||
def test_docstatus(self):
|
||||
doc = BaseDocument({"docstatus": 0})
|
||||
doc = BaseDocument({"docstatus": 0, "doctype": "ToDo"})
|
||||
self.assertTrue(doc.docstatus.is_draft())
|
||||
self.assertEqual(doc.docstatus, 0)
|
||||
|
||||
|
|
|
|||
|
|
@ -692,6 +692,29 @@ class TestReportview(unittest.TestCase):
|
|||
dt.delete()
|
||||
table_dt.delete()
|
||||
|
||||
def test_permission_query_condition(self):
|
||||
from frappe.desk.doctype.dashboard_settings.dashboard_settings import create_dashboard_settings
|
||||
|
||||
self.doctype = "Dashboard Settings"
|
||||
self.user = "test'5@example.com"
|
||||
|
||||
permission_query_conditions = DatabaseQuery.get_permission_query_conditions(self)
|
||||
|
||||
create_dashboard_settings(self.user)
|
||||
|
||||
dashboard_settings = frappe.db.sql(
|
||||
"""
|
||||
SELECT name
|
||||
FROM `tabDashboard Settings`
|
||||
WHERE {condition}
|
||||
""".format(
|
||||
condition=permission_query_conditions
|
||||
),
|
||||
as_dict=1,
|
||||
)[0]
|
||||
|
||||
self.assertTrue(dashboard_settings)
|
||||
|
||||
|
||||
def add_child_table_to_blog_post():
|
||||
child_table = frappe.get_doc(
|
||||
|
|
|
|||
12
frappe/tests/test_deferred_insert.py
Normal file
12
frappe/tests/test_deferred_insert.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import frappe
|
||||
from frappe.deferred_insert import deferred_insert, save_to_db
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
|
||||
class TestDeferredInsert(FrappeTestCase):
|
||||
def test_deferred_insert(self):
|
||||
route_history = {"route": frappe.generate_hash(), "user": "Administrator"}
|
||||
deferred_insert("Route History", [route_history])
|
||||
|
||||
save_to_db()
|
||||
self.assertTrue(frappe.db.exists("Route History", route_history))
|
||||
|
|
@ -341,3 +341,19 @@ class TestDocument(unittest.TestCase):
|
|||
|
||||
# run_method should get overridden
|
||||
self.assertEqual(doc.run_method("as_dict"), "success")
|
||||
|
||||
def test_extend(self):
|
||||
doc = frappe.get_last_doc("User")
|
||||
self.assertRaises(ValueError, doc.extend, "user_emails", None)
|
||||
|
||||
# allow calling doc.extend with iterable objects
|
||||
doc.extend("user_emails", ())
|
||||
doc.extend("user_emails", [])
|
||||
doc.extend("user_emails", (x for x in ()))
|
||||
|
||||
def test_set(self):
|
||||
doc = frappe.get_last_doc("User")
|
||||
|
||||
# setting None should init a table field to empty list
|
||||
doc.set("user_emails", None)
|
||||
self.assertEqual(doc.user_emails, [])
|
||||
|
|
|
|||
|
|
@ -53,9 +53,12 @@ def get_permission_query_conditions(user):
|
|||
.where(WorkflowActionPermittedRole.role.isin(roles))
|
||||
).get_sql()
|
||||
|
||||
return f"""(`tabWorkflow Action`.`name` in ({permitted_workflow_actions})
|
||||
or `tabWorkflow Action`.`user`='{user}')
|
||||
and `tabWorkflow Action`.`status`='Open'"""
|
||||
return """(`tabWorkflow Action`.`name` in ({permitted_workflow_actions})
|
||||
or `tabWorkflow Action`.`user`={user})
|
||||
and `tabWorkflow Action`.`status`='Open'
|
||||
""".format(
|
||||
permitted_workflow_actions=permitted_workflow_actions, user=frappe.db.escape(user)
|
||||
)
|
||||
|
||||
|
||||
def has_permission(doc, user):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue