Sfoglia il codice sorgente

feat: relative timeframe filters (#6792)

* feat: relative timeframe filters

* fix: resolve syntax errors

* fix: Translated options
version-14
sahil28297 6 anni fa
committed by Faris Ansari
parent
commit
3644f5f4cd
7 ha cambiato i file con 148 aggiunte e 10 eliminazioni
  1. +5
    -0
      cypress/fixtures/example.json
  2. +50
    -0
      cypress/integration/relative_filters.js
  3. +30
    -1
      frappe/model/db_query.py
  4. +2
    -0
      frappe/public/js/frappe/ui/filters/edit_filter.html
  5. +34
    -6
      frappe/public/js/frappe/ui/filters/filter.js
  6. +25
    -1
      frappe/tests/test_utils.py
  7. +2
    -2
      frappe/utils/data.py

+ 5
- 0
cypress/fixtures/example.json Vedi 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
- 0
cypress/integration/relative_filters.js Vedi 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();
});
});

+ 30
- 1
frappe/model/db_query.py Vedi File

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


+ 2
- 0
frappe/public/js/frappe/ui/filters/edit_filter.html Vedi File

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


+ 34
- 6
frappe/public/js/frappe/ui/filters/filter.js Vedi File

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



+ 25
- 1
frappe/tests/test_utils.py Vedi File

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

+ 2
- 2
frappe/utils/data.py Vedi File

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

Caricamento…
Annulla
Salva