Merge branch 'develop' into multistep_webforms

This commit is contained in:
Suraj Shetty 2022-01-06 18:31:37 +05:30 committed by GitHub
commit f930fd0fe1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 201 additions and 58 deletions

View file

@ -148,6 +148,7 @@
"context": true,
"before": true,
"beforeEach": true,
"after": true,
"qz": true,
"localforage": true,
"extend_cscript": true

View file

@ -0,0 +1,45 @@
context("First Day of the Week", () => {
before(() => {
cy.login();
});
beforeEach(() => {
cy.visit('/app/system-settings');
cy.findByText('Date and Number Format').click();
});
it("Date control starts with same day as selected in System Settings", () => {
cy.intercept('POST', '/api/method/frappe.core.doctype.system_settings.system_settings.load').as("load_settings");
cy.fill_field('first_day_of_the_week', 'Tuesday', 'Select');
cy.findByRole('button', {name: 'Save'}).click();
cy.wait("@load_settings");
cy.dialog({
title: 'Date',
fields: [
{
label: 'Date',
fieldname: 'date',
fieldtype: 'Date'
}
]
});
cy.get_field('date').click();
cy.get('.datepicker--day-name').eq(0).should('have.text', 'Tu');
});
it("Calendar view starts with same day as selected in System Settings", () => {
cy.intercept('POST', '/api/method/frappe.core.doctype.system_settings.system_settings.load').as("load_settings");
cy.fill_field('first_day_of_the_week', 'Monday', 'Select');
cy.findByRole('button', {name: 'Save'}).click();
cy.wait("@load_settings");
cy.visit("app/todo/view/calendar/default");
cy.get('.fc-day-header > span').eq(0).should('have.text', 'Mon');
});
after(() => {
cy.visit('/app/system-settings');
cy.findByText('Date and Number Format').click();
cy.fill_field('first_day_of_the_week', 'Sunday', 'Select');
cy.findByRole('button', {name: 'Save'}).click();
});
});

View file

@ -1,48 +1,39 @@
context('Grid Keyboard Shortcut', () => {
let total_count = 0;
beforeEach(() => {
cy.login();
cy.visit('/app/doctype/User');
});
before(() => {
cy.login();
cy.visit('/app/doctype/User');
return cy.window().its('frappe').then(frappe => {
frappe.db.count('DocField', {
filters: {
'parent': 'User', 'parentfield': 'fields', 'parenttype': 'DocType'
}
}).then((r) => {
total_count = r;
});
});
});
beforeEach(() => {
cy.reload();
cy.visit('/app/contact/new-contact-1');
cy.get('.frappe-control[data-fieldname="email_ids"]').find(".grid-add-row").click();
});
it('Insert new row at the end', () => {
cy.add_new_row_in_grid('{ctrl}{shift}{downarrow}', (cy, total_count) => {
cy.get('[data-name="new-docfield-1"]').should('have.attr', 'data-idx', `${total_count+1}`);
cy.get('[data-name="new-contact-email-1"]').should('have.attr', 'data-idx', `${total_count+1}`);
}, total_count);
});
it('Insert new row at the top', () => {
cy.add_new_row_in_grid('{ctrl}{shift}{uparrow}', (cy) => {
cy.get('[data-name="new-docfield-1"]').should('have.attr', 'data-idx', '1');
cy.get('[data-name="new-contact-email-1"]').should('have.attr', 'data-idx', '2');
});
});
it('Insert new row below', () => {
cy.add_new_row_in_grid('{ctrl}{downarrow}', (cy) => {
cy.get('[data-name="new-docfield-1"]').should('have.attr', 'data-idx', '2');
cy.get('[data-name="new-contact-email-1"]').should('have.attr', 'data-idx', '1');
});
});
it('Insert new row above', () => {
cy.add_new_row_in_grid('{ctrl}{uparrow}', (cy) => {
cy.get('[data-name="new-docfield-1"]').should('have.attr', 'data-idx', '1');
cy.get('[data-name="new-contact-email-1"]').should('have.attr', 'data-idx', '2');
});
});
});
Cypress.Commands.add('add_new_row_in_grid', (shortcut_keys, callbackFn, total_count) => {
cy.get('.frappe-control[data-fieldname="fields"]').as('table');
cy.get('@table').find('.grid-body .col-xs-2').first().click();
cy.get('@table').find('.grid-body .col-xs-2')
cy.get('.frappe-control[data-fieldname="email_ids"]').as('table');
cy.get('@table').find('.grid-body [data-fieldname="email_id"]').first().click();
cy.get('@table').find('.grid-body [data-fieldname="email_id"]')
.first().type(shortcut_keys);
callbackFn(cy, total_count);

View file

@ -193,7 +193,8 @@ Cypress.Commands.add('fill_field', (fieldname, value, fieldtype = 'Data') => {
});
Cypress.Commands.add('get_field', (fieldname, fieldtype = 'Data') => {
let selector = `[data-fieldname="${fieldname}"] input:visible`;
let field_element = fieldtype === 'Select' ? 'select': 'input';
let selector = `[data-fieldname="${fieldname}"] ${field_element}:visible`;
if (fieldtype === 'Text Editor') {
selector = `[data-fieldname="${fieldname}"] .ql-editor[contenteditable=true]:visible`;

View file

@ -10,6 +10,10 @@ frappe.ui.form.on("System Settings", {
frm.set_value(key, val);
frappe.sys_defaults[key] = val;
});
if (frm.re_setup_moment) {
frappe.app.setup_moment();
delete frm.re_setup_moment;
}
}
});
},
@ -38,5 +42,8 @@ frappe.ui.form.on("System Settings", {
// Clear cache after saving to refresh the values of boot.
frappe.ui.toolbar.clear_cache();
}
}
},
first_day_of_the_week(frm) {
frm.re_setup_moment = true;
},
});

