@@ -41,6 +41,7 @@ if __name__ == "__main__": | |||||
# this is a push build, run all builds | # this is a push build, run all builds | ||||
if not pr_number: | if not pr_number: | ||||
os.system('echo "::set-output name=build::strawberry"') | os.system('echo "::set-output name=build::strawberry"') | ||||
os.system('echo "::set-output name=build-server::strawberry"') | |||||
sys.exit(0) | sys.exit(0) | ||||
files_list = files_list or get_files_list(pr_number=pr_number, repo=repo) | files_list = files_list or get_files_list(pr_number=pr_number, repo=repo) | ||||
@@ -52,7 +53,8 @@ if __name__ == "__main__": | |||||
ci_files_changed = any(f for f in files_list if is_ci(f)) | ci_files_changed = any(f for f in files_list if is_ci(f)) | ||||
only_docs_changed = len(list(filter(is_docs, files_list))) == len(files_list) | only_docs_changed = len(list(filter(is_docs, files_list))) == len(files_list) | ||||
only_frontend_code_changed = len(list(filter(is_frontend_code, files_list))) == len(files_list) | only_frontend_code_changed = len(list(filter(is_frontend_code, files_list))) == len(files_list) | ||||
only_py_changed = len(list(filter(is_py, files_list))) == len(files_list) | |||||
updated_py_file_count = len(list(filter(is_py, files_list))) | |||||
only_py_changed = updated_py_file_count == len(files_list) | |||||
if ci_files_changed: | if ci_files_changed: | ||||
print("CI related files were updated, running all build processes.") | print("CI related files were updated, running all build processes.") | ||||
@@ -65,8 +67,12 @@ if __name__ == "__main__": | |||||
print("Only Frontend code was updated; Stopping Python build process.") | print("Only Frontend code was updated; Stopping Python build process.") | ||||
sys.exit(0) | sys.exit(0) | ||||
elif only_py_changed and build_type == "ui": | |||||
print("Only Python code was updated, stopping Cypress build process.") | |||||
sys.exit(0) | |||||
elif build_type == "ui": | |||||
if only_py_changed: | |||||
print("Only Python code was updated, stopping Cypress build process.") | |||||
sys.exit(0) | |||||
elif updated_py_file_count > 0: | |||||
# both frontend and backend code were updated | |||||
os.system('echo "::set-output name=build-server::strawberry"') | |||||
os.system('echo "::set-output name=build::strawberry"') | os.system('echo "::set-output name=build::strawberry"') |
@@ -142,6 +142,7 @@ jobs: | |||||
CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb | CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb | ||||
- name: Stop server | - name: Stop server | ||||
if: ${{ steps.check-build.outputs.build-server == 'strawberry' }} | |||||
run: | | run: | | ||||
ps -ef | grep "frappe serve" | awk '{print $2}' | xargs kill -s SIGINT 2> /dev/null || true | ps -ef | grep "frappe serve" | awk '{print $2}' | xargs kill -s SIGINT 2> /dev/null || true | ||||
sleep 5 | sleep 5 | ||||
@@ -163,7 +164,7 @@ jobs: | |||||
flags: ui-tests | flags: ui-tests | ||||
- name: Upload Server Coverage Data | - name: Upload Server Coverage Data | ||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }} | |||||
if: ${{ steps.check-build.outputs.build-server == 'strawberry' }} | |||||
uses: codecov/codecov-action@v2 | uses: codecov/codecov-action@v2 | ||||
with: | with: | ||||
name: MariaDB | name: MariaDB | ||||
@@ -55,10 +55,31 @@ context('Depends On', () => { | |||||
'read_only_depends_on': "eval:doc.test_field=='Some Other Value'", | 'read_only_depends_on': "eval:doc.test_field=='Some Other Value'", | ||||
'options': "Child Test Depends On" | 'options': "Child Test Depends On" | ||||
}, | }, | ||||
{ | |||||
"label": "Dependent Tab", | |||||
"fieldname": "dependent_tab", | |||||
"fieldtype": "Tab Break", | |||||
"depends_on": "eval:doc.test_field=='Show Tab'" | |||||
}, | |||||
{ | |||||
"fieldname": "tab_section", | |||||
"fieldtype": "Section Break", | |||||
}, | |||||
{ | |||||
"label": "Field in Tab", | |||||
"fieldname": "field_in_tab", | |||||
"fieldtype": "Data", | |||||
} | |||||
] | ] | ||||
}); | }); | ||||
}); | }); | ||||
}); | }); | ||||
it('should show the tab on other setting field value', () => { | |||||
cy.new_form('Test Depends On'); | |||||
cy.fill_field('test_field', 'Show Tab'); | |||||
cy.get('body').click(); | |||||
cy.findByRole("tab", {name: "Dependent Tab"}).should('be.visible'); | |||||
}); | |||||
it('should set the field as mandatory depending on other fields value', () => { | it('should set the field as mandatory depending on other fields value', () => { | ||||
cy.new_form('Test Depends On'); | cy.new_form('Test Depends On'); | ||||
cy.fill_field('test_field', 'Some Value'); | cy.fill_field('test_field', 'Some Value'); | ||||
@@ -3,7 +3,7 @@ | |||||
import frappe, json, os | import frappe, json, os | ||||
import unittest | import unittest | ||||
from frappe.desk.query_report import run, save_report | |||||
from frappe.desk.query_report import run, save_report, add_total_row | |||||
from frappe.desk.reportview import delete_report, save_report as _save_report | from frappe.desk.reportview import delete_report, save_report as _save_report | ||||
from frappe.custom.doctype.customize_form.customize_form import reset_customization | from frappe.custom.doctype.customize_form.customize_form import reset_customization | ||||
from frappe.core.doctype.user_permission.test_user_permission import create_user | from frappe.core.doctype.user_permission.test_user_permission import create_user | ||||
@@ -282,3 +282,55 @@ result = [ | |||||
# Set user back to administrator | # Set user back to administrator | ||||
frappe.set_user('Administrator') | frappe.set_user('Administrator') | ||||
def test_add_total_row_for_tree_reports(self): | |||||
report_settings = { | |||||
'tree': True, | |||||
'parent_field': 'parent_value' | |||||
} | |||||
columns = [ | |||||
{ | |||||
"fieldname": "parent_column", | |||||
"label": "Parent Column", | |||||
"fieldtype": "Data", | |||||
"width": 10 | |||||
}, | |||||
{ | |||||
"fieldname": "column_1", | |||||
"label": "Column 1", | |||||
"fieldtype": "Float", | |||||
"width": 10 | |||||
}, | |||||
{ | |||||
"fieldname": "column_2", | |||||
"label": "Column 2", | |||||
"fieldtype": "Float", | |||||
"width": 10 | |||||
} | |||||
] | |||||
result = [ | |||||
{ | |||||
"parent_column": "Parent 1", | |||||
"column_1": 200, | |||||
"column_2": 150.50 | |||||
}, | |||||
{ | |||||
"parent_column": "Child 1", | |||||
"column_1": 100, | |||||
"column_2": 75.25, | |||||
"parent_value": "Parent 1" | |||||
}, | |||||
{ | |||||
"parent_column": "Child 2", | |||||
"column_1": 100, | |||||
"column_2": 75.25, | |||||
"parent_value": "Parent 1" | |||||
} | |||||
] | |||||
result = add_total_row(result, columns, meta=None, report_settings=report_settings) | |||||
self.assertEqual(result[-1][0], "Total") | |||||
self.assertEqual(result[-1][1], 200) | |||||
self.assertEqual(result[-1][2], 150.50) |
@@ -73,7 +73,7 @@ def get_report_result(report, filters): | |||||
return res | return res | ||||
@frappe.read_only() | @frappe.read_only() | ||||
def generate_report_result(report, filters=None, user=None, custom_columns=None): | |||||
def generate_report_result(report, filters=None, user=None, custom_columns=None, report_settings=None): | |||||
user = user or frappe.session.user | user = user or frappe.session.user | ||||
filters = filters or [] | filters = filters or [] | ||||
@@ -108,7 +108,7 @@ def generate_report_result(report, filters=None, user=None, custom_columns=None) | |||||
result = get_filtered_data(report.ref_doctype, columns, result, user) | result = get_filtered_data(report.ref_doctype, columns, result, user) | ||||
if cint(report.add_total_row) and result and not skip_total_row: | if cint(report.add_total_row) and result and not skip_total_row: | ||||
result = add_total_row(result, columns) | |||||
result = add_total_row(result, columns, report_settings=report_settings) | |||||
return { | return { | ||||
"result": result, | "result": result, | ||||
@@ -210,7 +210,7 @@ def get_script(report_name): | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
@frappe.read_only() | @frappe.read_only() | ||||
def run(report_name, filters=None, user=None, ignore_prepared_report=False, custom_columns=None): | |||||
def run(report_name, filters=None, user=None, ignore_prepared_report=False, custom_columns=None, report_settings=None): | |||||
report = get_report_doc(report_name) | report = get_report_doc(report_name) | ||||
if not user: | if not user: | ||||
user = frappe.session.user | user = frappe.session.user | ||||
@@ -238,7 +238,7 @@ def run(report_name, filters=None, user=None, ignore_prepared_report=False, cust | |||||
dn = "" | dn = "" | ||||
result = get_prepared_report_result(report, filters, dn, user) | result = get_prepared_report_result(report, filters, dn, user) | ||||
else: | else: | ||||
result = generate_report_result(report, filters, user, custom_columns) | |||||
result = generate_report_result(report, filters, user, custom_columns, report_settings) | |||||
result["add_total_row"] = report.add_total_row and not result.get( | result["add_total_row"] = report.add_total_row and not result.get( | ||||
"skip_total_row", False | "skip_total_row", False | ||||
@@ -435,9 +435,19 @@ def build_xlsx_data(columns, data, visible_idx, include_indentation, ignore_visi | |||||
return result, column_widths | return result, column_widths | ||||
def add_total_row(result, columns, meta=None): | |||||
def add_total_row(result, columns, meta=None, report_settings=None): | |||||
total_row = [""] * len(columns) | total_row = [""] * len(columns) | ||||
has_percent = [] | has_percent = [] | ||||
is_tree = False | |||||
parent_field = '' | |||||
if report_settings: | |||||
if isinstance(report_settings, (str,)): | |||||
report_settings = json.loads(report_settings) | |||||
is_tree = report_settings.get('tree') | |||||
parent_field = report_settings.get('parent_field') | |||||
for i, col in enumerate(columns): | for i, col in enumerate(columns): | ||||
fieldtype, options, fieldname = None, None, None | fieldtype, options, fieldname = None, None, None | ||||
if isinstance(col, str): | if isinstance(col, str): | ||||
@@ -464,12 +474,12 @@ def add_total_row(result, columns, meta=None): | |||||
for row in result: | for row in result: | ||||
if i >= len(row): | if i >= len(row): | ||||
continue | continue | ||||
cell = row.get(fieldname) if isinstance(row, dict) else row[i] | cell = row.get(fieldname) if isinstance(row, dict) else row[i] | ||||
if fieldtype in ["Currency", "Int", "Float", "Percent", "Duration"] and flt( | if fieldtype in ["Currency", "Int", "Float", "Percent", "Duration"] and flt( | ||||
cell | cell | ||||
): | ): | ||||
total_row[i] = flt(total_row[i]) + flt(cell) | |||||
if not (is_tree and row.get(parent_field)): | |||||
total_row[i] = flt(total_row[i]) + flt(cell) | |||||
if fieldtype == "Percent" and i not in has_percent: | if fieldtype == "Percent" and i not in has_percent: | ||||
has_percent.append(i) | has_percent.append(i) | ||||
@@ -529,10 +529,9 @@ def extract_sql_gzip(sql_gz_path): | |||||
import subprocess | import subprocess | ||||
try: | try: | ||||
# dvf - decompress, verbose, force | |||||
original_file = sql_gz_path | original_file = sql_gz_path | ||||
decompressed_file = original_file.rstrip(".gz") | decompressed_file = original_file.rstrip(".gz") | ||||
cmd = 'gzip -dvf < {0} > {1}'.format(original_file, decompressed_file) | |||||
cmd = 'gzip --decompress --force < {0} > {1}'.format(original_file, decompressed_file) | |||||
subprocess.check_call(cmd, shell=True) | subprocess.check_call(cmd, shell=True) | ||||
except Exception: | except Exception: | ||||
raise | raise | ||||
@@ -554,19 +554,21 @@ frappe.ui.form.Layout = class Layout { | |||||
let has_dep = false; | let has_dep = false; | ||||
for (let fkey in this.fields_list) { | |||||
let f = this.fields_list[fkey]; | |||||
f.dependencies_clear = true; | |||||
const fields = this.fields_list.concat(this.tabs); | |||||
for (let fkey in fields) { | |||||
let f = fields[fkey]; | |||||
if (f.df.depends_on || f.df.mandatory_depends_on || f.df.read_only_depends_on) { | if (f.df.depends_on || f.df.mandatory_depends_on || f.df.read_only_depends_on) { | ||||
has_dep = true; | has_dep = true; | ||||
break; | |||||
} | } | ||||
} | } | ||||
if (!has_dep) return; | if (!has_dep) return; | ||||
// show / hide based on values | // show / hide based on values | ||||
for (let i = this.fields_list.length - 1; i >= 0; i--) { | |||||
let f = this.fields_list[i]; | |||||
for (let i = fields.length - 1; i >= 0; i--) { | |||||
let f = fields[i]; | |||||
f.guardian_has_value = true; | f.guardian_has_value = true; | ||||
if (f.df.depends_on) { | if (f.df.depends_on) { | ||||
// evaluate guardian | // evaluate guardian | ||||
@@ -40,7 +40,7 @@ export default class Tab { | |||||
hide = true; | hide = true; | ||||
} | } | ||||
hide && this.toggle(false); | |||||
this.toggle(!hide); | |||||
} | } | ||||
toggle(show) { | toggle(show) { | ||||
@@ -578,6 +578,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { | |||||
args: { | args: { | ||||
report_name: this.report_name, | report_name: this.report_name, | ||||
filters: filters, | filters: filters, | ||||
report_settings: this.report_settings | |||||
}, | }, | ||||
callback: resolve, | callback: resolve, | ||||
always: () => this.page.btn_secondary.prop('disabled', false) | always: () => this.page.btn_secondary.prop('disabled', false) | ||||
@@ -834,7 +835,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { | |||||
let data = this.data; | let data = this.data; | ||||
let columns = this.columns.filter((col) => !col.hidden); | let columns = this.columns.filter((col) => !col.hidden); | ||||
if (this.raw_data.add_total_row) { | |||||
if (this.raw_data.add_total_row && !this.report_settings.tree) { | |||||
data = data.slice(); | data = data.slice(); | ||||
data.splice(-1, 1); | data.splice(-1, 1); | ||||
} | } | ||||
@@ -854,7 +855,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { | |||||
treeView: this.tree_report, | treeView: this.tree_report, | ||||
layout: 'fixed', | layout: 'fixed', | ||||
cellHeight: 33, | cellHeight: 33, | ||||
showTotalRow: this.raw_data.add_total_row, | |||||
showTotalRow: this.raw_data.add_total_row && !this.report_settings.tree, | |||||
direction: frappe.utils.is_rtl() ? 'rtl' : 'ltr', | direction: frappe.utils.is_rtl() ? 'rtl' : 'ltr', | ||||
hooks: { | hooks: { | ||||
columnTotal: frappe.utils.report_column_total | columnTotal: frappe.utils.report_column_total | ||||