Merge branch 'develop' into 27880-child-table-titles-not-translating-in-notifications-with-custom-print-templates

This commit is contained in:
mergify[bot] 2026-02-27 06:23:48 +00:00 committed by GitHub
commit e32d5e0e3f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
63 changed files with 627 additions and 275 deletions

View file

@ -27,7 +27,9 @@ context("Web Form", () => {
cy.wait("@save_form");
cy.get('.frappe-control[data-fieldname="route"]').scrollIntoView();
cy.get_field("route").should("have.value", "note");
cy.get(".title-area .indicator-pill")
.should("contain.text", "Published")
.should("have.class", "green");

View file

@ -211,8 +211,7 @@ frappe.ui.form.on("Communication", {
],
primary_action_label: __("Move"),
primary_action(values) {
d.hide();
frappe.call({
return frappe.call({
method: "frappe.email.inbox.move_email",
args: {
communication: frm.doc.name,
@ -220,6 +219,7 @@ frappe.ui.form.on("Communication", {
},
freeze: true,
callback: function () {
d.hide();
window.history.back();
},
});

View file

@ -103,7 +103,7 @@ frappe.listview_settings["DocType"] = {
primary_action_label: __("Create & Continue"),
primary_action(values) {
if (!values.istable) values.editable_grid = 0;
frappe.db
return frappe.db
.insert({
doctype: "DocType",
...values,

View file

@ -890,6 +890,14 @@ def has_permission(doc, ptype=None, user=None, debug=False):
if user != "Guest" and doc.owner == user:
return True
if (
user != "Guest"
and ptype in ["read", "write", "share", "submit"]
and frappe.share.get_shared(
"File", filters=[["share_name", "=", doc.name]], rights=[ptype], user=user
)
):
return True
if doc.attached_to_doctype and doc.attached_to_name:
attached_to_doctype = doc.attached_to_doctype

View file

@ -427,6 +427,29 @@ def relink_mismatched_files(doc: "Document") -> None:
for df in attach_fields:
if doc.get(df.fieldname):
relink_files(doc, df.fieldname, doc.__temporary_name)
# Relink files in child table Attach fields
table_fields = doc.meta.get("fields", {"fieldtype": "Table"})
for table_df in table_fields:
child_rows = doc.get(table_df.fieldname) or []
if not child_rows:
continue
child_meta = frappe.get_meta(table_df.options)
child_attach_fields = child_meta.get("fields", {"fieldtype": ["in", ["Attach", "Attach Image"]]})
if not child_attach_fields:
continue
for child_row in child_rows:
for child_df in child_attach_fields:
file_url = child_row.get(child_df.fieldname)
if file_url:
frappe.db.set_value(
"File",
{"file_url": file_url, "attached_to_name": doc.__temporary_name},
{"attached_to_name": doc.name},
)
# delete temporary name after relinking is done
doc.delete_key("__temporary_name")

View file

@ -201,18 +201,19 @@ frappe.ui.form.on("User", {
},
],
primary_action: (values) => {
d.hide();
if (values.new_password !== values.confirm_password) {
frappe.throw(__("Passwords do not match!"));
}
frappe.call(
"frappe.integrations.doctype.ldap_settings.ldap_settings.reset_password",
{
user: frm.doc.email,
password: values.new_password,
logout: values.logout_sessions,
}
);
return frappe
.call(
"frappe.integrations.doctype.ldap_settings.ldap_settings.reset_password",
{
user: frm.doc.email,
password: values.new_password,
logout: values.logout_sessions,
}
)
.then(() => d.hide());
},
});
d.show();

View file

@ -25,7 +25,7 @@ frappe.query_reports["Database Storage Usage By Tables"] = {
size: "small",
primary_action_label: "Optimize",
primary_action(values) {
frappe.call({
return frappe.call({
method: "frappe.core.report.database_storage_usage_by_tables.database_storage_usage_by_tables.optimize_doctype",
args: {
doctype_name: values.doctype_name,
@ -38,9 +38,9 @@ frappe.query_reports["Database Storage Usage By Tables"] = {
)
);
}
d.hide();
},
});
d.hide();
},
});
d.show();

View file

@ -22,6 +22,7 @@ def execute(filters=None):
round((data_length / 1024 / 1024), 2) as data_size,
round((index_length / 1024 / 1024), 2) as index_size
FROM information_schema.TABLES
WHERE table_schema = DATABASE()
ORDER BY (data_length + index_length) DESC;
""",
"postgres": """

View file

@ -488,7 +488,13 @@ frappe.ui.form.on("Dashboard Chart", {
});
dialog.show();
dialog.set_values(frm.dynamic_filters);
if (frm.dynamic_filters) {
let filter_values = {};
frm.dynamic_filters.forEach((f) => {
filter_values[f[0] + ":" + f[1]] = f[3];
});
dialog.set_values(filter_values);
}
});
},

View file

@ -124,11 +124,6 @@ frappe.ui.form.on("Number Card", {
frappe.model.with_doctype(doctype, () => {
frappe.get_meta(doctype).fields.map((df) => {
if (frappe.model.numeric_fieldtypes.includes(df.fieldtype)) {
if (df.fieldtype == "Currency") {
if (!df.options || df.options !== "Company:company:default_currency") {
return;
}
}
aggregate_based_on_fields.push({ label: df.label, value: df.fieldname });
}
});
@ -405,7 +400,13 @@ frappe.ui.form.on("Number Card", {
});
dialog.show();
dialog.set_values(frm.dynamic_filters);
if (frm.dynamic_filters) {
let filter_values = {};
frm.dynamic_filters.forEach((f) => {
filter_values[f[0] + ":" + f[1]] = f[3];
});
dialog.set_values(filter_values);
}
});
},

View file

@ -437,37 +437,19 @@ def get_linked_docs(doctype: str, name: str, linkinfo: dict | None = None) -> di
is_target_doctype_table = frappe.get_meta(doctype).istable
for linked_doctype, link_context in linkinfo.items():
# Don't try to fetch linked documents if the user can't read the doctype
if not frappe.has_permission(linked_doctype):
continue
linked_doctype_meta = frappe.get_meta(linked_doctype)
if linked_doctype_meta.issingle:
continue
has_permission = frappe.has_permission(linked_doctype)
filters = []
or_filters = []
ret = None
parent_info = None
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"]
if add_fields := link_context.get("add_fields"):
fields += add_fields
fields = [sf.strip() for sf in fields if sf]
if filters_ctx := link_context.get("filters"):
ret = frappe.get_list(doctype=linked_doctype, fields=fields, filters=filters_ctx, order_by=None)
filters = filters_ctx
elif link_context.get("get_parent"):
# check for child table
@ -478,13 +460,10 @@ def get_linked_docs(doctype: str, name: str, linkinfo: dict | None = None) -> di
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,
)
if not (parent_info and parent_info.parenttype == linked_doctype):
continue
filters = [[linked_doctype, "name", "=", parent_info.parent]]
elif child_doctype := link_context.get("child_doctype"):
or_filters = [
@ -495,15 +474,6 @@ def get_linked_docs(doctype: str, name: str, linkinfo: dict | None = None) -> di
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]
@ -518,12 +488,51 @@ def get_linked_docs(doctype: str, name: str, linkinfo: dict | None = None) -> di
or frappe.db.exists(linked_doctype, {"parenttype": doctype, "parent": name})
):
continue
total_count = len(
frappe.get_all(
linked_doctype,
filters=filters,
or_filters=or_filters,
fields=["name"],
order_by=None,
)
)
if not total_count:
continue
if has_permission:
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"]
if add_fields := link_context.get("add_fields"):
fields += add_fields
fields = [sf.strip() for sf in fields if sf]
ret = frappe.get_list(
doctype=linked_doctype, fields=fields, filters=filters, or_filters=or_filters, order_by=None
doctype=linked_doctype,
fields=fields,
filters=filters,
or_filters=or_filters,
distinct=True,
order_by=None,
)
if ret:
results[linked_doctype] = ret
permitted_count = len(ret or [])
results[linked_doctype] = {
"docs": ret or [],
"hidden_count": total_count - permitted_count,
}
return results

View file

@ -87,12 +87,17 @@
}
}
.modal
.modal-body .icons-container,.folder-icon .icons-container {
.modal-body .icons-container, .folder-icon .icons-container {
padding:0px;
margin: 0px;
height: 100%;
overflow: auto;
}
.folder-icon .icons-container {
overflow: hidden;
}
.icons{
gap: 16px;
display: grid;

View file

@ -548,7 +548,6 @@ class DesktopPage {
frappe.router.on("change", function () {
if (frappe.get_route()[0] == "desktop" || frappe.get_route()[0] == "") {
me.setup_navbar();
me.setup_edit_button();
} else {
$(".navbar").show();
frappe.desktop_utils.close_desktop_modal();

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: developers@frappe.io\n"
"POT-Creation-Date: 2026-02-22 09:42+0000\n"
"PO-Revision-Date: 2026-02-23 22:07\n"
"PO-Revision-Date: 2026-02-25 23:15\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Bosnian\n"
"MIME-Version: 1.0\n"
@ -2870,7 +2870,7 @@ msgstr "Dodjela je Završena"
#. Label of the assignment_days (Table) field in DocType 'Assignment Rule'
#: frappe/automation/doctype/assignment_rule/assignment_rule.json
msgid "Assignment Days"
msgstr "Dani Dodjeljivanja"
msgstr "Dani Dodjele"
#. Name of a DocType
#. Label of the assignment_rule (Link) field in DocType 'ToDo'
@ -2888,7 +2888,7 @@ msgstr "Dan Dodjele Pravila"
#. Name of a DocType
#: frappe/automation/doctype/assignment_rule_user/assignment_rule_user.json
msgid "Assignment Rule User"
msgstr "Korisnik Dodjele Pravila"
msgstr "Korisnik Pravila Dodjele"
#: frappe/automation/doctype/assignment_rule/assignment_rule.py:55
msgid "Assignment Rule is not allowed on document type {0}"

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: developers@frappe.io\n"
"POT-Creation-Date: 2026-02-22 09:42+0000\n"
"PO-Revision-Date: 2026-02-23 22:07\n"
"PO-Revision-Date: 2026-02-25 23:14\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Persian\n"
"MIME-Version: 1.0\n"
@ -6314,7 +6314,7 @@ msgstr "سفارشی‌سازی"
#: frappe/custom/doctype/customize_form/customize_form.js:89
msgid "Customize Child Table"
msgstr "سفارشی کردن جدول فرزند"
msgstr "سفارشی‌سازی جدول فرزند"
#: frappe/public/js/frappe/views/dashboard/dashboard_view.js:38
msgid "Customize Dashboard"
@ -6339,7 +6339,7 @@ msgstr "سفارشی‌سازی فرم - {0}"
#. Name of a DocType
#: frappe/custom/doctype/customize_form_field/customize_form_field.json
msgid "Customize Form Field"
msgstr "سفارشی کردن فیلد فرم"
msgstr "سفارشی‌سازی فیلد فرم"
#: frappe/public/js/frappe/list/list_view.js:1994
msgctxt "Customize qucik filters of List View"
@ -18808,7 +18808,7 @@ msgstr ""
#: frappe/core/doctype/doctype/doctype.py:1699
msgid "Options for Rating field can range from 3 to 10"
msgstr "گزینه‌های فیلد رتبه بندی می‌تواند از 3 تا 10 باشد"
msgstr "گزینه‌های فیلد رتبهبندی می‌تواند از 3 تا 10 باشد"
#: frappe/custom/doctype/custom_field/custom_field.js:96
msgid "Options for select. Each option on a new line."

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: developers@frappe.io\n"
"POT-Creation-Date: 2026-02-22 09:42+0000\n"
"PO-Revision-Date: 2026-02-23 22:07\n"
"PO-Revision-Date: 2026-02-25 23:14\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Croatian\n"
"MIME-Version: 1.0\n"
@ -2870,7 +2870,7 @@ msgstr "Dodjela je Završena"
#. Label of the assignment_days (Table) field in DocType 'Assignment Rule'
#: frappe/automation/doctype/assignment_rule/assignment_rule.json
msgid "Assignment Days"
msgstr "Dani Dodjeljivanja"
msgstr "Dani Dodjele"
#. Name of a DocType
#. Label of the assignment_rule (Link) field in DocType 'ToDo'

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: developers@frappe.io\n"
"POT-Creation-Date: 2026-02-22 09:42+0000\n"
"PO-Revision-Date: 2026-02-23 22:07\n"
"PO-Revision-Date: 2026-02-26 23:27\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Serbian (Cyrillic)\n"
"MIME-Version: 1.0\n"
@ -1357,7 +1357,7 @@ msgstr "Додај параметре упита"
#. Label of the add_reply_to_header (Check) field in DocType 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "Add Reply-To header"
msgstr ""
msgstr "Додај заглавље адресе за одговор"
#: frappe/core/doctype/user/user.py:860
msgid "Add Roles"
@ -1523,7 +1523,7 @@ msgstr "Додај на контролну таблу"
#: frappe/desk/doctype/workspace/workspace.js:49
msgid "Add to Desktop"
msgstr ""
msgstr "Додај на радну површину"
#: frappe/public/js/frappe/form/sidebar/assign_to.js:110
msgid "Add to ToDo"
@ -1646,7 +1646,7 @@ msgstr "Адресе и контакти"
#. Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "Addresses added here will be used as the Reply-To header for outgoing emails sent from this account."
msgstr ""
msgstr "Адресе додате овде користиће се као адреса за одговор за излазне имејлове послате са овог налога."
#. Description of a DocType
#: frappe/custom/doctype/client_script/client_script.json
@ -3082,7 +3082,7 @@ msgstr "Историја измена"
#. Label of a Workspace Sidebar Item
#: frappe/workspace_sidebar/users.json
msgid "Audits"
msgstr ""
msgstr "Ревизије"
#. Label of the auth_url_data (Code) field in DocType 'Social Login Key'
#: frappe/integrations/doctype/social_login_key/social_login_key.json
@ -3516,7 +3516,7 @@ msgstr "Слика позадине"
#. Label of a Workspace Sidebar Item
#: frappe/workspace_sidebar/system.json
msgid "Background Job"
msgstr ""
msgstr "Позадински задатак"
#. Label of a Link in the Build Workspace
#. Label of the background_jobs_section (Section Break) field in DocType
@ -4934,7 +4934,7 @@ msgstr "Кликните да поставите филтере"
#: frappe/desk/page/desktop/desktop.js:1261
msgid "Click to edit"
msgstr ""
msgstr "Кликните за уређивање"
#: frappe/public/js/frappe/list/list_view.js:754
msgid "Click to sort by {0}"
@ -6537,7 +6537,7 @@ msgstr "Цијан"
#. 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "DELAY"
msgstr ""
msgstr "ОДЛАГАЊЕ"
#. Option for the 'Method' (Select) field in DocType 'Recorder'
#. Option for the 'Request Method' (Select) field in DocType 'Webhook'
@ -7365,7 +7365,7 @@ msgstr "Статус"
#. Label of the dsn_notify_type (Select) field in DocType 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "Delivery Status Notification Type"
msgstr ""
msgstr "Врста обавештења о статусу испоруке"
#. Option for the 'Sign ups' (Select) field in DocType 'Social Login Key'
#: frappe/integrations/doctype/social_login_key/social_login_key.json
@ -9430,7 +9430,7 @@ msgstr "Планер омогућен"
#. Label of the enabled (Check) field in DocType 'Notification Settings'
#: frappe/desk/doctype/notification_settings/notification_settings.json
msgid "Enabled System Notification"
msgstr ""
msgstr "Омогућено системско обавештење"
#: frappe/email/doctype/email_account/email_account.py:1101
msgid "Enabled email inbox for user {0}"
@ -10128,7 +10128,7 @@ msgstr "Додатни параметри"
#. 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "FAILURE"
msgstr ""
msgstr "НЕУСПЕХ"
#. Option for the 'Social Login Provider' (Select) field in DocType 'Social
#. Login Key'
@ -10272,7 +10272,7 @@ msgstr "Неуспешан покушај пријаве на Frappe Cloud"
#: frappe/email/doctype/email_account/email_account.py:232
msgid "Failed to retrieve the list of IMAP folders from the server. Please ensure the mailbox is accessible and the account has permission to list folders."
msgstr ""
msgstr "Неуспешно преузимање листе IMAP директоријума са сервера. Проверите да ли је поштанско сандуче доступно и да ли налог има дозволу за приказ директоријума."
#: frappe/email/doctype/email_queue/email_queue.py:311
msgid "Failed to send email with subject:"
@ -11339,7 +11339,7 @@ msgstr "Јединица фракције"
#. Label of a Desktop Icon
#: frappe/desktop_icon/framework.json
msgid "Framework"
msgstr ""
msgstr "Framework"
#. Option for the 'Social Login Provider' (Select) field in DocType 'Social
#. Login Key'
@ -12235,7 +12235,7 @@ msgstr "Наслов"
#. Label of a Workspace Sidebar Item
#: frappe/workspace_sidebar/system.json
msgid "Health Report"
msgstr ""
msgstr "Извештај о стању система"
#. Option for the 'Type' (Select) field in DocType 'Dashboard Chart'
#: frappe/desk/doctype/dashboard_chart/dashboard_chart.json
@ -12654,7 +12654,7 @@ msgstr "IMAP датотека"
#: frappe/email/doctype/email_account/email_account.py:235
#: frappe/email/doctype/email_account/email_account.py:263
msgid "IMAP Folder Not Found"
msgstr ""
msgstr "IMAP директоријум није пронађен"
#. Label of the ip_address (Data) field in DocType 'Activity Log'
#. Label of the ip_address (Data) field in DocType 'Comment'
@ -13414,7 +13414,7 @@ msgstr "Погрешан верификациони код"
#: frappe/public/js/frappe/views/gantt/gantt_view.js:88
msgid "Incorrect configuration"
msgstr ""
msgstr "Неисправна конфигурација"
#: frappe/model/document.py:1733
msgid "Incorrect value in row {0}:"
@ -16979,7 +16979,7 @@ msgstr "MyISAM"
#. 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "NEVER"
msgstr ""
msgstr "НИКАДА"
#: frappe/workflow/doctype/workflow/workflow.js:19
msgid "NOTE: If you add states or transitions in the table, it will be reflected in the Workflow Builder but you will have to position them manually. Also Workflow Builder is currently in <b>BETA</b>."
@ -17750,7 +17750,7 @@ msgstr "Нема података за извоз"
#: frappe/public/js/frappe/views/reports/query_report.js:1543
msgid "No data to perform this action"
msgstr ""
msgstr "Нема података за извршавање ове радње"
#: frappe/contacts/doctype/address/address.py:247
msgid "No default Address Template found. Please create a new one from Setup > Printing and Branding > Address Template."
@ -17795,7 +17795,7 @@ msgstr "Нема додатних записа"
#: frappe/public/js/frappe/views/reports/report_view.js:337
msgid "No matching entries in the current results"
msgstr ""
msgstr "Нема подударних записа у тренутним резултатима"
#: frappe/templates/includes/search_template.html:49
msgid "No matching records. Search something new"
@ -18429,7 +18429,7 @@ msgstr "OAuth грешка"
#. Label of a Workspace Sidebar Item
#: frappe/workspace_sidebar/integrations.json
msgid "OAuth Provider"
msgstr ""
msgstr "OAuth провајдер"
#. Name of a DocType
#. Label of a Link in the Integrations Workspace
@ -18698,7 +18698,7 @@ msgstr "Дозволи уређивање само за"
#: frappe/core/doctype/module_def/module_def.py:95
msgid "Only Custom Modules can be renamed."
msgstr ""
msgstr "Искључиво прилагођени модули могу бити преименовани."
#: frappe/core/doctype/doctype/doctype.py:1652
msgid "Only Options allowed for Data field are:"
@ -19969,7 +19969,7 @@ msgstr "Молимо Вас да додате валидан коментар."
#: frappe/public/js/frappe/views/reports/query_report.js:1544
msgid "Please adjust filters to include some data"
msgstr ""
msgstr "Прилагодите филтере како бисте укључили неке податке"
#: frappe/core/doctype/user/user.py:1122
msgid "Please ask your administrator to verify your sign-up"
@ -20029,7 +20029,7 @@ msgstr "Молимо Вас да кликнете на следећи линк
#: frappe/public/js/frappe/views/gantt/gantt_view.js:89
msgid "Please configure the start field for this Doctype in the controller file."
msgstr ""
msgstr "Молимо Вас да конфигуришете почетно поље за овај DocType у датотеци контролера."
#: frappe/www/confirm_workflow_action.html:4
msgid "Please confirm your action to {0} this document."
@ -21132,7 +21132,7 @@ msgstr "Љубичасто"
#. Label of a Workspace Sidebar Item
#: frappe/workspace_sidebar/integrations.json
msgid "Push Notification"
msgstr ""
msgstr "Push обавештење"
#. Name of a DocType
#. Label of a Link in the Integrations Workspace
@ -22225,16 +22225,16 @@ msgstr "Одговори свима"
#. Name of a DocType
#: frappe/email/doctype/reply_to_address/reply_to_address.json
msgid "Reply To Address"
msgstr ""
msgstr "Адреса за одговор"
#: frappe/email/doctype/email_account/email_account.py:278
msgid "Reply To email is required"
msgstr ""
msgstr "Адреса за одговор је обавезна"
#. Label of the reply_to_addresses (Table) field in DocType 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "Reply-To Addresses"
msgstr ""
msgstr "Адреса за одговор"
#. Label of the report (Check) field in DocType 'Custom DocPerm'
#. Label of the report (Link) field in DocType 'Custom Role'
@ -23276,19 +23276,19 @@ msgstr "SSL/TLS режим"
#. 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "SUCCESS"
msgstr ""
msgstr "УСПЕХ"
#. Option for the 'Delivery Status Notification Type' (Select) field in DocType
#. 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "SUCCESS,FAILURE"
msgstr ""
msgstr "УСПЕХ, НЕУСПЕХ"
#. Option for the 'Delivery Status Notification Type' (Select) field in DocType
#. 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "SUCCESS,FAILURE,DELAY"
msgstr ""
msgstr "УСПЕХ, НЕУСПЕХ, ОДЛАГАЊЕ"
#: frappe/public/js/frappe/color_picker/color_picker.js:20
msgid "SWATCHES"
@ -24115,7 +24115,7 @@ msgstr "Изабери две верзије за приказ разлика."
#. DocType 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "Select which delivery events should trigger a delivery status notification (DSN) from the SMTP server."
msgstr ""
msgstr "Изаберите који догађаји испоруке треба да покрену обавештење о статусу испоруке (DNS) са SMTP сервера."
#: frappe/public/js/frappe/form/link_selector.js:24
#: frappe/public/js/frappe/form/multi_select_dialog.js:80
@ -27098,7 +27098,7 @@ msgstr "Коментар не може бити празан"
#: frappe/email/doctype/email_account/email_account.py:290
msgid "The configured SMTP server does not support DSN (Delivery Status Notification)."
msgstr ""
msgstr "Конфигурисани SMTP сервер не подржава DNS (обавештење о статусу испоруке)."
#: frappe/templates/emails/workflow_action.html:9
msgid "The contents of this email are strictly confidential. Please do not forward this email to anyone."
@ -27160,7 +27160,7 @@ msgstr "Следећа скрипта заглавља ће додати тре
#: frappe/email/doctype/email_account/email_account.py:257
msgid "The following configured IMAP folder(s) were not found on the server:<br><ul>{0}</ul>Please verify the folder names exactly as they appear on the server (folder names are case-sensitive)."
msgstr ""
msgstr "Следећи конфигурисани IMAP директоријуми нису пронађени на серверу:<br><ul>{0}</ul>Молимо Вас да проверите називе директоријума тачно онако како су приказани на серверу (велика и мала слова су битна за називе датотека)."
#: frappe/core/doctype/data_import/importer.py:1092
msgid "The following values are invalid: {0}. Values must be one of {1}"
@ -27252,7 +27252,7 @@ msgstr "Изабрани документ {0} није {1}."
#: frappe/email/doctype/email_account/email_account.py:247
msgid "The server did not return any IMAP folders for this account."
msgstr ""
msgstr "Сервер није вратио ниједан IMAP директоријум за овај налог."
#: frappe/utils/response.py:343
msgid "The system is being updated. Please refresh again after a few moments."
@ -29089,7 +29089,7 @@ msgstr "Отпреми"
#: frappe/public/js/frappe/file_uploader/FileUploader.vue:663
msgid "Upload Failed"
msgstr ""
msgstr "Отпремање је неуспешно"
#: frappe/public/js/print_format_builder/LetterHeadEditor.vue:93
msgid "Upload Image"
@ -30783,7 +30783,7 @@ msgstr "Ставка бочне траке радног простора"
#: frappe/desk/doctype/workspace/workspace.js:58
msgid "Workspace added to desktop"
msgstr ""
msgstr "Радни простор је додат на радну површину"
#: frappe/public/js/frappe/views/workspace/workspace.js:558
msgid "Workspace {0} created"
@ -31698,7 +31698,7 @@ msgstr "нпр. \"Подршка\", \"Продаја\", \"Петар Петро
#: frappe/public/js/frappe/ui/toolbar/awesome_bar.js:230
msgid "e.g. (55 + 434) / 4"
msgstr ""
msgstr "на пример (55 + 434) / 4"
#. Description of the 'Incoming Server' (Data) field in DocType 'Email Account'
#. Description of the 'Incoming Server' (Data) field in DocType 'Email Domain'
@ -32860,7 +32860,7 @@ msgstr "{0} од {1} ({2} редова са зависним подацима)"
#: frappe/public/js/frappe/views/reports/report_view.js:456
msgid "{0} of {1} records match (filtered on visible rows only)"
msgstr ""
msgstr "{0} од {1} записа одговара критеријуму (филтрирано само по видљивим редовима)"
#: frappe/utils/data.py:1571
msgctxt "Money in words"

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: developers@frappe.io\n"
"POT-Creation-Date: 2026-02-22 09:42+0000\n"
"PO-Revision-Date: 2026-02-23 22:07\n"
"PO-Revision-Date: 2026-02-26 23:27\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Serbian (Latin)\n"
"MIME-Version: 1.0\n"
@ -1358,7 +1358,7 @@ msgstr "Dodaj parametre upita"
#. Label of the add_reply_to_header (Check) field in DocType 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "Add Reply-To header"
msgstr ""
msgstr "Dodaj zaglavlje adrese za odgovor"
#: frappe/core/doctype/user/user.py:860
msgid "Add Roles"
@ -1524,7 +1524,7 @@ msgstr "Dodaj na kontrolnu tablu"
#: frappe/desk/doctype/workspace/workspace.js:49
msgid "Add to Desktop"
msgstr ""
msgstr "Dodaj na radnu površinu"
#: frappe/public/js/frappe/form/sidebar/assign_to.js:110
msgid "Add to ToDo"
@ -1647,7 +1647,7 @@ msgstr "Adrese i kontakti"
#. Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "Addresses added here will be used as the Reply-To header for outgoing emails sent from this account."
msgstr ""
msgstr "Adrese dodate ovde koristiće se kao adresa za odgovor za izlazne imejlove poslate sa ovog naloga."
#. Description of a DocType
#: frappe/custom/doctype/client_script/client_script.json
@ -3083,7 +3083,7 @@ msgstr "Istorija izmena"
#. Label of a Workspace Sidebar Item
#: frappe/workspace_sidebar/users.json
msgid "Audits"
msgstr ""
msgstr "Revizije"
#. Label of the auth_url_data (Code) field in DocType 'Social Login Key'
#: frappe/integrations/doctype/social_login_key/social_login_key.json
@ -3517,7 +3517,7 @@ msgstr "Slika pozadine"
#. Label of a Workspace Sidebar Item
#: frappe/workspace_sidebar/system.json
msgid "Background Job"
msgstr ""
msgstr "Pozadinski zadatak"
#. Label of a Link in the Build Workspace
#. Label of the background_jobs_section (Section Break) field in DocType
@ -4935,7 +4935,7 @@ msgstr "Kliknite da postavite filtere"
#: frappe/desk/page/desktop/desktop.js:1261
msgid "Click to edit"
msgstr ""
msgstr "Kliknite za uređivanje"
#: frappe/public/js/frappe/list/list_view.js:754
msgid "Click to sort by {0}"
@ -6538,7 +6538,7 @@ msgstr "Cijan"
#. 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "DELAY"
msgstr ""
msgstr "ODLAGANJE"
#. Option for the 'Method' (Select) field in DocType 'Recorder'
#. Option for the 'Request Method' (Select) field in DocType 'Webhook'
@ -7366,7 +7366,7 @@ msgstr "Status"
#. Label of the dsn_notify_type (Select) field in DocType 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "Delivery Status Notification Type"
msgstr ""
msgstr "Vrsta obaveštenja o statusu isporuke"
#. Option for the 'Sign ups' (Select) field in DocType 'Social Login Key'
#: frappe/integrations/doctype/social_login_key/social_login_key.json
@ -9431,7 +9431,7 @@ msgstr "Planer omogućen"
#. Label of the enabled (Check) field in DocType 'Notification Settings'
#: frappe/desk/doctype/notification_settings/notification_settings.json
msgid "Enabled System Notification"
msgstr ""
msgstr "Omogućeno sistemsko obaveštenje"
#: frappe/email/doctype/email_account/email_account.py:1101
msgid "Enabled email inbox for user {0}"
@ -10129,7 +10129,7 @@ msgstr "Dodatni parametri"
#. 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "FAILURE"
msgstr ""
msgstr "NEUSPEH"
#. Option for the 'Social Login Provider' (Select) field in DocType 'Social
#. Login Key'
@ -10273,7 +10273,7 @@ msgstr "Neuspešan pokušaj prijave na Frappe Cloud"
#: frappe/email/doctype/email_account/email_account.py:232
msgid "Failed to retrieve the list of IMAP folders from the server. Please ensure the mailbox is accessible and the account has permission to list folders."
msgstr ""
msgstr "Neuspešno preuzimanje liste IMAP direktorijuma sa servera. Proverite da li je poštansko sanduče dostupno i da li nalog ima dozvolu za prikaz direktorijuma."
#: frappe/email/doctype/email_queue/email_queue.py:311
msgid "Failed to send email with subject:"
@ -11340,7 +11340,7 @@ msgstr "Jedinica frakcije"
#. Label of a Desktop Icon
#: frappe/desktop_icon/framework.json
msgid "Framework"
msgstr ""
msgstr "Framework"
#. Option for the 'Social Login Provider' (Select) field in DocType 'Social
#. Login Key'
@ -12236,7 +12236,7 @@ msgstr "Naslov"
#. Label of a Workspace Sidebar Item
#: frappe/workspace_sidebar/system.json
msgid "Health Report"
msgstr ""
msgstr "Izveštaj o stanju sistema"
#. Option for the 'Type' (Select) field in DocType 'Dashboard Chart'
#: frappe/desk/doctype/dashboard_chart/dashboard_chart.json
@ -12655,7 +12655,7 @@ msgstr "IMAP datoteka"
#: frappe/email/doctype/email_account/email_account.py:235
#: frappe/email/doctype/email_account/email_account.py:263
msgid "IMAP Folder Not Found"
msgstr ""
msgstr "IMAP direktorijum nije pronađen"
#. Label of the ip_address (Data) field in DocType 'Activity Log'
#. Label of the ip_address (Data) field in DocType 'Comment'
@ -13415,7 +13415,7 @@ msgstr "Pogrešan verifikacioni kod"
#: frappe/public/js/frappe/views/gantt/gantt_view.js:88
msgid "Incorrect configuration"
msgstr ""
msgstr "Neispravna konfiguracija"
#: frappe/model/document.py:1733
msgid "Incorrect value in row {0}:"
@ -16980,7 +16980,7 @@ msgstr "MyISAM"
#. 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "NEVER"
msgstr ""
msgstr "NIKADA"
#: frappe/workflow/doctype/workflow/workflow.js:19
msgid "NOTE: If you add states or transitions in the table, it will be reflected in the Workflow Builder but you will have to position them manually. Also Workflow Builder is currently in <b>BETA</b>."
@ -17751,7 +17751,7 @@ msgstr "Nema podataka za izvoz"
#: frappe/public/js/frappe/views/reports/query_report.js:1543
msgid "No data to perform this action"
msgstr ""
msgstr "Nema podataka za izvršavanje ove radnje"
#: frappe/contacts/doctype/address/address.py:247
msgid "No default Address Template found. Please create a new one from Setup > Printing and Branding > Address Template."
@ -17796,7 +17796,7 @@ msgstr "Nema dodatnih zapisa"
#: frappe/public/js/frappe/views/reports/report_view.js:337
msgid "No matching entries in the current results"
msgstr ""
msgstr "Nema podudarnih zapisa u trenutnim rezultatima"
#: frappe/templates/includes/search_template.html:49
msgid "No matching records. Search something new"
@ -18430,7 +18430,7 @@ msgstr "OAuth greška"
#. Label of a Workspace Sidebar Item
#: frappe/workspace_sidebar/integrations.json
msgid "OAuth Provider"
msgstr ""
msgstr "OAuth provajder"
#. Name of a DocType
#. Label of a Link in the Integrations Workspace
@ -18699,7 +18699,7 @@ msgstr "Dozvoli uređivanje samo za"
#: frappe/core/doctype/module_def/module_def.py:95
msgid "Only Custom Modules can be renamed."
msgstr ""
msgstr "Isključivo prilagođeni moduli mogu biti preimenovani."
#: frappe/core/doctype/doctype/doctype.py:1652
msgid "Only Options allowed for Data field are:"
@ -19970,7 +19970,7 @@ msgstr "Molimo Vas da dodate validan komentar."
#: frappe/public/js/frappe/views/reports/query_report.js:1544
msgid "Please adjust filters to include some data"
msgstr ""
msgstr "Prilagodite filtere kako biste uključili neke podatke"
#: frappe/core/doctype/user/user.py:1122
msgid "Please ask your administrator to verify your sign-up"
@ -20030,7 +20030,7 @@ msgstr "Molimo Vas da kliknete na sledeći link da biste postavili novu lozinku"
#: frappe/public/js/frappe/views/gantt/gantt_view.js:89
msgid "Please configure the start field for this Doctype in the controller file."
msgstr ""
msgstr "Molimo Vas da konfigurišete početno polje za ovaj DocType u datoteci kontrolera."
#: frappe/www/confirm_workflow_action.html:4
msgid "Please confirm your action to {0} this document."
@ -21133,7 +21133,7 @@ msgstr "Ljubičasto"
#. Label of a Workspace Sidebar Item
#: frappe/workspace_sidebar/integrations.json
msgid "Push Notification"
msgstr ""
msgstr "Push obaveštenje"
#. Name of a DocType
#. Label of a Link in the Integrations Workspace
@ -22226,16 +22226,16 @@ msgstr "Odgovori svima"
#. Name of a DocType
#: frappe/email/doctype/reply_to_address/reply_to_address.json
msgid "Reply To Address"
msgstr ""
msgstr "Adresa za odgovor"
#: frappe/email/doctype/email_account/email_account.py:278
msgid "Reply To email is required"
msgstr ""
msgstr "Adresa za odgovor je obavezna"
#. Label of the reply_to_addresses (Table) field in DocType 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "Reply-To Addresses"
msgstr ""
msgstr "Adrese za odgovor"
#. Label of the report (Check) field in DocType 'Custom DocPerm'
#. Label of the report (Link) field in DocType 'Custom Role'
@ -23277,19 +23277,19 @@ msgstr "SSL/TLS režim"
#. 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "SUCCESS"
msgstr ""
msgstr "USPEH"
#. Option for the 'Delivery Status Notification Type' (Select) field in DocType
#. 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "SUCCESS,FAILURE"
msgstr ""
msgstr "USPEH, NEUSPEH"
#. Option for the 'Delivery Status Notification Type' (Select) field in DocType
#. 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "SUCCESS,FAILURE,DELAY"
msgstr ""
msgstr "USPEH, NEUSPEH, ODLAGANJE"
#: frappe/public/js/frappe/color_picker/color_picker.js:20
msgid "SWATCHES"
@ -24116,7 +24116,7 @@ msgstr "Izaberi dve verzije za prikaz razlika."
#. DocType 'Email Account'
#: frappe/email/doctype/email_account/email_account.json
msgid "Select which delivery events should trigger a delivery status notification (DSN) from the SMTP server."
msgstr ""
msgstr "Izaberite koji događaji isporuke treba da pokrenu obaveštenje o statusu isporuke (DNS) sa SMTP servera."
#: frappe/public/js/frappe/form/link_selector.js:24
#: frappe/public/js/frappe/form/multi_select_dialog.js:80
@ -27099,7 +27099,7 @@ msgstr "Komentar ne može biti prazan"
#: frappe/email/doctype/email_account/email_account.py:290
msgid "The configured SMTP server does not support DSN (Delivery Status Notification)."
msgstr ""
msgstr "Konfigurisani SMTP server ne podržava DSN (obaveštenje o statusu isporuke)."
#: frappe/templates/emails/workflow_action.html:9
msgid "The contents of this email are strictly confidential. Please do not forward this email to anyone."
@ -27161,7 +27161,7 @@ msgstr "Sledeća skripta zaglavlja će dodati trenutni datum u element klase 'he
#: frappe/email/doctype/email_account/email_account.py:257
msgid "The following configured IMAP folder(s) were not found on the server:<br><ul>{0}</ul>Please verify the folder names exactly as they appear on the server (folder names are case-sensitive)."
msgstr ""
msgstr "Sledeći konfigurisani IMAP direktorijumi nisu pronađeni na serveru:<br><ul>{0}</ul>Molimo Vas da proverite nazive direktorijuma tačno onako kako su prikazani na serveru (velika i mala slova su bitna za nazive datoteka)."
#: frappe/core/doctype/data_import/importer.py:1092
msgid "The following values are invalid: {0}. Values must be one of {1}"
@ -27253,7 +27253,7 @@ msgstr "Izabrani dokument {0} nije {1}."
#: frappe/email/doctype/email_account/email_account.py:247
msgid "The server did not return any IMAP folders for this account."
msgstr ""
msgstr "Server nije vratio nijedan IMAP direktorijum za ovaj nalog."
#: frappe/utils/response.py:343
msgid "The system is being updated. Please refresh again after a few moments."
@ -29089,7 +29089,7 @@ msgstr "Otpremi"
#: frappe/public/js/frappe/file_uploader/FileUploader.vue:663
msgid "Upload Failed"
msgstr ""
msgstr "Otpremanje je neuspešno"
#: frappe/public/js/print_format_builder/LetterHeadEditor.vue:93
msgid "Upload Image"
@ -30783,7 +30783,7 @@ msgstr "Stavka bočne trake radnog prostora"
#: frappe/desk/doctype/workspace/workspace.js:58
msgid "Workspace added to desktop"
msgstr ""
msgstr "Radni prostor je dodat na radnu površinu"
#: frappe/public/js/frappe/views/workspace/workspace.js:558
msgid "Workspace {0} created"
@ -31698,7 +31698,7 @@ msgstr "npr. \"Podrška\", \"Prodaja\", \"Petar Petrović\""
#: frappe/public/js/frappe/ui/toolbar/awesome_bar.js:230
msgid "e.g. (55 + 434) / 4"
msgstr ""
msgstr "na primer (55 + 434) / 4"
#. Description of the 'Incoming Server' (Data) field in DocType 'Email Account'
#. Description of the 'Incoming Server' (Data) field in DocType 'Email Domain'
@ -32860,7 +32860,7 @@ msgstr "{0} od {1} ({2} redova sa zavisnim podacima)"
#: frappe/public/js/frappe/views/reports/report_view.js:456
msgid "{0} of {1} records match (filtered on visible rows only)"
msgstr ""
msgstr "{0} od {1} zapisa odgovara kriterijumu (filtrirano samo po vidljivim redovima)"
#: frappe/utils/data.py:1571
msgctxt "Money in words"

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: developers@frappe.io\n"
"POT-Creation-Date: 2026-02-22 09:42+0000\n"
"PO-Revision-Date: 2026-02-23 22:07\n"
"PO-Revision-Date: 2026-02-25 23:14\n"
"Last-Translator: developers@frappe.io\n"
"Language-Team: Swedish\n"
"MIME-Version: 1.0\n"
@ -2868,7 +2868,7 @@ msgstr "Tilldelning Klar"
#. Label of the assignment_days (Table) field in DocType 'Assignment Rule'
#: frappe/automation/doctype/assignment_rule/assignment_rule.json
msgid "Assignment Days"
msgstr "Automation Dagar"
msgstr "Tilldelning Dagar"
#. Name of a DocType
#. Label of the assignment_rule (Link) field in DocType 'ToDo'
@ -2876,7 +2876,7 @@ msgstr "Automation Dagar"
#: frappe/automation/doctype/assignment_rule/assignment_rule.json
#: frappe/desk/doctype/todo/todo.json frappe/workspace_sidebar/automation.json
msgid "Assignment Rule"
msgstr "Automation Regel"
msgstr "Tilldelning Regel"
#. Name of a DocType
#: frappe/automation/doctype/assignment_rule_day/assignment_rule_day.json
@ -2890,13 +2890,13 @@ msgstr "Automation Regel Användare"
#: frappe/automation/doctype/assignment_rule/assignment_rule.py:55
msgid "Assignment Rule is not allowed on document type {0}"
msgstr "Automation Regel är ej tillåten på dokument typ {0}"
msgstr "Tilldelning Regel är ej tillåten på dokument typ {0}"
#. Label of the assignment_rules_section (Section Break) field in DocType
#. 'Assignment Rule'
#: frappe/automation/doctype/assignment_rule/assignment_rule.json
msgid "Assignment Rules"
msgstr "Automation Regler"
msgstr "Tilldelning Regler"
#: frappe/desk/doctype/notification_log/notification_log.py:153
msgid "Assignment Update on {0}"

View file

@ -894,6 +894,12 @@ def get_field_currency(df, doc=None):
if frappe.get_meta(doc.parenttype).has_field(df.get("options")):
# only get_value if parent has currency field
currency = frappe.db.get_value(doc.parenttype, doc.parent, df.get("options"))
if not currency:
# Parent may not be in DB yet (new document being saved).
# Use the in-memory parent document reference if available.
parent = getattr(doc, "parent_doc", None)
if parent:
currency = parent.get(df.get("options"))
if currency:
frappe.local.field_currency.setdefault((doc.doctype, ref_docname), frappe._dict()).setdefault(

View file

@ -127,12 +127,12 @@
{
"fieldname": "image_height",
"fieldtype": "Float",
"label": "Image Height"
"label": "Image Height (px)"
},
{
"fieldname": "image_width",
"fieldtype": "Float",
"label": "Image Width"
"label": "Image Width (px)"
},
{
"depends_on": "eval:doc.footer_source==='Image' && doc.letter_head_name",
@ -148,12 +148,12 @@
{
"fieldname": "footer_image_height",
"fieldtype": "Float",
"label": "Image Height"
"label": "Image Height (px)"
},
{
"fieldname": "footer_image_width",
"fieldtype": "Float",
"label": "Image Width"
"label": "Image Width (px)"
},
{
"fieldname": "footer_align",
@ -203,7 +203,7 @@
"links": [],
"make_attachments_public": 1,
"max_attachments": 3,
"modified": "2026-02-24 20:53:14.297567",
"modified": "2026-02-25 14:37:57.061516",
"modified_by": "Administrator",
"module": "Printing",
"name": "Letter Head",

View file

@ -35,6 +35,7 @@ frappe.PrintFormatBuilder = class PrintFormatBuilder {
this.show_start();
} else {
this.page.set_title(this.print_format.name);
this.page.sidebar.toggle(true);
this.setup_print_format();
}
}
@ -65,6 +66,7 @@ frappe.PrintFormatBuilder = class PrintFormatBuilder {
this.page.main.html(frappe.render_template("print_format_builder_start", {}));
this.page.clear_actions();
this.page.set_title(__("Print Format Builder"));
this.page.sidebar.toggle(false);
this.start_edit_print_format();
this.start_new_print_format();
}

View file

@ -33,7 +33,14 @@ $(document).ready(function () {
!frappe.is_mobile() &&
frappe.user.has_role("System Manager");
if (visiblity_condition && isFCUser) {
addChatBubble();
frappe.router.on("change", function () {
if (frappe.get_route()[0] == "") {
addChatBubble();
toggleChatBubble(true);
} else {
toggleChatBubble(false);
}
});
}
if (isFCUser) {
$.extend(card_args, {
@ -89,13 +96,18 @@ function openFrappeCloudDashboard() {
}
function addChatBubble() {
if (checkBusinessHours()) {
const all_apps = frappe.utils.get_installed_apps();
const desk_apps = ["erpnext", "hrms"];
const apps_allowed = frappe.utils.is_sub_array(all_apps, desk_apps);
if (checkBusinessHours && apps_allowed) {
let chat_banner = document.createElement("script");
chat_banner.setAttribute("id", "chat_widget_trigger");
chat_banner.innerHTML =
'(function(d,t){var BASE_URL="https://chat.frappe.cloud";var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src=BASE_URL+"/packs/js/sdk.js";g.async=true;s.parentNode.insertBefore(g,s);g.onload=function(){window.chatwootSDK.run({websiteToken:"LdmfJzftdJGEcFjoTqk8CrSq",baseUrl:BASE_URL})}})(document,"script");';
document.body.append(chat_banner);
const root = document.documentElement;
root.style.setProperty("--s-700", "var(--gray-50)");
root.style.setProperty("--s-700", "var(--gray-500)");
}
}
@ -103,5 +115,15 @@ function checkBusinessHours() {
let currentTime = new Date();
const istTime = new Date(currentTime.toLocaleString("en-US", { timeZone: "Asia/Kolkata" }));
return istTime.getHours() >= 11 && istTime.getHours() <= 18;
return istTime.getHours() >= 11 && istTime.getHours() < 18;
}
function toggleChatBubble(toggle) {
if (toggle) {
$(".woot-widget-holder").show();
$("#cw-bubble-holder").show();
} else {
$(".woot-widget-holder").hide();
$("#cw-bubble-holder").hide();
}
}

View file

@ -514,22 +514,7 @@ function check_restrictions(file) {
return is_correct_type && valid_file_size;
}
function set_loading_state(dialog, loading) {
let $btn = dialog?.get_primary_btn();
if (loading) {
$btn?.css("width", $btn.outerWidth());
$btn?.html(`<i class="fa fa-spinner fa-spin"></i>`);
$btn?.prop("disabled", true);
dialog?.get_secondary_btn().prop("disabled", true);
} else {
$btn?.css("width", "");
$btn?.html(__("Upload"));
$btn?.prop("disabled", false);
dialog?.get_secondary_btn().prop("disabled", false);
}
}
function upload_files(dialog) {
set_loading_state(dialog, true);
function upload_files() {
if (show_file_browser.value) {
promise = upload_via_file_browser();
} else if (show_web_link.value) {
@ -542,7 +527,7 @@ function upload_files(dialog) {
} else {
promise = frappe.run_serially(files.value.map((file, i) => () => upload_file(file, i)));
}
return promise.finally(() => set_loading_state(dialog, false));
return promise;
}
function upload_via_file_browser() {
let selected_file = file_browser.value.selected_node;

View file

@ -151,6 +151,7 @@ class FileUploader {
const dialog_opts = {
title: title || __("Upload"),
primary_action_label: __("Upload"),
primary_action_loading_label: __("Uploading"),
primary_action: () => this.upload_files(),
on_page_show: () => {
this.uploader.wrapper_ready = true;

View file

@ -37,24 +37,35 @@ export default class Column {
}
resize_all_columns() {
// distribute all columns equally
let columns = this.section.wrapper.find(".form-column").length;
// distribute visible columns equally
let all_columns = this.section.wrapper.find(".form-column");
let visible_columns = all_columns.filter(":not(.hide-control)");
let columns = visible_columns.length || all_columns.length;
let colspan = cint(12 / columns);
if (columns == 5) {
colspan = 20;
}
this.section.wrapper
.find(".form-column")
.removeClass()
.addClass("form-column")
.addClass("col-sm-" + colspan);
all_columns.each(function () {
const $col = $(this);
const is_hidden = $col.hasClass("hide-control");
$col.removeClass()
.addClass("form-column")
.addClass("col-sm-" + colspan);
if (is_hidden) {
$col.addClass("hide-control");
}
});
}
add_field() {}
refresh() {
if (!this.df) return;
const hide = this.df.hidden || this.df.hidden_due_to_dependency;
this.wrapper.toggleClass("hide-control", !!hide);
this.resize_all_columns();
this.section.refresh();
}
}

View file

@ -2,7 +2,7 @@ import Picker from "../../color_picker/color_picker";
frappe.ui.form.ControlColor = class ControlColor extends frappe.ui.form.ControlData {
make_input() {
this.df.placeholder = this.df.placeholder || __("Choose a color");
this.df.placeholder = __(this.df.placeholder) || __("Choose a color");
super.make_input();
this.make_color_input();
}

View file

@ -241,7 +241,7 @@ frappe.ui.form.ControlData = class ControlData extends frappe.ui.form.ControlInp
this.$input
.attr("data-fieldtype", this.df.fieldtype)
.attr("data-fieldname", this.df.fieldname)
.attr("placeholder", this.df.placeholder || "");
.attr("placeholder", __(this.df.placeholder || ""));
if (this.doctype) {
this.$input.attr("data-doctype", this.doctype);
}

View file

@ -2,7 +2,7 @@ import Picker from "../../icon_picker/icon_picker";
frappe.ui.form.ControlIcon = class ControlIcon extends frappe.ui.form.ControlData {
make_input() {
this.df.placeholder = this.df.placeholder || __("Choose an icon");
this.df.placeholder = __(this.df.placeholder) || __("Choose an icon");
super.make_input();
this.get_all_icons();
this.make_icon_input();

View file

@ -28,7 +28,7 @@ frappe.ui.form.ControlSelect = class ControlSelect extends frappe.ui.form.Contro
const placeholder_html = `<div class="placeholder ellipsis text-extra-muted ${
is_xs_input ? "xs" : ""
}">
<span>${this.df.placeholder}</span>
<span>${__(this.df.placeholder)}</span>
</div>`;
if (this.only_input) {
this.$wrapper.append(placeholder_html);

View file

@ -235,7 +235,7 @@ frappe.ui.form.ControlTextEditor = class ControlTextEditor extends frappe.ui.for
theme: this.df.theme || "snow",
readOnly: this.disabled || this.df.read_only,
bounds: this.quill_container[0],
placeholder: this.df.placeholder || "",
placeholder: __(this.df.placeholder || ""),
};
// In a grid row where space is constrained, hide the toolbar.

View file

@ -1574,15 +1574,10 @@ frappe.ui.form.Form = class FrappeForm {
var scroll_to = frappe.route_options.scroll_to;
delete frappe.route_options.scroll_to;
var selector = [];
for (var key in scroll_to) {
var value = scroll_to[key];
selector.push(repl('[data-%(key)s="%(value)s"]', { key: key, value: value }));
}
selector = $(selector.join(" "));
if (selector.length) {
frappe.utils.scroll_to(selector);
if (this.scroll_to_field(scroll_to)) {
const url = new URL(window.location);
url.searchParams.delete("scroll_to");
history.replaceState(null, null, url);
}
} else if (window.location.hash) {
if ($(window.location.hash).length) {
@ -2050,6 +2045,9 @@ frappe.ui.form.Form = class FrappeForm {
if (df.fieldname === fieldname && isLinkToParent) {
new_doc[df.fieldname] = me.doc.name;
}
if (df.fieldtype === "Table" && df.options && df.reqd) {
me.set_link_field(df.options, new_doc[df.fieldname][0]);
}
return;
}
@ -2103,7 +2101,7 @@ frappe.ui.form.Form = class FrappeForm {
}
// scroll to input
frappe.utils.scroll_to($el, true, 15);
frappe.utils.scroll_to($el, true, 15, $(".main-section"));
// focus if text field
if (focus) {

View file

@ -1033,7 +1033,7 @@ export default class GridRow {
let is_focused = false;
var $col = $(
`<div class="col grid-static-col col-xs-${colsize} ${add_class}" style="${add_style}"></div>`
`<div class="col grid-static-col flex col-xs-${colsize} ${add_class}" style="${add_style}"></div>`
)
.attr("data-fieldname", df.fieldname)
.attr("data-fieldtype", df.fieldtype)
@ -1095,7 +1095,9 @@ export default class GridRow {
return out;
});
$col.field_area = $('<div class="field-area"></div>').appendTo($col).toggle(false);
$col.field_area = $('<div class="field-area flex flex-grow-1"></div>')
.appendTo($col)
.toggle(false);
$col.static_area = $('<div class="static-area ellipsis"></div>').appendTo($col).html(txt);
// set title attribute to see full label for columns in the heading row

View file

@ -745,7 +745,7 @@ frappe.ui.form.Layout = class Layout {
if (f.df.fieldtype === "Table") {
for (const row of f.grid?.grid_rows || []) {
row.refresh_dependency();
row?.refresh_dependency();
}
}
}

View file

@ -20,7 +20,8 @@ frappe.ui.form.LinkedWith = class LinkedWith {
make_dialog() {
this.dialog = new frappe.ui.Dialog({
title: __("Linked With"),
title: __("Links"),
minimizable: true,
});
this.dialog.on_page_show = () => {
@ -39,22 +40,40 @@ frappe.ui.form.LinkedWith = class LinkedWith {
make_html() {
let html = "";
const linked_docs = this.frm.__linked_docs;
const linked_doctypes = Object.keys(linked_docs);
const linked_doctypes = Object.keys(linked_docs).filter((dt) => {
const entry = linked_docs[dt];
return (entry.docs && entry.docs.length) || entry.hidden_count > 0;
});
if (linked_doctypes.length === 0) {
html = __("Not Linked to any record");
} else {
html = linked_doctypes
.map((doctype) => {
const docs = linked_docs[doctype];
return `
<div class="list-item-table margin-bottom">
${this.make_doc_head(doctype)}
${docs.map((doc) => this.make_doc_row(doc, doctype)).join("")}
html = `
<div class="margin-bottom">
${__("Following documents are linked with {0}", [
frappe.utils
.get_form_link(this.frm.doctype, this.frm.docname, true)
.bold(),
])}
</div>
`;
})
.join("");
${linked_doctypes
.map((doctype) => {
const { docs, hidden_count } = linked_docs[doctype];
let rows = (docs || [])
.map((doc) => this.make_doc_row(doc, doctype))
.join("");
if (hidden_count > 0) {
rows += this.make_hidden_count_row(hidden_count);
}
return `
<div class="list-item-table margin-bottom">
${this.make_doc_head(doctype)}
${rows}
</div>
`;
})
.join("")}
`;
}
$(this.dialog.body).html(html);
@ -68,6 +87,16 @@ frappe.ui.form.LinkedWith = class LinkedWith {
`;
}
make_hidden_count_row(count) {
return `<div class="list-row-container">
<div class="level list-row small text-muted">
<div class="level-left">
${count == 1 ? __("{0} restricted document", [count]) : __("{0} restricted documents", [count])}
</div>
</div>
</div>`;
}
make_doc_row(doc, doctype) {
return `<div class="list-row-container">
<div class="level list-row small">

View file

@ -48,8 +48,7 @@ export class ReminderManager {
],
primary_action_label: __("Create"),
primary_action: () => {
this.create_reminder();
this.dialog.hide();
return this.create_reminder().then(() => this.dialog.hide());
},
secondary_action_label: __("Cancel"),
secondary_action: () => {
@ -84,7 +83,7 @@ export class ReminderManager {
}
create_reminder() {
frappe
return frappe
.xcall("frappe.automation.doctype.reminder.reminder.create_new_reminder", {
remind_at: this.dialog.get_value("remind_at"),
description: this.dialog.get_value("description"),

View file

@ -231,7 +231,7 @@ frappe.ui.form.check_mandatory = function (frm) {
}
function scroll_to(fieldname) {
if (frm.scroll_to_field(fieldname)) {
if (frm.scroll_to_field(fieldname, false)) {
frm.scroll_set = true;
}
}

View file

@ -114,9 +114,7 @@ frappe.ui.form.AssignToDialog = class AssignToDialog {
let args = me.dialog.get_values();
if (args && args.assign_to) {
me.dialog.set_message("Assigning...");
frappe.call({
return frappe.call({
method: me.method,
args: $.extend(args, {
doctype: me.doctype,
@ -125,15 +123,12 @@ frappe.ui.form.AssignToDialog = class AssignToDialog {
bulk_assign: me.bulk_assign || false,
re_assign: me.re_assign || false,
}),
btn: me.dialog.get_primary_btn(),
callback: function (r) {
if (!r.exc) {
if (me.callback) {
me.callback(r);
}
me.dialog && me.dialog.hide();
} else {
me.dialog.clear_message();
}
},
});

View file

@ -180,8 +180,18 @@ frappe.ui.form.Attachments = class Attachments {
file_url = "/files/" + attachment.file_name;
}
}
const is_web_url = /^(https?:)?\/\//i.test(file_url);
file_url = encodeURI(file_url);
// hash is not escaped, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI
return encodeURI(file_url).replace(/#/g, "%23");
// only encode hash if it's a local file path, not a web URL
if (!is_web_url) {
file_url = file_url.replace(/#/g, "%23");
}
return file_url;
}
get_file_id_from_file_url(file_url) {
var fid;

View file

@ -452,9 +452,7 @@ export default class BulkOperations {
primary_action: () => {
let args = dialog.get_values();
if (args && args.tags) {
dialog.set_message("Adding Tags...");
frappe.call({
return frappe.call({
method: "frappe.desk.doctype.tag.tag.add_tags",
args: {
tags: args.tags,

View file

@ -120,7 +120,7 @@ export default class ListFilter {
fields: fields,
primary_action_label: __("Create"),
primary_action: (values) => {
this.bind_save_filter(dialog, values.filter_name, values?.is_global);
return this.bind_save_filter(dialog, values.filter_name, values?.is_global);
},
});
dialog.show();
@ -138,7 +138,7 @@ export default class ListFilter {
dialog.fields_dict.filter_name.set_description(__("Duplicate Filter Name"));
return;
}
this.save_filter(value, is_global).then(() => {
return this.save_filter(value, is_global).then(() => {
this.refresh_list_filter();
dialog.hide();
});

View file

@ -332,7 +332,8 @@ $.extend(frappe.meta, {
} else if (df && df.fieldtype === "Currency") {
precision = cint(frappe.defaults.get_default("currency_precision"));
if (!precision) {
var number_format = get_number_format();
var currency = frappe.meta.get_field_currency(df, doc);
var number_format = get_number_format(currency);
var number_format_info = get_number_format_info(number_format);
precision = number_format_info.precision;
}

View file

@ -48,9 +48,6 @@ frappe.ui.AddressAutocompleteDialog = class AddressAutocompleteDialog {
],
primary_action_label: __("Create Address"),
primary_action: () => {
// Insert the address into the database
dialog.hide();
const address = this.parse_selected_value();
address["doctype"] = "Address";
address["links"] = [
@ -59,7 +56,8 @@ frappe.ui.AddressAutocompleteDialog = class AddressAutocompleteDialog {
link_name: this.link_name,
},
];
frappe.db.insert(address).then((doc) => {
return frappe.db.insert(address).then((doc) => {
dialog.hide();
this.after_insert && this.after_insert(doc);
});
},

View file

@ -207,6 +207,7 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup {
this.has_primary_action = true;
var me = this;
const primary_btn = this.get_primary_btn().removeClass("hide").html(label);
const spinner = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" style="width: 13px; height: 13px; animation: spin 1s linear infinite;"><circle opacity=".25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/><path opacity=".25" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/></svg>`;
if (typeof click == "function") {
primary_btn.off("click").on("click", function () {
me.primary_action_fulfilled = true;
@ -215,7 +216,35 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup {
// if no values then return
var values = me.get_values();
if (!values) return;
click && click.apply(me, [values]);
const action = click.apply(me, [values]);
if (action && typeof action.then === "function") {
const loading_label = me.primary_action_loading_label;
primary_btn
.css({
"min-width": primary_btn.outerWidth(),
"min-height": primary_btn.outerHeight(),
})
.prop("disabled", true)
.addClass("btn-primary-dark")
.html(
`<div class="d-flex align-items-center justify-content-center" style="gap: 0.45rem;">
${spinner}
${
loading_label
? `<span class="text-muted" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${loading_label}</span>`
: ""
}
</div>`
);
Promise.resolve(action).finally(() => {
primary_btn
.css({ "min-width": "", "min-height": "" })
.prop("disabled", false)
.removeClass("btn-primary-dark")
.html(label);
});
}
});
}
return primary_btn;

View file

@ -18,12 +18,35 @@ frappe.ui.FieldGroup = class FieldGroup extends frappe.ui.form.Layout {
}
}
resolve_date_default_keywords(def_value, fieldtype) {
if (!def_value || typeof def_value !== "string") return def_value;
def_value = def_value.toLowerCase();
if (def_value == "today" && fieldtype == "Date") {
return frappe.datetime.get_today();
}
if (def_value == "now") {
if (fieldtype == "Datetime") {
return frappe.datetime.now_datetime();
}
if (fieldtype == "Time") {
return frappe.datetime.now_time();
}
}
return def_value;
}
make() {
let me = this;
if (this.fields) {
super.make();
this.refresh();
// set default
let defaults = {};
$.each(this.fields_list, function (i, field) {
let def_value = field.df["default"];
// loose equality check matches undefined also
@ -33,12 +56,14 @@ frappe.ui.FieldGroup = class FieldGroup extends frappe.ui.form.Layout {
)
return;
if (def_value == "Today" && field.df["fieldtype"] == "Date") {
def_value = frappe.datetime.get_today();
if (["Date", "Datetime", "Time"].includes(field.df.fieldtype)) {
def_value = me.resolve_date_default_keywords(def_value, field.df.fieldtype);
}
field.set_input(def_value);
// if default and has depends_on, render its fields.
defaults[field.df.fieldname] = def_value;
});
this.set_values(defaults).then(() => {
me.refresh_dependency();
});

View file

@ -206,7 +206,7 @@ frappe.msgprint = function (msg, title, is_minimizable, re_route) {
typeof data.primary_action.server_action === "string"
) {
data.primary_action.action = () => {
frappe.call({
return frappe.call({
method: data.primary_action.server_action,
args: data.primary_action.args,
callback() {

View file

@ -306,7 +306,7 @@ function markReset(step) {
</div>
<div v-else>
<span
class="text-base onb-step-text"
class="text-base onb-step-text text-extra-muted"
style="text-decoration-line: line-through"
>
{{ __(step.action_label) }}

View file

@ -264,7 +264,7 @@ frappe.dashboard_utils = {
primary_action: (values) => {
values.name = docname;
values.set_standard = frappe.boot.developer_mode;
frappe.xcall(method, { args: values }).then(() => {
return frappe.xcall(method, { args: values }).then(() => {
let dashboard_route_html = `<a href = "/desk/dashboard/${values.dashboard}">${values.dashboard}</a>`;
let message = __("{0} {1} added to Dashboard {2}", [
doctype,
@ -273,9 +273,8 @@ frappe.dashboard_utils = {
]);
frappe.msgprint(message);
dialog.hide();
});
dialog.hide();
},
});

View file

@ -328,7 +328,7 @@ Object.assign(frappe.utils, {
scroll_top =
typeof element == "number"
? element - cint(additional_offset)
: this.get_scroll_position(element, additional_offset);
: this.get_scroll_position(element, additional_offset, element_to_be_scrolled);
}
if (scroll_top < 0) {
@ -366,10 +366,33 @@ Object.assign(frappe.utils, {
element_to_be_scrolled.scrollTop(scroll_top);
}
},
get_scroll_position: function (element, additional_offset) {
let header_offset =
$(".navbar").height() + $(".page-head:visible").height() || $(".navbar").height();
return $(element).offset().top - header_offset - cint(additional_offset);
get_scroll_position: function (element, additional_offset, element_to_be_scrolled) {
const get_offset_relative_to_container = () => {
let offset = 0;
let el = element instanceof HTMLElement ? element : element[0];
const container = element_to_be_scrolled ? element_to_be_scrolled[0] : null;
while (el && el !== container && el.offsetParent) {
offset += el.offsetTop;
el = el.offsetParent;
}
return offset;
};
const get_header_offset = () => {
const navbar_height = $(".navbar").height() || 0;
const page_head_height = $(".page-head:visible").height() || 0;
const tabs_container_height = $(".form-tabs-list:visible").height() || 0;
return navbar_height + page_head_height + tabs_container_height;
};
const element_offset_top = get_offset_relative_to_container();
const header_offset = get_header_offset();
return element_offset_top - header_offset - cint(additional_offset);
},
filter_dict: function (dict, filters) {
var ret = [];
@ -2229,4 +2252,16 @@ Object.assign(frappe.utils, {
}
return value;
},
get_installed_apps() {
return frappe.boot.app_data.map((app) => {
return app.app_name;
});
},
is_sub_array(big, small) {
let i = 0;
for (let num of big) {
if (num === small[i]) i++;
}
return i === small.length;
},
});

View file

@ -449,7 +449,7 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
: chart.chart_type;
chart.document_type = this.doctype;
chart.filters_json = "[]";
frappe
return frappe
.xcall(
"frappe.desk.doctype.dashboard_chart.dashboard_chart.create_dashboard_chart",
{ args: chart }
@ -460,6 +460,7 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
name: doc.chart_name,
label: chart.label,
});
dialog.hide();
});
} else {
this.chart_group.new_widget.on_create({
@ -467,8 +468,8 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
label: __(chart.chart),
name: chart.chart,
});
dialog.hide();
}
dialog.hide();
},
});
dialog.show();

View file

@ -17,7 +17,7 @@ frappe.views.InteractionComposer = class InteractionComposer {
fields: me.get_fields(),
primary_action_label: __("Create"),
primary_action: function () {
me.create_action();
return me.create_action();
},
});

View file

@ -2109,7 +2109,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
},
],
primary_action: (values) => {
frappe.call({
return frappe.call({
method: "frappe.desk.query_report.save_report",
args: {
reference_report: this.report_name,

View file

@ -99,16 +99,6 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
});
}
setup_paging_area() {
super.setup_paging_area();
const message = __(
"For comparison, use >5, <10 or =324. For ranges, use 5:10 (for values between 5 & 10)."
);
this.$paging_area.before(
`<span class="comparison-message text-extra-muted">${message}</span>`
);
}
setup_sort_selector() {
this.sort_selector = new frappe.ui.SortSelector({
parent: this.filter_area.$filter_list_wrapper,
@ -430,6 +420,8 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
}
setup_inline_filter_observer() {
this.setup_inline_filter_help_icons();
this.$datatable_wrapper.on(
"keyup",
".dt-filter",
@ -439,6 +431,29 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
);
}
setup_inline_filter_help_icons() {
const message = __(
"For comparison, use >5, <10 or =324.\nFor ranges, use 5:10 (for values between 5 & 10)."
);
this.$datatable_wrapper.find(".dt-filter").each((_, input) => {
const $input = $(input);
if ($input.siblings(".comparison-help-icon").length) {
return;
}
const $icon = $(
`<span class="comparison-help-icon text-muted" title="${message}">${frappe.utils.icon(
"info",
"xs"
)}</span>`
);
$input.after($icon);
});
}
update_count_for_inline_filter() {
if (!this.datatable) return;

View file

@ -570,8 +570,14 @@ export default class ChartWidget extends Widget {
let setup_dashboard_chart = () => {
const chart_args = this.get_chart_args();
const is_circular_chart = ["Pie", "Donut", "Percentage"].includes(this.chart_doc.type);
if (!this.dashboard_chart) {
this.dashboard_chart = frappe.utils.make_chart(this.chart_wrapper[0], chart_args);
} else if (is_circular_chart) {
this.chart_wrapper.empty();
delete this.dashboard_chart;
this.dashboard_chart = frappe.utils.make_chart(this.chart_wrapper[0], chart_args);
} else {
this.dashboard_chart.update(this.data);
}
@ -619,6 +625,7 @@ export default class ChartWidget extends Widget {
colors: colors,
height: this.height,
maxSlices: this.chart_doc.number_of_groups || max_slices,
truncateLegends: 0,
axisOptions: {
xIsSeries: this.chart_doc.timeseries,
shortenYAxisNumbers: 1,

View file

@ -309,8 +309,8 @@
border-radius: 0px;
border: 0px;
padding-top: 10px;
padding-bottom: calc(var(--padding-md) - 3px);
height: auto;
padding-bottom: 10px;
height: 100%;
}
.link-btn {
@ -430,6 +430,7 @@
.frappe-control {
margin-bottom: 0px !important;
position: relative;
flex-grow: 1;
}
.col-sm-6 {
@ -779,7 +780,7 @@
.data-row.row {
flex-wrap: nowrap;
}
.frappe-control[data-fieldtype="Table"].form-group:has(.column-limit-reached) {
.frappe-control[data-fieldtype="Table"].form-group:has(.column-limit-reached):not(.highlight) {
overflow-x: clip;
}
.column-limit-reached {

View file

@ -2,6 +2,12 @@ h5.modal-title {
margin: 0px !important;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
// Hack to fix incorrect padding applied by Bootstrap
body.modal-open[style^="padding-right"] {
padding-right: 12px !important;
@ -103,6 +109,11 @@ body.modal-open[style^="padding-right"] {
button:not(:last-child) {
margin-right: var(--margin-xs);
}
.btn-primary-dark {
min-width: 80px;
max-width: 200px;
}
}
& > * {

View file

@ -371,6 +371,27 @@ input.list-header-checkbox {
.list-item-table {
border: 1px solid $border-color;
border-radius: 3px;
.list-row-head {
border-radius: unset;
}
.list-row-container {
border-bottom: 1px solid $border-color;
border-radius: unset;
&:last-child {
border-bottom: none;
}
}
.list-row-container:hover {
border-radius: unset;
}
.list-row-container .list-row {
border-bottom: none;
}
}
.list-item {

View file

@ -93,6 +93,37 @@
border-radius: var(--border-radius);
}
}
.report-view {
.layout-main-section {
height: calc(100vh - var(--page-head-height));
display: flex;
flex-direction: column;
overflow: hidden;
.page-form {
flex-shrink: 0;
}
.frappe-list {
flex-grow: 1;
display: flex;
flex-direction: column;
overflow: hidden;
.result,
.no-result {
flex-grow: 1;
overflow: auto;
}
.comparison-message {
display: none;
}
}
}
}
@include media-breakpoint-up(sm) {
.report-view {
width: calc(100% - 220px);
@ -129,6 +160,36 @@
@include get_textstyle("base", "regular");
}
.report-view {
.datatable .dt-row-filter .dt-cell__content {
position: relative;
}
.datatable .dt-row-filter .dt-filter.dt-input {
padding-inline-end: 1.5rem;
}
.datatable .dt-row-filter .comparison-help-icon {
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-50%);
display: inline-flex;
opacity: 0;
pointer-events: none;
transition: opacity 0.15s ease;
.icon {
stroke: currentColor;
}
}
.datatable .dt-row-filter .dt-filter.dt-input:focus + .comparison-help-icon {
opacity: 1;
pointer-events: auto;
}
}
.list-count {
margin-right: var(--margin-sm);
@include get_textstyle("base", "regular");

View file

@ -369,6 +369,12 @@ def prepare_header_footer(soup: BeautifulSoup):
# {"header-html": "/tmp/frappe-pdf-random.html"}
options[html_id] = fname
if html_id == "header-html":
options["margin-top"] = "25mm"
elif html_id == "footer-html":
options["margin-bottom"] = "25mm"
else:
if html_id == "header-html":
options["margin-top"] = "15mm"

View file

@ -16,6 +16,10 @@ frappe.ui.form.on("Website Settings", {
frm.add_custom_button(__("View Website"), () => {
window.open("/", "_blank");
});
// Check if templates have fields and show/hide edit button
frm.events.check_template_has_fields(frm, "navbar_template");
frm.events.check_template_has_fields(frm, "footer_template");
},
set_banner_from_image: function (frm) {
@ -100,11 +104,36 @@ frappe.ui.form.on("Website Settings", {
frappe.show_alert(__("Please select {0}", [frm.get_docfield(template_field).label]));
return;
}
let values = JSON.parse(frm.doc[values_field] || "{}");
open_web_template_values_editor(template, values).then((new_values) => {
frm.set_value(values_field, JSON.stringify(new_values));
});
},
check_template_has_fields(frm, template_field) {
let template = frm.doc[template_field];
let button_field = "edit_" + template_field + "_values";
if (!template || template === "Standard Navbar" || template === "Standard Footer") {
frm.toggle_display(button_field, false);
return;
}
frappe.model.with_doc("Web Template", template, () => {
let doc = frappe.model.get_doc("Web Template", template);
let has_fields = doc.fields && doc.fields.length > 0;
frm.toggle_display(button_field, has_fields);
});
},
navbar_template(frm) {
frm.events.check_template_has_fields(frm, "navbar_template");
},
footer_template(frm) {
frm.events.check_template_has_fields(frm, "footer_template");
},
});
frappe.ui.form.on("Top Bar Item", {

View file

@ -31,7 +31,7 @@ frappe.ui.form.on("Website Slideshow", {
],
primary_action_label: __("Add to table"),
primary_action: ({ reference_doctype, reference_name }) => {
frappe.db
return frappe.db
.get_list("File", {
fields: ["file_url"],
filters: {

View file

@ -20,7 +20,7 @@ dependencies = [
# We depend on internal attributes,
# do NOT add loose requirements on PyMySQL versions.
"PyMySQL==1.1.2",
"pypdf==6.7.1",
"pypdf==6.7.3",
"PyPika @ git+https://github.com/frappe/pypika@2c50e6142b2d61d2d243e466fdd5dc03b3d918f2",
"mysqlclient==2.2.7",
"PyQRCode~=1.2.1",

View file

@ -2055,9 +2055,9 @@ mime@^1.4.1:
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
minimatch@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.3.tgz#6a5cba9b31f503887018f579c89f81f61162e624"
integrity sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==
version "3.1.5"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e"
integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==
dependencies:
brace-expansion "^1.1.7"