View file

@ -17,10 +17,11 @@
"date_and_number_format",
"date_format",
"time_format",
"column_break_7",
"number_format",
"column_break_7",
"float_precision",
"currency_precision",
"first_day_of_the_week",
"sec_backup_limit",
"backup_limit",
"encrypt_backup",
@ -477,12 +478,19 @@
"fieldname": "disable_system_update_notification",
"fieldtype": "Check",
"label": "Disable System Update Notification"
},
{
"default": "Sunday",
"fieldname": "first_day_of_the_week",
"fieldtype": "Select",
"label": "First Day of the Week",
"options": "Sunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday"
}
],
"icon": "fa fa-cog",
"issingle": 1,
"links": [],
"modified": "2021-11-29 18:09:53.601629",
"modified": "2022-01-04 11:28:34.881192",
"modified_by": "Administrator",
"module": "Core",
"name": "System Settings",
@ -499,5 +507,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "ASC",
"states": [],
"track_changes": 1
}

View file

@ -206,6 +206,12 @@ class DbColumn:
if not current_def:
self.fieldname = validate_column_name(self.fieldname)
self.table.add_column.append(self)
if column_type not in ('text', 'longtext'):
if self.unique:
self.table.add_unique.append(self)
if self.set_index:
self.table.add_index.append(self)
return
# type

View file

@ -8,6 +8,7 @@ from frappe.desk.doctype.dashboard_chart.dashboard_chart import get
from datetime import datetime
from dateutil.relativedelta import relativedelta
from unittest.mock import patch
class TestDashboardChart(unittest.TestCase):
def test_period_ending(self):
@ -15,8 +16,9 @@ class TestDashboardChart(unittest.TestCase):
getdate('2019-04-10'))
# week starts on monday
self.assertEqual(get_period_ending('2019-04-10', 'Weekly'),
getdate('2019-04-14'))
with patch.object(frappe.utils.data, "get_first_day_of_the_week", return_value="Monday"):
self.assertEqual(get_period_ending('2019-04-10', 'Weekly'),
getdate('2019-04-14'))
self.assertEqual(get_period_ending('2019-04-10', 'Monthly'),
getdate('2019-04-30'))
@ -200,13 +202,14 @@ class TestDashboardChart(unittest.TestCase):
timeseries = 1
)).insert()
result = get(chart_name ='Test Weekly Dashboard Chart', refresh = 1)
with patch.object(frappe.utils.data, "get_first_day_of_the_week", return_value="Monday"):
result = get(chart_name ='Test Weekly Dashboard Chart', refresh = 1)
self.assertEqual(result.get('datasets')[0].get('values'), [50.0, 300.0, 800.0, 0.0])
self.assertEqual(
result.get('labels'),
['30-12-18', '06-01-19', '13-01-19', '20-01-19']
)
self.assertEqual(result.get('datasets')[0].get('values'), [50.0, 300.0, 800.0, 0.0])
self.assertEqual(
result.get('labels'),
['30-12-18', '06-01-19', '13-01-19', '20-01-19']
)
frappe.db.rollback()
@ -231,13 +234,13 @@ class TestDashboardChart(unittest.TestCase):
timeseries = 1
)).insert()
result = get(chart_name='Test Average Dashboard Chart', refresh = 1)
self.assertEqual(result.get('datasets')[0].get('values'), [50.0, 150.0, 266.6666666666667, 0.0])
self.assertEqual(
result.get('labels'),
['30-12-18', '06-01-19', '13-01-19', '20-01-19']
)
with patch.object(frappe.utils.data, "get_first_day_of_the_week", return_value="Monday"):
result = get(chart_name='Test Average Dashboard Chart', refresh = 1)
self.assertEqual(
result.get('labels'),
['30-12-18', '06-01-19', '13-01-19', '20-01-19']
)
self.assertEqual(result.get('datasets')[0].get('values'), [50.0, 150.0, 266.6666666666667, 0.0])
frappe.db.rollback()

