Merge branch 'develop' of https://github.com/frappe/frappe into custom_doctype_length

This commit is contained in:
Deepesh Garg 2021-08-31 10:47:15 +05:30
commit 62585b3ec2
19 changed files with 220 additions and 34 deletions

View file

@ -38,8 +38,12 @@ if __name__ == "__main__":
pr_number = os.environ.get("PR_NUMBER")
repo = os.environ.get("REPO_NAME")
if not files_list and pr_number:
files_list = get_files_list(pr_number=pr_number, repo=repo)
# this is a push build, run all builds
if not pr_number:
os.system('echo "::set-output name=build::strawberry"')
sys.exit(0)
files_list = files_list or get_files_list(pr_number=pr_number, repo=repo)
if not files_list:
print("No files' changes detected. Build is shutting")

View file

@ -2,6 +2,11 @@ name: Patch
on: [pull_request, workflow_dispatch]
concurrency:
group: patch-mariadb-develop-${{ github.event.number }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-18.04

View file

@ -6,6 +6,11 @@ on:
push:
branches: [ develop ]
concurrency:
group: server-mariadb-develop-${{ github.event.number }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-18.04

View file

@ -4,6 +4,10 @@ on:
pull_request:
workflow_dispatch:
concurrency:
group: server-postgres-develop-${{ github.event.number }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-18.04

View file

@ -6,6 +6,10 @@ on:
push:
branches: [ develop ]
concurrency:
group: ui-develop-${{ github.event.number }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-18.04

View file

@ -0,0 +1,19 @@
context('Datetime Field Validation', () => {
before(() => {
cy.login();
cy.visit('/app/communication');
cy.window().its('frappe').then(frappe => {
frappe.call("frappe.tests.ui_test_helpers.create_communication_records");
});
});
// validating datetime field value when value is set from backend and get validated on form load.
it('datetime field form validation', () => {
cy.visit('/app/communication');
cy.get('a[title="Test Form Communication 1"]').invoke('attr', 'data-name')
.then((name) => {
cy.visit(`/app/communication/${name}`);
cy.get('.indicator-pill').should('contain', 'Open').should('have.class', 'red');
});
});
});

View file

@ -0,0 +1,79 @@
context('Folder Navigation', () => {
before(() => {
cy.visit('/login');
cy.login();
cy.visit('/app/file');
});
it('Adding Folders', () => {
//Adding filter to go into the home folder
cy.get('.filter-selector > .btn').findByText('1 filter').click();
cy.findByRole('button', {name: 'Clear Filters'}).click();
cy.get('.filter-action-buttons > .text-muted').findByText('+ Add a Filter').click();
cy.get('.fieldname-select-area > .awesomplete > .form-control').type('Fol{enter}');
cy.get('.filter-field > .form-group > .link-field > .awesomplete > .input-with-feedback').type('Home{enter}');
cy.get('.filter-action-buttons > div > .btn-primary').findByText('Apply Filters').click();
//Adding folder (Test Folder)
cy.get('.menu-btn-group > .btn').click();
cy.get('.menu-btn-group [data-label="New Folder"]').click();
cy.get('form > [data-fieldname="value"]').type('Test Folder');
cy.findByRole('button', {name: 'Create'}).click();
});
it('Navigating the nested folders, checking if the URL formed is correct, checking if the added content in the child folder is correct', () => {
//Navigating inside the Attachments folder
cy.get('[title="Attachments"] > span').click();
//To check if the URL formed after visiting the attachments folder is correct
cy.location('pathname').should('eq', '/app/file/view/home/Attachments');
cy.visit('/app/file/view/home/Attachments');
//Adding folder inside the attachments folder
cy.get('.menu-btn-group > .btn').click();
cy.get('.menu-btn-group [data-label="New Folder"]').click();
cy.get('form > [data-fieldname="value"]').type('Test Folder');
cy.findByRole('button', {name: 'Create'}).click();
//Navigating inside the added folder in the Attachments folder
cy.get('[title="Test Folder"] > span').click();
//To check if the URL is correct after visiting the Test Folder
cy.location('pathname').should('eq', '/app/file/view/home/Attachments/Test%20Folder');
cy.visit('/app/file/view/home/Attachments/Test%20Folder');
//Adding a file inside the Test Folder
cy.findByRole('button', {name: 'Add File'}).eq(0).click({force: true});
cy.get('.file-uploader').findByText('Link').click();
cy.get('.input-group > .form-control').type('https://wallpaperplay.com/walls/full/8/2/b/72402.jpg');
cy.findByRole('button', {name: 'Upload'}).click();
//To check if the added file is present in the Test Folder
cy.get('span.level-item > span').should('contain', 'Test Folder');
cy.get('.list-row-container').eq(0).should('contain.text', '72402.jpg');
cy.get('.list-row-checkbox').eq(0).click();
//Deleting the added file from the Test folder
cy.findByRole('button', {name: 'Actions'}).click();
cy.get('.actions-btn-group [data-label="Delete"]').click();
cy.wait(700);
cy.findByRole('button', {name: 'Yes'}).click();
cy.wait(700);
//Deleting the Test Folder
cy.visit('/app/file/view/home/Attachments');
cy.get('.list-row-checkbox').eq(0).click();
cy.findByRole('button', {name: 'Actions'}).click();
cy.get('.actions-btn-group [data-label="Delete"]').click();
cy.findByRole('button', {name: 'Yes'}).click();
});
it('Deleting Test Folder from the home', () => {
//Deleting the Test Folder added in the home directory
cy.visit('/app/file/view/home');
cy.get('.level-left > .list-subject > .list-row-checkbox').eq(0).click({force: true, delay: 500});
cy.findByRole('button', {name: 'Actions'}).click();
cy.get('.actions-btn-group [data-label="Delete"]').click();
cy.findByRole('button', {name: 'Yes'}).click();
});
});

View file

@ -391,7 +391,7 @@ def handle_duration_fieldtype_values(result, columns):
return result
def build_xlsx_data(columns, data, visible_idx, include_indentation):
def build_xlsx_data(columns, data, visible_idx, include_indentation, ignore_visible_idx=False):
result = [[]]
column_widths = []
@ -407,7 +407,7 @@ def build_xlsx_data(columns, data, visible_idx, include_indentation):
# build table from result
for row_idx, row in enumerate(data.result):
# only pick up rows that are visible in the report
if row_idx in visible_idx:
if ignore_visible_idx or row_idx in visible_idx:
row_data = []
if isinstance(row, dict):
for col_idx, column in enumerate(data.columns):

View file

@ -13,6 +13,7 @@ from frappe.utils import (format_time, get_link_to_form, get_url_to_report,
from frappe.model.naming import append_number_if_name_exists
from frappe.utils.csvutils import to_csv
from frappe.utils.xlsxutils import make_xlsx
from frappe.desk.query_report import build_xlsx_data
max_reports_per_user = frappe.local.conf.max_reports_per_user or 3
@ -99,13 +100,21 @@ class AutoEmailReport(Document):
return self.get_html_table(columns, data)
elif self.format == 'XLSX':
spreadsheet_data = self.get_spreadsheet_data(columns, data)
xlsx_file = make_xlsx(spreadsheet_data, "Auto Email Report")
report_data = frappe._dict()
report_data['columns'] = columns
report_data['result'] = data
xlsx_data, column_widths = build_xlsx_data(columns, report_data, [], 1, ignore_visible_idx=True)
xlsx_file = make_xlsx(xlsx_data, "Auto Email Report", column_widths=column_widths)
return xlsx_file.getvalue()
elif self.format == 'CSV':
spreadsheet_data = self.get_spreadsheet_data(columns, data)
return to_csv(spreadsheet_data)
report_data = frappe._dict()
report_data['columns'] = columns
report_data['result'] = data
xlsx_data, column_widths = build_xlsx_data(columns, report_data, [], 1, ignore_visible_idx=True)
return to_csv(xlsx_data)
else:
frappe.throw(_('Invalid Output Format'))
@ -126,18 +135,6 @@ class AutoEmailReport(Document):
'edit_report_settings': get_link_to_form('Auto Email Report', self.name)
})
@staticmethod
def get_spreadsheet_data(columns, data):
out = [[_(df.label) for df in columns], ]
for row in data:
new_row = []
out.append(new_row)
for df in columns:
if df.fieldname not in row: continue
new_row.append(frappe.format(row[df.fieldname], df, row))
return out
def get_file_name(self):
return "{0}.{1}".format(self.report.replace(" ", "-").replace("/", "-"), self.format.lower())

View file

@ -874,7 +874,7 @@ class BaseDocument(object):
return self._precision[cache_key][fieldname]
def get_formatted(self, fieldname, doc=None, currency=None, absolute_value=False, translated=False):
def get_formatted(self, fieldname, doc=None, currency=None, absolute_value=False, translated=False, format=None):
from frappe.utils.formatters import format_value
df = self.meta.get_field(fieldname)
@ -898,7 +898,7 @@ class BaseDocument(object):
if (absolute_value or doc.get('absolute_value')) and isinstance(val, (int, float)):
val = abs(self.get(fieldname))
return format_value(val, df=df, doc=doc, currency=currency)
return format_value(val, df=df, doc=doc, currency=currency, format=format)
def is_print_hide(self, fieldname, df=None, for_print=True):
"""Returns true if fieldname is to be hidden for print.

View file

@ -13,6 +13,13 @@ frappe.data_import.DataExporter = class DataExporter {
this.dialog = new frappe.ui.Dialog({
title: __('Export Data'),
fields: [
{
fieldtype: 'Select',
fieldname: 'file_type',
label: __('File Type'),
options: ['Excel', 'CSV'],
default: 'CSV'
},
{
fieldtype: 'Select',
fieldname: 'export_records',
@ -45,13 +52,6 @@ frappe.data_import.DataExporter = class DataExporter {
fieldname: 'filter_area',
depends_on: doc => doc.export_records === 'by_filter'
},
{
fieldtype: 'Select',
fieldname: 'file_type',
label: __('File Type'),
options: ['Excel', 'CSV'],
default: 'CSV'
},
{
fieldtype: 'Section Break'
},
@ -141,7 +141,7 @@ frappe.data_import.DataExporter = class DataExporter {
let for_insert = this.exporting_for === 'Insert New Records';
let section_title = for_insert ? __('Select Fields To Insert') : __('Select Fields To Update');
let $select_all_buttons = $(`
<div>
<div class="mb-3">
<h6 class="form-section-heading uppercase">${section_title}</h6>
<button class="btn btn-default btn-xs" data-action="select_all">
${__('Select All')}

View file

@ -36,4 +36,9 @@ frappe.ui.form.ControlDatetime = class ControlDatetime extends frappe.ui.form.Co
$tp.$secondsText.prev().css('display', 'none');
}
}
get_model_value() {
let value = super.get_model_value();
return frappe.datetime.get_datetime_as_string(value);
}
};

View file

@ -261,4 +261,14 @@ export default class BulkOperations {
});
dialog.show();
}
export(doctype, docnames) {
frappe.require('data_import_tools.bundle.js', () => {
const data_exporter = new frappe.data_import.DataExporter(doctype, 'Insert New Records');
data_exporter.dialog.set_value('export_records', 'by_filter');
data_exporter.filter_group.add_filters_to_filter_group(
[[doctype, "name", "in", docnames, false]]
);
});
}
}

View file

@ -1732,11 +1732,25 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
};
};
const bulk_export = () => {
return {
label: __("Export"),
action: () => {
const docnames = this.get_checked_items(true);
bulk_operations.export(doctype, docnames);
},
standard: true
};
};
// bulk edit
if (has_editable_fields(doctype)) {
actions_menu_items.push(bulk_edit());
}
actions_menu_items.push(bulk_export());
// bulk assignment
actions_menu_items.push(bulk_assignment());

View file

@ -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')

View file

@ -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()

View file

@ -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)

View file

@ -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

View file

@ -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)