@@ -144,7 +145,7 @@ export default {
frappe.set_route("recorder");
this.clear();
});
-
+ this.$root.page.add_menu_item("Export data", () => this.export_data());
},
computed: {
pages: function() {
@@ -239,8 +240,36 @@ export default {
});
}
},
- route_to_request_detail(id) {
- this.$router.push({name: 'request-detail', params: {id}});
+ route_to_request_detail(request) {
+ this.$router.push({name: 'request-detail', params: {request, id: request.uuid}});
+ },
+ export_data: function() {
+ if (!this.requests) {
+ return;
+ }
+ frappe.call("frappe.recorder.export_data")
+ .then((r) => {
+ const data = r.message;
+ const filename = `${data[0]['uuid']}..${data[data.length -1]['uuid']}.json`
+
+ const el = document.createElement('a');
+ el.setAttribute('href', 'data:application/json,' + encodeURIComponent(JSON.stringify(data)));
+ el.setAttribute('download', filename);
+ el.click();
+ });
+ },
+ import_data: function(e) {
+ if (this.requests.length > 0) {
+ // don't replace existing capture
+ return;
+ }
+ const request_file = e.dataTransfer.files[0];
+
+ const file_reader = new FileReader();
+ file_reader.readAsText(request_file, 'UTF-8');
+ file_reader.onload = ({target: {result}}) => {
+ this.requests = JSON.parse(result);
+ }
}
}
};
diff --git a/frappe/public/js/frappe/recorder/RequestDetail.vue b/frappe/public/js/frappe/recorder/RequestDetail.vue
index 471306cba5..c07e6220a9 100644
--- a/frappe/public/js/frappe/recorder/RequestDetail.vue
+++ b/frappe/public/js/frappe/recorder/RequestDetail.vue
@@ -283,14 +283,21 @@ export default {
label: __('Recorder'),
route: '/app/recorder'
});
- frappe.call({
- method: "frappe.recorder.get",
- args: {
- uuid: this.$route.params.id
- }
- }).then( r => {
- this.request = r.message
- });
- }
+
+ const request = this.$route.params.request;
+ if (request.headers || request.form_dict || request.calls) {
+ // complete request data passed as parameter.
+ this.request = request;
+ } else {
+ frappe.call({
+ method: "frappe.recorder.get",
+ args: {
+ uuid: request.uuid
+ }
+ }).then( r => {
+ this.request = r.message
+ });
+ }
+ },
};
diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js
index f13f683d79..1053f9b7c5 100644
--- a/frappe/public/js/frappe/views/reports/query_report.js
+++ b/frappe/public/js/frappe/views/reports/query_report.js
@@ -854,6 +854,10 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
}
};
+ if (this.raw_data.add_total_row) {
+ this.$page.find('.layout-main-section').css('--report-total-height', '310px');
+ }
+
if (this.report_settings.get_datatable_options) {
datatable_options = this.report_settings.get_datatable_options(datatable_options);
}
diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js
index 2547dd6407..b46e6fb374 100644
--- a/frappe/public/js/frappe/views/reports/report_view.js
+++ b/frappe/public/js/frappe/views/reports/report_view.js
@@ -1401,7 +1401,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
}
];
- if (this.total_count > args.page_length) {
+ if (this.total_count > this.count_without_children || args.page_length) {
fields.push({
fieldtype: 'Check',
fieldname: 'export_all_rows',
diff --git a/frappe/public/scss/desk/form.scss b/frappe/public/scss/desk/form.scss
index bb25faed3b..b7fdf90f5f 100644
--- a/frappe/public/scss/desk/form.scss
+++ b/frappe/public/scss/desk/form.scss
@@ -115,7 +115,7 @@
margin-bottom: var(--margin-sm);
font-weight: var(--text-bold);
}
- .row:first-child {
+ .form-documents:first-of-type .row:first-child {
.form-link-title {
margin-top: 0;
}
diff --git a/frappe/public/scss/desk/report.scss b/frappe/public/scss/desk/report.scss
index e2aae431aa..2389a4f8f6 100644
--- a/frappe/public/scss/desk/report.scss
+++ b/frappe/public/scss/desk/report.scss
@@ -84,8 +84,9 @@
margin-bottom: 10px;
}
-.layout-main-section .frappe-card {
+.layout-main-section {
--report-filter-height: 0px;
+ --report-total-height: 275px;
}
.report-wrapper {
@@ -95,7 +96,7 @@
height: calc(100vh - var(--report-filter-height) - 205px);
.dt-scrollable {
- height: calc(100vh - var(--report-filter-height) - 275px);
+ height: calc(100vh - var(--report-filter-height) - var(--report-total-height));
}
}
}
diff --git a/frappe/recorder.py b/frappe/recorder.py
index 02036b7374..969e7aa4fa 100644
--- a/frappe/recorder.py
+++ b/frappe/recorder.py
@@ -181,6 +181,13 @@ def get(uuid=None, *args, **kwargs):
return result
+@frappe.whitelist()
+@do_not_record
+@administrator_only
+def export_data(*args, **kwargs):
+ return list(frappe.cache().hgetall(RECORDER_REQUEST_HASH).values())
+
+
@frappe.whitelist()
@do_not_record
@administrator_only
diff --git a/frappe/tests/data/sample_svg.svg b/frappe/tests/data/sample_svg.svg
new file mode 100644
index 0000000000..9fcb3c8242
--- /dev/null
+++ b/frappe/tests/data/sample_svg.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/frappe/tests/test_document.py b/frappe/tests/test_document.py
index 7c0c95671a..68c19a8bbf 100644
--- a/frappe/tests/test_document.py
+++ b/frappe/tests/test_document.py
@@ -227,3 +227,28 @@ class TestDocument(unittest.TestCase):
self.assertEqual(frappe.db.get_value("Currency", d.name), d.name)
frappe.delete_doc_if_exists("Currency", "Frappe Coin", 1)
+
+ def test_get_formatted(self):
+ frappe.get_doc({
+ 'doctype': 'DocType',
+ 'name': 'Test Formatted',
+ 'module': 'Custom',
+ 'custom': 1,
+ 'fields': [
+ {'label': 'Currency', 'fieldname': 'currency', 'reqd': 1, 'fieldtype': 'Currency'},
+ ]
+ }).insert()
+
+ frappe.delete_doc_if_exists("Currency", "INR", 1)
+
+ d = frappe.get_doc({
+ 'doctype': 'Currency',
+ 'currency_name': 'INR',
+ 'symbol': '₹',
+ }).insert()
+
+ d = frappe.get_doc({
+ 'doctype': 'Test Formatted',
+ 'currency': 100000
+ })
+ self.assertEquals(d.get_formatted('currency', currency='INR', format="#,###.##"), '₹ 100,000.00')
\ No newline at end of file
diff --git a/frappe/tests/test_fmt_money.py b/frappe/tests/test_fmt_money.py
index 160ea33378..96f220447a 100644
--- a/frappe/tests/test_fmt_money.py
+++ b/frappe/tests/test_fmt_money.py
@@ -92,6 +92,9 @@ class TestFmtMoney(unittest.TestCase):
self.assertEqual(fmt_money(1000.456), "1.000,456")
frappe.db.set_default("currency_precision", "")
+ def test_custom_fmt_money_format(self):
+ self.assertEqual(fmt_money(100000, format="#,###.##"), '100,000.00')
+
if __name__=="__main__":
frappe.connect()
unittest.main()
\ No newline at end of file
diff --git a/frappe/tests/test_formatter.py b/frappe/tests/test_formatter.py
index 5257e1c717..5454c2b1cd 100644
--- a/frappe/tests/test_formatter.py
+++ b/frappe/tests/test_formatter.py
@@ -17,9 +17,9 @@ class TestFormatter(unittest.TestCase):
frappe.db.set_default("currency", 'INR')
# if currency field is not passed then default currency should be used.
- self.assertEqual(format(100, df, doc), '₹ 100.00')
+ self.assertEqual(format(100000, df, doc, format="#,###.##"), '₹ 100,000.00')
doc.currency = 'USD'
- self.assertEqual(format(100, df, doc), "$ 100.00")
+ self.assertEqual(format(100000, df, doc, format="#,###.##"), "$ 100,000.00")
frappe.db.set_default("currency", None)
\ No newline at end of file
diff --git a/frappe/tests/test_search.py b/frappe/tests/test_search.py
index afb584ea15..affaf44466 100644
--- a/frappe/tests/test_search.py
+++ b/frappe/tests/test_search.py
@@ -3,8 +3,7 @@
import unittest
import frappe
-from frappe.desk.search import search_link
-from frappe.desk.search import search_widget
+from frappe.desk.search import search_link, search_widget, get_names_for_mentions
class TestSearch(unittest.TestCase):
@@ -47,6 +46,23 @@ class TestSearch(unittest.TestCase):
search_link, 'DocType', 'Customer', query=None, filters=None,
page_length=20, searchfield=';')
+ def test_only_enabled_in_mention(self):
+ email = 'test_disabled_user_in_mentions@example.com'
+ frappe.delete_doc('User', email)
+ if not frappe.db.exists('User', email):
+ user = frappe.new_doc('User')
+ user.update({
+ 'email' : email,
+ 'first_name' : email.split("@")[0],
+ 'enabled' : False,
+ 'allowed_in_mentions' : True,
+ })
+ # saved when roles are added
+ user.add_roles('System Manager',)
+
+ names_for_mention = [user.get('id') for user in get_names_for_mentions('')]
+ self.assertNotIn(email, names_for_mention)
+
def test_link_field_order(self):
# Making a request to the search_link with the tree doctype
search_link(doctype=self.tree_doctype_name, txt='all', query=None,
diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py
index cd441f8f10..d8ad728136 100644
--- a/frappe/tests/ui_test_helpers.py
+++ b/frappe/tests/ui_test_helpers.py
@@ -61,6 +61,18 @@ def create_todo_records():
"description": "this is fourth todo"
}).insert()
+@frappe.whitelist()
+def create_communication_records():
+ if frappe.db.get_all('Communication', {'subject': 'Test Form Communication 1'}):
+ return
+
+ frappe.get_doc({
+ "doctype": "Communication",
+ "recipients": "test@gmail.com",
+ "subject": "Test Form Communication 1",
+ "communication_date": frappe.utils.now_datetime(),
+ }).insert()
+
@frappe.whitelist()
def setup_workflow():
from frappe.workflow.doctype.workflow.test_workflow import create_todo_workflow
diff --git a/frappe/utils/formatters.py b/frappe/utils/formatters.py
index 9efccc15f0..cca2b4f1ba 100644
--- a/frappe/utils/formatters.py
+++ b/frappe/utils/formatters.py
@@ -7,7 +7,7 @@ from frappe.utils import formatdate, fmt_money, flt, cstr, cint, format_datetime
from frappe.model.meta import get_field_currency, get_field_precision
import re
-def format_value(value, df=None, doc=None, currency=None, translated=False):
+def format_value(value, df=None, doc=None, currency=None, translated=False, format=None):
'''Format value based on given fieldtype, document reference, currency reference.
If docfield info (df) is not given, it will try and guess based on the datatype of the value'''
if isinstance(df, str):
@@ -56,7 +56,7 @@ def format_value(value, df=None, doc=None, currency=None, translated=False):
elif df.get("fieldtype") == "Currency":
default_currency = frappe.db.get_default("currency")
currency = currency or get_field_currency(df, doc) or default_currency
- return fmt_money(value, precision=get_field_precision(df, doc), currency=currency)
+ return fmt_money(value, precision=get_field_precision(df, doc), currency=currency, format=format)
elif df.get("fieldtype") == "Float":
precision = get_field_precision(df, doc)