mirror of
https://github.com/frappe/books.git
synced 2025-02-02 12:08:27 +00:00
report fixes
This commit is contained in:
parent
dd88baaf41
commit
497c2ab742
@ -1,56 +1,54 @@
|
|||||||
const Page = require('frappejs/client/view/page');
|
const Page = require('frappejs/client/view/page');
|
||||||
const controls = require('frappejs/client/view/controls');
|
const Form = require('frappejs/client/view/form');
|
||||||
const DataTable = require('frappe-datatable');
|
const DataTable = require('frappe-datatable');
|
||||||
const frappe = require('frappejs');
|
const frappe = require('frappejs');
|
||||||
const utils = require('frappejs/client/ui/utils');
|
const utils = require('frappejs/client/ui/utils');
|
||||||
|
const Observable = require('frappejs/utils/observable');
|
||||||
|
|
||||||
// baseclass for report
|
// baseclass for report
|
||||||
// `url` url for report
|
// `url` url for report
|
||||||
// `getColumns` return columns
|
// `getColumns` return columns
|
||||||
|
|
||||||
module.exports = class ReportPage extends Page {
|
module.exports = class ReportPage extends Page {
|
||||||
constructor({title, }) {
|
constructor({title, filterFields}) {
|
||||||
super({title: title, hasRoute: true});
|
super({title: title, hasRoute: true});
|
||||||
|
|
||||||
this.fullPage = true;
|
this.fullPage = true;
|
||||||
|
this.filterFields = filterFields;
|
||||||
|
|
||||||
this.filterWrapper = frappe.ui.add('div', 'filter-toolbar form-inline', this.body);
|
this.filterWrapper = frappe.ui.add('div', 'filter-toolbar', this.body);
|
||||||
this.tableWrapper = frappe.ui.add('div', 'table-page-wrapper', this.body);
|
this.tableWrapper = frappe.ui.add('div', 'table-page-wrapper', this.body);
|
||||||
|
|
||||||
this.btnNew = this.addButton(frappe._('Refresh'), 'btn-primary', async () => {
|
this.btnNew = this.addButton(frappe._('Refresh'), 'btn-primary', async () => {
|
||||||
await this.run();
|
await this.run();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filters = {};
|
this.makeFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
getColumns() {
|
getColumns() {
|
||||||
// overrride
|
// overrride
|
||||||
}
|
}
|
||||||
|
|
||||||
addFilter(field) {
|
makeFilters() {
|
||||||
if (!field.fieldname) {
|
this.form = new Form({
|
||||||
field.fieldname = frappe.slug(field.label);
|
parent: this.filterWrapper,
|
||||||
}
|
meta: { fields: this.filterFields },
|
||||||
|
doc: new Observable(),
|
||||||
field.placeholder = field.label;
|
inline: true,
|
||||||
field.inline = true;
|
container: this
|
||||||
|
});
|
||||||
this.filters[field.fieldname] = controls.makeControl({field: field, form: this, parent: this.filterWrapper});
|
|
||||||
return this.filters[field.fieldname];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getFilterValues() {
|
getFilterValues() {
|
||||||
const values = {};
|
const values = {};
|
||||||
for (let fieldname in this.filters) {
|
for (let control of this.form.controlList) {
|
||||||
let control = this.filters[fieldname];
|
values[control.fieldname] = control.getInputValue();
|
||||||
values[fieldname] = control.getInputValue();
|
if (control.required && !values[control.fieldname]) {
|
||||||
if (control.required && !values[fieldname]) {
|
|
||||||
frappe.ui.showAlert({message: frappe._('{0} is mandatory', control.label), color: 'red'});
|
frappe.ui.showAlert({message: frappe._('{0} is mandatory', control.label), color: 'red'});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(values);
|
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,6 +58,16 @@ module.exports = class ReportPage extends Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frappe.params = null;
|
||||||
|
|
||||||
|
|
||||||
if (!this.datatable) {
|
if (!this.datatable) {
|
||||||
this.makeDataTable();
|
this.makeDataTable();
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ module.exports = class TablePage extends Page {
|
|||||||
this.filterSelector.reset(this.doctype);
|
this.filterSelector.reset(this.doctype);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frappe.params.filters) {
|
if (frappe.params && frappe.params.filters) {
|
||||||
this.filterSelector.setFilters(frappe.params.filters);
|
this.filterSelector.setFilters(frappe.params.filters);
|
||||||
}
|
}
|
||||||
frappe.params = null;
|
frappe.params = null;
|
||||||
|
@ -86,7 +86,8 @@ html {
|
|||||||
|
|
||||||
.form-inline {
|
.form-inline {
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-right: $spacer-2;
|
margin-right: $spacer-3;
|
||||||
|
margin-bottom: $spacer-3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,8 +86,8 @@ class BaseControl {
|
|||||||
if (!this.onlyInput) {
|
if (!this.onlyInput) {
|
||||||
this.makeDescription();
|
this.makeDescription();
|
||||||
}
|
}
|
||||||
if (this.placeholder) {
|
if (this.placeholder || this.inline) {
|
||||||
this.input.setAttribute('placeholder', this.placeholder);
|
this.input.setAttribute('placeholder', this.placeholder || this.label);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
9
client/view/controls/dynamicLink.js
Normal file
9
client/view/controls/dynamicLink.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const LinkControl = require('./link');
|
||||||
|
|
||||||
|
class DynamicLinkControl extends LinkControl {
|
||||||
|
getTarget() {
|
||||||
|
return this.doc[this.references];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = DynamicLinkControl;
|
@ -3,6 +3,7 @@ const controlClasses = {
|
|||||||
Code: require('./code'),
|
Code: require('./code'),
|
||||||
Data: require('./data'),
|
Data: require('./data'),
|
||||||
Date: require('./date'),
|
Date: require('./date'),
|
||||||
|
DynamicLink: require('./dynamicLink'),
|
||||||
Currency: require('./currency'),
|
Currency: require('./currency'),
|
||||||
Float: require('./float'),
|
Float: require('./float'),
|
||||||
Int: require('./int'),
|
Int: require('./int'),
|
||||||
|
@ -33,8 +33,8 @@ class LinkControl extends BaseControl {
|
|||||||
this.input.addEventListener('awesomplete-select', async (e) => {
|
this.input.addEventListener('awesomplete-select', async (e) => {
|
||||||
if (e.text && e.text.value === '__newItem') {
|
if (e.text && e.text.value === '__newItem') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const newDoc = await frappe.getNewDoc(this.target);
|
const newDoc = await frappe.getNewDoc(this.getTarget());
|
||||||
const formModal = await frappe.desk.showFormModal(this.target, newDoc.name);
|
const formModal = await frappe.desk.showFormModal(this.getTarget(), newDoc.name);
|
||||||
if (formModal.form.doc.meta.hasField('name')) {
|
if (formModal.form.doc.meta.hasField('name')) {
|
||||||
formModal.form.doc.set('name', this.input.value);
|
formModal.form.doc.set('name', this.input.value);
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ class LinkControl extends BaseControl {
|
|||||||
|
|
||||||
async getList(query) {
|
async getList(query) {
|
||||||
return (await frappe.db.getAll({
|
return (await frappe.db.getAll({
|
||||||
doctype: this.target,
|
doctype: this.getTarget(),
|
||||||
filters: this.getFilters(query, this),
|
filters: this.getFilters(query, this),
|
||||||
limit: 50
|
limit: 50
|
||||||
})).map(d => d.name);
|
})).map(d => d.name);
|
||||||
@ -58,6 +58,10 @@ class LinkControl extends BaseControl {
|
|||||||
getFilters(query) {
|
getFilters(query) {
|
||||||
return { keywords: ["like", query] }
|
return { keywords: ["like", query] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTarget() {
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = LinkControl;
|
module.exports = LinkControl;
|
@ -4,7 +4,7 @@ const Observable = require('frappejs/utils/observable');
|
|||||||
const keyboard = require('frappejs/client/ui/keyboard');
|
const keyboard = require('frappejs/client/ui/keyboard');
|
||||||
|
|
||||||
module.exports = class BaseForm extends Observable {
|
module.exports = class BaseForm extends Observable {
|
||||||
constructor({doctype, parent, submit_label='Submit', container}) {
|
constructor({doctype, parent, submit_label='Submit', container, meta, inline=false}) {
|
||||||
super();
|
super();
|
||||||
Object.assign(this, arguments[0]);
|
Object.assign(this, arguments[0]);
|
||||||
this.controls = {};
|
this.controls = {};
|
||||||
@ -12,12 +12,21 @@ module.exports = class BaseForm extends Observable {
|
|||||||
this.sections = [];
|
this.sections = [];
|
||||||
this.links = [];
|
this.links = [];
|
||||||
|
|
||||||
this.meta = frappe.getMeta(this.doctype);
|
if (!this.meta) {
|
||||||
|
this.meta = frappe.getMeta(this.doctype);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.setup) {
|
if (this.setup) {
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.make();
|
this.make();
|
||||||
this.bindFormEvents();
|
this.bindFormEvents();
|
||||||
|
|
||||||
|
if (this.doc) {
|
||||||
|
// bootstrapped with a doc
|
||||||
|
this.bindEvents(this.doc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
make() {
|
make() {
|
||||||
@ -25,10 +34,22 @@ module.exports = class BaseForm extends Observable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.body = frappe.ui.add('div', 'form-body', this.parent);
|
if (this.inline) {
|
||||||
this.makeToolbar();
|
this.body = this.parent
|
||||||
|
} else {
|
||||||
|
this.body = frappe.ui.add('div', 'form-body', this.parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.actions) {
|
||||||
|
this.makeToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
this.form = frappe.ui.add('form', 'form-container', this.body);
|
this.form = frappe.ui.add('form', 'form-container', this.body);
|
||||||
|
|
||||||
|
if (this.inline) {
|
||||||
|
this.form.classList.add('form-inline');
|
||||||
|
}
|
||||||
|
|
||||||
this.form.onValidate = true;
|
this.form.onValidate = true;
|
||||||
|
|
||||||
this.makeLayout();
|
this.makeLayout();
|
||||||
@ -74,6 +95,9 @@ module.exports = class BaseForm extends Observable {
|
|||||||
makeControls(fields, parent) {
|
makeControls(fields, parent) {
|
||||||
for(let field of fields) {
|
for(let field of fields) {
|
||||||
if (!field.hidden && controls.getControlClass(field.fieldtype)) {
|
if (!field.hidden && controls.getControlClass(field.fieldtype)) {
|
||||||
|
if (this.inline) {
|
||||||
|
field.inline = true;
|
||||||
|
}
|
||||||
let control = controls.makeControl({field: field, form: this, parent: parent});
|
let control = controls.makeControl({field: field, form: this, parent: parent});
|
||||||
this.controlList.push(control);
|
this.controlList.push(control);
|
||||||
this.controls[field.fieldname] = control;
|
this.controls[field.fieldname] = control;
|
||||||
@ -180,6 +204,8 @@ module.exports = class BaseForm extends Observable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTitle() {
|
setTitle() {
|
||||||
|
if (!this.container) return;
|
||||||
|
|
||||||
const doctypeLabel = this.doc.meta.label || this.doc.meta.name;
|
const doctypeLabel = this.doc.meta.label || this.doc.meta.name;
|
||||||
|
|
||||||
if (this.doc.meta.isSingle || this.doc.meta.naming === 'random') {
|
if (this.doc.meta.isSingle || this.doc.meta.naming === 'random') {
|
||||||
@ -216,6 +242,8 @@ module.exports = class BaseForm extends Observable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refreshLinks(links) {
|
refreshLinks(links) {
|
||||||
|
if (!this.container) return;
|
||||||
|
|
||||||
this.container.clearLinks();
|
this.container.clearLinks();
|
||||||
for(let link of links) {
|
for(let link of links) {
|
||||||
// make the link
|
// make the link
|
||||||
|
@ -11,8 +11,8 @@ module.exports = class Page extends Observable {
|
|||||||
}
|
}
|
||||||
this.make();
|
this.make();
|
||||||
this.dropdowns = {};
|
this.dropdowns = {};
|
||||||
|
|
||||||
if(this.title) {
|
if(this.title) {
|
||||||
|
this.wrapper.setAttribute('title', this.title);
|
||||||
this.setTitle(this.title);
|
this.setTitle(this.title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ module.exports = {
|
|||||||
naming: "name", // {random|autoincrement}
|
naming: "name", // {random|autoincrement}
|
||||||
isSingle: 0,
|
isSingle: 0,
|
||||||
isChild: 0,
|
isChild: 0,
|
||||||
|
isSubmittable: 0,
|
||||||
|
settings: null,
|
||||||
keywordFields: [],
|
keywordFields: [],
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
|
@ -5,11 +5,13 @@ const naming = require('./naming');
|
|||||||
module.exports = class BaseDocument extends Observable {
|
module.exports = class BaseDocument extends Observable {
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
super();
|
super();
|
||||||
this.fetchValues = {};
|
this.fetchValuesCache = {};
|
||||||
this.flags = {};
|
this.flags = {};
|
||||||
this.setup();
|
this.setup();
|
||||||
Object.assign(this, data);
|
Object.assign(this, data);
|
||||||
frappe.db.on('change', (params) => this.fetchValues[`${params.doctype}:${params.name}`] = {});
|
|
||||||
|
// clear fetch-values cache
|
||||||
|
frappe.db.on('change', (params) => this.fetchValuesCache[`${params.doctype}:${params.name}`] = {});
|
||||||
}
|
}
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
@ -30,10 +32,6 @@ module.exports = class BaseDocument extends Observable {
|
|||||||
return this._settings;
|
return this._settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(fieldname) {
|
|
||||||
return this[fieldname];
|
|
||||||
}
|
|
||||||
|
|
||||||
// set value and trigger change
|
// set value and trigger change
|
||||||
async set(fieldname, value) {
|
async set(fieldname, value) {
|
||||||
if (this[fieldname] !== value) {
|
if (this[fieldname] !== value) {
|
||||||
@ -298,7 +296,7 @@ module.exports = class BaseDocument extends Observable {
|
|||||||
|
|
||||||
async getFrom(doctype, name, fieldname) {
|
async getFrom(doctype, name, fieldname) {
|
||||||
if (!name) return '';
|
if (!name) return '';
|
||||||
let _values = this.fetchValues[`${doctype}:${name}`] || (this.fetchValues[`${doctype}:${name}`] = {});
|
let _values = this.fetchValuesCache[`${doctype}:${name}`] || (this.fetchValuesCache[`${doctype}:${name}`] = {});
|
||||||
if (!_values[fieldname]) {
|
if (!_values[fieldname]) {
|
||||||
_values[fieldname] = await frappe.db.getValue(doctype, name, fieldname);
|
_values[fieldname] = await frappe.db.getValue(doctype, name, fieldname);
|
||||||
}
|
}
|
||||||
|
@ -65,10 +65,22 @@ module.exports = {
|
|||||||
if (!e.status_code || e.status_code !== 404) {
|
if (!e.status_code || e.status_code !== 404) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
series = frappe.newDoc({doctype: 'NumberSeries', name: prefix, current: 0});
|
await this.createNumberSeries(prefix);
|
||||||
await series.insert();
|
|
||||||
}
|
}
|
||||||
let next = await series.next()
|
let next = await series.next()
|
||||||
return prefix + next;
|
return prefix + next;
|
||||||
|
},
|
||||||
|
|
||||||
|
async createNumberSeries(prefix, setting, start=1000) {
|
||||||
|
if (!(await frappe.db.exists('NumberSeries', prefix))) {
|
||||||
|
const series = frappe.newDoc({doctype: 'NumberSeries', name: prefix, current: start});
|
||||||
|
await series.insert();
|
||||||
|
|
||||||
|
if (setting) {
|
||||||
|
const settingDoc = await frappe.getSingle(setting);
|
||||||
|
settingDoc.numberSeries = series.name;
|
||||||
|
await settingDoc.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,16 @@ module.exports = class Observable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getter, setter stubs, so Observable can be used as a simple Document
|
||||||
|
get(key) {
|
||||||
|
return this[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, value) {
|
||||||
|
this[key] = value;
|
||||||
|
this.trigger('change', {doc: this, fieldname: key});
|
||||||
|
}
|
||||||
|
|
||||||
on(event, listener) {
|
on(event, listener) {
|
||||||
this._addListener('listeners', event, listener);
|
this._addListener('listeners', event, listener);
|
||||||
if (this._observable.socketClient) {
|
if (this._observable.socketClient) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user