View file

@ -100,5 +100,5 @@ frappe.ui.form.on('System Console', {
</tr></thead>
<tbody>${rows}</thead>`);
});
}
},
});

View file

@ -1,11 +1,13 @@
import requests
import json
import frappe
import base64
'''
FrappeClient is a library that helps you connect with other frappe systems
'''
import base64
import json
import requests
import frappe
class AuthError(Exception):
pass
@ -46,7 +48,7 @@ class FrappeClient(object):
def _login(self, username, password):
'''Login/start a sesion. Called internally on init'''
r = self.session.post(self.url, data={
r = self.session.post(self.url, params={
'cmd': 'login',
'usr': username,
'pwd': password
@ -289,14 +291,14 @@ class FrappeClient(object):
def get_api(self, method, params=None):
if params is None:
params = {}
res = self.session.get(self.url + "/api/method/" + method + "/",
res = self.session.get(f"{self.url}/api/method/{method}",
params=params, verify=self.verify, headers=self.headers)
return self.post_process(res)
def post_api(self, method, params=None):
if params is None:
params = {}
res = self.session.post(self.url + "/api/method/" + method + "/",
res = self.session.post(f"{self.url}/api/method/{method}",
params=params, verify=self.verify, headers=self.headers)
return self.post_process(res)

View file

@ -182,6 +182,7 @@ frappe.patches.v13_0.queryreport_columns
execute:frappe.reload_doc('core', 'doctype', 'doctype')
frappe.patches.v13_0.jinja_hook
frappe.patches.v13_0.update_notification_channel_if_empty
frappe.patches.v13_0.set_first_day_of_the_week
frappe.patches.v14_0.drop_data_import_legacy
frappe.patches.v14_0.rename_cancelled_documents
frappe.patches.v14_0.copy_mail_data #08.03.21

View file

@ -0,0 +1,7 @@
import frappe
def execute():
frappe.reload_doctype("System Settings")
# setting first_day_of_the_week value as "Monday" to avoid breaking change
# because before the configuration was introduced, system used to consider "Monday" as start of the week
frappe.db.set_value("System Settings", "System Settings", "first_day_of_the_week", "Monday")

View file

@ -275,11 +275,7 @@ frappe.Application = class Application {
this.set_globals();
this.sync_pages();
frappe.router.setup();
moment.locale("en");
moment.user_utc_offset = moment().utcOffset();
if(frappe.boot.timezone_info) {
moment.tz.add(frappe.boot.timezone_info);
}
this.setup_moment();
if(frappe.boot.print_css) {
frappe.dom.set_style(frappe.boot.print_css, "print-style");
}
@ -628,6 +624,19 @@ frappe.Application = class Application {
}
});
}
setup_moment() {
moment.updateLocale('en', {
week: {
dow: frappe.datetime.get_first_day_of_the_week_index(),
}
});
moment.locale("en");
moment.user_utc_offset = moment().utcOffset();
if (frappe.boot.timezone_info) {
moment.tz.add(frappe.boot.timezone_info);
}
}
}
frappe.get_module = function(m, default_module) {

View file

@ -62,6 +62,7 @@ frappe.ui.form.ControlDate = class ControlDate extends frappe.ui.form.ControlDat
dateFormat: date_format,
startDate: this.get_start_date(),
keyboardNav: false,
firstDay: frappe.datetime.get_first_day_of_the_week_index(),
onSelect: () => {
this.$input.trigger('change');
},

View file

@ -196,7 +196,7 @@ export default class GridRow {
// REDESIGN-TODO: Make translation contextual, this No is Number
var txt = (this.doc ? this.doc.idx : __("No."));
this.row_index = $(
`<div class="row-index sortable-handle col col-xs-1">
`<div class="row-index sortable-handle col">
${this.row_check_html}
<span class="hidden-xs">${txt}</span></div>`)
.appendTo(this.row)

View file

@ -254,6 +254,11 @@ $.extend(frappe.datetime, {
], true).isValid();
},
get_first_day_of_the_week_index() {
const first_day_of_the_week = frappe.sys_defaults.first_day_of_the_week || "Sunday";
return moment.weekdays().indexOf(first_day_of_the_week);
}
});
// Proxy for dateutil and get_today

View file

@ -15,6 +15,8 @@ import io
from mimetypes import guess_type
from datetime import datetime, timedelta, date
from unittest.mock import patch
class TestFilters(unittest.TestCase):
def test_simple_dict(self):
self.assertTrue(evaluate_filters({'doctype': 'User', 'status': 'Open'}, {'status': 'Open'}))
@ -306,3 +308,24 @@ class TestDiffUtils(unittest.TestCase):
diff = get_version_diff(old_version, latest_version)
self.assertIn('-2;', diff)
self.assertIn('+42;', diff)
class TestDateUtils(unittest.TestCase):
def test_first_day_of_week(self):
# Monday as start of the week
with patch.object(frappe.utils.data, "get_first_day_of_the_week", return_value="Monday"):
self.assertEqual(frappe.utils.get_first_day_of_week("2020-12-25"),
frappe.utils.getdate("2020-12-21"))
self.assertEqual(frappe.utils.get_first_day_of_week("2020-12-20"),
frappe.utils.getdate("2020-12-14"))
# Sunday as start of the week
self.assertEqual(frappe.utils.get_first_day_of_week("2020-12-25"),
frappe.utils.getdate("2020-12-20"))
self.assertEqual(frappe.utils.get_first_day_of_week("2020-12-21"),
frappe.utils.getdate("2020-12-20"))
def test_last_day_of_week(self):
self.assertEqual(frappe.utils.get_last_day_of_week("2020-12-24"),
frappe.utils.getdate("2020-12-26"))
self.assertEqual(frappe.utils.get_last_day_of_week("2020-12-28"),
frappe.utils.getdate("2021-01-02"))

View file

@ -11,11 +11,26 @@ from code import compile_command
from urllib.parse import quote, urljoin
from frappe.desk.utils import slug
from click import secho
from enum import Enum
DATE_FORMAT = "%Y-%m-%d"
TIME_FORMAT = "%H:%M:%S.%f"
DATETIME_FORMAT = DATE_FORMAT + " " + TIME_FORMAT
class Weekday(Enum):
Sunday = 0
Monday = 1
Tuesday = 2
Wednesday = 3
Thursday = 4
Friday = 5
Saturday = 6
def get_first_day_of_the_week():
return frappe.get_system_settings('first_day_of_the_week') or "Sunday"
def get_start_of_week_index():
return Weekday[get_first_day_of_the_week()].value
def is_invalid_date_string(date_string):
# dateutil parser does not agree with dates like "0001-01-01" or "0000-00-00"
@ -246,9 +261,22 @@ def get_quarter_start(dt, as_str=False):
def get_first_day_of_week(dt, as_str=False):
dt = getdate(dt)
date = dt - datetime.timedelta(days=dt.weekday())
date = dt - datetime.timedelta(days=get_week_start_offset_days(dt))
return date.strftime(DATE_FORMAT) if as_str else date
def get_week_start_offset_days(dt):
current_day_index = get_normalized_weekday_index(dt)
start_of_week_index = get_start_of_week_index()
if current_day_index >= start_of_week_index:
return current_day_index - start_of_week_index
else:
return 7 - (start_of_week_index - current_day_index)
def get_normalized_weekday_index(dt):
# starts Sunday with 0
return (dt.weekday() + 1) % 7
def get_year_start(dt, as_str=False):
dt = getdate(dt)
date = datetime.date(dt.year, 1, 1)

View file

@ -78,6 +78,10 @@ frappe.boot = {
sysdefaults: {
float_precision: parseInt("{{ frappe.get_system_settings('float_precision') or 3 }}"),
date_format: "{{ frappe.get_system_settings('date_format') or 'yyyy-mm-dd' }}",
},
time_zone: {
system: "{{ frappe.utils.get_time_zone() }}",
user: "{{ frappe.db.get_value('User', frappe.session.user, 'time_zone') or frappe.utils.get_time_zone() }}"
}
};
// for backward compatibility of some libs

View file

@ -216,8 +216,8 @@ def get_context(context):
"amount": amount,
"title": title,
"description": title,
"reference_doctype": "Web Form",
"reference_docname": self.name,
"reference_doctype": doc.doctype,
"reference_docname": doc.name,
"payer_email": frappe.session.user,
"payer_name": frappe.utils.get_fullname(frappe.session.user),
"order_id": doc.name,