Merge branch 'develop' into multistep_webforms
This commit is contained in:
commit
f930fd0fe1
20 changed files with 201 additions and 58 deletions
|
|
@ -148,6 +148,7 @@
|
|||
"context": true,
|
||||
"before": true,
|
||||
"beforeEach": true,
|
||||
"after": true,
|
||||
"qz": true,
|
||||
"localforage": true,
|
||||
"extend_cscript": true
|
||||
|
|
|
|||
45
cypress/integration/first_day_of_the_week.js
Normal file
45
cypress/integration/first_day_of_the_week.js
Normal 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();
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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`;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -100,5 +100,5 @@ frappe.ui.form.on('System Console', {
|
|||
</tr></thead>
|
||||
<tbody>${rows}</thead>`);
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
7
frappe/patches/v13_0/set_first_day_of_the_week.py
Normal file
7
frappe/patches/v13_0/set_first_day_of_the_week.py
Normal 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")
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue