Merge branch 'develop' into bugfix/postgres_schema_support
This commit is contained in:
commit
efc9bdd4f6
60 changed files with 1538 additions and 797 deletions
|
|
@ -442,6 +442,12 @@ def get_site_config(sites_path: str | None = None, site_path: str | None = None)
|
|||
# Set the user as database name if not set in config
|
||||
config["db_user"] = os.environ.get("FRAPPE_DB_USER") or config.get("db_user") or config.get("db_name")
|
||||
|
||||
# vice versa for dbname if not defined
|
||||
config["db_name"] = os.environ.get("FRAPPE_DB_NAME") or config.get("db_name") or config["db_user"]
|
||||
|
||||
# read password
|
||||
config["db_password"] = os.environ.get("FRAPPE_DB_PASSWORD") or config.get("db_password")
|
||||
|
||||
# Allow externally extending the config with hooks
|
||||
if extra_config := config.get("extra_config"):
|
||||
if isinstance(extra_config, str):
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ def get_preview_from_template(data_import, import_file=None, google_sheets_url=N
|
|||
|
||||
|
||||
@frappe.whitelist()
|
||||
def form_start_import(data_import):
|
||||
def form_start_import(data_import: str):
|
||||
return frappe.get_doc("Data Import", data_import).start_import()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ frappe.ui.form.on("DocType", {
|
|||
}
|
||||
}
|
||||
|
||||
const customize_form_link = "<a href='/app/customize-form'>Customize Form</a>";
|
||||
const customize_form_link = `<a href="/app/customize-form">${__("Customize Form")}</a>`;
|
||||
if (!frappe.boot.developer_mode && !frm.doc.custom) {
|
||||
// make the document read-only
|
||||
frm.set_read_only();
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ from frappe.core.api.file import (
|
|||
unzip_file,
|
||||
)
|
||||
from frappe.core.doctype.file.exceptions import FileTypeNotAllowed
|
||||
from frappe.core.doctype.file.utils import get_extension
|
||||
from frappe.core.doctype.file.utils import get_corrupted_image_msg, get_extension
|
||||
from frappe.desk.form.utils import add_comment
|
||||
from frappe.exceptions import ValidationError
|
||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||
|
|
@ -768,6 +768,25 @@ class TestFileUtils(FrappeTestCase):
|
|||
)
|
||||
self.assertRegex(communication.content, r"<img src=\"(.*)/files/pix\.png(.*)\">")
|
||||
|
||||
def test_broken_image(self):
|
||||
"""Ensure that broken inline images don't cause errors."""
|
||||
is_private = not frappe.get_meta("Communication").make_attachments_public
|
||||
communication = frappe.get_doc(
|
||||
doctype="Communication",
|
||||
communication_type="Communication",
|
||||
communication_medium="Email",
|
||||
content='<div class="ql-editor read-mode"><img src="data:image/png;filename=pix.png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CY="></div>',
|
||||
recipients="to <to@test.com>",
|
||||
cc=None,
|
||||
bcc=None,
|
||||
sender="sender@test.com",
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
self.assertFalse(
|
||||
frappe.db.exists("File", {"attached_to_name": communication.name, "is_private": is_private})
|
||||
)
|
||||
self.assertIn(f'<img src="#broken-image" alt="{get_corrupted_image_msg()}">', communication.content)
|
||||
|
||||
def test_create_new_folder(self):
|
||||
folder = create_new_folder("test_folder", "Home")
|
||||
self.assertTrue(folder.is_folder)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import hashlib
|
|||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
from binascii import Error as BinasciiError
|
||||
from io import BytesIO
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from urllib.parse import unquote
|
||||
|
|
@ -234,7 +235,12 @@ def extract_images_from_html(doc: "Document", content: str, is_private: bool = F
|
|||
content = content.encode("utf-8")
|
||||
if b"," in content:
|
||||
content = content.split(b",")[1]
|
||||
content = safe_b64decode(content)
|
||||
|
||||
try:
|
||||
content = safe_b64decode(content)
|
||||
except BinasciiError:
|
||||
frappe.flags.has_dataurl = True
|
||||
return f'<img src="#broken-image" alt="{get_corrupted_image_msg()}"'
|
||||
|
||||
if "filename=" in headers:
|
||||
filename = headers.split("filename=")[-1]
|
||||
|
|
@ -273,6 +279,10 @@ def extract_images_from_html(doc: "Document", content: str, is_private: bool = F
|
|||
return content
|
||||
|
||||
|
||||
def get_corrupted_image_msg():
|
||||
return _("Image: Corrupted Data Stream")
|
||||
|
||||
|
||||
def get_random_filename(content_type: str | None = None) -> str:
|
||||
extn = None
|
||||
if content_type:
|
||||
|
|
|
|||
|
|
@ -72,20 +72,23 @@ if doc.allocated_to:
|
|||
# retreive payment session state
|
||||
ps = doc.flags.payment_session
|
||||
|
||||
if ps.changed: # could be an idempotent run
|
||||
if ps.flags.status_changed_to in ps.flowstates.success:
|
||||
if ps.is_success:
|
||||
if ps.changed: # could be an idempotent run
|
||||
doc.set_as_paid()
|
||||
# custom process return values
|
||||
doc.flags.payment_result = {
|
||||
"message": "Thank you for your payment",
|
||||
"action": {"href": "https://shop.example.com", "label": "Return to shop"},
|
||||
}
|
||||
if ps.flags.status_changed_to in ps.flowstates.pre_authorized:
|
||||
# do something else
|
||||
if ps.flags.status_changed_to in ps.flowstates.processing:
|
||||
# do something else
|
||||
if ps.flags.status_changed_to in ps.flowstates.declined:
|
||||
# do something else
|
||||
# custom process return values
|
||||
doc.flags.payment_session.result = {
|
||||
"message": "Thank you for your payment",
|
||||
"action": {"href": "https://shop.example.com", "label": "Return to shop"},
|
||||
}
|
||||
if ps.is_pre_authorized:
|
||||
if ps.changed: # could be an idempotent run
|
||||
...
|
||||
if ps.is_processing:
|
||||
if ps.changed: # could be an idempotent run
|
||||
...
|
||||
if ps.is_declined:
|
||||
if ps.changed: # could be an idempotent run
|
||||
...
|
||||
</code>
|
||||
</pre>
|
||||
<p>The <i>On Payment Failed</i> (<code>on_payment_failed</code>) event only transports the error message which the controller implementation had extracted from the transaction.</p>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ from frappe.desk.doctype.notification_settings.notification_settings import (
|
|||
toggle_notifications,
|
||||
)
|
||||
from frappe.desk.notifications import clear_notifications
|
||||
from frappe.model.delete_doc import check_if_doc_is_linked
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder import DocType
|
||||
from frappe.rate_limiter import rate_limit
|
||||
|
|
@ -536,11 +535,20 @@ class User(Document):
|
|||
# Delete EPS data
|
||||
frappe.db.delete("Energy Point Log", {"user": self.name})
|
||||
|
||||
# Ask user to disable instead if document is still linked
|
||||
try:
|
||||
check_if_doc_is_linked(self)
|
||||
except frappe.LinkExistsError:
|
||||
frappe.throw(_("You can disable the user instead of deleting it."), frappe.LinkExistsError)
|
||||
# Remove user link from Workflow Action
|
||||
frappe.db.set_value("Workflow Action", {"user": self.name}, "user", None)
|
||||
|
||||
# Delete user's List Filters
|
||||
frappe.db.delete("List Filter", {"for_user": self.name})
|
||||
|
||||
# Remove user from Note's Seen By table
|
||||
seen_notes = frappe.get_all("Note", filters=[["Note Seen By", "user", "=", self.name]], pluck="name")
|
||||
for note_id in seen_notes:
|
||||
note = frappe.get_doc("Note", note_id)
|
||||
for row in note.seen_by:
|
||||
if row.user == self.name:
|
||||
note.remove(row)
|
||||
note.save(ignore_permissions=True)
|
||||
|
||||
def before_rename(self, old_name, new_name, merge=False):
|
||||
# if merging, delete the old user notification settings
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ frappe.PermissionEngine = class PermissionEngine {
|
|||
|
||||
reset_std_permissions(data) {
|
||||
let doctype = this.get_doctype();
|
||||
let d = frappe.confirm(__("Reset Permissions for {0}?", [doctype]), () => {
|
||||
let d = frappe.confirm(__("Reset Permissions for {0}?", [__(doctype)]), () => {
|
||||
return frappe
|
||||
.call({
|
||||
module: "frappe.core",
|
||||
|
|
@ -122,7 +122,7 @@ frappe.PermissionEngine = class PermissionEngine {
|
|||
// show standard permissions
|
||||
let $d = $(d.wrapper)
|
||||
.find(".frappe-confirm-message")
|
||||
.append("<hr><h5>Standard Permissions:</h5><br>");
|
||||
.append(`<hr><h5>${__("Standard Permissions")}:</h5><br>`);
|
||||
let $wrapper = $("<p></p>").appendTo($d);
|
||||
data.message.forEach((d) => {
|
||||
let rights = this.rights
|
||||
|
|
@ -134,7 +134,7 @@ frappe.PermissionEngine = class PermissionEngine {
|
|||
d.rights = rights.join(", ");
|
||||
|
||||
$wrapper.append(`<div class="row">\
|
||||
<div class="col-xs-5"><b>${d.role}</b>, Level ${d.permlevel || 0}</div>\
|
||||
<div class="col-xs-5"><b>${__(d.role)}</b>, ${__("Level")} ${d.permlevel || 0}</div>\
|
||||
<div class="col-xs-7">${d.rights}</div>\
|
||||
</div><br>`);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -397,7 +397,7 @@ frappe.ui.form.on("Dashboard Chart", {
|
|||
}
|
||||
}
|
||||
},
|
||||
primary_action_label: "Set",
|
||||
primary_action_label: __("Set"),
|
||||
});
|
||||
frappe.dashboards.filters_dialog = dialog;
|
||||
|
||||
|
|
@ -484,7 +484,7 @@ frappe.ui.form.on("Dashboard Chart", {
|
|||
}
|
||||
frm.trigger("set_dynamic_filters_in_table");
|
||||
},
|
||||
primary_action_label: "Set",
|
||||
primary_action_label: __("Set"),
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ def enqueue_create_notification(users: list[str] | str, doc: dict):
|
|||
doc=doc,
|
||||
users=users,
|
||||
now=frappe.flags.in_test,
|
||||
enqueue_after_commit=not frappe.flags.in_test,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ frappe.ui.form.on("Number Card", {
|
|||
},
|
||||
|
||||
create_add_to_dashboard_button: function (frm) {
|
||||
frm.add_custom_button("Add Card to Dashboard", () => {
|
||||
frm.add_custom_button(__("Add Card to Dashboard"), () => {
|
||||
const dialog = frappe.dashboard_utils.get_add_to_dashboard_dialog(
|
||||
frm.doc.name,
|
||||
"Number Card",
|
||||
|
|
@ -292,7 +292,7 @@ frappe.ui.form.on("Number Card", {
|
|||
frm.trigger("render_filters_table");
|
||||
}
|
||||
},
|
||||
primary_action_label: "Set",
|
||||
primary_action_label: __("Set"),
|
||||
});
|
||||
|
||||
if (is_document_type) {
|
||||
|
|
@ -384,7 +384,7 @@ frappe.ui.form.on("Number Card", {
|
|||
}
|
||||
frm.trigger("set_dynamic_filters_in_table");
|
||||
},
|
||||
primary_action_label: "Set",
|
||||
primary_action_label: __("Set"),
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
|
|
|
|||
|
|
@ -186,19 +186,40 @@ class SystemHealthReport(Document):
|
|||
# Exclude "maybe" curently executing job
|
||||
upper_threshold = add_to_date(None, minutes=-30, as_datetime=True)
|
||||
self.scheduler_status = get_scheduler_status().get("status")
|
||||
failing_jobs = frappe.db.sql(
|
||||
"""
|
||||
select scheduled_job_type,
|
||||
avg(CASE WHEN status != 'Complete' THEN 1 ELSE 0 END) * 100 as failure_rate
|
||||
from `tabScheduled Job Log`
|
||||
where
|
||||
creation > %(lower_threshold)s
|
||||
and modified > %(lower_threshold)s
|
||||
and creation < %(upper_threshold)s
|
||||
group by scheduled_job_type
|
||||
having failure_rate > 0
|
||||
order by failure_rate desc
|
||||
limit 5""",
|
||||
|
||||
mariadb_query = """
|
||||
SELECT scheduled_job_type,
|
||||
AVG(CASE WHEN status != 'Complete' THEN 1 ELSE 0 END) * 100 AS failure_rate
|
||||
FROM `tabScheduled Job Log`
|
||||
WHERE
|
||||
creation > %(lower_threshold)s
|
||||
AND modified > %(lower_threshold)s
|
||||
AND creation < %(upper_threshold)s
|
||||
GROUP BY scheduled_job_type
|
||||
HAVING failure_rate > 0
|
||||
ORDER BY failure_rate DESC
|
||||
LIMIT 5
|
||||
"""
|
||||
|
||||
postgres_query = """
|
||||
SELECT scheduled_job_type,
|
||||
AVG(CASE WHEN status != 'Complete' THEN 1 ELSE 0 END) * 100 AS "failure_rate"
|
||||
FROM "tabScheduled Job Log"
|
||||
WHERE
|
||||
creation > %(lower_threshold)s
|
||||
AND modified > %(lower_threshold)s
|
||||
AND creation < %(upper_threshold)s
|
||||
GROUP BY scheduled_job_type
|
||||
HAVING AVG(CASE WHEN status != 'Complete' THEN 1 ELSE 0 END) * 100 > 0
|
||||
ORDER BY "failure_rate" DESC
|
||||
LIMIT 5
|
||||
"""
|
||||
|
||||
failing_jobs = frappe.db.multisql(
|
||||
{
|
||||
"mariadb": mariadb_query,
|
||||
"postgres": postgres_query,
|
||||
},
|
||||
{"lower_threshold": lower_threshold, "upper_threshold": upper_threshold},
|
||||
as_dict=True,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -421,99 +421,85 @@ def get_linked_docs(doctype: str, name: str, linkinfo: dict | None = None) -> di
|
|||
if not linkinfo:
|
||||
return results
|
||||
|
||||
for dt, link in linkinfo.items():
|
||||
filters = []
|
||||
link["doctype"] = dt
|
||||
try:
|
||||
link_meta_bundle = frappe.desk.form.load.get_meta_bundle(dt)
|
||||
except Exception as e:
|
||||
if isinstance(e, frappe.DoesNotExistError):
|
||||
frappe.clear_last_message()
|
||||
is_target_doctype_table = frappe.get_meta(doctype).istable
|
||||
|
||||
for linked_doctype, link_context in linkinfo.items():
|
||||
linked_doctype_meta = frappe.get_meta(linked_doctype)
|
||||
|
||||
if linked_doctype_meta.issingle:
|
||||
continue
|
||||
linkmeta = link_meta_bundle[0]
|
||||
|
||||
if not linkmeta.get("issingle"):
|
||||
fields = [
|
||||
d.fieldname
|
||||
for d in linkmeta.get(
|
||||
"fields",
|
||||
{
|
||||
"in_list_view": 1,
|
||||
"fieldtype": ["not in", ("Image", "HTML", "Button", *frappe.model.table_fields)],
|
||||
},
|
||||
)
|
||||
] + ["name", "modified", "docstatus"]
|
||||
filters = []
|
||||
ret = None
|
||||
parent_info = None
|
||||
|
||||
if link.get("add_fields"):
|
||||
fields += link["add_fields"]
|
||||
fields = [
|
||||
d.fieldname
|
||||
for d in linked_doctype_meta.get(
|
||||
"fields",
|
||||
{
|
||||
"in_list_view": 1,
|
||||
"fieldtype": ["not in", ("Image", "HTML", "Button", *frappe.model.table_fields)],
|
||||
},
|
||||
)
|
||||
] + ["name", "modified", "docstatus"]
|
||||
|
||||
fields = [f"`tab{dt}`.`{sf.strip()}`" for sf in fields if sf and "`tab" not in sf]
|
||||
if add_fields := link_context.get("add_fields"):
|
||||
fields += add_fields
|
||||
|
||||
try:
|
||||
if link.get("filters"):
|
||||
ret = frappe.get_all(
|
||||
doctype=dt, fields=fields, filters=link.get("filters"), order_by=None
|
||||
)
|
||||
fields = [f"`tab{linked_doctype}`.`{sf.strip()}`" for sf in fields if sf and "`tab" not in sf]
|
||||
|
||||
elif link.get("get_parent"):
|
||||
ret = None
|
||||
|
||||
# check for child table
|
||||
if not frappe.get_meta(doctype).istable:
|
||||
continue
|
||||
|
||||
me = frappe.db.get_value(
|
||||
doctype, name, ["parenttype", "parent"], as_dict=True, order_by=None
|
||||
)
|
||||
if me and me.parenttype == dt:
|
||||
ret = frappe.get_all(
|
||||
doctype=dt, fields=fields, filters=[[dt, "name", "=", me.parent]], order_by=None
|
||||
)
|
||||
|
||||
elif link.get("child_doctype"):
|
||||
or_filters = [
|
||||
[link.get("child_doctype"), link_fieldnames, "=", name]
|
||||
for link_fieldnames in link.get("fieldname")
|
||||
]
|
||||
|
||||
# dynamic link
|
||||
if link.get("doctype_fieldname"):
|
||||
filters.append(
|
||||
[link.get("child_doctype"), link.get("doctype_fieldname"), "=", doctype]
|
||||
)
|
||||
|
||||
ret = frappe.get_all(
|
||||
doctype=dt,
|
||||
fields=fields,
|
||||
filters=filters,
|
||||
or_filters=or_filters,
|
||||
distinct=True,
|
||||
order_by=None,
|
||||
)
|
||||
|
||||
else:
|
||||
link_fieldnames = link.get("fieldname")
|
||||
if link_fieldnames:
|
||||
if isinstance(link_fieldnames, str):
|
||||
link_fieldnames = [link_fieldnames]
|
||||
or_filters = [[dt, fieldname, "=", name] for fieldname in link_fieldnames]
|
||||
# dynamic link
|
||||
if link.get("doctype_fieldname"):
|
||||
filters.append([dt, link.get("doctype_fieldname"), "=", doctype])
|
||||
ret = frappe.get_all(
|
||||
doctype=dt, fields=fields, filters=filters, or_filters=or_filters, order_by=None
|
||||
)
|
||||
|
||||
else:
|
||||
ret = None
|
||||
|
||||
except frappe.PermissionError:
|
||||
frappe.clear_last_message()
|
||||
if filters_ctx := link_context.get("filters"):
|
||||
ret = frappe.get_list(doctype=linked_doctype, fields=fields, filters=filters_ctx, order_by=None)
|
||||
|
||||
elif link_context.get("get_parent"):
|
||||
# check for child table
|
||||
if not is_target_doctype_table:
|
||||
continue
|
||||
|
||||
if ret:
|
||||
results[dt] = ret
|
||||
parent_info = parent_info or frappe.db.get_value(
|
||||
doctype, name, ["parenttype", "parent"], as_dict=True, order_by=None
|
||||
)
|
||||
|
||||
if parent_info and parent_info.parenttype == linked_doctype:
|
||||
ret = frappe.get_list(
|
||||
doctype=linked_doctype,
|
||||
fields=fields,
|
||||
filters=[[linked_doctype, "name", "=", parent_info.parent]],
|
||||
order_by=None,
|
||||
)
|
||||
|
||||
elif child_doctype := link_context.get("child_doctype"):
|
||||
or_filters = [
|
||||
[child_doctype, link_fieldnames, "=", name] for link_fieldnames in link_context["fieldname"]
|
||||
]
|
||||
|
||||
# dynamic link_context
|
||||
if doctype_fieldname := link_context.get("doctype_fieldname"):
|
||||
filters.append([child_doctype, doctype_fieldname, "=", doctype])
|
||||
|
||||
ret = frappe.get_list(
|
||||
doctype=linked_doctype,
|
||||
fields=fields,
|
||||
filters=filters,
|
||||
or_filters=or_filters,
|
||||
distinct=True,
|
||||
order_by=None,
|
||||
)
|
||||
|
||||
elif link_fieldnames := link_context.get("fieldname"):
|
||||
if isinstance(link_fieldnames, str):
|
||||
link_fieldnames = [link_fieldnames]
|
||||
or_filters = [[linked_doctype, fieldname, "=", name] for fieldname in link_fieldnames]
|
||||
# dynamic link_context
|
||||
if doctype_fieldname := link_context.get("doctype_fieldname"):
|
||||
filters.append([linked_doctype, doctype_fieldname, "=", doctype])
|
||||
ret = frappe.get_list(
|
||||
doctype=linked_doctype, fields=fields, filters=filters, or_filters=or_filters, order_by=None
|
||||
)
|
||||
|
||||
if ret:
|
||||
results[linked_doctype] = ret
|
||||
|
||||
return results
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ def getdoc(doctype, name, user=None):
|
|||
|
||||
if not doc.has_permission("read"):
|
||||
frappe.flags.error_message = _("Insufficient Permission for {0}").format(
|
||||
frappe.bold(doctype + " " + name)
|
||||
frappe.bold(_(doctype) + " " + name)
|
||||
)
|
||||
raise frappe.PermissionError(("read", doctype, name))
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ class UserProfile {
|
|||
render_heatmap() {
|
||||
this.heatmap = new frappe.Chart(".performance-heatmap", {
|
||||
type: "heatmap",
|
||||
countLabel: "Energy Points",
|
||||
countLabel: __("Energy Points"),
|
||||
data: {},
|
||||
discreteDomains: 1,
|
||||
radius: 3,
|
||||
|
|
@ -111,7 +111,7 @@ class UserProfile {
|
|||
value_based_on: "points",
|
||||
chart_type: "Sum",
|
||||
document_type: "Energy Point Log",
|
||||
name: "Energy Points",
|
||||
name: __("Energy Points"),
|
||||
width: "half",
|
||||
based_on: "creation",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,24 @@
|
|||
frappe.email_defaults = {
|
||||
"Frappe Mail": {
|
||||
domain: null,
|
||||
password: null,
|
||||
awaiting_password: 0,
|
||||
ascii_encode_password: 0,
|
||||
login_id_is_different: 0,
|
||||
login_id: null,
|
||||
use_imap: 0,
|
||||
use_ssl: 0,
|
||||
validate_ssl_certificate: 0,
|
||||
use_starttls: 0,
|
||||
email_server: null,
|
||||
incoming_port: 0,
|
||||
always_use_account_email_id_as_sender: 1,
|
||||
use_tls: 0,
|
||||
use_ssl_for_outgoing: 0,
|
||||
smtp_server: null,
|
||||
smtp_port: null,
|
||||
no_smtp_authentication: 0,
|
||||
},
|
||||
GMail: {
|
||||
email_server: "imap.gmail.com",
|
||||
incoming_port: 993,
|
||||
|
|
@ -144,11 +164,11 @@ frappe.ui.form.on("Email Account", {
|
|||
frm.refresh_field("imap_folder");
|
||||
}
|
||||
set_default_max_attachment_size(frm);
|
||||
frm.events.show_oauth_authorization_message(frm);
|
||||
},
|
||||
|
||||
refresh: function (frm) {
|
||||
frm.events.enable_incoming(frm);
|
||||
frm.events.show_oauth_authorization_message(frm);
|
||||
|
||||
if (frappe.route_flags.delete_user_from_locals && frappe.route_flags.linked_user) {
|
||||
delete frappe.route_flags.delete_user_from_locals;
|
||||
|
|
@ -169,6 +189,15 @@ frappe.ui.form.on("Email Account", {
|
|||
oauth_access(frm);
|
||||
},
|
||||
|
||||
validate_frappe_mail_settings: function (frm) {
|
||||
if (frm.doc.service == "Frappe Mail") {
|
||||
frappe.call({
|
||||
doc: frm.doc,
|
||||
method: "validate_frappe_mail_settings",
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
show_oauth_authorization_message(frm) {
|
||||
if (frm.doc.auth_method === "OAuth" && frm.doc.connected_app) {
|
||||
frappe.call({
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:email_account_name",
|
||||
"creation": "2014-09-11 12:04:34.163728",
|
||||
"creation": "2024-06-11 16:39:01.323289",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
|
|
@ -13,22 +13,30 @@
|
|||
"enable_incoming",
|
||||
"enable_outgoing",
|
||||
"column_break_3",
|
||||
"domain",
|
||||
"service",
|
||||
"domain",
|
||||
"frappe_mail_site",
|
||||
"authentication_column",
|
||||
"auth_method",
|
||||
"authorize_api_access",
|
||||
"validate_frappe_mail_settings",
|
||||
"password",
|
||||
"awaiting_password",
|
||||
"ascii_encode_password",
|
||||
"column_break_10",
|
||||
"api_key",
|
||||
"api_secret",
|
||||
"connected_app",
|
||||
"connected_user",
|
||||
"login_id_is_different",
|
||||
"login_id",
|
||||
"incoming_popimap_tab",
|
||||
"mailbox_settings",
|
||||
"section_break_uc6h",
|
||||
"default_incoming",
|
||||
"column_break_uynb",
|
||||
"attachment_limit",
|
||||
"last_synced_at",
|
||||
"mailbox_settings",
|
||||
"use_imap",
|
||||
"use_ssl",
|
||||
"validate_ssl_certificate",
|
||||
|
|
@ -36,7 +44,6 @@
|
|||
"email_server",
|
||||
"incoming_port",
|
||||
"column_break_18",
|
||||
"attachment_limit",
|
||||
"email_sync_option",
|
||||
"initial_sync_count",
|
||||
"section_break_25",
|
||||
|
|
@ -50,19 +57,19 @@
|
|||
"notify_if_unreplied",
|
||||
"unreplied_for_mins",
|
||||
"send_notification_to",
|
||||
"outgoing_smtp_tab",
|
||||
"outgoing_tab",
|
||||
"default_outgoing",
|
||||
"column_break_h5pd",
|
||||
"always_use_account_email_id_as_sender",
|
||||
"always_use_account_name_as_sender_name",
|
||||
"send_unsubscribe_message",
|
||||
"track_email_status",
|
||||
"outgoing_mail_settings",
|
||||
"column_break_bidn",
|
||||
"use_tls",
|
||||
"use_ssl_for_outgoing",
|
||||
"smtp_server",
|
||||
"smtp_port",
|
||||
"column_break_38",
|
||||
"default_outgoing",
|
||||
"always_use_account_email_id_as_sender",
|
||||
"always_use_account_name_as_sender_name",
|
||||
"send_unsubscribe_message",
|
||||
"track_email_status",
|
||||
"no_smtp_authentication",
|
||||
"signature_section",
|
||||
"add_signature",
|
||||
|
|
@ -92,6 +99,7 @@
|
|||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval: doc.service != \"Frappe Mail\"",
|
||||
"fieldname": "login_id_is_different",
|
||||
"fieldtype": "Check",
|
||||
"hide_days": 1,
|
||||
|
|
@ -107,7 +115,7 @@
|
|||
"label": "Alternative Email ID"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.auth_method === \"Basic\"",
|
||||
"depends_on": "eval: doc.auth_method === \"Basic\" && doc.service != \"Frappe Mail\"",
|
||||
"fieldname": "password",
|
||||
"fieldtype": "Password",
|
||||
"hide_days": 1,
|
||||
|
|
@ -116,7 +124,7 @@
|
|||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval: doc.auth_method === \"Basic\"",
|
||||
"depends_on": "eval: doc.auth_method === \"Basic\" && doc.service != \"Frappe Mail\"",
|
||||
"fieldname": "awaiting_password",
|
||||
"fieldtype": "Check",
|
||||
"hide_days": 1,
|
||||
|
|
@ -125,7 +133,7 @@
|
|||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval: doc.auth_method === \"Basic\"",
|
||||
"depends_on": "eval: doc.auth_method === \"Basic\" && doc.service != \"Frappe Mail\"",
|
||||
"fieldname": "ascii_encode_password",
|
||||
"fieldtype": "Check",
|
||||
"hide_days": 1,
|
||||
|
|
@ -159,9 +167,10 @@
|
|||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Service",
|
||||
"options": "\nGMail\nSendgrid\nSparkPost\nYahoo Mail\nOutlook.com\nYandex.Mail"
|
||||
"options": "\nFrappe Mail\nGMail\nSendgrid\nSparkPost\nYahoo Mail\nOutlook.com\nYandex.Mail"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.service != \"Frappe Mail\"",
|
||||
"fieldname": "mailbox_settings",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
|
|
@ -289,6 +298,7 @@
|
|||
"mandatory_depends_on": "notify_if_unreplied"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.service != \"Frappe Mail\"",
|
||||
"fieldname": "outgoing_mail_settings",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
|
|
@ -621,23 +631,65 @@
|
|||
"depends_on": "enable_incoming",
|
||||
"fieldname": "incoming_popimap_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Incoming (POP/IMAP)"
|
||||
"label": "Incoming"
|
||||
},
|
||||
{
|
||||
"default": "https://frappemail.com",
|
||||
"depends_on": "eval: doc.service == \"Frappe Mail\"",
|
||||
"fieldname": "frappe_mail_site",
|
||||
"fieldtype": "Data",
|
||||
"label": "Frappe Mail Site",
|
||||
"mandatory_depends_on": "eval: doc.service == \"Frappe Mail\""
|
||||
},
|
||||
{
|
||||
"depends_on": "enable_outgoing",
|
||||
"fieldname": "outgoing_smtp_tab",
|
||||
"fieldname": "outgoing_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Outgoing (SMTP)"
|
||||
"label": "Outgoing"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_bidn",
|
||||
"fieldname": "column_break_h5pd",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_uc6h",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_uynb",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.service == \"Frappe Mail\"",
|
||||
"fieldname": "last_synced_at",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "Last Synced At"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: (doc.service == \"Frappe Mail\" && doc.auth_method != \"Basic\" && !doc.__islocal && !doc.__unsaved)",
|
||||
"fieldname": "validate_frappe_mail_settings",
|
||||
"fieldtype": "Button",
|
||||
"label": "Validate Frappe Mail Settings"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.service == \"Frappe Mail\" && doc.auth_method == \"Basic\"",
|
||||
"fieldname": "api_key",
|
||||
"fieldtype": "Data",
|
||||
"label": "API Key",
|
||||
"mandatory_depends_on": "eval: doc.service == \"Frappe Mail\" && doc.auth_method == \"Basic\""
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.service == \"Frappe Mail\" && doc.auth_method == \"Basic\"",
|
||||
"fieldname": "api_secret",
|
||||
"fieldtype": "Password",
|
||||
"label": "API Secret",
|
||||
"mandatory_depends_on": "eval: doc.service == \"Frappe Mail\" && doc.auth_method == \"Basic\""
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-inbox",
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2024-04-17 14:46:38.836631",
|
||||
"modified": "2024-06-28 08:45:43.565934",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Email Account",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import frappe
|
|||
from frappe import _, are_emails_muted, safe_encode
|
||||
from frappe.desk.form import assign_to
|
||||
from frappe.email.doctype.email_domain.email_domain import EMAIL_DOMAIN_FIELDS
|
||||
from frappe.email.frappemail import FrappeMail
|
||||
from frappe.email.receive import EmailServer, InboundMail, SentEmailInInboxError
|
||||
from frappe.email.smtp import SMTPServer
|
||||
from frappe.email.utils import get_port
|
||||
|
|
@ -61,6 +62,8 @@ class EmailAccount(Document):
|
|||
add_signature: DF.Check
|
||||
always_use_account_email_id_as_sender: DF.Check
|
||||
always_use_account_name_as_sender_name: DF.Check
|
||||
api_key: DF.Data | None
|
||||
api_secret: DF.Password | None
|
||||
append_emails_to_sent_folder: DF.Check
|
||||
append_to: DF.Link | None
|
||||
ascii_encode_password: DF.Check
|
||||
|
|
@ -84,9 +87,11 @@ class EmailAccount(Document):
|
|||
enable_incoming: DF.Check
|
||||
enable_outgoing: DF.Check
|
||||
footer: DF.TextEditor | None
|
||||
frappe_mail_site: DF.Data | None
|
||||
imap_folder: DF.Table[IMAPFolder]
|
||||
incoming_port: DF.Data | None
|
||||
initial_sync_count: DF.Literal["100", "250", "500"]
|
||||
last_synced_at: DF.Datetime | None
|
||||
login_id: DF.Data | None
|
||||
login_id_is_different: DF.Check
|
||||
no_failed: DF.Int
|
||||
|
|
@ -96,13 +101,7 @@ class EmailAccount(Document):
|
|||
send_notification_to: DF.SmallText | None
|
||||
send_unsubscribe_message: DF.Check
|
||||
service: DF.Literal[
|
||||
"",
|
||||
"GMail",
|
||||
"Sendgrid",
|
||||
"SparkPost",
|
||||
"Yahoo Mail",
|
||||
"Outlook.com",
|
||||
"Yandex.Mail",
|
||||
"", "Frappe Mail", "GMail", "Sendgrid", "SparkPost", "Yahoo Mail", "Outlook.com", "Yandex.Mail"
|
||||
]
|
||||
signature: DF.TextEditor | None
|
||||
smtp_port: DF.Data | None
|
||||
|
|
@ -142,6 +141,13 @@ class EmailAccount(Document):
|
|||
else:
|
||||
self.login_id = None
|
||||
|
||||
if self.service == "Frappe Mail":
|
||||
self.use_imap = 0
|
||||
self.always_use_account_email_id_as_sender = 1
|
||||
|
||||
if self.auth_method == "Basic" or self.get_oauth_token():
|
||||
self.validate_frappe_mail_settings()
|
||||
|
||||
# validate the imap settings
|
||||
if self.enable_incoming and self.use_imap and len(self.imap_folder) <= 0:
|
||||
frappe.throw(_("You need to set one IMAP folder for {0}").format(frappe.bold(self.email_id)))
|
||||
|
|
@ -158,7 +164,11 @@ class EmailAccount(Document):
|
|||
self.awaiting_password = 0
|
||||
self.password = None
|
||||
|
||||
if not frappe.local.flags.in_install and not self.awaiting_password:
|
||||
if (
|
||||
not frappe.local.flags.in_install
|
||||
and not self.awaiting_password
|
||||
and not self.service == "Frappe Mail"
|
||||
):
|
||||
if validate_oauth or self.password or self.smtp_server in ("127.0.0.1", "localhost"):
|
||||
if self.enable_incoming:
|
||||
self.get_incoming_server()
|
||||
|
|
@ -184,6 +194,12 @@ class EmailAccount(Document):
|
|||
if folder.append_to not in valid_doctypes:
|
||||
frappe.throw(_("Append To can be one of {0}").format(comma_or(valid_doctypes)))
|
||||
|
||||
@frappe.whitelist()
|
||||
def validate_frappe_mail_settings(self):
|
||||
if self.service == "Frappe Mail":
|
||||
frappe_mail_client = self.get_frappe_mail_client()
|
||||
frappe_mail_client.validate(for_inbound=self.enable_incoming, for_outbound=self.enable_outgoing)
|
||||
|
||||
def validate_smtp_conn(self):
|
||||
if not self.smtp_server:
|
||||
frappe.throw(_("SMTP Server is required"))
|
||||
|
|
@ -476,9 +492,11 @@ class EmailAccount(Document):
|
|||
|
||||
return account_details
|
||||
|
||||
def sendmail_config(self):
|
||||
def get_access_token(self) -> str | None:
|
||||
oauth_token = self.get_oauth_token()
|
||||
return oauth_token.get_password("access_token") if oauth_token else None
|
||||
|
||||
def sendmail_config(self):
|
||||
return {
|
||||
"email_account": self.name,
|
||||
"server": self.smtp_server,
|
||||
|
|
@ -488,7 +506,7 @@ class EmailAccount(Document):
|
|||
"use_ssl": cint(self.use_ssl_for_outgoing),
|
||||
"use_tls": cint(self.use_tls),
|
||||
"use_oauth": self.auth_method == "OAuth",
|
||||
"access_token": oauth_token.get_password("access_token") if oauth_token else None,
|
||||
"access_token": self.get_access_token(),
|
||||
}
|
||||
|
||||
def get_smtp_server(self):
|
||||
|
|
@ -504,6 +522,26 @@ class EmailAccount(Document):
|
|||
config = self.sendmail_config()
|
||||
return SMTPServer(**config)
|
||||
|
||||
def get_frappe_mail_client(self):
|
||||
return self._frappe_mail_client
|
||||
|
||||
@functools.cached_property
|
||||
def _frappe_mail_client(self):
|
||||
if self.auth_method == "OAuth":
|
||||
if access_token := self.get_access_token():
|
||||
return FrappeMail(self.frappe_mail_site, self.email_id, access_token=access_token)
|
||||
|
||||
frappe.throw(
|
||||
_("Please Authorize OAuth for Email Account {0}").format(
|
||||
frappe.bold(self.email_account_name)
|
||||
),
|
||||
title=_("Frappe Mail OAuth Error"),
|
||||
)
|
||||
else:
|
||||
return FrappeMail(
|
||||
self.frappe_mail_site, self.email_id, self.api_key, self.get_password("api_secret")
|
||||
)
|
||||
|
||||
def remove_unpicklable_values(self, state):
|
||||
super().remove_unpicklable_values(state)
|
||||
state.pop("_smtp_server_instance", None)
|
||||
|
|
@ -594,25 +632,33 @@ class EmailAccount(Document):
|
|||
if not self.enable_incoming:
|
||||
return []
|
||||
|
||||
email_sync_rule = self.build_email_sync_rule()
|
||||
try:
|
||||
email_server = self.get_incoming_server(in_receive=True, email_sync_rule=email_sync_rule)
|
||||
if self.use_imap:
|
||||
# process all given imap folder
|
||||
for folder in self.imap_folder:
|
||||
if email_server.select_imap_folder(folder.folder_name):
|
||||
email_server.settings["uid_validity"] = folder.uidvalidity
|
||||
messages = email_server.get_messages(folder=f'"{folder.folder_name}"') or {}
|
||||
process_mail(messages, folder.append_to)
|
||||
else:
|
||||
# process the pop3 account
|
||||
messages = email_server.get_messages() or {}
|
||||
if self.service == "Frappe Mail":
|
||||
frappe_mail_client = self.get_frappe_mail_client()
|
||||
messages = frappe_mail_client.pull_raw(last_synced_at=self.last_synced_at)
|
||||
process_mail(messages)
|
||||
# close connection to mailserver
|
||||
email_server.logout()
|
||||
self.db_set("last_synced_at", messages["last_synced_at"], update_modified=False)
|
||||
else:
|
||||
email_sync_rule = self.build_email_sync_rule()
|
||||
email_server = self.get_incoming_server(in_receive=True, email_sync_rule=email_sync_rule)
|
||||
if self.use_imap:
|
||||
# process all given imap folder
|
||||
for folder in self.imap_folder:
|
||||
if email_server.select_imap_folder(folder.folder_name):
|
||||
email_server.settings["uid_validity"] = folder.uidvalidity
|
||||
messages = email_server.get_messages(folder=f'"{folder.folder_name}"') or {}
|
||||
process_mail(messages, folder.append_to)
|
||||
else:
|
||||
# process the pop3 account
|
||||
messages = email_server.get_messages() or {}
|
||||
process_mail(messages)
|
||||
|
||||
# close connection to mailserver
|
||||
email_server.logout()
|
||||
except Exception:
|
||||
self.log_error(title=_("Error while connecting to email account {0}").format(self.name))
|
||||
return []
|
||||
|
||||
return mails
|
||||
|
||||
def handle_bad_emails(self, uid, raw, reason):
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from frappe.core.utils import html2text
|
|||
from frappe.database.database import savepoint
|
||||
from frappe.email.doctype.email_account.email_account import EmailAccount
|
||||
from frappe.email.email_body import add_attachment, get_email, get_formatted_html
|
||||
from frappe.email.frappemail import FrappeMail
|
||||
from frappe.email.queue import get_unsubcribed_url, get_unsubscribe_message
|
||||
from frappe.email.smtp import SMTPServer
|
||||
from frappe.model.document import Document
|
||||
|
|
@ -153,13 +154,13 @@ class EmailQueue(Document):
|
|||
|
||||
return True
|
||||
|
||||
def send(self, smtp_server_instance: SMTPServer = None):
|
||||
def send(self, smtp_server_instance: SMTPServer = None, frappe_mail_client: FrappeMail = None):
|
||||
"""Send emails to recipients."""
|
||||
if not self.can_send_now():
|
||||
return
|
||||
|
||||
with SendMailContext(self, smtp_server_instance) as ctx:
|
||||
ctx.fetch_smtp_server()
|
||||
with SendMailContext(self, smtp_server_instance, frappe_mail_client) as ctx:
|
||||
ctx.fetch_outgoing_server()
|
||||
message = None
|
||||
for recipient in self.recipients:
|
||||
if recipient.is_mail_sent():
|
||||
|
|
@ -168,8 +169,14 @@ class EmailQueue(Document):
|
|||
message = ctx.build_message(recipient.recipient)
|
||||
if method := get_hook_method("override_email_send"):
|
||||
method(self, self.sender, recipient.recipient, message)
|
||||
else:
|
||||
if not frappe.flags.in_test or frappe.flags.testing_email:
|
||||
elif not frappe.flags.in_test or frappe.flags.testing_email:
|
||||
if ctx.email_account_doc.service == "Frappe Mail":
|
||||
ctx.frappe_mail_client.send_raw(
|
||||
sender=self.sender,
|
||||
recipients=recipient.recipient,
|
||||
message=message.decode("utf-8"),
|
||||
)
|
||||
else:
|
||||
ctx.smtp_server.session.sendmail(
|
||||
from_addr=self.sender,
|
||||
to_addrs=recipient.recipient,
|
||||
|
|
@ -231,17 +238,23 @@ class SendMailContext:
|
|||
self,
|
||||
queue_doc: Document,
|
||||
smtp_server_instance: SMTPServer = None,
|
||||
frappe_mail_client: FrappeMail = None,
|
||||
):
|
||||
self.queue_doc: EmailQueue = queue_doc
|
||||
self.smtp_server: SMTPServer = smtp_server_instance
|
||||
self.frappe_mail_client: FrappeMail = frappe_mail_client
|
||||
self.sent_to_atleast_one_recipient = any(
|
||||
rec.recipient for rec in self.queue_doc.recipients if rec.is_mail_sent()
|
||||
)
|
||||
self.email_account_doc = None
|
||||
|
||||
def fetch_smtp_server(self):
|
||||
def fetch_outgoing_server(self):
|
||||
self.email_account_doc = self.queue_doc.get_email_account(raise_error=True)
|
||||
if not self.smtp_server:
|
||||
|
||||
if self.email_account_doc.service == "Frappe Mail":
|
||||
if not self.frappe_mail_client:
|
||||
self.frappe_mail_client = self.email_account_doc.get_frappe_mail_client()
|
||||
elif not self.smtp_server:
|
||||
self.smtp_server = self.email_account_doc.get_smtp_server()
|
||||
|
||||
def __enter__(self):
|
||||
|
|
@ -751,16 +764,21 @@ class QueueBuilder:
|
|||
def send_emails(self, queue_data, final_recipients):
|
||||
# This is used to bulk send emails from same sender to multiple recipients separately
|
||||
# This re-uses smtp server instance to minimize the cost of new session creation
|
||||
frappe_mail_client = None
|
||||
smtp_server_instance = None
|
||||
for r in final_recipients:
|
||||
recipients = list(set([r, *self.final_cc(), *self.bcc]))
|
||||
q = EmailQueue.new({**queue_data, **{"recipients": recipients}}, ignore_permissions=True)
|
||||
if not smtp_server_instance:
|
||||
if not frappe_mail_client and not smtp_server_instance:
|
||||
email_account = q.get_email_account(raise_error=True)
|
||||
smtp_server_instance = email_account.get_smtp_server()
|
||||
|
||||
if email_account.service == "Frappe Mail":
|
||||
frappe_mail_client = email_account.get_frappe_mail_client()
|
||||
else:
|
||||
smtp_server_instance = email_account.get_smtp_server()
|
||||
|
||||
with suppress(Exception):
|
||||
q.send(smtp_server_instance=smtp_server_instance)
|
||||
q.send(smtp_server_instance=smtp_server_instance, frappe_mail_client=frappe_mail_client)
|
||||
|
||||
smtp_server_instance.quit()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) 2018, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
const DATE_BASED_EVENTS = ["Days Before", "Days After"];
|
||||
|
||||
frappe.notification = {
|
||||
setup_fieldname_select: function (frm) {
|
||||
// get the doctype to update fields
|
||||
|
|
@ -129,6 +131,8 @@ Last comment: {{ comments[-1].comment }} by {{ comments[-1].by }}
|
|||
frappe.ui.form.on("Notification", {
|
||||
onload: function (frm) {
|
||||
frm.set_query("document_type", function () {
|
||||
if (DATE_BASED_EVENTS.includes(frm.doc.event)) return;
|
||||
|
||||
return {
|
||||
filters: {
|
||||
istable: 0,
|
||||
|
|
@ -166,23 +170,23 @@ frappe.ui.form.on("Notification", {
|
|||
frappe.set_route("Form", "Customize Form");
|
||||
},
|
||||
event: function (frm) {
|
||||
if (["Days Before", "Days After"].includes(frm.doc.event)) {
|
||||
frm.add_custom_button(__("Get Alerts for Today"), function () {
|
||||
frappe.call({
|
||||
method: "frappe.email.doctype.notification.notification.get_documents_for_today",
|
||||
args: {
|
||||
notification: frm.doc.name,
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r.message && r.message.length > 0) {
|
||||
frappe.msgprint(r.message.toString());
|
||||
} else {
|
||||
frappe.msgprint(__("No alerts for today"));
|
||||
}
|
||||
},
|
||||
});
|
||||
if (!DATE_BASED_EVENTS.includes(frm.doc.event) || frm.is_new()) return;
|
||||
|
||||
frm.add_custom_button(__("Get Alerts for Today"), function () {
|
||||
frappe.call({
|
||||
method: "frappe.email.doctype.notification.notification.get_documents_for_today",
|
||||
args: {
|
||||
notification: frm.doc.name,
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r.message && r.message.length > 0) {
|
||||
frappe.msgprint(r.message.toString());
|
||||
} else {
|
||||
frappe.msgprint(__("No alerts for today"));
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
channel: function (frm) {
|
||||
frm.toggle_reqd("recipients", frm.doc.channel == "Email");
|
||||
|
|
|
|||
|
|
@ -8,16 +8,16 @@
|
|||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"enabled",
|
||||
"is_standard",
|
||||
"module",
|
||||
"column_break_2",
|
||||
"channel",
|
||||
"slack_webhook_url",
|
||||
"filters",
|
||||
"subject",
|
||||
"document_type",
|
||||
"is_standard",
|
||||
"module",
|
||||
"col_break_1",
|
||||
"event",
|
||||
"document_type",
|
||||
"col_break_1",
|
||||
"method",
|
||||
"date_changed",
|
||||
"days_in_advance",
|
||||
|
|
@ -119,7 +119,6 @@
|
|||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.document_type",
|
||||
"fieldname": "event",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
|
|
@ -292,7 +291,7 @@
|
|||
"icon": "fa fa-envelope",
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2024-06-17 04:03:22.591781",
|
||||
"modified": "2024-07-04 05:53:40.595130",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Notification",
|
||||
|
|
@ -315,4 +314,4 @@
|
|||
"states": [],
|
||||
"title_field": "subject",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,8 @@ from frappe.utils.jinja import validate_template
|
|||
from frappe.utils.safe_exec import get_safe_globals
|
||||
|
||||
FORMATS = {"HTML": ".html", "Markdown": ".md", "Plain Text": ".txt"}
|
||||
FORBIDDEN_DOCUMENT_TYPES = frozenset(("Email Queue",))
|
||||
DATE_BASED_EVENTS = frozenset(("Days Before", "Days After"))
|
||||
|
||||
|
||||
class Notification(Document):
|
||||
|
|
@ -27,9 +29,7 @@ class Notification(Document):
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.email.doctype.notification_recipient.notification_recipient import (
|
||||
NotificationRecipient,
|
||||
)
|
||||
from frappe.email.doctype.notification_recipient.notification_recipient import NotificationRecipient
|
||||
from frappe.types import DF
|
||||
|
||||
attach_print: DF.Check
|
||||
|
|
@ -90,7 +90,7 @@ class Notification(Document):
|
|||
if self.event == "Value Change" and not self.value_changed:
|
||||
frappe.throw(_("Please specify which value field must be checked"))
|
||||
|
||||
self.validate_forbidden_types()
|
||||
self.validate_forbidden_document_types()
|
||||
self.validate_condition()
|
||||
self.validate_standard()
|
||||
frappe.cache.hdel("notifications", self.document_type)
|
||||
|
|
@ -130,12 +130,16 @@ def get_context(context):
|
|||
except Exception:
|
||||
frappe.throw(_("The Condition '{0}' is invalid").format(self.condition))
|
||||
|
||||
def validate_forbidden_types(self):
|
||||
forbidden_document_types = ("Email Queue",)
|
||||
if self.document_type in forbidden_document_types or frappe.get_meta(self.document_type).istable:
|
||||
# currently notifications don't work on child tables as events are not fired for each record of child table
|
||||
|
||||
frappe.throw(_("Cannot set Notification on Document Type {0}").format(self.document_type))
|
||||
def validate_forbidden_document_types(self):
|
||||
if self.document_type in FORBIDDEN_DOCUMENT_TYPES or (
|
||||
frappe.get_meta(self.document_type).istable and self.event not in DATE_BASED_EVENTS
|
||||
):
|
||||
# only date based events are allowed for child tables
|
||||
frappe.throw(
|
||||
_("Cannot set Notification with event {0} on Document Type {1}").format(
|
||||
_(self.event), _(self.document_type)
|
||||
)
|
||||
)
|
||||
|
||||
def get_documents_for_today(self):
|
||||
"""get list of documents that will be triggered today"""
|
||||
|
|
@ -237,8 +241,8 @@ def get_context(context):
|
|||
|
||||
notification_doc = {
|
||||
"type": "Alert",
|
||||
"document_type": doc.doctype,
|
||||
"document_name": doc.name,
|
||||
"document_type": get_reference_doctype(doc),
|
||||
"document_name": get_reference_name(doc),
|
||||
"subject": subject,
|
||||
"from_user": doc.modified_by or doc.owner,
|
||||
"email_content": frappe.render_template(self.message, context),
|
||||
|
|
@ -270,8 +274,8 @@ def get_context(context):
|
|||
# No need to add if it is already a communication.
|
||||
if doc.doctype != "Communication":
|
||||
communication = make_communication(
|
||||
doctype=doc.doctype,
|
||||
name=doc.name,
|
||||
doctype=get_reference_doctype(doc),
|
||||
name=get_reference_name(doc),
|
||||
content=message,
|
||||
subject=subject,
|
||||
sender=sender,
|
||||
|
|
@ -294,8 +298,8 @@ def get_context(context):
|
|||
cc=cc,
|
||||
bcc=bcc,
|
||||
message=message,
|
||||
reference_doctype=doc.doctype,
|
||||
reference_name=doc.name,
|
||||
reference_doctype=get_reference_doctype(doc),
|
||||
reference_name=get_reference_name(doc),
|
||||
attachments=attachments,
|
||||
expose_recipients="header",
|
||||
print_letterhead=((attachments and attachments[0].get("print_letterhead")) or False),
|
||||
|
|
@ -306,8 +310,8 @@ def get_context(context):
|
|||
send_slack_message(
|
||||
webhook_url=self.slack_webhook_url,
|
||||
message=frappe.render_template(self.message, context),
|
||||
reference_doctype=doc.doctype,
|
||||
reference_name=doc.name,
|
||||
reference_doctype=get_reference_doctype(doc),
|
||||
reference_name=get_reference_name(doc),
|
||||
)
|
||||
|
||||
def send_sms(self, doc, context):
|
||||
|
|
@ -543,3 +547,11 @@ def get_emails_from_template(template, context):
|
|||
|
||||
emails = frappe.render_template(template, context) if "{" in template else template
|
||||
return filter(None, emails.replace(",", "\n").split("\n"))
|
||||
|
||||
|
||||
def get_reference_doctype(doc):
|
||||
return doc.parenttype if doc.meta.istable else doc.doctype
|
||||
|
||||
|
||||
def get_reference_name(doc):
|
||||
return doc.parent if doc.meta.istable else doc.name
|
||||
|
|
|
|||
140
frappe/email/frappemail.py
Normal file
140
frappe/email/frappemail.py
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import pytz
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.frappeclient import FrappeClient, FrappeOAuth2Client
|
||||
from frappe.utils import convert_utc_to_system_timezone, get_datetime, get_datetime_str, get_system_timezone
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from requests import Response
|
||||
|
||||
|
||||
class FrappeMail:
|
||||
"""Class to interact with the Frappe Mail API."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
site: str,
|
||||
mailbox: str,
|
||||
api_key: str | None = None,
|
||||
api_secret: str | None = None,
|
||||
access_token: str | None = None,
|
||||
) -> None:
|
||||
self.site = site
|
||||
self.mailbox = mailbox
|
||||
self.api_key = api_key
|
||||
self.api_secret = api_secret
|
||||
self.access_token = access_token
|
||||
|
||||
@staticmethod
|
||||
def get_client(
|
||||
site: str,
|
||||
mailbox: str,
|
||||
api_key: str | None = None,
|
||||
api_secret: str | None = None,
|
||||
access_token: str | None = None,
|
||||
) -> FrappeClient | FrappeOAuth2Client:
|
||||
"""Returns a FrappeClient or FrappeOAuth2Client instance."""
|
||||
|
||||
if hasattr(frappe.local, "frappe_mail_clients"):
|
||||
if client := frappe.local.frappe_mail_clients.get(mailbox):
|
||||
return client
|
||||
else:
|
||||
frappe.local.frappe_mail_clients = {}
|
||||
|
||||
client = (
|
||||
FrappeOAuth2Client(url=site, access_token=access_token)
|
||||
if access_token
|
||||
else FrappeClient(url=site, api_key=api_key, api_secret=api_secret)
|
||||
)
|
||||
frappe.local.frappe_mail_clients[mailbox] = client
|
||||
|
||||
return client
|
||||
|
||||
def request(
|
||||
self,
|
||||
method: str,
|
||||
endpoint: str,
|
||||
params: dict | None = None,
|
||||
data: dict | None = None,
|
||||
json: dict | None = None,
|
||||
headers: dict[str, str] | None = None,
|
||||
timeout: int | tuple[int, int] = (60, 120),
|
||||
raise_exception: bool = True,
|
||||
) -> "Response":
|
||||
"""Makes a HTTP request to the Frappe Mail API."""
|
||||
|
||||
url = urljoin(self.site, endpoint)
|
||||
client = self.get_client(self.site, self.mailbox, self.api_key, self.api_secret, self.access_token)
|
||||
|
||||
headers = headers or {}
|
||||
headers.update(client.headers)
|
||||
|
||||
response = client.session.request(
|
||||
method=method, url=url, params=params, data=data, json=json, headers=headers, timeout=timeout
|
||||
)
|
||||
|
||||
if not response.ok and raise_exception:
|
||||
error_msg = response.text
|
||||
if response.status_code == 401:
|
||||
if self.access_token:
|
||||
error_msg = _("Authentication Error: Reauthorize OAuth for Email Account {0}.").format(
|
||||
frappe.bold(self.mailbox)
|
||||
)
|
||||
else:
|
||||
error_msg = _("Authentication Error: Invalid API Key or Secret")
|
||||
|
||||
frappe.throw(title=_("Frappe Mail"), msg=error_msg)
|
||||
|
||||
return response
|
||||
|
||||
def validate(self, for_outbound: bool = False, for_inbound: bool = False) -> None:
|
||||
"""Validates the mailbox for inbound and outbound emails."""
|
||||
|
||||
endpoint = "auth/validate"
|
||||
data = {"mailbox": self.mailbox, "for_outbound": for_outbound, "for_inbound": for_inbound}
|
||||
response = self.request("POST", endpoint=endpoint, data=data, raise_exception=False)
|
||||
|
||||
if not response.ok:
|
||||
if error_msg := response.json().get("exception"):
|
||||
if error_msg == "frappe.exceptions.AuthenticationError":
|
||||
error_msg += ": Invalid API Key or Secret"
|
||||
|
||||
frappe.throw(title="Frappe Mail", msg=error_msg)
|
||||
|
||||
def send_raw(self, sender: str, recipients: str, message: str) -> None:
|
||||
"""Sends an email using the Frappe Mail API."""
|
||||
|
||||
endpoint = "outbound/send-raw"
|
||||
json_data = {"from": sender, "to": recipients, "raw_message": message}
|
||||
self.request("POST", endpoint=endpoint, json=json_data)
|
||||
|
||||
def pull_raw(self, limit: int = 50, last_synced_at: str | None = None) -> dict[str, list[str] | str]:
|
||||
"""Pulls emails from the mailbox using the Frappe Mail API."""
|
||||
|
||||
endpoint = "inbound/pull-raw"
|
||||
if last_synced_at:
|
||||
last_synced_at = convert_to_utc(last_synced_at)
|
||||
|
||||
data = {"mailbox": self.mailbox, "limit": limit, "last_synced_at": last_synced_at}
|
||||
headers = {"X-Site": frappe.utils.get_url()}
|
||||
response = self.request("GET", endpoint=endpoint, data=data, headers=headers).json()["message"]
|
||||
last_synced_at = convert_utc_to_system_timezone(get_datetime(response["last_synced_at"]))
|
||||
|
||||
return {"latest_messages": response["mails"], "last_synced_at": last_synced_at}
|
||||
|
||||
|
||||
def convert_to_utc(date_time: datetime | str, from_timezone: str | None = None) -> str:
|
||||
"""Converts datetime to UTC timezone."""
|
||||
|
||||
dt = (
|
||||
pytz.timezone(from_timezone or get_system_timezone())
|
||||
.localize(get_datetime(date_time))
|
||||
.astimezone(pytz.utc)
|
||||
)
|
||||
|
||||
return get_datetime_str(dt)
|
||||
|
|
@ -2731,18 +2731,6 @@
|
|||
],
|
||||
"isd": "+216"
|
||||
},
|
||||
"Turkey": {
|
||||
"code": "tr",
|
||||
"currency": "TRY",
|
||||
"currency_fraction": "Kuru\u015f",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "\u20ba",
|
||||
"number_format": "#.###,##",
|
||||
"timezones": [
|
||||
"Europe/Istanbul"
|
||||
],
|
||||
"isd": "+90"
|
||||
},
|
||||
"Turkmenistan": {
|
||||
"code": "tm",
|
||||
"currency": "TMM",
|
||||
|
|
@ -2774,6 +2762,18 @@
|
|||
"Pacific/Funafuti"
|
||||
],
|
||||
"isd": "+688"
|
||||
},
|
||||
"T\u00fcrkiye": {
|
||||
"code": "tr",
|
||||
"currency": "TRY",
|
||||
"currency_fraction": "Kuru\u015f",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "\u20ba",
|
||||
"number_format": "#.###,##",
|
||||
"timezones": [
|
||||
"Europe/Istanbul"
|
||||
],
|
||||
"isd": "+90"
|
||||
},
|
||||
"Uganda": {
|
||||
"code": "ug",
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@
|
|||
"link_fieldname": "connected_app"
|
||||
}
|
||||
],
|
||||
"modified": "2024-03-23 16:01:30.633764",
|
||||
"modified": "2024-07-05 08:24:50.182706",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Integrations",
|
||||
"name": "Connected App",
|
||||
|
|
@ -162,6 +162,7 @@
|
|||
"role": "All"
|
||||
}
|
||||
],
|
||||
"show_title_field_in_link": 1,
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: developers@frappe.io\n"
|
||||
"POT-Creation-Date: 2024-06-30 09:33+0000\n"
|
||||
"PO-Revision-Date: 2024-07-02 15:49\n"
|
||||
"POT-Creation-Date: 2024-07-07 09:33+0000\n"
|
||||
"PO-Revision-Date: 2024-07-10 16:24\n"
|
||||
"Last-Translator: developers@frappe.io\n"
|
||||
"Language-Team: Esperanto\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
|
@ -1003,8 +1003,8 @@ msgid "Add Child"
|
|||
msgstr "crwdns90826:0crwdne90826:0"
|
||||
|
||||
#: public/js/frappe/views/kanban/kanban_board.html:4
|
||||
#: public/js/frappe/views/reports/query_report.js:1696
|
||||
#: public/js/frappe/views/reports/query_report.js:1699
|
||||
#: public/js/frappe/views/reports/query_report.js:1681
|
||||
#: public/js/frappe/views/reports/query_report.js:1684
|
||||
#: public/js/frappe/views/reports/report_view.js:324
|
||||
#: public/js/frappe/views/reports/report_view.js:349
|
||||
msgid "Add Column"
|
||||
|
|
@ -3719,6 +3719,7 @@ msgstr "crwdns128570:0crwdne128570:0"
|
|||
|
||||
#. Name of a DocType
|
||||
#: desk/doctype/calendar_view/calendar_view.json
|
||||
#: public/js/frappe/list/base_list.js:208
|
||||
msgid "Calendar View"
|
||||
msgstr "crwdns91974:0crwdne91974:0"
|
||||
|
||||
|
|
@ -3895,7 +3896,7 @@ msgstr "crwdns92056:0crwdne92056:0"
|
|||
msgid "Cannot Remove"
|
||||
msgstr "crwdns92058:0crwdne92058:0"
|
||||
|
||||
#: model/base_document.py:1072
|
||||
#: model/base_document.py:1073
|
||||
msgid "Cannot Update After Submit"
|
||||
msgstr "crwdns92060:0crwdne92060:0"
|
||||
|
||||
|
|
@ -3983,7 +3984,7 @@ msgstr "crwdns92100:0{0}crwdne92100:0"
|
|||
msgid "Cannot delete {0}"
|
||||
msgstr "crwdns92102:0{0}crwdne92102:0"
|
||||
|
||||
#: utils/nestedset.py:296
|
||||
#: utils/nestedset.py:299
|
||||
msgid "Cannot delete {0} as it has child nodes"
|
||||
msgstr "crwdns92104:0{0}crwdne92104:0"
|
||||
|
||||
|
|
@ -3991,7 +3992,7 @@ msgstr "crwdns92104:0{0}crwdne92104:0"
|
|||
msgid "Cannot edit Standard Dashboards"
|
||||
msgstr "crwdns92106:0crwdne92106:0"
|
||||
|
||||
#: email/doctype/notification/notification.py:121
|
||||
#: email/doctype/notification/notification.py:122
|
||||
msgid "Cannot edit Standard Notification. To edit, please disable this and duplicate it"
|
||||
msgstr "crwdns92108:0crwdne92108:0"
|
||||
|
||||
|
|
@ -4060,7 +4061,7 @@ msgstr "crwdns92136:0crwdne92136:0"
|
|||
msgid "Cannot set 'Report' permission if 'Only If Creator' permission is set"
|
||||
msgstr "crwdns110830:0crwdne110830:0"
|
||||
|
||||
#: email/doctype/notification/notification.py:137
|
||||
#: email/doctype/notification/notification.py:138
|
||||
msgid "Cannot set Notification on Document Type {0}"
|
||||
msgstr "crwdns92138:0{0}crwdne92138:0"
|
||||
|
||||
|
|
@ -4234,6 +4235,8 @@ msgstr "crwdns128610:0crwdne128610:0"
|
|||
#. Label of the chart_name (Link) field in DocType 'Workspace Chart'
|
||||
#: desk/doctype/dashboard_chart/dashboard_chart.json
|
||||
#: desk/doctype/workspace_chart/workspace_chart.json
|
||||
#: public/js/frappe/views/reports/query_report.js:289
|
||||
#: public/js/frappe/widgets/widget_dialog.js:137
|
||||
msgid "Chart Name"
|
||||
msgstr "crwdns128612:0crwdne128612:0"
|
||||
|
||||
|
|
@ -4650,7 +4653,7 @@ msgctxt "Shrink code field."
|
|||
msgid "Collapse"
|
||||
msgstr "crwdns92402:0crwdne92402:0"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1979
|
||||
#: public/js/frappe/views/reports/query_report.js:1964
|
||||
#: public/js/frappe/views/treeview.js:121
|
||||
msgid "Collapse All"
|
||||
msgstr "crwdns92404:0crwdne92404:0"
|
||||
|
|
@ -6179,6 +6182,10 @@ msgstr "crwdns128842:0crwdne128842:0"
|
|||
msgid "Dashboard Settings"
|
||||
msgstr "crwdns93118:0crwdne93118:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:205
|
||||
msgid "Dashboard View"
|
||||
msgstr "crwdns142844:0crwdne142844:0"
|
||||
|
||||
#. Label of the tab_break_2 (Tab Break) field in DocType 'Workspace'
|
||||
#: desk/doctype/workspace/workspace.json
|
||||
msgid "Dashboards"
|
||||
|
|
@ -6760,7 +6767,8 @@ msgid "Department"
|
|||
msgstr "crwdns128922:0crwdne128922:0"
|
||||
|
||||
#. Label of the dependencies (Data) field in DocType 'Workspace Link'
|
||||
#: desk/doctype/workspace_link/workspace_link.json www/attribution.html:29
|
||||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:318 www/attribution.html:29
|
||||
msgid "Dependencies"
|
||||
msgstr "crwdns112688:0crwdne112688:0"
|
||||
|
||||
|
|
@ -7219,6 +7227,7 @@ msgstr "crwdns93582:0{0}crwdne93582:0"
|
|||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: integrations/doctype/webhook/webhook.json
|
||||
#: printing/doctype/print_format/print_format.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:164
|
||||
#: website/doctype/website_slideshow/website_slideshow.js:18
|
||||
msgid "DocType"
|
||||
msgstr "crwdns93584:0crwdne93584:0"
|
||||
|
|
@ -7277,6 +7286,7 @@ msgstr "crwdns93632:0crwdne93632:0"
|
|||
|
||||
#. Label of the doc_view (Select) field in DocType 'Workspace Shortcut'
|
||||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:477
|
||||
msgid "DocType View"
|
||||
msgstr "crwdns128992:0crwdne128992:0"
|
||||
|
||||
|
|
@ -7800,7 +7810,7 @@ msgstr "crwdns129042:0crwdne129042:0"
|
|||
msgid "Download Your Data"
|
||||
msgstr "crwdns93896:0crwdne93896:0"
|
||||
|
||||
#: contacts/doctype/contact/contact.js:93
|
||||
#: contacts/doctype/contact/contact.js:98
|
||||
msgid "Download vCard"
|
||||
msgstr "crwdns140790:0crwdne140790:0"
|
||||
|
||||
|
|
@ -7988,7 +7998,7 @@ msgstr "crwdns93972:0crwdne93972:0"
|
|||
#: public/js/frappe/form/templates/contact_list.html:7
|
||||
#: public/js/frappe/form/toolbar.js:681
|
||||
#: public/js/frappe/views/reports/query_report.js:815
|
||||
#: public/js/frappe/views/reports/query_report.js:1649
|
||||
#: public/js/frappe/views/reports/query_report.js:1634
|
||||
#: public/js/frappe/views/workspace/workspace.js:460
|
||||
#: public/js/frappe/views/workspace/workspace.js:816
|
||||
#: public/js/frappe/widgets/base_widget.js:64
|
||||
|
|
@ -8965,9 +8975,9 @@ msgstr "crwdns94422:0crwdne94422:0"
|
|||
msgid "Error in Header/Footer Script"
|
||||
msgstr "crwdns110924:0crwdne110924:0"
|
||||
|
||||
#: email/doctype/notification/notification.py:394
|
||||
#: email/doctype/notification/notification.py:510
|
||||
#: email/doctype/notification/notification.py:516
|
||||
#: email/doctype/notification/notification.py:395
|
||||
#: email/doctype/notification/notification.py:511
|
||||
#: email/doctype/notification/notification.py:517
|
||||
msgid "Error in Notification"
|
||||
msgstr "crwdns94424:0crwdne94424:0"
|
||||
|
||||
|
|
@ -8979,7 +8989,7 @@ msgstr "crwdns94426:0{0}crwdnd94426:0{1}crwdne94426:0"
|
|||
msgid "Error while connecting to email account {0}"
|
||||
msgstr "crwdns94428:0{0}crwdne94428:0"
|
||||
|
||||
#: email/doctype/notification/notification.py:507
|
||||
#: email/doctype/notification/notification.py:508
|
||||
msgid "Error while evaluating Notification {0}. Please fix your template."
|
||||
msgstr "crwdns94430:0{0}crwdne94430:0"
|
||||
|
||||
|
|
@ -9136,7 +9146,7 @@ msgstr "crwdns94494:0crwdne94494:0"
|
|||
msgid "Executing..."
|
||||
msgstr "crwdns94496:0crwdne94496:0"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1993
|
||||
#: public/js/frappe/views/reports/query_report.js:1978
|
||||
msgid "Execution Time: {0} sec"
|
||||
msgstr "crwdns94498:0{0}crwdne94498:0"
|
||||
|
||||
|
|
@ -9154,7 +9164,7 @@ msgctxt "Enlarge code field."
|
|||
msgid "Expand"
|
||||
msgstr "crwdns94504:0crwdne94504:0"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1979
|
||||
#: public/js/frappe/views/reports/query_report.js:1964
|
||||
#: public/js/frappe/views/treeview.js:125
|
||||
msgid "Expand All"
|
||||
msgstr "crwdns94506:0crwdne94506:0"
|
||||
|
|
@ -9210,7 +9220,7 @@ msgstr "crwdns129232:0crwdne129232:0"
|
|||
#: core/doctype/docperm/docperm.json core/doctype/recorder/recorder_list.js:37
|
||||
#: public/js/frappe/data_import/data_exporter.js:91
|
||||
#: public/js/frappe/data_import/data_exporter.js:242
|
||||
#: public/js/frappe/views/reports/query_report.js:1684
|
||||
#: public/js/frappe/views/reports/query_report.js:1669
|
||||
#: public/js/frappe/views/reports/report_view.js:1550
|
||||
msgid "Export"
|
||||
msgstr "crwdns94526:0crwdne94526:0"
|
||||
|
|
@ -9551,7 +9561,7 @@ msgstr "crwdns94660:0crwdne94660:0"
|
|||
#: public/js/frappe/list/bulk_operations.js:297
|
||||
#: public/js/frappe/list/list_view_permission_restrictions.html:3
|
||||
#: public/js/frappe/views/reports/query_report.js:236
|
||||
#: public/js/frappe/views/reports/query_report.js:1738
|
||||
#: public/js/frappe/views/reports/query_report.js:1723
|
||||
#: website/doctype/web_form_field/web_form_field.json
|
||||
#: website/doctype/web_form_list_column/web_form_list_column.json
|
||||
msgid "Field"
|
||||
|
|
@ -9859,7 +9869,7 @@ msgstr "crwdns94824:0crwdne94824:0"
|
|||
#: desk/doctype/number_card/number_card.js:205
|
||||
#: desk/doctype/number_card/number_card.js:336
|
||||
#: email/doctype/auto_email_report/auto_email_report.js:90
|
||||
#: public/js/frappe/list/base_list.js:882
|
||||
#: public/js/frappe/list/base_list.js:890
|
||||
#: public/js/frappe/ui/filters/filter_list.js:134
|
||||
#: website/doctype/web_form/web_form.js:197
|
||||
msgid "Filter"
|
||||
|
|
@ -10295,7 +10305,7 @@ msgstr "crwdns95024:0crwdne95024:0"
|
|||
msgid "For Value"
|
||||
msgstr "crwdns129392:0crwdne129392:0"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1990
|
||||
#: public/js/frappe/views/reports/query_report.js:1975
|
||||
#: public/js/frappe/views/reports/report_view.js:96
|
||||
msgid "For comparison, use >5, <10 or =324. For ranges, use 5:10 (for values between 5 & 10)."
|
||||
msgstr "crwdns95034:0crwdne95034:0"
|
||||
|
|
@ -10542,7 +10552,7 @@ msgstr "crwdns95156:0crwdne95156:0"
|
|||
msgid "From Date Field"
|
||||
msgstr "crwdns129430:0crwdne129430:0"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1704
|
||||
#: public/js/frappe/views/reports/query_report.js:1689
|
||||
msgid "From Document Type"
|
||||
msgstr "crwdns95162:0crwdne95162:0"
|
||||
|
||||
|
|
@ -10638,6 +10648,10 @@ msgstr "crwdns129446:0crwdne129446:0"
|
|||
msgid "Gantt"
|
||||
msgstr "crwdns95208:0crwdne95208:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:206
|
||||
msgid "Gantt View"
|
||||
msgstr "crwdns142846:0crwdne142846:0"
|
||||
|
||||
#. Label of the gender (Link) field in DocType 'Contact'
|
||||
#. Name of a DocType
|
||||
#. Label of the gender (Data) field in DocType 'Gender'
|
||||
|
|
@ -12009,6 +12023,10 @@ msgstr "crwdns129686:0crwdne129686:0"
|
|||
msgid "Image Link"
|
||||
msgstr "crwdns129688:0crwdne129688:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:209
|
||||
msgid "Image View"
|
||||
msgstr "crwdns142848:0crwdne142848:0"
|
||||
|
||||
#. Label of the image_width (Float) field in DocType 'Letter Head'
|
||||
#. Label of the footer_image_width (Float) field in DocType 'Letter Head'
|
||||
#: printing/doctype/letter_head/letter_head.json
|
||||
|
|
@ -12280,6 +12298,10 @@ msgstr "crwdns95966:0crwdne95966:0"
|
|||
msgid "Inbox User"
|
||||
msgstr "crwdns95970:0crwdne95970:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:210
|
||||
msgid "Inbox View"
|
||||
msgstr "crwdns142850:0crwdne142850:0"
|
||||
|
||||
#. Label of the include_name_field (Check) field in DocType 'Form Tour'
|
||||
#: desk/doctype/form_tour/form_tour.json
|
||||
msgid "Include Name Field"
|
||||
|
|
@ -12299,11 +12321,11 @@ msgstr "crwdns95976:0crwdne95976:0"
|
|||
msgid "Include Web View Link in Email"
|
||||
msgstr "crwdns129732:0crwdne129732:0"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1521
|
||||
#: public/js/frappe/views/reports/query_report.js:1506
|
||||
msgid "Include filters"
|
||||
msgstr "crwdns95980:0crwdne95980:0"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1513
|
||||
#: public/js/frappe/views/reports/query_report.js:1498
|
||||
msgid "Include indentation"
|
||||
msgstr "crwdns95982:0crwdne95982:0"
|
||||
|
||||
|
|
@ -12459,7 +12481,7 @@ msgstr "crwdns110970:0crwdne110970:0"
|
|||
|
||||
#. Label of the insert_after (Select) field in DocType 'Custom Field'
|
||||
#: custom/doctype/custom_field/custom_field.json
|
||||
#: public/js/frappe/views/reports/query_report.js:1744
|
||||
#: public/js/frappe/views/reports/query_report.js:1729
|
||||
msgid "Insert After"
|
||||
msgstr "crwdns96046:0crwdne96046:0"
|
||||
|
||||
|
|
@ -12666,7 +12688,7 @@ msgstr "crwdns96132:0{0}crwdne96132:0"
|
|||
msgid "Invalid \"mandatory_depends_on\" expression"
|
||||
msgstr "crwdns96134:0crwdne96134:0"
|
||||
|
||||
#: utils/nestedset.py:177
|
||||
#: utils/nestedset.py:178
|
||||
msgid "Invalid Action"
|
||||
msgstr "crwdns96136:0crwdne96136:0"
|
||||
|
||||
|
|
@ -13037,6 +13059,7 @@ msgstr "crwdns96308:0crwdne96308:0"
|
|||
|
||||
#. Label of the is_query_report (Check) field in DocType 'Workspace Link'
|
||||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:336
|
||||
msgid "Is Query Report"
|
||||
msgstr "crwdns129828:0crwdne129828:0"
|
||||
|
||||
|
|
@ -13140,7 +13163,7 @@ msgstr "crwdns129850:0crwdne129850:0"
|
|||
msgid "Item Type"
|
||||
msgstr "crwdns129852:0crwdne129852:0"
|
||||
|
||||
#: utils/nestedset.py:228
|
||||
#: utils/nestedset.py:229
|
||||
msgid "Item cannot be added to its own descendants"
|
||||
msgstr "crwdns96372:0crwdne96372:0"
|
||||
|
||||
|
|
@ -13265,6 +13288,7 @@ msgstr "crwdns129880:0crwdne129880:0"
|
|||
#. Label of the kanban_board (Link) field in DocType 'Workspace Shortcut'
|
||||
#: desk/doctype/kanban_board/kanban_board.json
|
||||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:509
|
||||
msgid "Kanban Board"
|
||||
msgstr "crwdns96432:0crwdne96432:0"
|
||||
|
||||
|
|
@ -13284,6 +13308,10 @@ msgctxt "Button in kanban view menu"
|
|||
msgid "Kanban Settings"
|
||||
msgstr "crwdns96444:0crwdne96444:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:207
|
||||
msgid "Kanban View"
|
||||
msgstr "crwdns142852:0crwdne142852:0"
|
||||
|
||||
#. Description of a DocType
|
||||
#: core/doctype/activity_log/activity_log.json
|
||||
msgid "Keep track of all update feeds"
|
||||
|
|
@ -13529,7 +13557,10 @@ msgstr "crwdns96524:0{0}crwdne96524:0"
|
|||
#: desk/doctype/workspace_quick_list/workspace_quick_list.json
|
||||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: printing/page/print_format_builder/print_format_builder.js:474
|
||||
#: public/js/frappe/widgets/widget_dialog.js:187
|
||||
#: public/js/frappe/widgets/widget_dialog.js:255
|
||||
#: public/js/frappe/widgets/widget_dialog.js:304
|
||||
#: public/js/frappe/widgets/widget_dialog.js:421
|
||||
#: public/js/frappe/widgets/widget_dialog.js:645
|
||||
#: public/js/frappe/widgets/widget_dialog.js:678
|
||||
#: templates/form_grid/fields.html:37
|
||||
|
|
@ -14096,6 +14127,8 @@ msgstr "crwdns130038:0crwdne130038:0"
|
|||
#. Label of the link_to (Dynamic Link) field in DocType 'Workspace Shortcut'
|
||||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:285
|
||||
#: public/js/frappe/widgets/widget_dialog.js:430
|
||||
msgid "Link To"
|
||||
msgstr "crwdns130040:0crwdne130040:0"
|
||||
|
||||
|
|
@ -14105,6 +14138,7 @@ msgstr "crwdns110990:0crwdne110990:0"
|
|||
|
||||
#. Label of the link_type (Select) field in DocType 'Workspace Link'
|
||||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:277
|
||||
msgid "Link Type"
|
||||
msgstr "crwdns130042:0crwdne130042:0"
|
||||
|
||||
|
|
@ -14148,7 +14182,7 @@ msgstr "crwdns96840:0crwdne96840:0"
|
|||
#. Label of the links (Table) field in DocType 'Customize Form'
|
||||
#. Label of the links (Table) field in DocType 'Workspace'
|
||||
#: contacts/doctype/address/address.js:39 contacts/doctype/address/address.json
|
||||
#: contacts/doctype/contact/contact.js:87 contacts/doctype/contact/contact.json
|
||||
#: contacts/doctype/contact/contact.js:92 contacts/doctype/contact/contact.json
|
||||
#: core/doctype/doctype/doctype.json
|
||||
#: custom/doctype/customize_form/customize_form.json
|
||||
#: desk/doctype/workspace/workspace.json public/js/frappe/form/toolbar.js:377
|
||||
|
|
@ -14201,6 +14235,10 @@ msgctxt "Button in list view menu"
|
|||
msgid "List Settings"
|
||||
msgstr "crwdns96868:0crwdne96868:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:203
|
||||
msgid "List View"
|
||||
msgstr "crwdns142854:0crwdne142854:0"
|
||||
|
||||
#. Name of a DocType
|
||||
#: desk/doctype/list_view_settings/list_view_settings.json
|
||||
msgid "List View Settings"
|
||||
|
|
@ -14237,7 +14275,7 @@ msgstr "crwdns96884:0crwdne96884:0"
|
|||
msgid "Load Balancing"
|
||||
msgstr "crwdns130066:0crwdne130066:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:378
|
||||
#: public/js/frappe/list/base_list.js:386
|
||||
#: website/doctype/blog_post/templates/blog_post_list.html:50
|
||||
#: website/doctype/help_article/templates/help_article_list.html:30
|
||||
msgid "Load More"
|
||||
|
|
@ -14251,7 +14289,7 @@ msgstr "crwdns96890:0crwdne96890:0"
|
|||
#: core/page/permission_manager/permission_manager.js:165
|
||||
#: public/js/frappe/form/controls/multicheck.js:13
|
||||
#: public/js/frappe/form/linked_with.js:13
|
||||
#: public/js/frappe/list/base_list.js:490
|
||||
#: public/js/frappe/list/base_list.js:498
|
||||
#: public/js/frappe/list/list_view.js:335 public/js/frappe/ui/listing.html:16
|
||||
#: public/js/frappe/views/reports/query_report.js:1017
|
||||
msgid "Loading"
|
||||
|
|
@ -14279,7 +14317,7 @@ msgstr "crwdns110996:0crwdne110996:0"
|
|||
#: public/js/frappe/views/kanban/kanban_board.html:11
|
||||
#: public/js/frappe/widgets/chart_widget.js:50
|
||||
#: public/js/frappe/widgets/number_card_widget.js:174
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:126
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:128
|
||||
msgid "Loading..."
|
||||
msgstr "crwdns96898:0crwdne96898:0"
|
||||
|
||||
|
|
@ -14664,6 +14702,10 @@ msgstr "crwdns130124:0crwdne130124:0"
|
|||
msgid "Map Columns"
|
||||
msgstr "crwdns97072:0crwdne97072:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:212
|
||||
msgid "Map View"
|
||||
msgstr "crwdns142856:0crwdne142856:0"
|
||||
|
||||
#: public/js/frappe/data_import/import_preview.js:290
|
||||
msgid "Map columns from {0} to fields in {1}"
|
||||
msgstr "crwdns111002:0{0}crwdnd111002:0{1}crwdne111002:0"
|
||||
|
|
@ -14893,7 +14935,7 @@ msgstr "crwdns97168:0crwdne97168:0"
|
|||
msgid "Merge with existing"
|
||||
msgstr "crwdns97170:0crwdne97170:0"
|
||||
|
||||
#: utils/nestedset.py:304
|
||||
#: utils/nestedset.py:307
|
||||
msgid "Merging is only possible between Group-to-Group or Leaf Node-to-Leaf Node"
|
||||
msgstr "crwdns97172:0crwdne97172:0"
|
||||
|
||||
|
|
@ -15049,6 +15091,10 @@ msgstr "crwdns97252:0crwdne97252:0"
|
|||
msgid "Method"
|
||||
msgstr "crwdns130200:0crwdne130200:0"
|
||||
|
||||
#: __init__.py:936
|
||||
msgid "Method Not Allowed"
|
||||
msgstr "crwdns142858:0crwdne142858:0"
|
||||
|
||||
#: desk/doctype/number_card/number_card.py:70
|
||||
msgid "Method is required to create a number card"
|
||||
msgstr "crwdns97266:0crwdne97266:0"
|
||||
|
|
@ -15440,7 +15486,7 @@ msgstr "crwdns130234:0crwdne130234:0"
|
|||
msgid "Mozilla doesn't support :has() so you can pass parent selector here as workaround"
|
||||
msgstr "crwdns130236:0crwdne130236:0"
|
||||
|
||||
#: utils/nestedset.py:328
|
||||
#: utils/nestedset.py:331
|
||||
msgid "Multiple root nodes not allowed."
|
||||
msgstr "crwdns97478:0crwdne97478:0"
|
||||
|
||||
|
|
@ -15674,7 +15720,7 @@ msgstr "crwdns97574:0crwdne97574:0"
|
|||
msgid "Negative Value"
|
||||
msgstr "crwdns97576:0crwdne97576:0"
|
||||
|
||||
#: utils/nestedset.py:93
|
||||
#: utils/nestedset.py:94
|
||||
msgid "Nested set error. Please contact the Administrator."
|
||||
msgstr "crwdns97578:0crwdne97578:0"
|
||||
|
||||
|
|
@ -16021,7 +16067,7 @@ msgstr "crwdns130294:0crwdne130294:0"
|
|||
#: public/js/form_builder/utils.js:341
|
||||
#: public/js/frappe/form/controls/link.js:475
|
||||
#: public/js/frappe/list/list_sidebar_group_by.js:223
|
||||
#: public/js/frappe/views/reports/query_report.js:1541
|
||||
#: public/js/frappe/views/reports/query_report.js:1526
|
||||
#: website/doctype/help_article/templates/help_article.html:26
|
||||
msgid "No"
|
||||
msgstr "crwdns97696:0crwdne97696:0"
|
||||
|
|
@ -16065,7 +16111,7 @@ msgstr "crwdns97718:0crwdne97718:0"
|
|||
msgid "No Data to Show"
|
||||
msgstr "crwdns111038:0crwdne111038:0"
|
||||
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:131
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:133
|
||||
msgid "No Data..."
|
||||
msgstr "crwdns111040:0crwdne111040:0"
|
||||
|
||||
|
|
@ -16532,7 +16578,7 @@ msgstr "crwdns97892:0crwdne97892:0"
|
|||
msgid "Not allowed for {0}: {1}"
|
||||
msgstr "crwdns97894:0{0}crwdnd97894:0{1}crwdne97894:0"
|
||||
|
||||
#: email/doctype/notification/notification.py:391
|
||||
#: email/doctype/notification/notification.py:392
|
||||
msgid "Not allowed to attach {0} document, please enable Allow Print For {0} in Print Settings"
|
||||
msgstr "crwdns97896:0{0}crwdnd97896:0{0}crwdne97896:0"
|
||||
|
||||
|
|
@ -16651,7 +16697,7 @@ msgstr "crwdns97940:0crwdne97940:0"
|
|||
msgid "Nothing left to undo"
|
||||
msgstr "crwdns97942:0crwdne97942:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:362
|
||||
#: public/js/frappe/list/base_list.js:370
|
||||
#: public/js/frappe/views/reports/query_report.js:105
|
||||
#: templates/includes/list/list.html:7
|
||||
#: website/doctype/blog_post/templates/blog_post_list.html:41
|
||||
|
|
@ -17057,9 +17103,14 @@ msgstr "crwdns111096:0{0}crwdnd111096:0{1}crwdne111096:0"
|
|||
|
||||
#. Label of the onboard (Check) field in DocType 'Workspace Link'
|
||||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:330
|
||||
msgid "Onboard"
|
||||
msgstr "crwdns130402:0crwdne130402:0"
|
||||
|
||||
#: public/js/frappe/widgets/widget_dialog.js:236
|
||||
msgid "Onboarding Name"
|
||||
msgstr "crwdns142860:0crwdne142860:0"
|
||||
|
||||
#. Name of a DocType
|
||||
#: desk/doctype/onboarding_permission/onboarding_permission.json
|
||||
msgid "Onboarding Permission"
|
||||
|
|
@ -17167,6 +17218,7 @@ msgstr "crwdns127690:0crwdne127690:0"
|
|||
|
||||
#. Label of the only_for (Link) field in DocType 'Workspace Link'
|
||||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:323
|
||||
msgid "Only for"
|
||||
msgstr "crwdns130412:0crwdne130412:0"
|
||||
|
||||
|
|
@ -17516,7 +17568,7 @@ msgstr "crwdns130460:0crwdne130460:0"
|
|||
|
||||
#: printing/page/print/print.js:71
|
||||
#: public/js/frappe/form/templates/print_layout.html:44
|
||||
#: public/js/frappe/views/reports/query_report.js:1669
|
||||
#: public/js/frappe/views/reports/query_report.js:1654
|
||||
msgid "PDF"
|
||||
msgstr "crwdns98288:0crwdne98288:0"
|
||||
|
||||
|
|
@ -17908,8 +17960,8 @@ msgid "Password not found for {0} {1} {2}"
|
|||
msgstr "crwdns98464:0{0}crwdnd98464:0{1}crwdnd98464:0{2}crwdne98464:0"
|
||||
|
||||
#: core/doctype/user/user.py:1021
|
||||
msgid "Password reset instructions have been sent to your email"
|
||||
msgstr "crwdns98466:0crwdne98466:0"
|
||||
msgid "Password reset instructions have been sent to {}'s email"
|
||||
msgstr "crwdns142862:0crwdne142862:0"
|
||||
|
||||
#: www/update-password.html:164
|
||||
msgid "Password set"
|
||||
|
|
@ -18308,7 +18360,7 @@ msgstr "crwdns98646:0crwdne98646:0"
|
|||
msgid "Please check the filter values set for Dashboard Chart: {}"
|
||||
msgstr "crwdns98648:0crwdne98648:0"
|
||||
|
||||
#: model/base_document.py:872
|
||||
#: model/base_document.py:873
|
||||
msgid "Please check the value of \"Fetch From\" set for field {0}"
|
||||
msgstr "crwdns98650:0{0}crwdne98650:0"
|
||||
|
||||
|
|
@ -18610,11 +18662,11 @@ msgstr "crwdns98790:0crwdne98790:0"
|
|||
msgid "Please specify a valid parent DocType for {0}"
|
||||
msgstr "crwdns98792:0{0}crwdne98792:0"
|
||||
|
||||
#: email/doctype/notification/notification.py:87
|
||||
#: email/doctype/notification/notification.py:88
|
||||
msgid "Please specify which date field must be checked"
|
||||
msgstr "crwdns98794:0crwdne98794:0"
|
||||
|
||||
#: email/doctype/notification/notification.py:90
|
||||
#: email/doctype/notification/notification.py:91
|
||||
msgid "Please specify which value field must be checked"
|
||||
msgstr "crwdns98796:0crwdne98796:0"
|
||||
|
||||
|
|
@ -18947,7 +18999,7 @@ msgstr "crwdns112704:0{0}crwdne112704:0"
|
|||
#: public/js/frappe/form/templates/print_layout.html:46
|
||||
#: public/js/frappe/form/toolbar.js:332 public/js/frappe/form/toolbar.js:344
|
||||
#: public/js/frappe/list/bulk_operations.js:87
|
||||
#: public/js/frappe/views/reports/query_report.js:1655
|
||||
#: public/js/frappe/views/reports/query_report.js:1640
|
||||
#: public/js/frappe/views/reports/report_view.js:1460
|
||||
#: public/js/frappe/views/treeview.js:469 www/printview.html:18
|
||||
msgid "Print"
|
||||
|
|
@ -19809,7 +19861,7 @@ msgstr "crwdns99328:0crwdne99328:0"
|
|||
msgid "Rebuild Tree"
|
||||
msgstr "crwdns99330:0crwdne99330:0"
|
||||
|
||||
#: utils/nestedset.py:176
|
||||
#: utils/nestedset.py:177
|
||||
msgid "Rebuilding of tree is not supported for {}"
|
||||
msgstr "crwdns99332:0crwdne99332:0"
|
||||
|
||||
|
|
@ -20167,7 +20219,7 @@ msgstr "crwdns99526:0crwdne99526:0"
|
|||
#: public/js/frappe/desk.js:533 public/js/frappe/form/form.js:1196
|
||||
#: public/js/frappe/form/templates/print_layout.html:6
|
||||
#: public/js/frappe/list/base_list.js:66
|
||||
#: public/js/frappe/views/reports/query_report.js:1644
|
||||
#: public/js/frappe/views/reports/query_report.js:1629
|
||||
#: public/js/frappe/views/treeview.js:475
|
||||
#: public/js/frappe/widgets/chart_widget.js:290
|
||||
#: public/js/frappe/widgets/number_card_widget.js:324
|
||||
|
|
@ -20265,7 +20317,7 @@ msgstr "crwdns99566:0crwdne99566:0"
|
|||
msgid "Reload File"
|
||||
msgstr "crwdns111162:0crwdne111162:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:242
|
||||
#: public/js/frappe/list/base_list.js:250
|
||||
msgid "Reload List"
|
||||
msgstr "crwdns99568:0crwdne99568:0"
|
||||
|
||||
|
|
@ -20534,7 +20586,7 @@ msgstr "crwdns99694:0crwdne99694:0"
|
|||
#: core/doctype/report/report.json
|
||||
#: desk/doctype/dashboard_chart/dashboard_chart.json
|
||||
#: desk/doctype/number_card/number_card.json
|
||||
#: public/js/frappe/views/reports/query_report.js:1825
|
||||
#: public/js/frappe/views/reports/query_report.js:1810
|
||||
msgid "Report Name"
|
||||
msgstr "crwdns99696:0crwdne99696:0"
|
||||
|
||||
|
|
@ -20557,6 +20609,10 @@ msgstr "crwdns130836:0crwdne130836:0"
|
|||
msgid "Report Type"
|
||||
msgstr "crwdns130838:0crwdne130838:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:204
|
||||
msgid "Report View"
|
||||
msgstr "crwdns142864:0crwdne142864:0"
|
||||
|
||||
#: core/doctype/doctype/doctype.py:1780
|
||||
msgid "Report cannot be set for Single types"
|
||||
msgstr "crwdns99718:0crwdne99718:0"
|
||||
|
|
@ -20591,7 +20647,7 @@ msgstr "crwdns99730:0crwdne99730:0"
|
|||
msgid "Report was not saved (there were errors)"
|
||||
msgstr "crwdns99732:0crwdne99732:0"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1863
|
||||
#: public/js/frappe/views/reports/query_report.js:1848
|
||||
msgid "Report with more than 10 columns looks better in Landscape mode."
|
||||
msgstr "crwdns99734:0crwdne99734:0"
|
||||
|
||||
|
|
@ -21177,7 +21233,7 @@ msgstr "crwdns130922:0crwdne130922:0"
|
|||
msgid "Roles can be set for users from their User page."
|
||||
msgstr "crwdns111176:0crwdne111176:0"
|
||||
|
||||
#: utils/nestedset.py:277
|
||||
#: utils/nestedset.py:280
|
||||
msgid "Root {0} cannot be deleted"
|
||||
msgstr "crwdns100012:0{0}crwdne100012:0"
|
||||
|
||||
|
|
@ -21250,7 +21306,7 @@ msgstr "crwdns111178:0crwdne111178:0"
|
|||
msgid "Row # {0}: Non administrator user can not set the role {1} to the custom doctype"
|
||||
msgstr "crwdns100056:0{0}crwdnd100056:0{1}crwdne100056:0"
|
||||
|
||||
#: model/base_document.py:903
|
||||
#: model/base_document.py:904
|
||||
msgid "Row #{0}:"
|
||||
msgstr "crwdns100058:0#{0}crwdne100058:0"
|
||||
|
||||
|
|
@ -21534,11 +21590,11 @@ msgstr "crwdns130978:0crwdne130978:0"
|
|||
#: public/js/frappe/views/kanban/kanban_settings.js:45
|
||||
#: public/js/frappe/views/kanban/kanban_settings.js:189
|
||||
#: public/js/frappe/views/kanban/kanban_view.js:343
|
||||
#: public/js/frappe/views/reports/query_report.js:1817
|
||||
#: public/js/frappe/views/reports/query_report.js:1802
|
||||
#: public/js/frappe/views/reports/report_view.js:1640
|
||||
#: public/js/frappe/views/workspace/workspace.js:501
|
||||
#: public/js/frappe/widgets/base_widget.js:142
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:117
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:119
|
||||
#: public/js/print_format_builder/print_format_builder.bundle.js:15
|
||||
#: public/js/workflow_builder/workflow_builder.bundle.js:33
|
||||
msgid "Save"
|
||||
|
|
@ -21565,7 +21621,7 @@ msgstr "crwdns100180:0crwdne100180:0"
|
|||
msgid "Save Filter"
|
||||
msgstr "crwdns111194:0crwdne111194:0"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1820
|
||||
#: public/js/frappe/views/reports/query_report.js:1805
|
||||
msgid "Save Report"
|
||||
msgstr "crwdns100182:0crwdne100182:0"
|
||||
|
||||
|
|
@ -22689,7 +22745,7 @@ msgid "Set Filters"
|
|||
msgstr "crwdns100696:0crwdne100696:0"
|
||||
|
||||
#: public/js/frappe/widgets/chart_widget.js:395
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:102
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:104
|
||||
msgid "Set Filters for {0}"
|
||||
msgstr "crwdns100698:0{0}crwdne100698:0"
|
||||
|
||||
|
|
@ -22902,7 +22958,7 @@ msgstr "crwdns111218:0crwdne111218:0"
|
|||
msgid "Setup Approval Workflows"
|
||||
msgstr "crwdns100772:0crwdne100772:0"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1690
|
||||
#: public/js/frappe/views/reports/query_report.js:1675
|
||||
#: public/js/frappe/views/reports/report_view.js:1618
|
||||
msgid "Setup Auto Email"
|
||||
msgstr "crwdns100774:0crwdne100774:0"
|
||||
|
|
@ -24936,7 +24992,7 @@ msgid "The Client ID obtained from the Google Cloud Console under <a href=\"http
|
|||
"</a>"
|
||||
msgstr "crwdns131442:0crwdne131442:0"
|
||||
|
||||
#: email/doctype/notification/notification.py:130
|
||||
#: email/doctype/notification/notification.py:131
|
||||
msgid "The Condition '{0}' is invalid"
|
||||
msgstr "crwdns101636:0{0}crwdne101636:0"
|
||||
|
||||
|
|
@ -25390,7 +25446,7 @@ msgstr "crwdns131482:0crwdne131482:0"
|
|||
msgid "This goes above the slideshow."
|
||||
msgstr "crwdns131484:0crwdne131484:0"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:2027
|
||||
#: public/js/frappe/views/reports/query_report.js:2012
|
||||
msgid "This is a background report. Please set the appropriate filters and then generate a new one."
|
||||
msgstr "crwdns101812:0crwdne101812:0"
|
||||
|
||||
|
|
@ -26249,6 +26305,10 @@ msgstr "crwdns131624:0crwdne131624:0"
|
|||
msgid "Tree"
|
||||
msgstr "crwdns131626:0crwdne131626:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:211
|
||||
msgid "Tree View"
|
||||
msgstr "crwdns142866:0crwdne142866:0"
|
||||
|
||||
#. Description of the 'Is Tree' (Check) field in DocType 'DocType'
|
||||
#: core/doctype/doctype/doctype.json
|
||||
msgid "Tree structures are implemented using Nested Set"
|
||||
|
|
@ -26354,6 +26414,7 @@ msgstr "crwdns131640:0crwdne131640:0"
|
|||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: public/js/frappe/views/file/file_view.js:337
|
||||
#: public/js/frappe/widgets/widget_dialog.js:399
|
||||
#: social/doctype/energy_point_log/energy_point_log.json
|
||||
#: website/doctype/web_template/web_template.json www/attribution.html:35
|
||||
msgid "Type"
|
||||
|
|
@ -26435,6 +26496,7 @@ msgstr "crwdns131652:0crwdne131652:0"
|
|||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: integrations/doctype/integration_request/integration_request.json
|
||||
#: integrations/doctype/webhook_request_log/webhook_request_log.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:469
|
||||
#: website/doctype/top_bar_item/top_bar_item.json
|
||||
#: website/doctype/website_slideshow_item/website_slideshow_item.json
|
||||
msgid "URL"
|
||||
|
|
@ -27111,7 +27173,7 @@ msgid "User Permission"
|
|||
msgstr "crwdns102624:0crwdne102624:0"
|
||||
|
||||
#: core/page/permission_manager/permission_manager_help.html:30
|
||||
#: public/js/frappe/views/reports/query_report.js:1804
|
||||
#: public/js/frappe/views/reports/query_report.js:1789
|
||||
#: public/js/frappe/views/reports/report_view.js:1666
|
||||
msgid "User Permissions"
|
||||
msgstr "crwdns102628:0crwdne102628:0"
|
||||
|
|
@ -27429,7 +27491,7 @@ msgstr "crwdns131784:0crwdne131784:0"
|
|||
msgid "Value To Be Set"
|
||||
msgstr "crwdns131786:0crwdne131786:0"
|
||||
|
||||
#: model/base_document.py:965 model/document.py:682
|
||||
#: model/base_document.py:966 model/document.py:682
|
||||
msgid "Value cannot be changed for {0}"
|
||||
msgstr "crwdns102750:0{0}crwdne102750:0"
|
||||
|
||||
|
|
@ -27472,7 +27534,7 @@ msgstr "crwdns102766:0{0}crwdne102766:0"
|
|||
msgid "Value to Validate"
|
||||
msgstr "crwdns131790:0crwdne131790:0"
|
||||
|
||||
#: model/base_document.py:1035
|
||||
#: model/base_document.py:1036
|
||||
msgid "Value too big"
|
||||
msgstr "crwdns102770:0crwdne102770:0"
|
||||
|
||||
|
|
@ -27571,7 +27633,7 @@ msgid "View Full Log"
|
|||
msgstr "crwdns111324:0crwdne111324:0"
|
||||
|
||||
#: public/js/frappe/views/treeview.js:463
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:245
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:247
|
||||
msgid "View List"
|
||||
msgstr "crwdns102808:0crwdne102808:0"
|
||||
|
||||
|
|
@ -28453,7 +28515,7 @@ msgstr "crwdns127790:0crwdne127790:0"
|
|||
msgid "Write"
|
||||
msgstr "crwdns131894:0crwdne131894:0"
|
||||
|
||||
#: model/base_document.py:875
|
||||
#: model/base_document.py:876
|
||||
msgid "Wrong Fetch From value"
|
||||
msgstr "crwdns103194:0crwdne103194:0"
|
||||
|
||||
|
|
@ -28540,7 +28602,7 @@ msgstr "crwdns131908:0crwdne131908:0"
|
|||
#: public/js/form_builder/utils.js:336
|
||||
#: public/js/frappe/form/controls/link.js:475
|
||||
#: public/js/frappe/list/list_sidebar_group_by.js:223
|
||||
#: public/js/frappe/views/reports/query_report.js:1541
|
||||
#: public/js/frappe/views/reports/query_report.js:1526
|
||||
#: website/doctype/help_article/templates/help_article.html:25
|
||||
msgid "Yes"
|
||||
msgstr "crwdns103238:0crwdne103238:0"
|
||||
|
|
@ -29753,7 +29815,7 @@ msgstr "crwdns104008:0crwdne104008:0"
|
|||
msgid "via Google Meet"
|
||||
msgstr "crwdns132068:0crwdne132068:0"
|
||||
|
||||
#: email/doctype/notification/notification.py:215
|
||||
#: email/doctype/notification/notification.py:216
|
||||
msgid "via Notification"
|
||||
msgstr "crwdns104012:0crwdne104012:0"
|
||||
|
||||
|
|
@ -29896,7 +29958,7 @@ msgstr "crwdns104080:0{0}crwdne104080:0"
|
|||
msgid "{0} Name"
|
||||
msgstr "crwdns104082:0{0}crwdne104082:0"
|
||||
|
||||
#: model/base_document.py:1065
|
||||
#: model/base_document.py:1066
|
||||
msgid "{0} Not allowed to change {1} after submission from {2} to {3}"
|
||||
msgstr "crwdns104084:0{0}crwdnd104084:0{1}crwdnd104084:0{2}crwdnd104084:0{3}crwdne104084:0"
|
||||
|
||||
|
|
@ -29922,10 +29984,6 @@ msgstr "crwdns104088:0{0}crwdne104088:0"
|
|||
msgid "{0} Tree"
|
||||
msgstr "crwdns104090:0{0}crwdne104090:0"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:209
|
||||
msgid "{0} View"
|
||||
msgstr "crwdns104092:0{0}crwdne104092:0"
|
||||
|
||||
#: public/js/frappe/form/footer/form_timeline.js:126
|
||||
#: public/js/frappe/form/sidebar/form_sidebar.js:86
|
||||
msgid "{0} Web page views"
|
||||
|
|
@ -30539,11 +30597,11 @@ msgstr "crwdns104370:0{0}crwdnd104370:0{1}crwdnd104370:0{2}crwdne104370:0"
|
|||
msgid "{0} {1} already exists"
|
||||
msgstr "crwdns104372:0{0}crwdnd104372:0{1}crwdne104372:0"
|
||||
|
||||
#: model/base_document.py:908
|
||||
#: model/base_document.py:909
|
||||
msgid "{0} {1} cannot be \"{2}\". It should be one of \"{3}\""
|
||||
msgstr "crwdns104374:0{0}crwdnd104374:0{1}crwdnd104374:0{2}crwdnd104374:0{3}crwdne104374:0"
|
||||
|
||||
#: utils/nestedset.py:337
|
||||
#: utils/nestedset.py:340
|
||||
msgid "{0} {1} cannot be a leaf node as it has children"
|
||||
msgstr "crwdns104376:0{0}crwdnd104376:0{1}crwdne104376:0"
|
||||
|
||||
|
|
@ -30563,11 +30621,11 @@ msgstr "crwdns104382:0{0}crwdnd104382:0{1}crwdne104382:0"
|
|||
msgid "{0} {1}: Submitted Record cannot be deleted. You must {2} Cancel {3} it first."
|
||||
msgstr "crwdns104384:0{0}crwdnd104384:0{1}crwdnd104384:0{2}crwdnd104384:0{3}crwdne104384:0"
|
||||
|
||||
#: model/base_document.py:1026
|
||||
#: model/base_document.py:1027
|
||||
msgid "{0}, Row {1}"
|
||||
msgstr "crwdns104386:0{0}crwdnd104386:0{1}crwdne104386:0"
|
||||
|
||||
#: model/base_document.py:1031
|
||||
#: model/base_document.py:1032
|
||||
msgid "{0}: '{1}' ({3}) will get truncated, as max characters allowed is {2}"
|
||||
msgstr "crwdns104388:0{0}crwdnd104388:0{1}crwdnd104388:0{3}crwdnd104388:0{2}crwdne104388:0"
|
||||
|
||||
|
|
@ -30660,7 +30718,7 @@ msgid "{0}: fieldname cannot be set to reserved keyword {1}"
|
|||
msgstr "crwdns104430:0{0}crwdnd104430:0{1}crwdne104430:0"
|
||||
|
||||
#: contacts/doctype/address/address.js:35
|
||||
#: contacts/doctype/contact/contact.js:83
|
||||
#: contacts/doctype/contact/contact.js:88
|
||||
#: public/js/frappe/views/workspace/workspace.js:170
|
||||
msgid "{0}: {1}"
|
||||
msgstr "crwdns104432:0{0}crwdnd104432:0{1}crwdne104432:0"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,8 +2,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: developers@frappe.io\n"
|
||||
"POT-Creation-Date: 2024-06-30 09:33+0000\n"
|
||||
"PO-Revision-Date: 2024-07-02 15:49\n"
|
||||
"POT-Creation-Date: 2024-07-07 09:33+0000\n"
|
||||
"PO-Revision-Date: 2024-07-14 17:15\n"
|
||||
"Last-Translator: developers@frappe.io\n"
|
||||
"Language-Team: Swedish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
|
@ -540,7 +540,7 @@ msgstr "<h4>E-post Svar Exempel</h4>\n\n"
|
|||
"- Kund: {{ customer }}\n"
|
||||
"- Belopp: {{ grand_total }}\n"
|
||||
"</pre>\n\n"
|
||||
"<h4>Hur man får fältnamn</h4>\n\n"
|
||||
"<h4>Hur hämtas fältnamn</h4>\n\n"
|
||||
"<p>Fältnamn som kan använda i e-post mall är fält i dokument som du skickar e-post meddelande från. Ta reda på fält namn för alla dokument via Inställningar > Anpassa formulär vy och välja dokument typ (t.ex. Försäljning Faktura)</p>\n\n"
|
||||
"<h4>Mallar</h4>\n\n"
|
||||
"<p>Mallar kompileras med Jinja Templating Language. För att lära dig mer om Jinja, <a class=\"strong\" href=\"http://jinja.pocoo.org/docs/dev/templates/\">läs denna dokumentation.</a></p>\n"
|
||||
|
|
@ -1182,8 +1182,8 @@ msgid "Add Child"
|
|||
msgstr "Lägg till Underval"
|
||||
|
||||
#: public/js/frappe/views/kanban/kanban_board.html:4
|
||||
#: public/js/frappe/views/reports/query_report.js:1696
|
||||
#: public/js/frappe/views/reports/query_report.js:1699
|
||||
#: public/js/frappe/views/reports/query_report.js:1681
|
||||
#: public/js/frappe/views/reports/query_report.js:1684
|
||||
#: public/js/frappe/views/reports/report_view.js:324
|
||||
#: public/js/frappe/views/reports/report_view.js:349
|
||||
msgid "Add Column"
|
||||
|
|
@ -1292,7 +1292,7 @@ msgstr "Lägg till Mall"
|
|||
#. Label of the add_total_row (Check) field in DocType 'Report'
|
||||
#: core/doctype/report/report.json
|
||||
msgid "Add Total Row"
|
||||
msgstr "Lägg till Totalt Rad"
|
||||
msgstr "Lägg till Totalt Antal Rader"
|
||||
|
||||
#. Label of the add_unsubscribe_link (Check) field in DocType 'Email Queue'
|
||||
#: email/doctype/email_queue/email_queue.json
|
||||
|
|
@ -3154,7 +3154,7 @@ msgstr "Bakgrund Jobb"
|
|||
#. Report'
|
||||
#: desk/doctype/system_health_report/system_health_report.json
|
||||
msgid "Background Jobs Check"
|
||||
msgstr "Bakgrund Jobb Koll"
|
||||
msgstr "Bakgrund Jobb Status"
|
||||
|
||||
#. Label of the background_jobs_queue (Autocomplete) field in DocType 'Webhook'
|
||||
#: integrations/doctype/webhook/webhook.json
|
||||
|
|
@ -3407,7 +3407,7 @@ msgstr "Faktura Kontakt"
|
|||
#. Label of the binary_logging (Data) field in DocType 'System Health Report'
|
||||
#: desk/doctype/system_health_report/system_health_report.json
|
||||
msgid "Binary Logging"
|
||||
msgstr "Fakturering Logg"
|
||||
msgstr "Binär Logg"
|
||||
|
||||
#. Label of the bio (Small Text) field in DocType 'User'
|
||||
#. Label of the bio (Small Text) field in DocType 'About Us Team Member'
|
||||
|
|
@ -3899,6 +3899,7 @@ msgstr "Kalender Namn"
|
|||
|
||||
#. Name of a DocType
|
||||
#: desk/doctype/calendar_view/calendar_view.json
|
||||
#: public/js/frappe/list/base_list.js:208
|
||||
msgid "Calendar View"
|
||||
msgstr "Kalender Vy"
|
||||
|
||||
|
|
@ -4075,7 +4076,7 @@ msgstr "Kan inte Hämta Värden"
|
|||
msgid "Cannot Remove"
|
||||
msgstr "Kan inte Ta Bort"
|
||||
|
||||
#: model/base_document.py:1072
|
||||
#: model/base_document.py:1073
|
||||
msgid "Cannot Update After Submit"
|
||||
msgstr "Kan inte Uppdatera efter Godkännande"
|
||||
|
||||
|
|
@ -4163,7 +4164,7 @@ msgstr "Kan inte ta bort system skapad fält <strong>{0}</strong>. Dölj det ist
|
|||
msgid "Cannot delete {0}"
|
||||
msgstr "Kan inte radera {0}"
|
||||
|
||||
#: utils/nestedset.py:296
|
||||
#: utils/nestedset.py:299
|
||||
msgid "Cannot delete {0} as it has child nodes"
|
||||
msgstr "Kan inte radera {0} eftersom det har underordnade noder"
|
||||
|
||||
|
|
@ -4171,7 +4172,7 @@ msgstr "Kan inte radera {0} eftersom det har underordnade noder"
|
|||
msgid "Cannot edit Standard Dashboards"
|
||||
msgstr "Kan inte redigera standard översikt panel"
|
||||
|
||||
#: email/doctype/notification/notification.py:121
|
||||
#: email/doctype/notification/notification.py:122
|
||||
msgid "Cannot edit Standard Notification. To edit, please disable this and duplicate it"
|
||||
msgstr "Kan inte redigera standard avisering. Kopiera och skapa ny"
|
||||
|
||||
|
|
@ -4240,7 +4241,7 @@ msgstr "Kan inte ta bort ID fält"
|
|||
msgid "Cannot set 'Report' permission if 'Only If Creator' permission is set"
|
||||
msgstr "Kan inte ange \"Rapport\" behörighet om behörighet \"Endast om Ägare\" är angiven"
|
||||
|
||||
#: email/doctype/notification/notification.py:137
|
||||
#: email/doctype/notification/notification.py:138
|
||||
msgid "Cannot set Notification on Document Type {0}"
|
||||
msgstr "Kan inte ange Avisering för DocType {0}"
|
||||
|
||||
|
|
@ -4354,7 +4355,7 @@ msgstr "Ändra"
|
|||
#: tests/test_translate.py:99
|
||||
msgctxt "Coins"
|
||||
msgid "Change"
|
||||
msgstr "Ändra"
|
||||
msgstr "Växel"
|
||||
|
||||
#. Label of the label (Data) field in DocType 'Customize Form'
|
||||
#: custom/doctype/customize_form/customize_form.json
|
||||
|
|
@ -4415,6 +4416,8 @@ msgstr "Diagram Inställningar"
|
|||
#. Label of the chart_name (Link) field in DocType 'Workspace Chart'
|
||||
#: desk/doctype/dashboard_chart/dashboard_chart.json
|
||||
#: desk/doctype/workspace_chart/workspace_chart.json
|
||||
#: public/js/frappe/views/reports/query_report.js:289
|
||||
#: public/js/frappe/widgets/widget_dialog.js:137
|
||||
msgid "Chart Name"
|
||||
msgstr "Diagram Namn"
|
||||
|
||||
|
|
@ -4831,7 +4834,7 @@ msgctxt "Shrink code field."
|
|||
msgid "Collapse"
|
||||
msgstr "Fäll In"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1979
|
||||
#: public/js/frappe/views/reports/query_report.js:1964
|
||||
#: public/js/frappe/views/treeview.js:121
|
||||
msgid "Collapse All"
|
||||
msgstr "Fäll In Alla"
|
||||
|
|
@ -6362,6 +6365,10 @@ msgstr "Översikt Panel Namn"
|
|||
msgid "Dashboard Settings"
|
||||
msgstr "Översikt Panel Inställningar"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:205
|
||||
msgid "Dashboard View"
|
||||
msgstr "Översikt Panel Vy"
|
||||
|
||||
#. Label of the tab_break_2 (Tab Break) field in DocType 'Workspace'
|
||||
#: desk/doctype/workspace/workspace.json
|
||||
msgid "Dashboards"
|
||||
|
|
@ -6943,7 +6950,8 @@ msgid "Department"
|
|||
msgstr "Avdelning"
|
||||
|
||||
#. Label of the dependencies (Data) field in DocType 'Workspace Link'
|
||||
#: desk/doctype/workspace_link/workspace_link.json www/attribution.html:29
|
||||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:318 www/attribution.html:29
|
||||
msgid "Dependencies"
|
||||
msgstr "Beroenden "
|
||||
|
||||
|
|
@ -7405,6 +7413,7 @@ msgstr "Status för följande tillstånd är ändrad:<br><strong>{0}</strong><br
|
|||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: integrations/doctype/webhook/webhook.json
|
||||
#: printing/doctype/print_format/print_format.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:164
|
||||
#: website/doctype/website_slideshow/website_slideshow.js:18
|
||||
msgid "DocType"
|
||||
msgstr "DocType"
|
||||
|
|
@ -7463,6 +7472,7 @@ msgstr "DocType Tillstånd"
|
|||
|
||||
#. Label of the doc_view (Select) field in DocType 'Workspace Shortcut'
|
||||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:477
|
||||
msgid "DocType View"
|
||||
msgstr "DocType Vy"
|
||||
|
||||
|
|
@ -7986,7 +7996,7 @@ msgstr "Ladda ner Mall"
|
|||
msgid "Download Your Data"
|
||||
msgstr "Ladda ner Data"
|
||||
|
||||
#: contacts/doctype/contact/contact.js:93
|
||||
#: contacts/doctype/contact/contact.js:98
|
||||
msgid "Download vCard"
|
||||
msgstr "Ladda ner vCard"
|
||||
|
||||
|
|
@ -8174,7 +8184,7 @@ msgstr "Varje dokument skapad i System kan ha unikt ID genererad för det, med d
|
|||
#: public/js/frappe/form/templates/contact_list.html:7
|
||||
#: public/js/frappe/form/toolbar.js:681
|
||||
#: public/js/frappe/views/reports/query_report.js:815
|
||||
#: public/js/frappe/views/reports/query_report.js:1649
|
||||
#: public/js/frappe/views/reports/query_report.js:1634
|
||||
#: public/js/frappe/views/workspace/workspace.js:460
|
||||
#: public/js/frappe/views/workspace/workspace.js:816
|
||||
#: public/js/frappe/widgets/base_widget.js:64
|
||||
|
|
@ -9152,9 +9162,9 @@ msgstr "Fel i Klient Skript."
|
|||
msgid "Error in Header/Footer Script"
|
||||
msgstr "Fel i Sidhuvud/Sidfot Skript"
|
||||
|
||||
#: email/doctype/notification/notification.py:394
|
||||
#: email/doctype/notification/notification.py:510
|
||||
#: email/doctype/notification/notification.py:516
|
||||
#: email/doctype/notification/notification.py:395
|
||||
#: email/doctype/notification/notification.py:511
|
||||
#: email/doctype/notification/notification.py:517
|
||||
msgid "Error in Notification"
|
||||
msgstr "Fel i Avisering"
|
||||
|
||||
|
|
@ -9166,7 +9176,7 @@ msgstr "Fel i Utskrift Format på rad {0}: {1}"
|
|||
msgid "Error while connecting to email account {0}"
|
||||
msgstr "Fel vid anslutning till E-post Konto {0}"
|
||||
|
||||
#: email/doctype/notification/notification.py:507
|
||||
#: email/doctype/notification/notification.py:508
|
||||
msgid "Error while evaluating Notification {0}. Please fix your template."
|
||||
msgstr "Fel vid test av Avisering {0}. Fixa Mall."
|
||||
|
||||
|
|
@ -9324,7 +9334,7 @@ msgstr "Kör Konsol Skript"
|
|||
msgid "Executing..."
|
||||
msgstr "Kör..."
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1993
|
||||
#: public/js/frappe/views/reports/query_report.js:1978
|
||||
msgid "Execution Time: {0} sec"
|
||||
msgstr "Exekvering Tid: {0} sek"
|
||||
|
||||
|
|
@ -9342,7 +9352,7 @@ msgctxt "Enlarge code field."
|
|||
msgid "Expand"
|
||||
msgstr "Expandera"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1979
|
||||
#: public/js/frappe/views/reports/query_report.js:1964
|
||||
#: public/js/frappe/views/treeview.js:125
|
||||
msgid "Expand All"
|
||||
msgstr "Expandera Alla"
|
||||
|
|
@ -9398,7 +9408,7 @@ msgstr "Förfallo Tid för QR Kod Bild Sida"
|
|||
#: core/doctype/docperm/docperm.json core/doctype/recorder/recorder_list.js:37
|
||||
#: public/js/frappe/data_import/data_exporter.js:91
|
||||
#: public/js/frappe/data_import/data_exporter.js:242
|
||||
#: public/js/frappe/views/reports/query_report.js:1684
|
||||
#: public/js/frappe/views/reports/query_report.js:1669
|
||||
#: public/js/frappe/views/reports/report_view.js:1550
|
||||
msgid "Export"
|
||||
msgstr "Export"
|
||||
|
|
@ -9739,7 +9749,7 @@ msgstr "Hämtar standard Global Sökning dokument."
|
|||
#: public/js/frappe/list/bulk_operations.js:297
|
||||
#: public/js/frappe/list/list_view_permission_restrictions.html:3
|
||||
#: public/js/frappe/views/reports/query_report.js:236
|
||||
#: public/js/frappe/views/reports/query_report.js:1738
|
||||
#: public/js/frappe/views/reports/query_report.js:1723
|
||||
#: website/doctype/web_form_field/web_form_field.json
|
||||
#: website/doctype/web_form_list_column/web_form_list_column.json
|
||||
msgid "Field"
|
||||
|
|
@ -10047,7 +10057,7 @@ msgstr "Filer"
|
|||
#: desk/doctype/number_card/number_card.js:205
|
||||
#: desk/doctype/number_card/number_card.js:336
|
||||
#: email/doctype/auto_email_report/auto_email_report.js:90
|
||||
#: public/js/frappe/list/base_list.js:882
|
||||
#: public/js/frappe/list/base_list.js:890
|
||||
#: public/js/frappe/ui/filters/filter_list.js:134
|
||||
#: website/doctype/web_form/web_form.js:197
|
||||
msgid "Filter"
|
||||
|
|
@ -10483,7 +10493,7 @@ msgstr "För Användare"
|
|||
msgid "For Value"
|
||||
msgstr "För Värde"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1990
|
||||
#: public/js/frappe/views/reports/query_report.js:1975
|
||||
#: public/js/frappe/views/reports/report_view.js:96
|
||||
msgid "For comparison, use >5, <10 or =324. For ranges, use 5:10 (for values between 5 & 10)."
|
||||
msgstr "För jämförelse, använd >5, <10 eller = 324. För intervall, använd 5:10 (för värden mellan 5 och 10)."
|
||||
|
|
@ -10730,7 +10740,7 @@ msgstr "Från Datum"
|
|||
msgid "From Date Field"
|
||||
msgstr "Från Datum"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1704
|
||||
#: public/js/frappe/views/reports/query_report.js:1689
|
||||
msgid "From Document Type"
|
||||
msgstr "Från DocType"
|
||||
|
||||
|
|
@ -10826,6 +10836,10 @@ msgstr "GNU General Public License"
|
|||
msgid "Gantt"
|
||||
msgstr "Gantt"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:206
|
||||
msgid "Gantt View"
|
||||
msgstr "Gantt Vy"
|
||||
|
||||
#. Label of the gender (Link) field in DocType 'Contact'
|
||||
#. Name of a DocType
|
||||
#. Label of the gender (Data) field in DocType 'Gender'
|
||||
|
|
@ -12197,6 +12211,10 @@ msgstr "Bild Höjd"
|
|||
msgid "Image Link"
|
||||
msgstr "Bild Länk"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:209
|
||||
msgid "Image View"
|
||||
msgstr "Visa Bild"
|
||||
|
||||
#. Label of the image_width (Float) field in DocType 'Letter Head'
|
||||
#. Label of the footer_image_width (Float) field in DocType 'Letter Head'
|
||||
#: printing/doctype/letter_head/letter_head.json
|
||||
|
|
@ -12468,6 +12486,10 @@ msgstr "Inkorg"
|
|||
msgid "Inbox User"
|
||||
msgstr "Inkorg Användare"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:210
|
||||
msgid "Inbox View"
|
||||
msgstr "Inkorg Vy"
|
||||
|
||||
#. Label of the include_name_field (Check) field in DocType 'Form Tour'
|
||||
#: desk/doctype/form_tour/form_tour.json
|
||||
msgid "Include Name Field"
|
||||
|
|
@ -12487,11 +12509,11 @@ msgstr "Inkludera Tema från Appar"
|
|||
msgid "Include Web View Link in Email"
|
||||
msgstr "Inkludera Länk till Webbvy i E-post"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1521
|
||||
#: public/js/frappe/views/reports/query_report.js:1506
|
||||
msgid "Include filters"
|
||||
msgstr "Inkludera Filter"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1513
|
||||
#: public/js/frappe/views/reports/query_report.js:1498
|
||||
msgid "Include indentation"
|
||||
msgstr "Inkludera Fördjupning"
|
||||
|
||||
|
|
@ -12647,7 +12669,7 @@ msgstr "Infoga \tOvan"
|
|||
|
||||
#. Label of the insert_after (Select) field in DocType 'Custom Field'
|
||||
#: custom/doctype/custom_field/custom_field.json
|
||||
#: public/js/frappe/views/reports/query_report.js:1744
|
||||
#: public/js/frappe/views/reports/query_report.js:1729
|
||||
msgid "Insert After"
|
||||
msgstr "Infoga Efter"
|
||||
|
||||
|
|
@ -12854,7 +12876,7 @@ msgstr "Ogiltig uttryck 'beroende på' i filter {0}"
|
|||
msgid "Invalid \"mandatory_depends_on\" expression"
|
||||
msgstr "Ogiltigt uttryck för \"obligatoriskt_beror_på\""
|
||||
|
||||
#: utils/nestedset.py:177
|
||||
#: utils/nestedset.py:178
|
||||
msgid "Invalid Action"
|
||||
msgstr "Ogiltig åtgärd"
|
||||
|
||||
|
|
@ -13048,7 +13070,7 @@ msgstr "Ogiltiga begäran argument"
|
|||
|
||||
#: integrations/doctype/connected_app/connected_app.py:173
|
||||
msgid "Invalid state."
|
||||
msgstr "Ogiltig status."
|
||||
msgstr "Ogiltigt tillstånd."
|
||||
|
||||
#: core/doctype/data_import/importer.py:423
|
||||
msgid "Invalid template file for import"
|
||||
|
|
@ -13225,6 +13247,7 @@ msgstr "Är Publicerad Fält måste vara giltig Fält Namn"
|
|||
|
||||
#. Label of the is_query_report (Check) field in DocType 'Workspace Link'
|
||||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:336
|
||||
msgid "Is Query Report"
|
||||
msgstr "Är Dataförfråga Rapport"
|
||||
|
||||
|
|
@ -13328,7 +13351,7 @@ msgstr "Artikel Titel"
|
|||
msgid "Item Type"
|
||||
msgstr "Artikel Typ"
|
||||
|
||||
#: utils/nestedset.py:228
|
||||
#: utils/nestedset.py:229
|
||||
msgid "Item cannot be added to its own descendants"
|
||||
msgstr "Artikel kan inte läggas till egna undertyper"
|
||||
|
||||
|
|
@ -13453,6 +13476,7 @@ msgstr "Anslag Tavla"
|
|||
#. Label of the kanban_board (Link) field in DocType 'Workspace Shortcut'
|
||||
#: desk/doctype/kanban_board/kanban_board.json
|
||||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:509
|
||||
msgid "Kanban Board"
|
||||
msgstr "Anslag Tavla"
|
||||
|
||||
|
|
@ -13472,6 +13496,10 @@ msgctxt "Button in kanban view menu"
|
|||
msgid "Kanban Settings"
|
||||
msgstr "Anslag Tavla Inställningar"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:207
|
||||
msgid "Kanban View"
|
||||
msgstr "Anslag Tavla Vy"
|
||||
|
||||
#. Description of a DocType
|
||||
#: core/doctype/activity_log/activity_log.json
|
||||
msgid "Keep track of all update feeds"
|
||||
|
|
@ -13717,7 +13745,10 @@ msgstr "LDAP Inställningar felaktiga. validering svar var: {0}"
|
|||
#: desk/doctype/workspace_quick_list/workspace_quick_list.json
|
||||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: printing/page/print_format_builder/print_format_builder.js:474
|
||||
#: public/js/frappe/widgets/widget_dialog.js:187
|
||||
#: public/js/frappe/widgets/widget_dialog.js:255
|
||||
#: public/js/frappe/widgets/widget_dialog.js:304
|
||||
#: public/js/frappe/widgets/widget_dialog.js:421
|
||||
#: public/js/frappe/widgets/widget_dialog.js:645
|
||||
#: public/js/frappe/widgets/widget_dialog.js:678
|
||||
#: templates/form_grid/fields.html:37
|
||||
|
|
@ -13848,7 +13879,7 @@ msgstr "Senaste Tilldelning Datum"
|
|||
#. Option for the 'Timespan' (Select) field in DocType 'Dashboard Chart'
|
||||
#: desk/doctype/dashboard_chart/dashboard_chart.json
|
||||
msgid "Last Quarter"
|
||||
msgstr "Sista Kvartal"
|
||||
msgstr "Förra Kvartal"
|
||||
|
||||
#. Label of the last_reset_password_key_generated_on (Datetime) field in
|
||||
#. DocType 'User'
|
||||
|
|
@ -14284,6 +14315,8 @@ msgstr "Länk Titel"
|
|||
#. Label of the link_to (Dynamic Link) field in DocType 'Workspace Shortcut'
|
||||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:285
|
||||
#: public/js/frappe/widgets/widget_dialog.js:430
|
||||
msgid "Link To"
|
||||
msgstr "Länk Till"
|
||||
|
||||
|
|
@ -14293,6 +14326,7 @@ msgstr "Länk Till i Rad"
|
|||
|
||||
#. Label of the link_type (Select) field in DocType 'Workspace Link'
|
||||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:277
|
||||
msgid "Link Type"
|
||||
msgstr "Länk Typ"
|
||||
|
||||
|
|
@ -14336,7 +14370,7 @@ msgstr "Länkad Med"
|
|||
#. Label of the links (Table) field in DocType 'Customize Form'
|
||||
#. Label of the links (Table) field in DocType 'Workspace'
|
||||
#: contacts/doctype/address/address.js:39 contacts/doctype/address/address.json
|
||||
#: contacts/doctype/contact/contact.js:87 contacts/doctype/contact/contact.json
|
||||
#: contacts/doctype/contact/contact.js:92 contacts/doctype/contact/contact.json
|
||||
#: core/doctype/doctype/doctype.json
|
||||
#: custom/doctype/customize_form/customize_form.json
|
||||
#: desk/doctype/workspace/workspace.json public/js/frappe/form/toolbar.js:377
|
||||
|
|
@ -14389,6 +14423,10 @@ msgctxt "Button in list view menu"
|
|||
msgid "List Settings"
|
||||
msgstr "Lista Inställningar"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:203
|
||||
msgid "List View"
|
||||
msgstr "List Vy"
|
||||
|
||||
#. Name of a DocType
|
||||
#: desk/doctype/list_view_settings/list_view_settings.json
|
||||
msgid "List View Settings"
|
||||
|
|
@ -14425,7 +14463,7 @@ msgstr "Listor"
|
|||
msgid "Load Balancing"
|
||||
msgstr "Last Balansering"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:378
|
||||
#: public/js/frappe/list/base_list.js:386
|
||||
#: website/doctype/blog_post/templates/blog_post_list.html:50
|
||||
#: website/doctype/help_article/templates/help_article_list.html:30
|
||||
msgid "Load More"
|
||||
|
|
@ -14439,7 +14477,7 @@ msgstr "Ladda Mer Korenspondens"
|
|||
#: core/page/permission_manager/permission_manager.js:165
|
||||
#: public/js/frappe/form/controls/multicheck.js:13
|
||||
#: public/js/frappe/form/linked_with.js:13
|
||||
#: public/js/frappe/list/base_list.js:490
|
||||
#: public/js/frappe/list/base_list.js:498
|
||||
#: public/js/frappe/list/list_view.js:335 public/js/frappe/ui/listing.html:16
|
||||
#: public/js/frappe/views/reports/query_report.js:1017
|
||||
msgid "Loading"
|
||||
|
|
@ -14467,7 +14505,7 @@ msgstr "Laddar versioner..."
|
|||
#: public/js/frappe/views/kanban/kanban_board.html:11
|
||||
#: public/js/frappe/widgets/chart_widget.js:50
|
||||
#: public/js/frappe/widgets/number_card_widget.js:174
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:126
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:128
|
||||
msgid "Loading..."
|
||||
msgstr "Laddar..."
|
||||
|
||||
|
|
@ -14852,6 +14890,10 @@ msgstr "Mapp"
|
|||
msgid "Map Columns"
|
||||
msgstr "Mappa Kolumner"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:212
|
||||
msgid "Map View"
|
||||
msgstr "Mapp Vy"
|
||||
|
||||
#: public/js/frappe/data_import/import_preview.js:290
|
||||
msgid "Map columns from {0} to fields in {1}"
|
||||
msgstr "Mappa Kolumner från {0} till fält i {1}"
|
||||
|
|
@ -15081,7 +15123,7 @@ msgstr "Meny"
|
|||
msgid "Merge with existing"
|
||||
msgstr "Slå samman med befintlig"
|
||||
|
||||
#: utils/nestedset.py:304
|
||||
#: utils/nestedset.py:307
|
||||
msgid "Merging is only possible between Group-to-Group or Leaf Node-to-Leaf Node"
|
||||
msgstr "Sammanslafning är endast möjlig mellan grupp till grupp eller underordnad till underordnad"
|
||||
|
||||
|
|
@ -15237,6 +15279,10 @@ msgstr "Meta Titel för SEO"
|
|||
msgid "Method"
|
||||
msgstr "Sätt"
|
||||
|
||||
#: __init__.py:936
|
||||
msgid "Method Not Allowed"
|
||||
msgstr "Metod ej Tillåten"
|
||||
|
||||
#: desk/doctype/number_card/number_card.py:70
|
||||
msgid "Method is required to create a number card"
|
||||
msgstr "Sätt erfodras för att skapa nummerkort"
|
||||
|
|
@ -15628,7 +15674,7 @@ msgstr "Flytta till nästa steg när du klickar i det markerade området."
|
|||
msgid "Mozilla doesn't support :has() so you can pass parent selector here as workaround"
|
||||
msgstr "Mozilla stöder inte :has() så du kan skicka överordnad väljare här som lösning"
|
||||
|
||||
#: utils/nestedset.py:328
|
||||
#: utils/nestedset.py:331
|
||||
msgid "Multiple root nodes not allowed."
|
||||
msgstr "Flera rot noder är inte tillåtna."
|
||||
|
||||
|
|
@ -15866,7 +15912,7 @@ msgstr "Arbetsyta Ansvarig roll erfodras för att dölja/visa publika arbetsyto
|
|||
msgid "Negative Value"
|
||||
msgstr "Negativ Värde"
|
||||
|
||||
#: utils/nestedset.py:93
|
||||
#: utils/nestedset.py:94
|
||||
msgid "Nested set error. Please contact the Administrator."
|
||||
msgstr "Nested set fel. Kontakta Administratör."
|
||||
|
||||
|
|
@ -16074,7 +16120,7 @@ msgstr "Ny {0}: {1}"
|
|||
|
||||
#: utils/change_log.py:373
|
||||
msgid "New {} releases for the following apps are available"
|
||||
msgstr "Nya {} utgåvor för följande appar finns tillgängliga"
|
||||
msgstr "Nya {} versioner för följande appar finns tillgängliga"
|
||||
|
||||
#: core/doctype/user/user.py:753
|
||||
msgid "Newly created user {0} has no roles enabled."
|
||||
|
|
@ -16213,7 +16259,7 @@ msgstr "Nästa på Klick"
|
|||
#: public/js/form_builder/utils.js:341
|
||||
#: public/js/frappe/form/controls/link.js:475
|
||||
#: public/js/frappe/list/list_sidebar_group_by.js:223
|
||||
#: public/js/frappe/views/reports/query_report.js:1541
|
||||
#: public/js/frappe/views/reports/query_report.js:1526
|
||||
#: website/doctype/help_article/templates/help_article.html:26
|
||||
msgid "No"
|
||||
msgstr "Nej"
|
||||
|
|
@ -16257,7 +16303,7 @@ msgstr "Ingen Data"
|
|||
msgid "No Data to Show"
|
||||
msgstr "Ingen Data att visa"
|
||||
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:131
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:133
|
||||
msgid "No Data..."
|
||||
msgstr "Ingen Data..."
|
||||
|
||||
|
|
@ -16724,7 +16770,7 @@ msgstr "Inte Aktiv"
|
|||
msgid "Not allowed for {0}: {1}"
|
||||
msgstr "Ej tillåtet för {0}: {1}"
|
||||
|
||||
#: email/doctype/notification/notification.py:391
|
||||
#: email/doctype/notification/notification.py:392
|
||||
msgid "Not allowed to attach {0} document, please enable Allow Print For {0} in Print Settings"
|
||||
msgstr "Ej Tillåtet att bifoga {0} dokument, aktivera \"Tillåt Utskrift\" för {0} i Utskrift Inställningar"
|
||||
|
||||
|
|
@ -16843,7 +16889,7 @@ msgstr "Inget mer att göra om"
|
|||
msgid "Nothing left to undo"
|
||||
msgstr "Inget mer att ångra"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:362
|
||||
#: public/js/frappe/list/base_list.js:370
|
||||
#: public/js/frappe/views/reports/query_report.js:105
|
||||
#: templates/includes/list/list.html:7
|
||||
#: website/doctype/blog_post/templates/blog_post_list.html:41
|
||||
|
|
@ -17047,7 +17093,7 @@ msgstr "Antal dagar efter vilka dokument Webb Vy länk delad i e-post kommer att
|
|||
#. Label of the cache_keys (Int) field in DocType 'System Health Report'
|
||||
#: desk/doctype/system_health_report/system_health_report.json
|
||||
msgid "Number of keys"
|
||||
msgstr "Antal nycklar"
|
||||
msgstr "Antal Nycklar"
|
||||
|
||||
#. Label of the onsite_backups (Int) field in DocType 'System Health Report'
|
||||
#: desk/doctype/system_health_report/system_health_report.json
|
||||
|
|
@ -17249,9 +17295,14 @@ msgstr "{0}, {1} skrev"
|
|||
|
||||
#. Label of the onboard (Check) field in DocType 'Workspace Link'
|
||||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:330
|
||||
msgid "Onboard"
|
||||
msgstr "Introduktion"
|
||||
|
||||
#: public/js/frappe/widgets/widget_dialog.js:236
|
||||
msgid "Onboarding Name"
|
||||
msgstr "Introduktion Namn"
|
||||
|
||||
#. Name of a DocType
|
||||
#: desk/doctype/onboarding_permission/onboarding_permission.json
|
||||
msgid "Onboarding Permission"
|
||||
|
|
@ -17359,6 +17410,7 @@ msgstr "Endast utkast dokument kan förkastas"
|
|||
|
||||
#. Label of the only_for (Link) field in DocType 'Workspace Link'
|
||||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:323
|
||||
msgid "Only for"
|
||||
msgstr "Endast för"
|
||||
|
||||
|
|
@ -17658,7 +17710,7 @@ msgstr "Utgående (SMTP) Inställningar"
|
|||
#. Health Report'
|
||||
#: desk/doctype/system_health_report/system_health_report.json
|
||||
msgid "Outgoing Emails (Last 7 days)"
|
||||
msgstr "Utgående E-post (sista 7 dagar)"
|
||||
msgstr "Utgående E-post (Senaste 7 dagar)"
|
||||
|
||||
#. Label of the smtp_server (Data) field in DocType 'Email Account'
|
||||
#. Label of the smtp_server (Data) field in DocType 'Email Domain'
|
||||
|
|
@ -17708,7 +17760,7 @@ msgstr "PATCH"
|
|||
|
||||
#: printing/page/print/print.js:71
|
||||
#: public/js/frappe/form/templates/print_layout.html:44
|
||||
#: public/js/frappe/views/reports/query_report.js:1669
|
||||
#: public/js/frappe/views/reports/query_report.js:1654
|
||||
msgid "PDF"
|
||||
msgstr "PDF"
|
||||
|
||||
|
|
@ -18100,8 +18152,8 @@ msgid "Password not found for {0} {1} {2}"
|
|||
msgstr "Lösenord hittades inte för {0} {1} {2}"
|
||||
|
||||
#: core/doctype/user/user.py:1021
|
||||
msgid "Password reset instructions have been sent to your email"
|
||||
msgstr "Lösenord Återställning instruktioner har skickats till din E-post"
|
||||
msgid "Password reset instructions have been sent to {}'s email"
|
||||
msgstr "Instruktioner för återställning av lösenord är skickade till {}'s e-post"
|
||||
|
||||
#: www/update-password.html:164
|
||||
msgid "Password set"
|
||||
|
|
@ -18198,24 +18250,24 @@ msgstr "Pågående"
|
|||
#. Request'
|
||||
#: website/doctype/personal_data_deletion_request/personal_data_deletion_request.json
|
||||
msgid "Pending Approval"
|
||||
msgstr "Pågående Godkännande"
|
||||
msgstr "Godkännande Kö"
|
||||
|
||||
#. Label of the pending_emails (Int) field in DocType 'System Health Report'
|
||||
#: desk/doctype/system_health_report/system_health_report.json
|
||||
msgid "Pending Emails"
|
||||
msgstr "Pågående E-post"
|
||||
msgstr "E-post Kö"
|
||||
|
||||
#. Label of the pending_jobs (Int) field in DocType 'System Health Report
|
||||
#. Queue'
|
||||
#: desk/doctype/system_health_report_queue/system_health_report_queue.json
|
||||
msgid "Pending Jobs"
|
||||
msgstr "Pågående Jobb"
|
||||
msgstr "Jobb Kö"
|
||||
|
||||
#. Option for the 'Status' (Select) field in DocType 'Personal Data Deletion
|
||||
#. Request'
|
||||
#: website/doctype/personal_data_deletion_request/personal_data_deletion_request.json
|
||||
msgid "Pending Verification"
|
||||
msgstr "Pågående Verifiering"
|
||||
msgstr "Verifiering Kö"
|
||||
|
||||
#. Option for the 'Type' (Select) field in DocType 'DocField'
|
||||
#. Option for the 'Field Type' (Select) field in DocType 'Custom Field'
|
||||
|
|
@ -18500,7 +18552,7 @@ msgstr "Kontrollera OpenID Configuration URL"
|
|||
msgid "Please check the filter values set for Dashboard Chart: {}"
|
||||
msgstr "Kontrollera filter värden angivna för Översikt Panel Diagram: {}"
|
||||
|
||||
#: model/base_document.py:872
|
||||
#: model/base_document.py:873
|
||||
msgid "Please check the value of \"Fetch From\" set for field {0}"
|
||||
msgstr "Kontrollera värde för uppsättning 'Hämta från' för fält {0}"
|
||||
|
||||
|
|
@ -18802,11 +18854,11 @@ msgstr "Specificera"
|
|||
msgid "Please specify a valid parent DocType for {0}"
|
||||
msgstr "Ange giltig överordnad DocType för {0}"
|
||||
|
||||
#: email/doctype/notification/notification.py:87
|
||||
#: email/doctype/notification/notification.py:88
|
||||
msgid "Please specify which date field must be checked"
|
||||
msgstr "Ange Datum Fält som måste kontrolleras"
|
||||
|
||||
#: email/doctype/notification/notification.py:90
|
||||
#: email/doctype/notification/notification.py:91
|
||||
msgid "Please specify which value field must be checked"
|
||||
msgstr "Ange Värde Fält som måste kontrolleras"
|
||||
|
||||
|
|
@ -19139,7 +19191,7 @@ msgstr "Primär nyckel för doctype {0} kan inte ändras eftersom det finns befi
|
|||
#: public/js/frappe/form/templates/print_layout.html:46
|
||||
#: public/js/frappe/form/toolbar.js:332 public/js/frappe/form/toolbar.js:344
|
||||
#: public/js/frappe/list/bulk_operations.js:87
|
||||
#: public/js/frappe/views/reports/query_report.js:1655
|
||||
#: public/js/frappe/views/reports/query_report.js:1640
|
||||
#: public/js/frappe/views/reports/report_view.js:1460
|
||||
#: public/js/frappe/views/treeview.js:469 www/printview.html:18
|
||||
msgid "Print"
|
||||
|
|
@ -20001,7 +20053,7 @@ msgstr "Uppdatera"
|
|||
msgid "Rebuild Tree"
|
||||
msgstr "Uppdatera Träd Vy"
|
||||
|
||||
#: utils/nestedset.py:176
|
||||
#: utils/nestedset.py:177
|
||||
msgid "Rebuilding of tree is not supported for {}"
|
||||
msgstr "Uppdatering av Träd Vy stöds inte för {}"
|
||||
|
||||
|
|
@ -20359,7 +20411,7 @@ msgstr "Referens"
|
|||
#: public/js/frappe/desk.js:533 public/js/frappe/form/form.js:1196
|
||||
#: public/js/frappe/form/templates/print_layout.html:6
|
||||
#: public/js/frappe/list/base_list.js:66
|
||||
#: public/js/frappe/views/reports/query_report.js:1644
|
||||
#: public/js/frappe/views/reports/query_report.js:1629
|
||||
#: public/js/frappe/views/treeview.js:475
|
||||
#: public/js/frappe/widgets/chart_widget.js:290
|
||||
#: public/js/frappe/widgets/number_card_widget.js:324
|
||||
|
|
@ -20457,7 +20509,7 @@ msgstr "Ladda om"
|
|||
msgid "Reload File"
|
||||
msgstr "Ladda om Fil"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:242
|
||||
#: public/js/frappe/list/base_list.js:250
|
||||
msgid "Reload List"
|
||||
msgstr "Ladda om Lista"
|
||||
|
||||
|
|
@ -20726,7 +20778,7 @@ msgstr "Rapport Ansvarig"
|
|||
#: core/doctype/report/report.json
|
||||
#: desk/doctype/dashboard_chart/dashboard_chart.json
|
||||
#: desk/doctype/number_card/number_card.json
|
||||
#: public/js/frappe/views/reports/query_report.js:1825
|
||||
#: public/js/frappe/views/reports/query_report.js:1810
|
||||
msgid "Report Name"
|
||||
msgstr "Rapport Namn"
|
||||
|
||||
|
|
@ -20749,6 +20801,10 @@ msgstr "Rapportera Referens DocType"
|
|||
msgid "Report Type"
|
||||
msgstr "Rapport Typ"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:204
|
||||
msgid "Report View"
|
||||
msgstr "Rapport Vy"
|
||||
|
||||
#: core/doctype/doctype/doctype.py:1780
|
||||
msgid "Report cannot be set for Single types"
|
||||
msgstr "Rapport kan inte anges för Enskilda Typer"
|
||||
|
|
@ -20783,7 +20839,7 @@ msgstr "Rapport är uppdaterad"
|
|||
msgid "Report was not saved (there were errors)"
|
||||
msgstr "Rapport är inte sparad (det fanns fel)"
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1863
|
||||
#: public/js/frappe/views/reports/query_report.js:1848
|
||||
msgid "Report with more than 10 columns looks better in Landscape mode."
|
||||
msgstr "Rapport med mer än 10 kolumner ser bättre ut i Liggande Läge."
|
||||
|
||||
|
|
@ -21369,7 +21425,7 @@ msgstr "Roller HTML"
|
|||
msgid "Roles can be set for users from their User page."
|
||||
msgstr "Roller kan anges för användare från deras Användarsida."
|
||||
|
||||
#: utils/nestedset.py:277
|
||||
#: utils/nestedset.py:280
|
||||
msgid "Root {0} cannot be deleted"
|
||||
msgstr "Root {0} kan inte raderas"
|
||||
|
||||
|
|
@ -21442,7 +21498,7 @@ msgstr "Rad #"
|
|||
msgid "Row # {0}: Non administrator user can not set the role {1} to the custom doctype"
|
||||
msgstr "Rad # {0}: Användare som inte är administratör kan inte ange roll {1} till anpassad Dokument Typ"
|
||||
|
||||
#: model/base_document.py:903
|
||||
#: model/base_document.py:904
|
||||
msgid "Row #{0}:"
|
||||
msgstr "Rad # {0}:"
|
||||
|
||||
|
|
@ -21726,11 +21782,11 @@ msgstr "Lördag"
|
|||
#: public/js/frappe/views/kanban/kanban_settings.js:45
|
||||
#: public/js/frappe/views/kanban/kanban_settings.js:189
|
||||
#: public/js/frappe/views/kanban/kanban_view.js:343
|
||||
#: public/js/frappe/views/reports/query_report.js:1817
|
||||
#: public/js/frappe/views/reports/query_report.js:1802
|
||||
#: public/js/frappe/views/reports/report_view.js:1640
|
||||
#: public/js/frappe/views/workspace/workspace.js:501
|
||||
#: public/js/frappe/widgets/base_widget.js:142
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:117
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:119
|
||||
#: public/js/print_format_builder/print_format_builder.bundle.js:15
|
||||
#: public/js/workflow_builder/workflow_builder.bundle.js:33
|
||||
msgid "Save"
|
||||
|
|
@ -21757,7 +21813,7 @@ msgstr "Spara Anpassningar"
|
|||
msgid "Save Filter"
|
||||
msgstr "Spara Filter "
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1820
|
||||
#: public/js/frappe/views/reports/query_report.js:1805
|
||||
msgid "Save Report"
|
||||
msgstr "Spara Rapport"
|
||||
|
||||
|
|
@ -22881,7 +22937,7 @@ msgid "Set Filters"
|
|||
msgstr "Ange Filter"
|
||||
|
||||
#: public/js/frappe/widgets/chart_widget.js:395
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:102
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:104
|
||||
msgid "Set Filters for {0}"
|
||||
msgstr "Ange Filter för {0}"
|
||||
|
||||
|
|
@ -23118,7 +23174,7 @@ msgstr "Inställningar > Användar Behörigheter"
|
|||
msgid "Setup Approval Workflows"
|
||||
msgstr "Ange Arbetsflöde för Godkännande "
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:1690
|
||||
#: public/js/frappe/views/reports/query_report.js:1675
|
||||
#: public/js/frappe/views/reports/report_view.js:1618
|
||||
msgid "Setup Auto Email"
|
||||
msgstr "Automatisk E-post Rapport"
|
||||
|
|
@ -25154,7 +25210,7 @@ msgid "The Client ID obtained from the Google Cloud Console under <a href=\"http
|
|||
"</a>"
|
||||
msgstr "Klient ID som erhållits från Google Cloud Console under <a href=\"https://console.cloud.google.com/apis/credentials\">\"API och tjänster\" > \"Inloggningsuppgifter\"</a>"
|
||||
|
||||
#: email/doctype/notification/notification.py:130
|
||||
#: email/doctype/notification/notification.py:131
|
||||
msgid "The Condition '{0}' is invalid"
|
||||
msgstr "Villkor '{0}' är ogiltig"
|
||||
|
||||
|
|
@ -25613,7 +25669,7 @@ msgstr "Detta format används om land specifika format inte hittas"
|
|||
msgid "This goes above the slideshow."
|
||||
msgstr "Text ovanför Bildspel."
|
||||
|
||||
#: public/js/frappe/views/reports/query_report.js:2027
|
||||
#: public/js/frappe/views/reports/query_report.js:2012
|
||||
msgid "This is a background report. Please set the appropriate filters and then generate a new one."
|
||||
msgstr "Detta är bakgrund rapport. Ange lämplig filter och skapa ny rapport."
|
||||
|
||||
|
|
@ -26284,16 +26340,16 @@ msgstr "Totalt"
|
|||
#. Report'
|
||||
#: desk/doctype/system_health_report/system_health_report.json
|
||||
msgid "Total Background Workers"
|
||||
msgstr "Totalt Bakgrund Tjänster"
|
||||
msgstr "Totalt Antal Bakgrund Tjänster"
|
||||
|
||||
#. Label of the total_errors (Int) field in DocType 'System Health Report'
|
||||
#: desk/doctype/system_health_report/system_health_report.json
|
||||
msgid "Total Errors (last 1 day)"
|
||||
msgstr "Totalt Fel (sista dagen)"
|
||||
msgstr "Totalt Antal Fel (Senaste dag)"
|
||||
|
||||
#: public/js/frappe/ui/capture.js:259
|
||||
msgid "Total Images"
|
||||
msgstr "Totalt Bilder"
|
||||
msgstr "Totalt Antal Bilder"
|
||||
|
||||
#. Label of the total_outgoing_emails (Int) field in DocType 'System Health
|
||||
#. Report'
|
||||
|
|
@ -26322,18 +26378,18 @@ msgstr "Totalt Användare"
|
|||
#. Label of the total_views (Int) field in DocType 'Newsletter'
|
||||
#: email/doctype/newsletter/newsletter.json
|
||||
msgid "Total Views"
|
||||
msgstr "Totalt Visningar"
|
||||
msgstr "Totalt Antal Visningar"
|
||||
|
||||
#. Label of the total_working_time (Duration) field in DocType 'RQ Worker'
|
||||
#: core/doctype/rq_worker/rq_worker.json
|
||||
msgid "Total Working Time"
|
||||
msgstr "Totalt Antal Arbetstimmar"
|
||||
msgstr "Totalt Antal Arbetade Timmar"
|
||||
|
||||
#. Description of the 'Initial Sync Count' (Select) field in DocType 'Email
|
||||
#. Account'
|
||||
#: email/doctype/email_account/email_account.json
|
||||
msgid "Total number of emails to sync in initial sync process "
|
||||
msgstr "Totalt antal E-post meddelande som ska synkroniseras i första synkronisering behandling"
|
||||
msgstr "Totalt antal E-post meddelande som ska synkroniseras i första synkronisering "
|
||||
|
||||
#: public/js/frappe/views/reports/report_view.js:1178
|
||||
msgid "Totals"
|
||||
|
|
@ -26341,7 +26397,7 @@ msgstr "Totals"
|
|||
|
||||
#: public/js/frappe/views/reports/report_view.js:1153
|
||||
msgid "Totals Row"
|
||||
msgstr "Totalt Rad"
|
||||
msgstr "Totalt Antal Rader"
|
||||
|
||||
#. Label of the trace_id (Data) field in DocType 'Error Log'
|
||||
#: core/doctype/error_log/error_log.json
|
||||
|
|
@ -26478,6 +26534,10 @@ msgstr "Skräp"
|
|||
msgid "Tree"
|
||||
msgstr "Träd Vy"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:211
|
||||
msgid "Tree View"
|
||||
msgstr "Träd Vy"
|
||||
|
||||
#. Description of the 'Is Tree' (Check) field in DocType 'DocType'
|
||||
#: core/doctype/doctype/doctype.json
|
||||
msgid "Tree structures are implemented using Nested Set"
|
||||
|
|
@ -26583,6 +26643,7 @@ msgstr "Två Faktor Autentisering Sätt"
|
|||
#: desk/doctype/workspace_link/workspace_link.json
|
||||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: public/js/frappe/views/file/file_view.js:337
|
||||
#: public/js/frappe/widgets/widget_dialog.js:399
|
||||
#: social/doctype/energy_point_log/energy_point_log.json
|
||||
#: website/doctype/web_template/web_template.json www/attribution.html:35
|
||||
msgid "Type"
|
||||
|
|
@ -26665,6 +26726,7 @@ msgstr "URI för att ta emot behörighet kod när Användare tillåter tillgång
|
|||
#: desk/doctype/workspace_shortcut/workspace_shortcut.json
|
||||
#: integrations/doctype/integration_request/integration_request.json
|
||||
#: integrations/doctype/webhook_request_log/webhook_request_log.json
|
||||
#: public/js/frappe/widgets/widget_dialog.js:469
|
||||
#: website/doctype/top_bar_item/top_bar_item.json
|
||||
#: website/doctype/website_slideshow_item/website_slideshow_item.json
|
||||
msgid "URL"
|
||||
|
|
@ -27341,7 +27403,7 @@ msgid "User Permission"
|
|||
msgstr "Användare Behörighet"
|
||||
|
||||
#: core/page/permission_manager/permission_manager_help.html:30
|
||||
#: public/js/frappe/views/reports/query_report.js:1804
|
||||
#: public/js/frappe/views/reports/query_report.js:1789
|
||||
#: public/js/frappe/views/reports/report_view.js:1666
|
||||
msgid "User Permissions"
|
||||
msgstr "Användare Behörigheter"
|
||||
|
|
@ -27659,7 +27721,7 @@ msgstr "Värde Ändrad"
|
|||
msgid "Value To Be Set"
|
||||
msgstr "Värde som ska Anges"
|
||||
|
||||
#: model/base_document.py:965 model/document.py:682
|
||||
#: model/base_document.py:966 model/document.py:682
|
||||
msgid "Value cannot be changed for {0}"
|
||||
msgstr "Värde kan inte ändras för {0}"
|
||||
|
||||
|
|
@ -27702,7 +27764,7 @@ msgstr "Värde måste vara ett av {0}"
|
|||
msgid "Value to Validate"
|
||||
msgstr "Värde att Validera"
|
||||
|
||||
#: model/base_document.py:1035
|
||||
#: model/base_document.py:1036
|
||||
msgid "Value too big"
|
||||
msgstr "Värde för hög"
|
||||
|
||||
|
|
@ -27801,7 +27863,7 @@ msgid "View Full Log"
|
|||
msgstr "Visa Full Logg"
|
||||
|
||||
#: public/js/frappe/views/treeview.js:463
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:245
|
||||
#: public/js/frappe/widgets/quick_list_widget.js:247
|
||||
msgid "View List"
|
||||
msgstr "Visa Lista"
|
||||
|
||||
|
|
@ -28569,7 +28631,7 @@ msgstr "Arbetsflöde Tillstånd inte angiven"
|
|||
|
||||
#: model/workflow.py:197 model/workflow.py:205
|
||||
msgid "Workflow State transition not allowed from {0} to {1}"
|
||||
msgstr "Arbetsflöde Övergång inte tillåten från {0} till {1}"
|
||||
msgstr "Arbetsflöde Tillstånd Övergång inte tillåten från {0} till {1}"
|
||||
|
||||
#: model/workflow.py:320
|
||||
msgid "Workflow Status"
|
||||
|
|
@ -28683,7 +28745,7 @@ msgstr "Slutför"
|
|||
msgid "Write"
|
||||
msgstr "Skriva"
|
||||
|
||||
#: model/base_document.py:875
|
||||
#: model/base_document.py:876
|
||||
msgid "Wrong Fetch From value"
|
||||
msgstr "Fel Hämtning Från Värde"
|
||||
|
||||
|
|
@ -28770,7 +28832,7 @@ msgstr "Gul"
|
|||
#: public/js/form_builder/utils.js:336
|
||||
#: public/js/frappe/form/controls/link.js:475
|
||||
#: public/js/frappe/list/list_sidebar_group_by.js:223
|
||||
#: public/js/frappe/views/reports/query_report.js:1541
|
||||
#: public/js/frappe/views/reports/query_report.js:1526
|
||||
#: website/doctype/help_article/templates/help_article.html:25
|
||||
msgid "Yes"
|
||||
msgstr "Ja"
|
||||
|
|
@ -29983,7 +30045,7 @@ msgstr "via Data Import"
|
|||
msgid "via Google Meet"
|
||||
msgstr "via Google Meet"
|
||||
|
||||
#: email/doctype/notification/notification.py:215
|
||||
#: email/doctype/notification/notification.py:216
|
||||
msgid "via Notification"
|
||||
msgstr "via Avisering"
|
||||
|
||||
|
|
@ -30126,7 +30188,7 @@ msgstr "{0} Moduler"
|
|||
msgid "{0} Name"
|
||||
msgstr "{0} Namn"
|
||||
|
||||
#: model/base_document.py:1065
|
||||
#: model/base_document.py:1066
|
||||
msgid "{0} Not allowed to change {1} after submission from {2} to {3}"
|
||||
msgstr "{0} Ej Tillåtet att ändra {1} efter godkännande från {2} till {3}"
|
||||
|
||||
|
|
@ -30152,10 +30214,6 @@ msgstr "{0} Inställningar"
|
|||
msgid "{0} Tree"
|
||||
msgstr "{0} Träd Vy"
|
||||
|
||||
#: public/js/frappe/list/base_list.js:209
|
||||
msgid "{0} View"
|
||||
msgstr "{0} Vy"
|
||||
|
||||
#: public/js/frappe/form/footer/form_timeline.js:126
|
||||
#: public/js/frappe/form/sidebar/form_sidebar.js:86
|
||||
msgid "{0} Web page views"
|
||||
|
|
@ -30595,7 +30653,7 @@ msgstr "{0} måste börja och sluta med bokstav och får bara innehålla bokstä
|
|||
|
||||
#: workflow/doctype/workflow/workflow.py:91
|
||||
msgid "{0} not a valid State"
|
||||
msgstr "{0} inte en giltig Tillstånd"
|
||||
msgstr "{0} är inte giltig Tillstånd"
|
||||
|
||||
#: model/rename_doc.py:380
|
||||
msgid "{0} not allowed to be renamed"
|
||||
|
|
@ -30770,11 +30828,11 @@ msgstr "{0} {1} är lagd till i Översikt Panel {2}"
|
|||
msgid "{0} {1} already exists"
|
||||
msgstr "{0} {1} finns redan"
|
||||
|
||||
#: model/base_document.py:908
|
||||
#: model/base_document.py:909
|
||||
msgid "{0} {1} cannot be \"{2}\". It should be one of \"{3}\""
|
||||
msgstr "{0} {1} kan inte vara \"{2}\". Det kan vara en av följande: \"{3}\""
|
||||
|
||||
#: utils/nestedset.py:337
|
||||
#: utils/nestedset.py:340
|
||||
msgid "{0} {1} cannot be a leaf node as it has children"
|
||||
msgstr "{0} {1} kan inte vara undernod då den har undernoder"
|
||||
|
||||
|
|
@ -30794,11 +30852,11 @@ msgstr "{0} {1} hittades inte"
|
|||
msgid "{0} {1}: Submitted Record cannot be deleted. You must {2} Cancel {3} it first."
|
||||
msgstr "{0} {1}: Godkänd Post kan inte raderas. Du måste {2} Annullera {3} det först."
|
||||
|
||||
#: model/base_document.py:1026
|
||||
#: model/base_document.py:1027
|
||||
msgid "{0}, Row {1}"
|
||||
msgstr "{0}, Rad {1}"
|
||||
|
||||
#: model/base_document.py:1031
|
||||
#: model/base_document.py:1032
|
||||
msgid "{0}: '{1}' ({3}) will get truncated, as max characters allowed is {2}"
|
||||
msgstr "{0}: {1} ({3}) kommer att förminskas då maximum tillåtna antal tecken är {2}"
|
||||
|
||||
|
|
@ -30891,7 +30949,7 @@ msgid "{0}: fieldname cannot be set to reserved keyword {1}"
|
|||
msgstr "{0}: fältnamn kan inte anges för reserverad sökord {1}"
|
||||
|
||||
#: contacts/doctype/address/address.js:35
|
||||
#: contacts/doctype/contact/contact.js:83
|
||||
#: contacts/doctype/contact/contact.js:88
|
||||
#: public/js/frappe/views/workspace/workspace.js:170
|
||||
msgid "{0}: {1}"
|
||||
msgstr "{0}: {1}"
|
||||
|
|
|
|||
|
|
@ -127,8 +127,17 @@ def delete_doc(
|
|||
|
||||
# check if links exist
|
||||
if not force:
|
||||
check_if_doc_is_linked(doc)
|
||||
check_if_doc_is_dynamically_linked(doc)
|
||||
try:
|
||||
check_if_doc_is_linked(doc)
|
||||
check_if_doc_is_dynamically_linked(doc)
|
||||
except frappe.LinkExistsError as e:
|
||||
if doc.meta.has_field("enabled") or doc.meta.has_field("disabled"):
|
||||
frappe.throw(
|
||||
_("You can disable this {0} instead of deleting it.").format(_(doctype)),
|
||||
frappe.LinkExistsError,
|
||||
)
|
||||
else:
|
||||
raise e
|
||||
|
||||
update_naming_series(doc)
|
||||
delete_from_table(doctype, name, ignore_doctypes, doc)
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ class Document(BaseDocument):
|
|||
def raise_no_permission_to(self, perm_type):
|
||||
"""Raise `frappe.PermissionError`."""
|
||||
frappe.flags.error_message = (
|
||||
_("Insufficient Permission for {0}").format(self.doctype) + f" ({frappe.bold(_(perm_type))})"
|
||||
_("Insufficient Permission for {0}").format(_(self.doctype)) + f" ({frappe.bold(_(perm_type))})"
|
||||
)
|
||||
raise frappe.PermissionError
|
||||
|
||||
|
|
@ -458,7 +458,7 @@ class Document(BaseDocument):
|
|||
d: Document
|
||||
d.db_update()
|
||||
|
||||
def get_doc_before_save(self) -> "Document":
|
||||
def get_doc_before_save(self) -> "Self":
|
||||
return getattr(self, "_doc_before_save", None)
|
||||
|
||||
def has_value_changed(self, fieldname):
|
||||
|
|
@ -1031,7 +1031,7 @@ class Document(BaseDocument):
|
|||
"on_cancel": "Cancel",
|
||||
}
|
||||
|
||||
if not self.flags.in_insert:
|
||||
if not self.flags.in_insert and not self.flags.in_delete:
|
||||
# value change is not applicable in insert
|
||||
event_map["on_change"] = "Value Change"
|
||||
|
||||
|
|
|
|||
|
|
@ -419,7 +419,7 @@
|
|||
</symbol>
|
||||
|
||||
<symbol fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" id="icon-map">
|
||||
<g stroke="#111" stroke-miterlimit="10">
|
||||
<g stroke="var(--icon-stroke)" stroke-miterlimit="10">
|
||||
<path d="M11.467 3.458c1.958 1.957 1.958 5.088.027 7.02L7.97 14l-3.523-3.523a4.945 4.945 0 010-6.993l.026-.026a4.922 4.922 0 016.993 0zm0 0c-.026-.026-.026-.026 0 0z"></path>
|
||||
<path d="M7.971 8.259a1.305 1.305 0 100-2.61 1.305 1.305 0 000 2.61z"></path>
|
||||
</g>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
|
|
@ -63,7 +63,7 @@ let field_df = computedAsync(async () => {
|
|||
watch(
|
||||
() => props.value,
|
||||
(value) => {
|
||||
[doctype.value, fieldname.value] = value?.split(".") || ["", ""];
|
||||
if (value) [doctype.value, fieldname.value] = value.split(".") || ["", ""];
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ frappe.data_import.DataExporter = class DataExporter {
|
|||
columns: 2,
|
||||
on_change: () => this.update_primary_action(),
|
||||
options: this.get_multicheck_options(this.doctype),
|
||||
sort_options: false,
|
||||
},
|
||||
...frappe.meta.get_table_fields(this.doctype).map((df) => {
|
||||
let doctype = df.options;
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ frappe.ui.form.ControlData = class ControlData extends frappe.ui.form.ControlInp
|
|||
make_input() {
|
||||
if (this.$input) return;
|
||||
|
||||
let { html_element, input_type } = this.constructor;
|
||||
let { html_element, input_type, input_mode } = this.constructor;
|
||||
|
||||
this.$input = $("<" + html_element + ">")
|
||||
.attr("type", input_type)
|
||||
.attr("inputmode", input_mode)
|
||||
.attr("autocomplete", "off")
|
||||
.addClass("input-with-feedback form-control")
|
||||
.prependTo(this.input_area);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,13 @@ frappe.ui.form.ControlDuration = class ControlDuration extends frappe.ui.form.Co
|
|||
this.make_picker();
|
||||
}
|
||||
|
||||
validate(value) {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
return super.validate(value);
|
||||
}
|
||||
|
||||
make_picker() {
|
||||
this.inputs = [];
|
||||
this.set_duration_options();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
frappe.ui.form.ControlInt = class ControlInt extends frappe.ui.form.ControlData {
|
||||
static trigger_change_on_input_event = false;
|
||||
static input_mode = "numeric";
|
||||
make() {
|
||||
super.make();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
|
|||
doctype: doctype,
|
||||
ignore_user_permissions: me.df.ignore_user_permissions,
|
||||
reference_doctype: me.get_reference_doctype() || "",
|
||||
page_length: cint(frappe.boot.sysdefaults.link_field_results_limit) || 10,
|
||||
page_length: cint(frappe.boot.sysdefaults?.link_field_results_limit) || 10,
|
||||
};
|
||||
|
||||
me.set_custom_query(args);
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ frappe.ui.form.ControlMultiSelectList = class ControlMultiSelectList extends (
|
|||
//Unselect old values
|
||||
this.values.forEach((value) => {
|
||||
this.$list_wrapper
|
||||
.find(`.selectable-item[data-value=${value}]`)
|
||||
.find(`.selectable-item[data-value=${CSS.escape(value)}]`)
|
||||
.toggleClass("selected");
|
||||
});
|
||||
this.values = value;
|
||||
|
|
@ -147,7 +147,7 @@ frappe.ui.form.ControlMultiSelectList = class ControlMultiSelectList extends (
|
|||
this.update_selected_values(value);
|
||||
//Select new values
|
||||
this.$list_wrapper
|
||||
.find(`.selectable-item[data-value=${value}]`)
|
||||
.find(`.selectable-item[data-value=${CSS.escape(value)}]`)
|
||||
.toggleClass("selected");
|
||||
});
|
||||
this.parse_validate_and_set_in_model("");
|
||||
|
|
|
|||
|
|
@ -633,8 +633,8 @@ frappe.ui.form.Dashboard = class FormDashboard {
|
|||
}
|
||||
|
||||
// TODO: Review! code related to headline should be the part of layout/form
|
||||
set_headline(html, color) {
|
||||
this.frm.layout.show_message(html, color);
|
||||
set_headline(html, color, permanent = false) {
|
||||
this.frm.layout.show_message(html, color, permanent);
|
||||
}
|
||||
|
||||
clear_headline() {
|
||||
|
|
@ -642,7 +642,7 @@ frappe.ui.form.Dashboard = class FormDashboard {
|
|||
}
|
||||
|
||||
add_comment(text, alert_class, permanent) {
|
||||
this.set_headline_alert(text, alert_class);
|
||||
this.set_headline_alert(text, alert_class, permanent);
|
||||
if (!permanent) {
|
||||
setTimeout(() => {
|
||||
this.clear_headline();
|
||||
|
|
@ -654,9 +654,9 @@ frappe.ui.form.Dashboard = class FormDashboard {
|
|||
this.clear_headline();
|
||||
}
|
||||
|
||||
set_headline_alert(text, color) {
|
||||
set_headline_alert(text, color, permanent = false) {
|
||||
if (text) {
|
||||
this.set_headline(`<div>${text}</div>`, color);
|
||||
this.set_headline(`<div>${text}</div>`, color, permanent);
|
||||
} else {
|
||||
this.clear_headline();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -550,7 +550,6 @@ frappe.ui.form.Form = class FrappeForm {
|
|||
}
|
||||
|
||||
trigger_onload(switched) {
|
||||
this.cscript.is_onload = false;
|
||||
if (!this.opendocs[this.docname]) {
|
||||
var me = this;
|
||||
this.cscript.is_onload = true;
|
||||
|
|
@ -628,6 +627,7 @@ frappe.ui.form.Form = class FrappeForm {
|
|||
() => this.cscript.is_onload && this.is_new() && this.focus_on_first_input(),
|
||||
() => this.run_after_load_hook(),
|
||||
() => this.dashboard.after_refresh(),
|
||||
() => (this.cscript.is_onload = false),
|
||||
]);
|
||||
} else {
|
||||
this.refresh_header(switched);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ frappe.ui.form.FormTour = class FormTour {
|
|||
padding: 10,
|
||||
overlayClickNext: true,
|
||||
keyboardControl: true,
|
||||
nextBtnText: "Next",
|
||||
prevBtnText: "Previous",
|
||||
nextBtnText: __("Next"),
|
||||
prevBtnText: __("Previous"),
|
||||
doneBtnText: __("Done"),
|
||||
closeBtnText: __("Close"),
|
||||
opacity: 0.25,
|
||||
onHighlighted: (step) => {
|
||||
// if last step is to save, then attach a listener to save button
|
||||
|
|
@ -135,7 +137,11 @@ frappe.ui.form.FormTour = class FormTour {
|
|||
return {
|
||||
element,
|
||||
name,
|
||||
popover: { title, description, position: frappe.router.slug(position || "Bottom") },
|
||||
popover: {
|
||||
title: __(title),
|
||||
description: __(description),
|
||||
position: frappe.router.slug(position || "Bottom"),
|
||||
},
|
||||
onNext: on_next,
|
||||
onPrevious: on_prev,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ frappe.ui.form.Layout = class Layout {
|
|||
return fields;
|
||||
}
|
||||
|
||||
show_message(html, color) {
|
||||
show_message(html, color, permanent = false) {
|
||||
if (this.message_color) {
|
||||
// remove previous color
|
||||
this.message.removeClass(this.message_color);
|
||||
|
|
@ -112,8 +112,10 @@ frappe.ui.form.Layout = class Layout {
|
|||
}
|
||||
this.message.removeClass("hidden").addClass(this.message_color);
|
||||
$(html).appendTo(this.message);
|
||||
close_message.appendTo(this.message);
|
||||
close_message.on("click", () => this.message.empty().addClass("hidden"));
|
||||
if (!permanent) {
|
||||
close_message.appendTo(this.message);
|
||||
close_message.on("click", () => this.message.empty().addClass("hidden"));
|
||||
}
|
||||
} else {
|
||||
this.message.empty().addClass("hidden");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,12 +260,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
set_primary_action() {
|
||||
if (this.can_create && !frappe.boot.read_only) {
|
||||
const doctype_name = __(frappe.router.doctype_layout) || __(this.doctype);
|
||||
|
||||
// Better style would be __("Add {0}", [doctype_name], "Primary action in list view")
|
||||
// Keeping it like this to not disrupt existing translations
|
||||
const label = `${__("Add", null, "Primary action in list view")} ${doctype_name}`;
|
||||
this.page.set_primary_action(
|
||||
label,
|
||||
__("Add {0}", [doctype_name], "Primary action in list view"),
|
||||
() => {
|
||||
if (this.settings.primary_action) {
|
||||
this.settings.primary_action();
|
||||
|
|
@ -315,6 +311,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
this.update_checkbox();
|
||||
this.update_url_with_filters();
|
||||
this.setup_realtime_updates();
|
||||
this.apply_styles_basedon_dropdown();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -547,7 +544,9 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
|
||||
toggle_result_area() {
|
||||
super.toggle_result_area();
|
||||
this.toggle_actions_menu_button(this.$result.find(".list-row-check:checked").length > 0);
|
||||
this.toggle_actions_menu_button(
|
||||
this.$result.find(".list-row-checkbox:checked").length > 0
|
||||
);
|
||||
}
|
||||
|
||||
toggle_actions_menu_button(toggle) {
|
||||
|
|
@ -787,9 +786,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
}
|
||||
|
||||
const format = () => {
|
||||
if (df.fieldtype === "Code") {
|
||||
return value;
|
||||
} else if (df.fieldtype === "Percent") {
|
||||
if (df.fieldtype === "Percent") {
|
||||
return `<div class="progress" style="margin: 0px;">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar"
|
||||
aria-valuenow="${value}"
|
||||
|
|
@ -841,11 +838,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
data-filter="${fieldname},=,${value}">
|
||||
${_value}
|
||||
</a>`;
|
||||
} else if (
|
||||
["Text Editor", "Text", "Small Text", "HTML Editor", "Markdown Editor"].includes(
|
||||
df.fieldtype
|
||||
)
|
||||
) {
|
||||
} else if (frappe.model.html_fieldtypes.includes(df.fieldtype)) {
|
||||
html = `<span class="ellipsis">
|
||||
${_value}
|
||||
</span>`;
|
||||
|
|
@ -915,8 +908,10 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
|
||||
get_meta_html(doc) {
|
||||
let html = "";
|
||||
let settings_button = "";
|
||||
let button_section = "";
|
||||
const dropdown_button = this.generate_dropdown_html(doc);
|
||||
|
||||
let settings_button = null;
|
||||
if (this.settings.button && this.settings.button.show(doc)) {
|
||||
settings_button = `
|
||||
<span class="list-actions">
|
||||
|
|
@ -929,6 +924,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
`;
|
||||
}
|
||||
|
||||
button_section = settings_button + dropdown_button;
|
||||
const modified = comment_when(doc.modified, true);
|
||||
|
||||
let assigned_to = ``;
|
||||
|
|
@ -950,13 +946,13 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
|
||||
html += `
|
||||
<div class="level-item list-row-activity hidden-xs">
|
||||
<div class="hidden-md hidden-xs">
|
||||
${settings_button || assigned_to}
|
||||
<div class="hidden-md hidden-xs d-flex">
|
||||
${button_section || assigned_to}
|
||||
</div>
|
||||
<span class="modified">${modified}</span>
|
||||
${comment_count || ""}
|
||||
${comment_count ? '<span class="mx-2">·</span>' : ""}
|
||||
<span class="list-row-like hidden-xs style="margin-bottom: 1px;">
|
||||
<span class="list-row-like hidden-xs" style="margin-bottom: 1px;">
|
||||
${this.get_like_html(doc)}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -968,6 +964,47 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
return html;
|
||||
}
|
||||
|
||||
generate_dropdown_html(doc) {
|
||||
let dropdown_button = "";
|
||||
if (this.settings.dropdown_button) {
|
||||
let button_actions = "";
|
||||
this.settings.dropdown_button.buttons.forEach((button, index) => {
|
||||
if (!button.show || button.show(doc)) {
|
||||
let description = button.get_description ? button.get_description(doc) : "";
|
||||
button_actions += `
|
||||
<a class="dropdown-item" href="#" onclick="return false;" data-idx="${doc._idx}" button-idx="${index}" title="${description}">
|
||||
${button.get_label}
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
});
|
||||
|
||||
if (button_actions) {
|
||||
dropdown_button = `
|
||||
<div class="inner-group-button mr-2" data-name="${doc.name}" data-label="${
|
||||
this.settings.dropdown_button.get_label
|
||||
}">
|
||||
<button type="button" class="btn btn-xs btn-default ellipsis" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
${this.settings.dropdown_button.get_label}
|
||||
${frappe.utils.icon("select", "xs")}
|
||||
</button>
|
||||
<div role="menu" class="dropdown-menu">${button_actions}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
return dropdown_button;
|
||||
}
|
||||
|
||||
apply_styles_basedon_dropdown() {
|
||||
if ($(".list-actions").length > 0 && $(".inner-group-button").length > 0) {
|
||||
$(".list-row .level-left, .list-row-head .level-left").css({
|
||||
flex: "2",
|
||||
"min-width": "72%",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get_count_str() {
|
||||
let current_count = this.data.length;
|
||||
let count_without_children = this.data.uniqBy((d) => d.name).length;
|
||||
|
|
@ -1277,6 +1314,9 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
this.on_row_checked();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($target.is("[data-toggle='dropdown']")) return true;
|
||||
|
||||
// don't open form when checkbox, like, filterable are clicked
|
||||
if (
|
||||
$target.hasClass("filterable") ||
|
||||
|
|
@ -1342,6 +1382,20 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
e.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
|
||||
this.$result.on("click", ".inner-group-button .dropdown-item", (e) => {
|
||||
const $button = $(e.currentTarget);
|
||||
const doc = this.data[$button.attr("data-idx")];
|
||||
const btn_idx = parseInt($button.attr("button-idx"), 10);
|
||||
const button = this.settings.dropdown_button.buttons[btn_idx];
|
||||
|
||||
if (button && button.action) {
|
||||
button.action(doc);
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
setup_check_events() {
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ frappe.get_indicator = function (doc, doctype, show_workflow_state) {
|
|||
|
||||
// based on status
|
||||
if (doc.status) {
|
||||
return [__(doc.status), frappe.utils.guess_colour(doc.status)];
|
||||
return [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status];
|
||||
}
|
||||
|
||||
// based on enabled
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ frappe.ui.RealtimeChart = class RealtimeChart extends frappe.Chart {
|
|||
} else {
|
||||
this.currentSize++;
|
||||
}
|
||||
this.addDataPoint(label, data);
|
||||
this.addDataPoint(__(label), data);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ frappe.msgprint = function (msg, title, is_minimizable) {
|
|||
}
|
||||
|
||||
frappe.msg_dialog.set_primary_action(
|
||||
__(data.primary_action.label || data.primary_action_label || "Done"),
|
||||
__(data.primary_action.label) || __(data.primary_action_label) || __("Done"),
|
||||
data.primary_action.action
|
||||
);
|
||||
} else {
|
||||
|
|
@ -240,7 +240,9 @@ frappe.msgprint = function (msg, title, is_minimizable) {
|
|||
|
||||
if (data.secondary_action) {
|
||||
frappe.msg_dialog.set_secondary_action(data.secondary_action.action);
|
||||
frappe.msg_dialog.set_secondary_action_label(__(data.secondary_action.label || "Close"));
|
||||
frappe.msg_dialog.set_secondary_action_label(
|
||||
__(data.secondary_action.label) || __("Close")
|
||||
);
|
||||
}
|
||||
|
||||
if (data.message == null) {
|
||||
|
|
|
|||
|
|
@ -160,9 +160,9 @@ frappe.dashboard_utils = {
|
|||
fieldtype: "HTML",
|
||||
fieldname: "description",
|
||||
options: `<div>
|
||||
<p>Set dynamic filter values in JavaScript for the required fields here.
|
||||
<p>${__("Set dynamic filter values in JavaScript for the required fields here.")}
|
||||
</p>
|
||||
<p>Ex:
|
||||
<p>${__("For example:")}
|
||||
<code>frappe.defaults.get_user_default("Company")</code>
|
||||
</p>
|
||||
</div>`,
|
||||
|
|
|
|||
|
|
@ -1330,14 +1330,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
|
|||
raise && this.toggle_message(false);
|
||||
|
||||
return this.filters
|
||||
.filter((f) => {
|
||||
const filter_value = f.get_value();
|
||||
if (typeof filter_value === "object") {
|
||||
return filter_value.length > 0;
|
||||
} else {
|
||||
return filter_value;
|
||||
}
|
||||
})
|
||||
.filter((f) => f.get_value())
|
||||
.map((f) => {
|
||||
var v = f.get_value();
|
||||
// hidden fields dont have $input
|
||||
|
|
@ -1480,7 +1473,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
|
|||
|
||||
get_filters_html_for_print() {
|
||||
const applied_filters = this.get_filter_values();
|
||||
const filter_html = Object.keys(applied_filters)
|
||||
return Object.keys(applied_filters)
|
||||
.map((fieldname) => {
|
||||
const docfield = frappe.query_report.get_filter(fieldname).df;
|
||||
const value = applied_filters[fieldname];
|
||||
|
|
@ -1489,14 +1482,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
|
|||
</div>`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
return `<div>${filter_html}</div>
|
||||
<style>
|
||||
.filter-row div {
|
||||
/* prevent newline + right alignment of number fields */
|
||||
display: inline-block;
|
||||
}
|
||||
</style>`;
|
||||
}
|
||||
|
||||
export_report() {
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ export default class ChartWidget extends Widget {
|
|||
if (this.chart_doc.type == "Heatmap") {
|
||||
filters = [
|
||||
{
|
||||
label: this.chart_settings.heatmap_year || this.chart_doc.heatmap_year,
|
||||
label: __(this.chart_settings.heatmap_year) || __(this.chart_doc.heatmap_year),
|
||||
options: frappe.dashboard_utils.get_years_since_creation(
|
||||
frappe.boot.user.creation
|
||||
),
|
||||
|
|
@ -141,7 +141,8 @@ export default class ChartWidget extends Widget {
|
|||
} else {
|
||||
filters = [
|
||||
{
|
||||
label: this.chart_settings.time_interval || this.chart_doc.time_interval,
|
||||
label:
|
||||
__(this.chart_settings.time_interval) || __(this.chart_doc.time_interval),
|
||||
options: ["Yearly", "Quarterly", "Monthly", "Weekly", "Daily"],
|
||||
icon: "calendar",
|
||||
class: "time-interval-filter",
|
||||
|
|
@ -233,7 +234,7 @@ export default class ChartWidget extends Widget {
|
|||
df: {
|
||||
fieldtype: "DateRange",
|
||||
fieldname: "from_date",
|
||||
placeholder: "Date Range",
|
||||
placeholder: __("Date Range"),
|
||||
input_class: "input-xs",
|
||||
default: [this.chart_settings.from_date, this.chart_settings.to_date],
|
||||
value: [this.chart_settings.from_date, this.chart_settings.to_date],
|
||||
|
|
@ -314,7 +315,7 @@ export default class ChartWidget extends Widget {
|
|||
|
||||
if (this.chart_doc.document_type) {
|
||||
actions.push({
|
||||
label: __("{0} List", [this.chart_doc.document_type]),
|
||||
label: __("{0} List", [__(this.chart_doc.document_type)]),
|
||||
action: "action-list",
|
||||
handler: () => {
|
||||
frappe.set_route("List", this.chart_doc.document_type);
|
||||
|
|
@ -322,7 +323,7 @@ export default class ChartWidget extends Widget {
|
|||
});
|
||||
} else if (this.chart_doc.chart_type === "Report") {
|
||||
actions.push({
|
||||
label: __("{0} Report", [this.chart_doc.report_name]),
|
||||
label: __("{0} Report", [__(this.chart_doc.report_name)]),
|
||||
action: "action-list",
|
||||
handler: () => {
|
||||
frappe.set_route("query-report", this.chart_doc.report_name, this.filters);
|
||||
|
|
@ -392,7 +393,7 @@ export default class ChartWidget extends Widget {
|
|||
setup_filter_dialog(fields) {
|
||||
let me = this;
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: __("Set Filters for {0}", [this.chart_doc.chart_name]),
|
||||
title: __("Set Filters for {0}", [__(this.chart_doc.chart_name)]),
|
||||
fields: fields,
|
||||
primary_action: function () {
|
||||
let values = this.get_values();
|
||||
|
|
@ -403,7 +404,7 @@ export default class ChartWidget extends Widget {
|
|||
me.fetch_and_update_chart();
|
||||
}
|
||||
},
|
||||
primary_action_label: "Set",
|
||||
primary_action_label: __("Set"),
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
|
|
@ -473,7 +474,9 @@ export default class ChartWidget extends Widget {
|
|||
${actions
|
||||
.map(
|
||||
(action) =>
|
||||
`<li><a class="dropdown-item" data-action="${action.action}">${action.label}</a></li>`
|
||||
`<li><a class="dropdown-item" data-action="${action.action}">${__(
|
||||
action.label
|
||||
)}</a></li>`
|
||||
)
|
||||
.join("")}
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -129,9 +129,9 @@ export default class OnboardingWidget extends Widget {
|
|||
.on("click", toggle_video);
|
||||
} else {
|
||||
$(
|
||||
`<button class="btn btn-default btn-sm">${__(
|
||||
step.action_label || step.action
|
||||
)}</button>`
|
||||
`<button class="btn btn-default btn-sm">${
|
||||
__(step.action_label) || __(step.action)
|
||||
}</button>`
|
||||
)
|
||||
.appendTo(this.step_footer)
|
||||
.on("click", () => actions[step.action](step));
|
||||
|
|
@ -139,8 +139,8 @@ export default class OnboardingWidget extends Widget {
|
|||
};
|
||||
|
||||
const set_description = () => {
|
||||
let content = step.description
|
||||
? frappe.markdown(step.description)
|
||||
let content = __(step.description)
|
||||
? frappe.markdown(__(step.description))
|
||||
: `<h1>${__(step.title)}</h1>`;
|
||||
|
||||
if (step.action === "Create Entry") {
|
||||
|
|
@ -168,9 +168,9 @@ export default class OnboardingWidget extends Widget {
|
|||
});
|
||||
|
||||
$(
|
||||
`<button class="btn btn-primary btn-sm">${__(
|
||||
step.action_label || step.action
|
||||
)}</button>`
|
||||
`<button class="btn btn-primary btn-sm">${
|
||||
__(step.action_label) || __(step.action)
|
||||
}</button>`
|
||||
)
|
||||
.appendTo(this.step_footer)
|
||||
.on("click", () => {
|
||||
|
|
|
|||
|
|
@ -46,3 +46,8 @@
|
|||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
|
||||
/* prevent newline and right alignment of number fields in printed report filters */
|
||||
.filter-row div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,4 +5,7 @@
|
|||
<a class="btn {% if action.is_primary %} btn-primary {% endif %}" style="margin-right: 10px" href="{{ action.action_link }}">{{_(action.action_name)}}</a>
|
||||
{% endfor %}
|
||||
</p>
|
||||
<div class="text-muted text-small" style="padding-top: 20px;">
|
||||
{{ _("The contents of this email are strictly confidential. Please do not forward this email to anyone.") }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
{% endfor %}
|
||||
<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem" class="breadcrumb-item active" aria-current="page">
|
||||
<span itemprop="item">
|
||||
<span itemprop="name">{{ title }}</span>
|
||||
<span itemprop="name">{{ _(title) }}</span>
|
||||
<meta itemprop="position" content="{{ count }}"/>
|
||||
</span>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -329,7 +329,7 @@ var request_otp = function (r) {
|
|||
$('.login-content:visible').append(
|
||||
`<div id="twofactor_div">
|
||||
<form class="form-verify">
|
||||
<div class="page-card-head">
|
||||
<div class="page-card-head p-0">
|
||||
<span class="indicator blue" data-text="Verification">{{ _("Verification") | e }}</span>
|
||||
</div>
|
||||
<div id="otp_div"></div>
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ data-fieldname="{{ df.fieldname }}" data-fieldtype="{{ df.fieldtype }}"
|
|||
{% elif df.fieldtype=="Signature" %}
|
||||
<img src="{{ doc[df.fieldname] }}" class="signature-img img-responsive"
|
||||
{%- if df.print_width %} style="width: {{ get_width(df) }};"{% endif %}>
|
||||
{% elif df.fieldtype in ("Attach", "Attach Image") %}
|
||||
{% elif df.fieldtype in ("Attach", "Attach Image") and frappe.utils.is_image(doc[df.fieldname]) %}
|
||||
<img src="{{ doc[df.fieldname] }}" class="img-responsive"
|
||||
{%- if df.print_width %} style="width: {{ get_width(df) }};"{% endif %}>
|
||||
{% elif df.fieldtype=="HTML" %}
|
||||
|
|
|
|||
|
|
@ -1080,7 +1080,6 @@ class TestSqlIterator(FrappeTestCase):
|
|||
with frappe.db.unbuffered_cursor():
|
||||
self.test_db_sql_iterator()
|
||||
|
||||
|
||||
class ExtFrappeTestCase(FrappeTestCase):
|
||||
def assertSqlException(self):
|
||||
class SqlExceptionContextManager:
|
||||
|
|
@ -1259,3 +1258,88 @@ class TestPostgresSchemaQueryIndependence(ExtFrappeTestCase):
|
|||
self.assertEqual(rows, []) # there are no records in the alt_schema table
|
||||
|
||||
del frappe.conf["db_schema"]
|
||||
|
||||
class TestDbConnectWithEnvCredentials(FrappeTestCase):
|
||||
current_site = frappe.local.site
|
||||
|
||||
def tearDown(self):
|
||||
frappe.init(self.current_site, force=True)
|
||||
frappe.connect()
|
||||
|
||||
def test_connect_fails_with_wrong_credentials_by_env(self) -> None:
|
||||
import contextlib
|
||||
import os
|
||||
import re
|
||||
|
||||
@contextlib.contextmanager
|
||||
def set_env_variable(key, value):
|
||||
if orig_value_set := key in os.environ:
|
||||
orig_value = os.environ.get(key)
|
||||
|
||||
os.environ[key] = value
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if orig_value_set:
|
||||
os.environ[key] = orig_value
|
||||
else:
|
||||
del os.environ[key]
|
||||
|
||||
# with wrong db name
|
||||
with set_env_variable("FRAPPE_DB_NAME", "dbiq"):
|
||||
frappe.init(self.current_site, force=True)
|
||||
frappe.connect()
|
||||
|
||||
with self.assertRaises(Exception) as cm:
|
||||
frappe.db.connect()
|
||||
|
||||
self.assertTrue(re.search(r"database [\"']dbiq[\"']", str(cm.exception)))
|
||||
|
||||
# with wrong host
|
||||
with set_env_variable("FRAPPE_DB_HOST", "iqx.local"):
|
||||
frappe.init(self.current_site, force=True)
|
||||
frappe.connect()
|
||||
|
||||
with self.assertRaises(Exception) as cm:
|
||||
frappe.db.connect()
|
||||
|
||||
self.assertTrue(re.search(r"(host name|server on) [\"']iqx.local[\"']", str(cm.exception)))
|
||||
|
||||
# with wrong user name
|
||||
with set_env_variable("FRAPPE_DB_USER", "uname"):
|
||||
frappe.init(self.current_site, force=True)
|
||||
frappe.connect()
|
||||
|
||||
with self.assertRaises(Exception) as cm:
|
||||
frappe.db.connect()
|
||||
|
||||
self.assertTrue(re.search(r"user [\"']uname[\"']", str(cm.exception)))
|
||||
|
||||
# with wrong password
|
||||
with set_env_variable("FRAPPE_DB_PASSWORD", "pass"):
|
||||
frappe.init(self.current_site, force=True)
|
||||
frappe.connect()
|
||||
|
||||
with self.assertRaises(Exception) as cm:
|
||||
frappe.db.connect()
|
||||
|
||||
self.assertTrue(
|
||||
re.search(r"(password authentication failed|Access denied for)", str(cm.exception))
|
||||
)
|
||||
|
||||
# with wrong password
|
||||
with set_env_variable("FRAPPE_DB_PORT", "1111"):
|
||||
frappe.init(self.current_site, force=True)
|
||||
frappe.connect()
|
||||
|
||||
with self.assertRaises(Exception) as cm:
|
||||
frappe.db.connect()
|
||||
|
||||
self.assertTrue(re.search("(port 1111 failed|Errno 111)", str(cm.exception)))
|
||||
|
||||
# now with configured settings without any influences from env
|
||||
# finally connect should work without any error (when no wrong credentials are given via ENV)
|
||||
frappe.init(self.current_site, force=True)
|
||||
frappe.connect()
|
||||
frappe.db.connect()
|
||||
|
|
|
|||
|
|
@ -344,30 +344,14 @@ def send_token_via_email(user, token, otp_secret, otp_issuer, subject=None, mess
|
|||
hotp = pyotp.HOTP(otp_secret)
|
||||
otp = hotp.at(int(token))
|
||||
template_args = {"otp": otp, "otp_issuer": otp_issuer}
|
||||
if not subject:
|
||||
subject = get_email_subject_for_2fa(template_args)
|
||||
if not message:
|
||||
message = get_email_body_for_2fa(template_args)
|
||||
|
||||
email_args = {
|
||||
"recipients": user_email,
|
||||
"sender": None,
|
||||
"subject": subject,
|
||||
"message": message,
|
||||
"header": [_("Verfication Code"), "blue"],
|
||||
"delayed": False,
|
||||
"retry": 3,
|
||||
}
|
||||
|
||||
enqueue(
|
||||
method=frappe.sendmail,
|
||||
queue="short",
|
||||
timeout=300,
|
||||
event=None,
|
||||
is_async=True,
|
||||
job_name=None,
|
||||
now=False,
|
||||
**email_args,
|
||||
frappe.sendmail(
|
||||
recipients=user_email,
|
||||
subject=subject or get_email_subject_for_2fa(template_args),
|
||||
message=message or get_email_body_for_2fa(template_args),
|
||||
header=[_("Verfication Code"), "blue"],
|
||||
delayed=False,
|
||||
retry=3,
|
||||
)
|
||||
return True
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ def enqueue(
|
|||
|
||||
def enqueue_call():
|
||||
return q.enqueue_call(
|
||||
execute_job,
|
||||
"frappe.utils.background_jobs.execute_job",
|
||||
on_success=Callback(func=on_success) if on_success else None,
|
||||
on_failure=Callback(func=on_failure) if on_failure else None,
|
||||
timeout=timeout,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
# License: MIT. See LICENSE
|
||||
import pickle
|
||||
import re
|
||||
from contextlib import suppress
|
||||
|
||||
import redis
|
||||
from redis.commands.search import Search
|
||||
|
|
@ -62,14 +63,8 @@ class RedisWrapper(redis.Redis):
|
|||
if not expires_in_sec:
|
||||
frappe.local.cache[key] = val
|
||||
|
||||
try:
|
||||
if expires_in_sec:
|
||||
self.setex(name=key, time=expires_in_sec, value=pickle.dumps(val))
|
||||
else:
|
||||
self.set(key, pickle.dumps(val))
|
||||
|
||||
except redis.exceptions.ConnectionError:
|
||||
return None
|
||||
with suppress(redis.exceptions.ConnectionError):
|
||||
self.set(name=key, value=pickle.dumps(val), ex=expires_in_sec)
|
||||
|
||||
def get_value(self, key, generator=None, user=None, expires=False, shared=False):
|
||||
"""Return cache value. If not found and generator function is
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ dependencies = [
|
|||
"markdownify~=0.11.6",
|
||||
|
||||
# integration dependencies
|
||||
"boto3~=1.28.10",
|
||||
"boto3~=1.34.143",
|
||||
"dropbox~=11.36.2",
|
||||
"google-api-python-client~=2.2.0",
|
||||
"google-auth-oauthlib~=0.4.4",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue