feat: relative timeframe filters (#6792)
* feat: relative timeframe filters * fix: resolve syntax errors * fix: Translated options
This commit is contained in:
parent
d1418781fa
commit
3644f5f4cd
7 changed files with 148 additions and 10 deletions
5
cypress/fixtures/example.json
Normal file
5
cypress/fixtures/example.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
50
cypress/integration/relative_filters.js
Normal file
50
cypress/integration/relative_filters.js
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
context('Relative Timeframe', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('Administrator', 'qwe');
|
||||
cy.visit('/desk');
|
||||
});
|
||||
before(() => {
|
||||
cy.login('Administrator', 'qwe');
|
||||
cy.visit('/desk');
|
||||
cy.window().its('frappe').then(frappe => {
|
||||
frappe.call("frappe.tests.test_utils.create_todo_records");
|
||||
});
|
||||
});
|
||||
it('set relative filter for Previous and check list', () => {
|
||||
cy.visit('/desk#List/ToDo/List');
|
||||
cy.get('.list-row:contains("this is fourth todo")').should('exist');
|
||||
cy.get('.tag-filters-area .btn:contains("Add Filter")').click();
|
||||
cy.get('.fieldname-select-area').should('exist');
|
||||
cy.get('.fieldname-select-area input').type("Due Date{enter}", { delay: 100 });
|
||||
cy.get('select.condition.form-control').select("Previous");
|
||||
cy.get('.filter-field select.input-with-feedback.form-control').select("1 week");
|
||||
cy.server();
|
||||
cy.route({
|
||||
method: 'POST',
|
||||
url: '/'
|
||||
}).as('applyFilter');
|
||||
cy.get('.filter-box .btn:contains("Apply")').click();
|
||||
cy.wait('@applyFilter');
|
||||
cy.get('.list-row-container').its('length').should('eq', 1);
|
||||
cy.get('.list-row-container').should('contain', 'this is second todo');
|
||||
cy.get('.remove-filter.btn').click();
|
||||
});
|
||||
it('set relative filter for Next and check list', () => {
|
||||
cy.visit('/desk#List/ToDo/List');
|
||||
cy.get('.list-row:contains("this is fourth todo")').should('exist');
|
||||
cy.get('.tag-filters-area .btn:contains("Add Filter")').click();
|
||||
cy.get('.fieldname-select-area input').type("Due Date{enter}", { delay: 100 });
|
||||
cy.get('select.condition.form-control').select("Next");
|
||||
cy.get('.filter-field select.input-with-feedback.form-control').select("1 week");
|
||||
cy.server();
|
||||
cy.route({
|
||||
method: 'POST',
|
||||
url: '/'
|
||||
}).as('applyFilter');
|
||||
cy.get('.filter-box .btn:contains("Apply")').click();
|
||||
cy.wait('@applyFilter');
|
||||
cy.get('.list-row-container').its('length').should('eq', 1);
|
||||
cy.get('.list-row').should('contain', 'this is first todo');
|
||||
cy.get('.remove-filter.btn').click();
|
||||
});
|
||||
});
|
||||
|
|
@ -16,7 +16,7 @@ import frappe, json, copy, re
|
|||
from frappe.model import optional_fields
|
||||
from frappe.client import check_parent_permission
|
||||
from frappe.model.utils.user_settings import get_user_settings, update_user_settings
|
||||
from frappe.utils import flt, cint, get_time, make_filter_tuple, get_filter, add_to_date, cstr
|
||||
from frappe.utils import flt, cint, get_time, make_filter_tuple, get_filter, add_to_date, cstr, nowdate
|
||||
|
||||
class DatabaseQuery(object):
|
||||
def __init__(self, doctype, user=None):
|
||||
|
|
@ -402,6 +402,35 @@ class DatabaseQuery(object):
|
|||
if df and df.fieldtype in ("Check", "Float", "Int", "Currency", "Percent"):
|
||||
can_be_null = False
|
||||
|
||||
if f.operator.lower() in ('previous', 'next'):
|
||||
if f.operator.lower() == "previous":
|
||||
if f.value == "1 week":
|
||||
date_range = [add_to_date(nowdate(), days=-7), nowdate()]
|
||||
elif f.value == "1 month":
|
||||
date_range = [add_to_date(nowdate(), months=-1), nowdate()]
|
||||
elif f.value == "3 months":
|
||||
date_range = [add_to_date(nowdate(), months=-3), nowdate()]
|
||||
elif f.value == "6 months":
|
||||
date_range = [add_to_date(nowdate(), months=-6), nowdate()]
|
||||
elif f.value == "1 year":
|
||||
date_range = [add_to_date(nowdate(), years=-1), nowdate()]
|
||||
elif f.operator.lower() == "next":
|
||||
if f.value == "1 week":
|
||||
date_range = [nowdate(), add_to_date(nowdate(), days=7)]
|
||||
elif f.value == "1 month":
|
||||
date_range = [nowdate(), add_to_date(nowdate(), months=1)]
|
||||
elif f.value == "3 months":
|
||||
date_range = [nowdate(), add_to_date(nowdate(), months=3)]
|
||||
elif f.value == "6 months":
|
||||
date_range = [nowdate(), add_to_date(nowdate(), months=6)]
|
||||
elif f.value == "1 year":
|
||||
date_range = [nowdate(), add_to_date(nowdate(), years=1)]
|
||||
if df.fieldtype=="Datetime":
|
||||
date_range = [frappe.db.format_datetime(date_range[0]), frappe.db.format_datetime(date_range[1])]
|
||||
f.operator = "Between"
|
||||
f.value = date_range
|
||||
fallback = "'0001-01-01 00:00:00'"
|
||||
|
||||
if f.operator in ('>', '<') and (f.fieldname in ('creation', 'modified')):
|
||||
value = cstr(f.value)
|
||||
fallback = "NULL"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
<option value="not descendants of">{%= __("Not Descendants Of") %}</option>
|
||||
<option value="ancestors of">{%= __("Ancestors Of") %}</option>
|
||||
<option value="not ancestors of">{%= __("Not Ancestors Of") %}</option>
|
||||
<option value="Previous">{%=__("Previous")%}</option>
|
||||
<option value="Next">{%=__("Next")%}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xs-12">
|
||||
|
|
|
|||
|
|
@ -19,16 +19,18 @@ frappe.ui.Filter = class {
|
|||
["<=", "<="],
|
||||
["Between", __("Between")],
|
||||
["descendants of", __("Descendants Of")],
|
||||
["ancestors of", __("Ancestors Of")]
|
||||
["ancestors of", __("Ancestors Of")],
|
||||
["Previous", __("Previous")],
|
||||
["Next", __("Next")]
|
||||
];
|
||||
this.invalid_condition_map = {
|
||||
Date: ['like', 'not like'],
|
||||
Datetime: ['like', 'not like'],
|
||||
Data: ['Between'],
|
||||
Select: ["Between", "<=", ">=", "<", ">"],
|
||||
Link: ["Between"],
|
||||
Currency: ["Between"],
|
||||
Color: ["Between"]
|
||||
Data: ['Between', 'Previous', 'Next'],
|
||||
Select: ['like', 'not like'],
|
||||
Link: ["Between", 'Previous', 'Next'],
|
||||
Currency: ["Between", 'Previous', 'Next'],
|
||||
Color: ["Between", 'Previous', 'Next']
|
||||
};
|
||||
this.make();
|
||||
this.make_select();
|
||||
|
|
@ -182,6 +184,32 @@ frappe.ui.Filter = class {
|
|||
this.fieldselect.selected_doctype = doctype;
|
||||
this.fieldselect.selected_fieldname = fieldname;
|
||||
|
||||
if(["Previous", "Next"].includes(condition) && ['Date', 'Datetime', 'DateRange', 'Select'].includes(this.field.df.fieldtype)) {
|
||||
df.fieldtype = 'Select';
|
||||
df.options = [
|
||||
{
|
||||
label: __('1 week'),
|
||||
value: '1 week'
|
||||
},
|
||||
{
|
||||
label: __('1 month'),
|
||||
value: '1 month'
|
||||
},
|
||||
{
|
||||
label: __('3 months'),
|
||||
value: '3 months'
|
||||
},
|
||||
{
|
||||
label: __('6 months'),
|
||||
value: '6 months'
|
||||
},
|
||||
{
|
||||
label: __('1 year'),
|
||||
value: '1 year'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
this.make_field(df, cur.fieldtype);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
import frappe
|
||||
|
||||
from frappe.utils import evaluate_filters, money_in_words, scrub_urls, get_url
|
||||
from frappe.utils import ceil, floor
|
||||
from frappe.utils import ceil, floor, now, add_to_date
|
||||
|
||||
class TestFilters(unittest.TestCase):
|
||||
def test_simple_dict(self):
|
||||
|
|
@ -122,3 +123,26 @@ class TestHTMLUtils(unittest.TestCase):
|
|||
clean = clean_email_html(sample)
|
||||
self.assertTrue('<h1>Hello</h1>' in clean)
|
||||
self.assertTrue('<a href="http://test.com">text</a>' in clean)
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_todo_records():
|
||||
frappe.get_doc({
|
||||
"doctype": "ToDo",
|
||||
"date": add_to_date(now(), days=3),
|
||||
"description": "this is first todo"
|
||||
}).insert()
|
||||
frappe.get_doc({
|
||||
"doctype": "ToDo",
|
||||
"date": add_to_date(now(), days=-3),
|
||||
"description": "this is second todo"
|
||||
}).insert()
|
||||
frappe.get_doc({
|
||||
"doctype": "ToDo",
|
||||
"date": add_to_date(now(), months=2),
|
||||
"description": "this is third todo"
|
||||
}).insert()
|
||||
frappe.get_doc({
|
||||
"doctype": "ToDo",
|
||||
"date": add_to_date(now(), months=-2),
|
||||
"description": "this is fourth todo"
|
||||
}).insert()
|
||||
|
|
|
|||
|
|
@ -855,7 +855,7 @@ def get_filter(doctype, f):
|
|||
f.operator = "="
|
||||
|
||||
valid_operators = ("=", "!=", ">", "<", ">=", "<=", "like", "not like", "in", "not in",
|
||||
"between", "descendants of", "ancestors of", "not descendants of", "not ancestors of")
|
||||
"between", "descendants of", "ancestors of", "not descendants of", "not ancestors of", "previous", "next")
|
||||
if f.operator.lower() not in valid_operators:
|
||||
frappe.throw(frappe._("Operator must be one of {0}").format(", ".join(valid_operators)))
|
||||
|
||||
|
|
@ -1004,4 +1004,4 @@ def get_source_value(source, key):
|
|||
|
||||
def is_subset(list_a, list_b):
|
||||
'''Returns whether list_a is a subset of list_b'''
|
||||
return len(list(set(list_a) & set(list_b))) == len(list_a)
|
||||
return len(list(set(list_a) & set(list_b))) == len(list_a)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue