2
0
mirror of https://github.com/frappe/books.git synced 2024-12-23 03:19:01 +00:00

Merge pull request #50 from netchampfaris/nestedset

Reports
This commit is contained in:
Faris Ansari 2018-04-24 13:38:55 +05:30 committed by GitHub
commit 9d5808e4d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 150 additions and 36 deletions

View File

@ -388,31 +388,68 @@ module.exports = class Database extends Observable {
getFilterConditions(filters) {
// {"status": "Open"} => `status = "Open"`
// {"status": "Open", "name": ["like", "apple%"]}
// => `status="Open" and name like "apple%"
let conditions = [];
let values = [];
// {"date": [">=", "2017-09-09", "<=", "2017-11-01"]}
// => `date >= 2017-09-09 and date <= 2017-11-01`
let filtersArray = [];
for (let key in filters) {
const value = filters[key];
if (value instanceof Array) {
const condition = value[0];
// if its like, we should add the wildcard "%" if the user has not added
if (condition.toLowerCase()==='includes') {
condition = 'like';
let value = filters[key];
let field = key;
let operator = '=';
let comparisonValue = value;
if (Array.isArray(value)) {
operator = value[0];
comparisonValue = value[1];
operator = operator.toLowerCase();
if (operator === 'includes') {
operator = 'like';
}
if (['like', 'includes'].includes(condition.toLowerCase()) && !value[1].includes('%')) {
value[1] = `%${value[1]}%`;
if (['like', 'includes'].includes(operator) && !comparisonValue.includes('%')) {
comparisonValue = `%${comparisonValue}%`;
}
conditions.push(`ifnull(${key}, '') ${condition} ?`);
values.push(value[1]);
} else {
conditions.push(`ifnull(${key}, '') = ?`);
values.push(value);
}
filtersArray.push([field, operator, comparisonValue]);
if (Array.isArray(value) && value.length > 2) {
// multiple conditions
let operator = value[2];
let comparisonValue = value[3];
filtersArray.push([field, operator, comparisonValue]);
}
}
let conditions = filtersArray.map(filter => {
const [field, operator, comparisonValue] = filter;
let placeholder = Array.isArray(comparisonValue) ?
comparisonValue.map(v => '?').join(', ') :
'?';
return `ifnull(${field}, '') ${operator} (${placeholder})`;
});
let values = filtersArray.reduce((acc, filter) => {
const comparisonValue = filter[2];
if (Array.isArray(comparisonValue)) {
acc = acc.concat(comparisonValue);
} else {
acc.push(comparisonValue);
}
return acc;
}, []);
return {
conditions: conditions.length ? conditions.join(" and ") : "",
values: values
values
};
}

View File

@ -1,5 +1,5 @@
const Page = require('frappejs/client/view/page');
const Form = require('frappejs/client/view/form');
const FormLayout = require('frappejs/client/view/formLayout');
const DataTable = require('frappe-datatable');
const frappe = require('frappejs');
const utils = require('frappejs/client/ui/utils');
@ -10,7 +10,7 @@ const Observable = require('frappejs/utils/observable');
// `getColumns` return columns
module.exports = class ReportPage extends Page {
constructor({title, filterFields}) {
constructor({title, filterFields = []}) {
super({title: title, hasRoute: true});
this.fullPage = true;
@ -30,19 +30,24 @@ module.exports = class ReportPage extends Page {
// overrride
}
getRowsForDataTable(data) {
return data;
}
makeFilters() {
this.form = new Form({
this.filters = new FormLayout({
parent: this.filterWrapper,
meta: { fields: this.filterFields },
fields: this.filterFields,
doc: new Observable(),
inline: true,
container: this
inline: true
});
this.filterWrapper.appendChild(this.filters.form);
}
getFilterValues() {
const values = {};
for (let control of this.form.formLayout.controlList) {
for (let control of this.filters.controlList) {
values[control.fieldname] = control.getInputValue();
if (control.required && !values[control.fieldname]) {
frappe.ui.showAlert({message: frappe._('{0} is mandatory', control.label), color: 'red'});
@ -60,8 +65,8 @@ module.exports = class ReportPage extends Page {
async run() {
if (frappe.params && frappe.params.filters) {
for (let key in frappe.params.filters) {
if (this.form.controls[key]) {
this.form.controls[key].setInputValue(frappe.params.filters[key]);
if (this.filters.controls[key]) {
this.filters.controls[key].setInputValue(frappe.params.filters[key]);
}
}
}
@ -79,14 +84,17 @@ module.exports = class ReportPage extends Page {
method: this.method,
args: filterValues
});
this.datatable.refresh(data);
const rows = this.getRowsForDataTable(data);
const columns = utils.convertFieldsToDatatableColumns(this.getColumns(data), this.layout);
this.datatable.refresh(rows, columns);
}
makeDataTable() {
this.datatable = new DataTable(this.tableWrapper, {
this.datatable = new DataTable(this.tableWrapper, Object.assign({
columns: utils.convertFieldsToDatatableColumns(this.getColumns(), this.layout),
data: [],
layout: this.layout || 'fluid',
});
}, this.datatableOptions || {}));
}
}

View File

@ -11,7 +11,9 @@ class DateControl extends BaseControl {
'mm/dd/yyyy': 'm/d/Y',
'mm-dd-yyyy': 'm-d-Y'
}
let altFormat = dateFormat[frappe.SystemSettings.dateFormat];
let altFormat = frappe.SystemSettings ?
dateFormat[frappe.SystemSettings.dateFormat] :
dateFormat['yyyy-mm-dd'];
super.make();
this.input.setAttribute('type', 'text');

View File

@ -14,6 +14,12 @@ class LinkControl extends BaseControl {
minChars: 0,
maxItems: 99,
filter: () => true,
sort: (a, b) => {
if (a.value === '__newitem' || b.value === '__newitem') {
return -1;
}
return a.value > b.value;
}
});
// rebuild the list on input

View File

@ -206,7 +206,7 @@ module.exports = class BaseForm extends Observable {
}
refreshLinks(links) {
if (!this.container) return;
if (!(this.container && this.container.clearLinks)) return;
this.container.clearLinks();
for(let link of links) {

View File

@ -3,7 +3,7 @@ const controls = require('./controls');
const Observable = require('frappejs/utils/observable');
module.exports = class FormLayout extends Observable {
constructor({fields, doc, layout, events = []}) {
constructor({fields, doc, layout, inline = false, events = []}) {
super();
Object.assign(this, arguments[0]);
this.controls = {};
@ -14,6 +14,11 @@ module.exports = class FormLayout extends Observable {
this.form = document.createElement('div');
this.form.classList.add('form-body');
if (this.inline) {
this.form.classList.add('row');
this.form.classList.add('p-0');
}
this.makeLayout();
if (doc) {
@ -63,6 +68,9 @@ module.exports = class FormLayout extends Observable {
let control = controls.makeControl({field: field, form: this, parent: parent});
this.controlList.push(control);
this.controls[field.fieldname] = control;
if (this.inline) {
control.inputContainer.classList.add('col');
}
}
}
}

View File

@ -105,6 +105,8 @@ module.exports = class BaseTree extends BaseList {
if (action === 'edit') {
this.edit(treeNode.props.doc.name);
} else if (action === 'addChild') {
this.addChildNode(treeNode.props.doc.name);
}
});
@ -115,6 +117,15 @@ module.exports = class BaseTree extends BaseList {
frappe.desk.showFormModal(this.doctype, name);
}
async addChildNode(name) {
const newDoc = await frappe.getNewDoc(this.doctype);
const formModal = await frappe.desk.showFormModal(this.doctype, newDoc.name);
const parentField = this.treeSettings.parentField;
if (formModal.form.doc.meta.hasField(parentField)) {
formModal.form.doc.set(parentField, name);
}
}
async getData(node) {
let fields = this.getFields();
let filters = {};
@ -150,8 +161,8 @@ module.exports = class BaseTree extends BaseList {
getActionButtonsHTML() {
return [
{ id: 'edit', label: frappe._('Edit') }
// { id: 'addChild', label: frappe._('Add Child') },
{ id: 'edit', label: frappe._('Edit') },
{ id: 'addChild', label: frappe._('Add Child') },
// { id: 'delete', label: frappe._('Delete') },
].map(button => {
return `<button class="btn btn-link btn-sm m-0" slot="actions" data-action="${button.id}">

View File

@ -7,8 +7,6 @@ async function getHTML(doctype, name) {
let doc = await frappe.getDoc(doctype, name);
let context = {doc: doc, frappe: frappe};
console.log(context);
let html;
try {
html = nunjucks.renderString(printFormat.template, context);

View File

@ -34,5 +34,13 @@ module.exports = {
{
fieldname: 'parentfield', fieldtype: 'Data', required: 1
}
],
treeFields: [
{
fieldname: 'lft', fieldtype: 'Int', required: 1
},
{
fieldname: 'rgt', fieldtype: 'Int', required: 1
}
]
};
};

View File

@ -112,6 +112,15 @@ module.exports = class BaseMeta extends BaseDocument {
}
}
if (this.isTree) {
// tree fields
for (let field of model.treeFields) {
if (frappe.db.typeMap[field.fieldtype] && !doctype_fields.includes(field.fieldname)) {
_add(field);
}
}
}
// doctype fields
for (let field of this.fields) {
let include = frappe.db.typeMap[field.fieldtype];

View File

@ -16,12 +16,14 @@
"clusterize.js": "^0.18.0",
"codemirror": "^5.35.0",
"commander": "^2.13.0",
"deepmerge": "^2.1.0",
"eslint": "^4.19.1",
"express": "^4.16.2",
"flatpickr": "^4.3.2",
"frappe-datatable": "frappe/datatable",
"frappejs": "../frappejs",
"jquery": "^3.3.1",
"luxon": "^1.0.0",
"mkdirp": "^0.5.1",
"mocha": "^4.1.0",
"moment": "^2.20.1",

View File

@ -66,4 +66,19 @@ module.exports = {
});
},
/**
* Returns array from 0 to n - 1
* @param {Number} n
*/
range(n) {
return Array.from(Array(4)).map((d, i) => i)
},
unique(list, key = it => it) {
var seen = {};
return list.filter(item => {
var k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
});
}
};

View File

@ -872,6 +872,10 @@ deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
deepmerge@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.0.tgz#511a54fff405fc346f0240bb270a3e9533a31102"
defined@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
@ -1392,6 +1396,7 @@ frappejs@../frappejs:
clusterize.js "^0.18.0"
codemirror "^5.35.0"
commander "^2.13.0"
deepmerge "^2.1.0"
eslint "^4.19.1"
express "^4.16.2"
flatpickr "^4.3.2"
@ -1412,6 +1417,7 @@ frappejs@../frappejs:
puppeteer "^1.2.0"
rollup "^0.55.1"
rollup-plugin-commonjs "^8.3.0"
rollup-plugin-html "^0.2.1"
rollup-plugin-json "^2.3.0"
rollup-plugin-node-resolve "^3.0.2"
rollup-plugin-postcss "^1.2.7"
@ -2264,6 +2270,10 @@ lru-cache@^4.0.1:
pseudomap "^1.0.2"
yallist "^2.1.2"
luxon@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.0.0.tgz#ec1cba8cf53be14d2375c2f17e3468eb195c20bb"
macaddress@^0.2.8:
version "0.2.8"
resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"