2
0
mirror of https://github.com/frappe/books.git synced 2024-09-20 03:29:00 +00:00

table filters, wip

This commit is contained in:
Rushabh Mehta 2018-03-08 19:01:22 +05:30
parent 9de539517f
commit fb23980490
29 changed files with 451 additions and 113 deletions

View File

@ -396,7 +396,7 @@ module.exports = class Database extends Observable {
const value = filters[key];
if (value instanceof Array) {
// if its like, we should add the wildcard "%" if the user has not added
if (value[0].toLowerCase() === 'like' && !value[1].includes('%')) {
if (['like', 'includes'].includes(value[0].toLowerCase()) && !value[1].includes('%')) {
value[1] = `%${value[1]}%`;
}
conditions.push(`${key} ${value[0]} ?`);

View File

@ -6,6 +6,7 @@ module.exports = class FormPage extends Page {
constructor(doctype) {
let meta = frappe.getMeta(doctype);
super({title: `Edit ${meta.name}`, hasRoute: true});
this.wrapper.classList.add('page-form');
this.meta = meta;
this.doctype = doctype;
@ -16,6 +17,10 @@ module.exports = class FormPage extends Page {
actions: ['save', 'delete', 'duplicate', 'settings', 'print']
});
if (this.meta.pageSettings && this.meta.pageSettings.hideTitle) {
this.titleElement.classList.add('hide');
}
// if name is different after saving, change the route
this.form.on('save', async (params) => {
let route = frappe.router.getRoute();

View File

@ -131,8 +131,8 @@ module.exports = class Desk {
if (!this.pages[view]) this.pages[view] = {};
if (!this.pages[view][doctype]) this.pages[view][doctype] = new views[view](doctype);
const page = this.pages[view][doctype];
await page.show(params);
this.toggleCenter(page.fullPage ? false : true);
await page.show(params);
}
async showFormModal(doctype, name) {
@ -163,8 +163,7 @@ module.exports = class Desk {
}
addSidebarItem(label, action) {
let item = frappe.ui.add('a', 'list-group-item list-group-item-action', this.sidebarList);
item.textContent = label;
let item = frappe.ui.add('a', 'list-group-item list-group-item-action', this.sidebarList, label);
if (typeof action === 'string') {
item.href = action;
this.routeItems[action] = item;

View File

@ -14,6 +14,8 @@ module.exports = class ListPage extends Page {
hasRoute: hasRoute
});
this.name = name;
this.list = new (view.getListClass(name))({
doctype: name,
parent: this.body,
@ -29,7 +31,8 @@ module.exports = class ListPage extends Page {
async show(params) {
super.show();
this.setTitle(name===this.list.doctype ? (this.list.meta.label || this.list.meta.name) : name);
this.setTitle(this.name===this.list.meta.name ? (this.list.meta.label || this.list.meta.name) : this.name);
if (frappe.desk.body.activePage && frappe.router.getRoute()[0]==='list') {
frappe.desk.body.activePage.hide();
}

View File

@ -12,8 +12,7 @@ module.exports = class DeskMenu {
}
addItem(label, action) {
let item = frappe.ui.add('div', 'list-row', this.listGroup);
item.textContent = label;
let item = frappe.ui.add('div', 'list-row', this.listGroup, label);
if (typeof action === 'string') {
this.routeItems[action] = item;
}

View File

@ -6,9 +6,8 @@ module.exports = class Navbar {
this.items = {};
this.navbar = frappe.ui.add('div', 'navbar navbar-expand-md border-bottom navbar-dark bg-dark', document.querySelector('body'));
this.brand = frappe.ui.add('a', 'navbar-brand', this.navbar);
this.brand = frappe.ui.add('a', 'navbar-brand', this.navbar, brand_label);
this.brand.href = '#';
this.brand.textContent = brand_label;
this.toggler = frappe.ui.add('button', 'navbar-toggler', this.navbar);
this.toggler.setAttribute('type', 'button');
@ -24,8 +23,7 @@ module.exports = class Navbar {
addItem(label, route) {
let item = frappe.ui.add('li', 'nav-item', this.nav);
item.link = frappe.ui.add('a', 'nav-link', item);
item.link.textContent = label;
item.link = frappe.ui.add('a', 'nav-link', item, label);
item.link.href = route;
this.items[label] = item;
return item;

View File

@ -10,6 +10,7 @@ module.exports = class PrintPage extends Page {
super({title: `${meta.name}`, hasRoute: true});
this.meta = meta;
this.doctype = doctype;
this.titleElement.classList.add('hide');
this.addButton(frappe._('Edit'), 'primary', () => {
frappe.router.setRoute('edit', this.doctype, this.name)
@ -36,8 +37,7 @@ module.exports = class PrintPage extends Page {
try {
this.body.innerHTML = `<div class="print-page">${nunjucks.renderString(this.printFormat.template, context)}</div>`;
this.setTitle(doc.name);
if (doc.submitted) this.addTitleBadge('✓', 'Submitted');
// this.setTitle(doc.name);
} catch (e) {
this.renderError('Template Error', e);
throw e;

View File

@ -5,21 +5,45 @@ const ModelTable = require('frappejs/client/ui/modelTable');
module.exports = class TablePage extends Page {
constructor(doctype) {
let meta = frappe.getMeta(doctype);
super({title: `${meta.name}`, hasRoute: true});
super({title: `${meta.label || meta.name}`, hasRoute: true});
this.tableWrapper = frappe.ui.add('div', 'table-page-wrapper', this.body);
this.doctype = doctype;
this.fullPage = true;
}
this.addButton('Set Filters', 'btn-secondary', async () => {
const formModal = await frappe.desk.showFormModal('FilterSelector');
formModal.form.once('apply-filters', () => {
formModal.hide();
this.run();
})
});
}
async show(params) {
super.show();
if (!this.modelTable) {
this.modelTable = new ModelTable({doctype: this.doctype, parent: this.body, layout: 'fixed'});
if (!this.filterSelector) {
this.filterSelector = await frappe.getSingle('FilterSelector');
this.filterSelector.reset(this.doctype);
}
if (!this.modelTable) {
this.modelTable = new ModelTable({
doctype: this.doctype,
parent: this.tableWrapper,
layout: 'fluid'
});
}
this.run();
}
async run() {
const data = await frappe.db.getAll({
doctype: this.doctype,
fields: ['*'],
filters: this.filterSelector.getFilters(),
start: this.start,
limit: 500
});

View File

@ -35,21 +35,30 @@ html {
.page {
padding-bottom: $spacer-4;
.page-head {
.page-nav {
padding: $spacer-2 $spacer-3;
background-color: $gray-100;
border-bottom: 1px solid $gray-300;
.page-title {
display: inline-block;
margin-top: 0.3rem;
}
.btn {
margin-left: $spacer-2;
}
}
.page-title {
font-weight: bold;
padding: $spacer-4;
padding-bottom: 0;
}
.page-links {
padding: $spacer-3 $spacer-4;
.page-link {
display: inline-block;
}
}
.page-error {
text-align: center;
padding: 200px 0px;
@ -57,7 +66,7 @@ html {
}
.form-body {
padding: $spacer-4;
padding: $spacer-3 $spacer-4;
.form-check {
margin-bottom: $spacer-2;
@ -80,12 +89,12 @@ html {
}
.list-search {
padding: $spacer-3;
padding: $spacer-3 $spacer-4;
}
.list-body {
.list-row {
padding: $spacer-2 15px;
padding: $spacer-2 $spacer-4;
border-bottom: 1px solid $gray-200;
cursor: pointer;
@ -146,50 +155,11 @@ html {
}
}
.CodeMirror {
font-family: "SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;
border: 1px solid $gray-300;
border-radius: 0.25rem;
padding: $spacer-2;
.table-page-wrapper {
width: 100%;
padding: $spacer-3 $spacer-4;
}
.awesomplete {
display: block;
> ul > li {
padding: .75rem .375rem;
}
> ul > li:hover {
background: $gray-300;
color: $body-color;
}
> ul > li[aria-selected="true"] {
background: $gray-300;
color: $body-color;
}
> ul > li[aria-selected="true"]:hover {
background: $gray-300;
color: $body-color;
}
li[aria-selected="true"] mark, li[aria-selected="false"] mark {
background: inherit;
color: inherit;
padding: 0px;
}
}
mark {
padding: none;
background: inherit;
}
.table-wrapper {
margin-top: $spacer-4;
margin-bottom: $spacer-4;
@ -232,3 +202,46 @@ mark {
}
}
.CodeMirror {
font-family: "SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;
border: 1px solid $gray-300;
border-radius: 0.25rem;
padding: $spacer-2;
}
.awesomplete {
display: block;
> ul > li {
padding: .75rem .375rem;
}
> ul > li:hover {
background: $gray-300;
color: $body-color;
}
> ul > li[aria-selected="true"] {
background: $gray-300;
color: $body-color;
}
> ul > li[aria-selected="true"]:hover {
background: $gray-300;
color: $body-color;
}
li[aria-selected="true"] mark, li[aria-selected="false"] mark {
background: inherit;
color: inherit;
padding: 0px;
}
}
mark {
padding: none;
background: inherit;
}

View File

@ -31,8 +31,7 @@ class Dropdown {
}
addItem(label, action) {
let item = frappe.ui.add('button', 'dropdown-item', this.dropdownMenu);
item.textContent = label;
let item = frappe.ui.add('button', 'dropdown-item', this.dropdownMenu, label);
item.setAttribute('type', 'button');
if (typeof action === 'string') {
item.addEventListener('click', async () => {

View File

@ -2,7 +2,7 @@ const frappe = require('frappejs');
const Dropdown = require('./dropdown');
module.exports = {
add(tag, className, parent) {
add(tag, className, parent, textContent) {
let element = document.createElement(tag);
if (className) {
for (let c of className.split(' ')) {
@ -12,6 +12,9 @@ module.exports = {
if (parent) {
parent.appendChild(element);
}
if (textContent) {
element.textContent = textContent;
}
return element;
},
@ -19,6 +22,12 @@ module.exports = {
element.parentNode.removeChild(element);
},
empty(element) {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
},
addClass(element, className) {
if (element.classList) {
element.classList.add(className);

View File

@ -4,7 +4,7 @@ const controls = require('frappejs/client/view/controls');
const Modal = require('frappejs/client/ui/modal');
module.exports = class ModelTable {
constructor({doctype, parent, layout='fixed', parentControl, getRowDoc,
constructor({doctype, parent, layout, parentControl, getRowDoc,
isDisabled, getTableData}) {
Object.assign(this, arguments[0]);
this.meta = frappe.getMeta(this.doctype);
@ -15,12 +15,16 @@ module.exports = class ModelTable {
this.datatable = new DataTable(this.parent, {
columns: this.getColumns(),
data: [],
layout: this.meta.layout || 'fixed',
layout: this.meta.layout || this.layout || 'fluid',
addCheckboxColumn: true,
getEditor: this.getTableInput.bind(this),
});
}
resize() {
this.datatable.setDimensions();
}
getColumns() {
return this.getTableFields().map(field => {
if (!field.width) {
@ -51,7 +55,7 @@ module.exports = class ModelTable {
getTableInput(colIndex, rowIndex, value, parent) {
let field = this.datatable.getColumn(colIndex).field;
if (field.disabled || field.forumla || (this.isDisabled && this.isDisabled())) {
if (field.disabled || (this.isDisabled && this.isDisabled())) {
return false;
}
@ -59,7 +63,8 @@ module.exports = class ModelTable {
// text in modal
parent = this.getControlModal(field).getBody();
}
return this.getControl(field, parent);
const editor = this.getControl(field, parent);
return editor;
}
getControl(field, parent) {
@ -75,12 +80,11 @@ module.exports = class ModelTable {
column.activeControl = control;
control.parentControl = this.parentControl;
control.doc = doc;
control.set_focus();
return control.setInputValue(control.doc[column.id]);
control.setFocus();
control.setInputValue(control.doc[column.id]);
return control;
},
setValue: async (value, rowIndex, column) => {
if (this.doc) this.doc._dirty = true;
control.doc._dirty = true;
control.handleChange();
},
getValue: () => {

View File

@ -68,8 +68,7 @@ class BaseControl {
}
makeLabel(labelClass = null) {
this.labelElement = frappe.ui.add('label', labelClass, this.inputContainer);
this.labelElement.textContent = this.label;
this.labelElement = frappe.ui.add('label', labelClass, this.inputContainer, this.label);
this.labelElement.setAttribute('for', this.id);
}
@ -88,7 +87,17 @@ class BaseControl {
}
isDisabled() {
return this.disabled || this.formula || (this.doc && this.doc.submitted);
let disabled = this.disabled;
if (this.doc && this.doc.submitted) {
disabled = true;
}
if (this.formula && this.fieldtype !== 'Table') {
disabled = true;
}
return disabled;
}
setDisabled() {
@ -112,8 +121,8 @@ class BaseControl {
makeDescription() {
if (this.description) {
this.description_element = frappe.ui.add('small', 'form-text text-muted', this.inputContainer);
this.description_element.textContent = this.description;
this.description_element = frappe.ui.add('small', 'form-text text-muted',
this.inputContainer, this.description);
}
}
@ -162,6 +171,7 @@ class BaseControl {
if (this.parentControl) {
// its a child
this.doc[this.fieldname] = value;
this.parentControl.doc._dirty = true;
await this.parentControl.doc.applyChange(this.fieldname);
} else {
// parent
@ -178,7 +188,7 @@ class BaseControl {
this.input.removeAttribute('disabled');
}
set_focus() {
setFocus() {
this.input.focus();
}
}

View File

@ -50,7 +50,7 @@ class LinkControl extends BaseControl {
async getList(query) {
return (await frappe.db.getAll({
doctype: this.target,
filters: this.getFilters(query),
filters: this.getFilters(query, this),
limit: 50
})).map(d => d.name);
}

View File

@ -3,19 +3,46 @@ const frappe = require('frappejs');
class SelectControl extends BaseControl {
makeInput() {
this.input = frappe.ui.add('select', 'form-control', this.inputContainer);
this.input = frappe.ui.add('select', 'form-control', this.getInputParent());
this.addOptions();
}
refresh() {
this.addOptions();
super.refresh();
}
addOptions() {
const options = this.getOptions();
if (this.areOptionsSame(options)) return;
frappe.ui.empty(this.input);
for (let value of options) {
let option = frappe.ui.add('option', null, this.input, value.label || value);
option.setAttribute('value', value.value || value);
}
this.lastOptions = options;
}
getOptions() {
let options = this.options;
if (typeof options==='string') {
options = options.split('\n');
}
for (let value of options) {
let option = frappe.ui.add('option', null, this.input);
option.textContent = value;
option.setAttribute('value', value);
}
return options;
}
areOptionsSame(options) {
let same = false;
if (this.lastOptions && options.length===this.lastOptions.length) {
same = options.every((v ,i) => {
const v1 = this.lastOptions[i];
return (v.value || v) === (v1.value || v1)
});
}
return same;
}
make() {
super.make();
this.input.setAttribute('row', '3');

View File

@ -9,7 +9,7 @@ class TableControl extends BaseControl {
doctype: this.childtype,
parent: this.wrapper.querySelector('.datatable-wrapper'),
parentControl: this,
layout: this.layout || 'fixed',
layout: this.layout || 'ratio',
getRowDoc: (rowIndex) => this.doc[this.fieldname][rowIndex],
isDisabled: () => this.isDisabled(),
getTableData: () => this.getTableData()
@ -20,7 +20,7 @@ class TableControl extends BaseControl {
makeWrapper() {
this.wrapper = frappe.ui.add('div', 'table-wrapper', this.getInputParent());
this.wrapper.innerHTML =
`<div class="datatable-wrapper"></div>
`<div class="datatable-wrapper" style="width: 100%"></div>
<div class="table-toolbar">
<button type="button" class="btn btn-sm btn-outline-secondary btn-add">
${frappe._("Add")}</button>
@ -38,7 +38,7 @@ class TableControl extends BaseControl {
this.wrapper.querySelector('.btn-remove').addEventListener('click', async (event) => {
let checked = this.modelTable.getChecked();
this.doc[this.fieldname] = this.doc[this.fieldname].filter(d => !checked.includes(d.idx));
this.doc[this.fieldname] = this.doc[this.fieldname].filter(d => !checked.includes(d.idx + ''));
await this.doc.commit();
this.refresh();
this.modelTable.checkAll(false);
@ -69,7 +69,7 @@ class TableControl extends BaseControl {
}
getTableData(value) {
return value || this.getDefaultData();
return (value && value.length) ? value : this.getDefaultData();
}
getDefaultData() {
@ -77,10 +77,15 @@ class TableControl extends BaseControl {
if (!this.doc) {
return [];
}
if (!this.doc[this.fieldname]) {
this.doc[this.fieldname] = [{idx: 0}];
}
if (this.doc[this.fieldname].length === 0 && this.neverEmpty) {
this.doc[this.fieldname] = [{idx: 0}];
}
return this.doc[this.fieldname];
}

View File

@ -16,6 +16,7 @@ module.exports = class BaseForm extends Observable {
this.setup();
}
this.make();
this.bindFormEvents();
}
make() {
@ -33,6 +34,14 @@ module.exports = class BaseForm extends Observable {
this.bindKeyboard();
}
bindFormEvents() {
if (this.meta.formEvents) {
for (let key in this.meta.formEvents) {
this.on(key, this.meta.formEvents[key]);
}
}
}
makeLayout() {
if (this.meta.layout) {
for (let section of this.meta.layout) {
@ -194,6 +203,7 @@ module.exports = class BaseForm extends Observable {
control.bind(this.doc);
}
this.refresh();
this.setupDocListener();
this.trigger('use', {doc:doc});
}
@ -239,6 +249,7 @@ module.exports = class BaseForm extends Observable {
for(let control of this.controlList) {
control.refresh();
}
this.trigger('refresh', this);
}
async submit() {

View File

@ -264,8 +264,7 @@ module.exports = class BaseList extends Observable {
}
makeMoreBtn() {
this.btnMore = frappe.ui.add('button', 'btn btn-secondary hide', this.parent);
this.btnMore.textContent = 'More';
this.btnMore = frappe.ui.add('button', 'btn btn-secondary hide', this.parent, 'More');
this.btnMore.addEventListener('click', () => {
this.append();
})

View File

@ -19,13 +19,10 @@ module.exports = class Page extends Observable {
make() {
this.wrapper = frappe.ui.add('div', 'page hide', this.parent);
this.wrapper.innerHTML = `<div class="page-head clearfix hide">
<span class="page-title font-weight-bold"></span>
</div>
<div class="page-body"></div>`
this.head = this.wrapper.querySelector('.page-head');
this.body = this.wrapper.querySelector('.page-body');
this.titleElement = this.head.querySelector('.page-title');
this.head = frappe.ui.add('div', 'page-nav clearfix hide', this.wrapper);
this.titleElement = frappe.ui.add('h3', 'page-title', this.wrapper);
this.linksElement = frappe.ui.add('div', 'page-links hide', this.wrapper);
this.body = frappe.ui.add('div', 'page-body', this.wrapper);
}
setTitle(title) {
@ -40,6 +37,15 @@ module.exports = class Page extends Observable {
${message}</span>`;
}
addLink(label, action, unhide = true) {
const link = frappe.ui.add('a', 'page-link', this.linksElement, label);
link.addEventListener('click', action);
if (unhide) {
this.linksElement.classList.remove('hide');
}
return link;
}
hide() {
this.parent.activePage = null;
this.wrapper.classList.add('hide');

View File

@ -7,7 +7,8 @@ module.exports = {
fs.mkdirSync(`./models/doctype/${name}`);
fs.writeFileSync(`./models/doctype/${name}/${name}.js`, `module.exports = {
name: "${name}",
doctype: "DocType",
label: "${name}",
naming: "name", // {random|autoincrement}
isSingle: 0,
isChild: 0,
keywordFields: [],

View File

@ -201,7 +201,10 @@ module.exports = class BaseDocument extends Observable {
// for each row
for (let row of this[tablefield.fieldname]) {
for (let field of formulaFields) {
row[field.fieldname] = await field.formula(row, doc);
const val = await field.formula(row, doc);
if (val !== false) {
row[field.fieldname] = val;
}
}
}
}
@ -209,7 +212,10 @@ module.exports = class BaseDocument extends Observable {
// parent
for (let field of this.meta.getFormulaFields()) {
doc[field.fieldname] = await field.formula(doc);
const val = await field.formula(doc);
if (val !== false) {
doc[field.fieldname] = val;
}
}
return true;

View File

@ -134,7 +134,7 @@ module.exports = class BaseMeta extends BaseDocument {
if (!this._keywordFields) {
this._keywordFields = this.keywordFields;
if (!(this._keywordFields && this._keywordFields.length && this.fields)) {
this._keywordFields = this.fields.filter(field => field.required).map(field => field.fieldname);
this._keywordFields = this.fields.filter(field => field.fieldtype !== 'Table' && field.required).map(field => field.fieldname);
}
if (!(this._keywordFields && this._keywordFields.length)) {
this._keywordFields = ['name']
@ -145,6 +145,8 @@ module.exports = class BaseMeta extends BaseDocument {
validateSelect(field, value) {
let options = field.options;
if (!options) return;
if (typeof options === 'string') {
// values given as string
options = field.options.split('\n');

View File

@ -0,0 +1,28 @@
module.exports = {
name: "FilterGroup",
isSingle: 0,
isChild: 0,
keywordFields: [],
fields: [
{
fieldname: "name",
label: "Name",
fieldtype: "Data",
required: 1
},
{
fieldname: "forDocType",
label: "Document Type",
fieldtype: "Data",
required: 1,
disabled: 1,
},
{
fieldname: "items",
fieldtype: "Table",
childtype: "FilterItem",
label: "Items",
required: 1
}
]
}

View File

@ -0,0 +1,36 @@
module.exports = {
name: "FilterItem",
doctype: "DocType",
isSingle: 0,
isChild: 1,
keywordFields: [],
fields: [
{
fieldname: "field",
label: "Field",
fieldtype: "Select",
required: 1
},
{
fieldname: "condition",
label: "Condition",
fieldtype: "Select",
options: [
'Equals',
'>',
'<',
'>=',
'<=',
'Between',
'Includes',
'One Of'
],
required: 1
},
{
fieldname: "value",
label: "Value",
fieldtype: "Data",
}
]
}

View File

@ -0,0 +1,79 @@
const frappe = require('frappejs');
module.exports = {
name: "FilterSelector",
label: "Set Filters",
documentClass: require('./FilterSelectorDocument'),
isSingle: 1,
isChild: 0,
keywordFields: [],
fields: [
{
fieldname: "forDocType",
label: "Document Type",
fieldtype: "Data",
hidden: 1,
},
{
fieldname: "filterGroup",
label: "Saved Filters",
fieldtype: "Link",
target: "FilterGroup",
getFilters: (query, control) => {
return {
forDocType: control.doc.forDocType,
keywords: ["like", query]
}
}
},
{
fieldname: "filterGroupName",
label: "New Filter Name",
fieldtype: "Data",
},
{
fieldname: "items",
label: "Items",
fieldtype: "Table",
childtype: "FilterItem",
neverEmpty: 1,
// copy items from saved filter group
formula: async (doc) => {
if (doc._lastFilterGroup !== doc.filterGroup) {
// fitler changed
if (doc.filterGroup) {
doc.items = [];
const filterGroup = await frappe.getDoc('FilterGroup', doc.filterGroup);
// copy items
for(let source of filterGroup.items) {
const item = Object.assign({}, source);
item.parent = item.name = '';
doc.items.push(item);
}
} else {
// no filter group selected
doc.items = [{idx: 0}];
}
doc._lastFilterGroup = doc.filterGroup;
}
return false;
},
}
],
formEvents: {
// set the fields of the selected item in the 'select'
refresh: (form) => {
// override the `getOptions` method in the `field` property
frappe.getMeta('FilterItem').getField('field').getOptions = () => {
return frappe.getMeta(form.doc.forDocType).fields.map((f) => {
return {label: f.label, value: f.fieldname};
});
}
}
}
}

View File

@ -0,0 +1,55 @@
const BaseDocument = require('frappejs/model/document');
const frappe = require('frappejs');
module.exports = class FormSelector extends BaseDocument {
reset(doctype) {
this.forDocType = doctype;
this.items = [];
this.filterGroup = '';
this.filterGroupName = '';
}
getFilters() {
const filters = {};
for (let item of (this.items || [])) {
if (item.condition === 'Equals') item.condition = '=';
filters[item.field] = [item.condition, item.value];
}
return filters;
}
async update() {
// save new group filter
if (frappe.isServer) {
if (this.filterGroupName) {
await this.makeFilterGroup();
} else if (this.filterGroup) {
await this.updateFilterGroup();
}
return this;
} else {
return super.update();
}
}
async makeFilterGroup() {
const filterGroup = frappe.newDoc({doctype:'FilterGroup'});
filterGroup.name = this.filterGroupName;
this.updateFilterGroupValues(filterGroup);
await filterGroup.insert();
}
async updateFilterGroup() {
const filterGroup = await frappe.getDoc('FilterGroup', this.filterGroup);
this.updateFilterGroupValues(filterGroup);
await filterGroup.update();
}
updateFilterGroupValues(filterGroup) {
filterGroup.forDocType = this.forDocType;
filterGroup.items = [];
for (let item of this.items) {
filterGroup.items.push({field: item.field, condition: item.condition, value: item.value});
}
}
}

View File

@ -0,0 +1,14 @@
const BaseForm = require('frappejs/client/view/form');
const frappe = require('frappejs');
module.exports = class FilterSelectorForm extends BaseForm {
makeSaveButton() {
this.saveButton = this.container.addButton(frappe._("Apply"), 'primary', async (event) => {
if (this.doc.filterGroupName || (this.doc.filterGroup && this.doc._dirty)) {
// new filter, call update
await this.save();
}
this.trigger('apply-filters');
});
}
}

View File

@ -1,7 +1,10 @@
module.exports = {
"naming": "autoincrement",
"name": "ToDo",
"doctype": "DocType",
name: "ToDo",
label: "To Do",
naming: "autoincrement",
pageSettings: {
hideTitle: true
},
"isSingle": 0,
"keywordFields": [
"subject",

View File

@ -1,5 +1,8 @@
module.exports = {
models: {
FilterItem: require('./doctype/FilterItem/FilterItem.js'),
FilterGroup: require('./doctype/FilterGroup/FilterGroup.js'),
FilterSelector: require('./doctype/FilterSelector/FilterSelector.js'),
NumberSeries: require('./doctype/NumberSeries/NumberSeries.js'),
PrintFormat: require('./doctype/PrintFormat/PrintFormat.js'),
Role: require('./doctype/Role/Role.js'),