mirror of
https://github.com/frappe/books.git
synced 2025-01-11 02:36:14 +00:00
added reportpage.js
This commit is contained in:
parent
3646e7d778
commit
a91489cfbc
@ -38,7 +38,7 @@ module.exports = class HTTPClient extends Observable {
|
|||||||
async getAll({ doctype, fields, filters, start, limit, sort_by, order }) {
|
async getAll({ doctype, fields, filters, start, limit, sort_by, order }) {
|
||||||
let url = this.getURL('/api/resource', doctype);
|
let url = this.getURL('/api/resource', doctype);
|
||||||
|
|
||||||
url = url + "?" + this.getQueryString({
|
url = url + "?" + frappe.getQueryString({
|
||||||
fields: JSON.stringify(fields),
|
fields: JSON.stringify(fields),
|
||||||
filters: JSON.stringify(filters),
|
filters: JSON.stringify(filters),
|
||||||
start: start,
|
start: start,
|
||||||
@ -107,13 +107,6 @@ module.exports = class HTTPClient extends Observable {
|
|||||||
return this.protocol + '://' + this.server + parts.join('/');
|
return this.protocol + '://' + this.server + parts.join('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
getQueryString(params) {
|
|
||||||
return Object.keys(params)
|
|
||||||
.map(k => params[k] != null ? encodeURIComponent(k) + '=' + encodeURIComponent(params[k]) : null)
|
|
||||||
.filter(v => v)
|
|
||||||
.join('&');
|
|
||||||
}
|
|
||||||
|
|
||||||
getHeaders() {
|
getHeaders() {
|
||||||
return {
|
return {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
|
@ -53,7 +53,7 @@ module.exports = class mysqlDatabase extends Database{
|
|||||||
|
|
||||||
|
|
||||||
updateColumnDefinition(df, columns, indexes) {
|
updateColumnDefinition(df, columns, indexes) {
|
||||||
columns.push(`${df.fieldname} ${this.typeMap[df.fieldtype]} ${df.reqd && !df.default ? "not null" : ""} ${df.default ? `default '${df.default}'` : ""}`);
|
columns.push(`${df.fieldname} ${this.typeMap[df.fieldtype]} ${df.required && !df.default ? "not null" : ""} ${df.default ? `default '${df.default}'` : ""}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTableColumns(doctype) {
|
async getTableColumns(doctype) {
|
||||||
|
@ -78,7 +78,7 @@ module.exports = class sqliteDatabase extends Database {
|
|||||||
if (field.fieldname==='name') {
|
if (field.fieldname==='name') {
|
||||||
def += ' PRIMARY KEY NOT NULL';
|
def += ' PRIMARY KEY NOT NULL';
|
||||||
}
|
}
|
||||||
else if (field.reqd) {
|
else if (field.required) {
|
||||||
def += ' NOT NULL';
|
def += ' NOT NULL';
|
||||||
}
|
}
|
||||||
if (field.default) {
|
if (field.default) {
|
||||||
|
@ -131,7 +131,6 @@ module.exports = class Desk {
|
|||||||
if (!this.pages[view]) this.pages[view] = {};
|
if (!this.pages[view]) this.pages[view] = {};
|
||||||
if (!this.pages[view][doctype]) this.pages[view][doctype] = new views[view](doctype);
|
if (!this.pages[view][doctype]) this.pages[view][doctype] = new views[view](doctype);
|
||||||
const page = this.pages[view][doctype];
|
const page = this.pages[view][doctype];
|
||||||
this.toggleCenter(page.fullPage ? false : true);
|
|
||||||
await page.show(params);
|
await page.show(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
86
client/desk/reportpage.js
Normal file
86
client/desk/reportpage.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
const Page = require('frappejs/client/view/page');
|
||||||
|
const controls = require('frappejs/client/view/controls');
|
||||||
|
const DataTable = require('frappe-datatable');
|
||||||
|
const frappe = require('frappejs');
|
||||||
|
const utils = require('frappejs/client/ui/utils');
|
||||||
|
|
||||||
|
// baseclass for report
|
||||||
|
// `url` url for report
|
||||||
|
// `getColumns` return columns
|
||||||
|
|
||||||
|
module.exports = class ReportPage extends Page {
|
||||||
|
constructor({title, }) {
|
||||||
|
super({title: title, hasRoute: true});
|
||||||
|
|
||||||
|
this.fullPage = true;
|
||||||
|
|
||||||
|
this.filterWrapper = frappe.ui.add('div', 'filter-toolbar form-inline', this.body);
|
||||||
|
this.tableWrapper = frappe.ui.add('div', 'table-page-wrapper', this.body);
|
||||||
|
|
||||||
|
this.btnNew = this.addButton(frappe._('Refresh'), 'btn-primary', async () => {
|
||||||
|
await this.run();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.filters = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
getColumns() {
|
||||||
|
// overrride
|
||||||
|
}
|
||||||
|
|
||||||
|
addFilter(field) {
|
||||||
|
if (field.fieldname) {
|
||||||
|
field.fieldname = frappe.slug(field.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
field.placeholder = field.label;
|
||||||
|
field.inline = true;
|
||||||
|
|
||||||
|
this.filters[field.fieldname] = controls.makeControl({field: field, form: this, parent: this.filterWrapper});
|
||||||
|
return this.filters[field.fieldname];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilterValues() {
|
||||||
|
const values = {};
|
||||||
|
for (let fieldname in this.filters) {
|
||||||
|
let control = this.filters[fieldname];
|
||||||
|
values[fieldname] = control.getInputValue();
|
||||||
|
if (control.required && !values[fieldname]) {
|
||||||
|
frappe.ui.showAlert({message: frappe._('{0} is mandatory', control.label), color: 'red'});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
async show(params) {
|
||||||
|
super.show();
|
||||||
|
await this.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
if (!this.datatable) {
|
||||||
|
this.makeDataTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterValues = this.getFilterValues();
|
||||||
|
if (filterValues === false) return;
|
||||||
|
|
||||||
|
let response = await fetch(this.url + '?' + frappe.getQueryString(filterValues), {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const data = await response.json();
|
||||||
|
this.datatable.refresh(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
makeDataTable() {
|
||||||
|
this.datatable = new DataTable(this.tableWrapper, {
|
||||||
|
columns: utils.convertFieldsToDatatableColumns(this.getColumns(), this.layout),
|
||||||
|
data: [],
|
||||||
|
layout: this.layout || 'fluid',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -68,7 +68,7 @@ html {
|
|||||||
margin-bottom: $spacer-2;
|
margin-bottom: $spacer-2;
|
||||||
|
|
||||||
.form-check-input {
|
.form-check-input {
|
||||||
margin-top: 0.25rem;
|
margin-top: $spacer-1;
|
||||||
}
|
}
|
||||||
.form-check-label {
|
.form-check-label {
|
||||||
margin-left: $spacer-1;
|
margin-left: $spacer-1;
|
||||||
@ -84,6 +84,12 @@ html {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-inline {
|
||||||
|
.form-group {
|
||||||
|
margin-right: $spacer-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.list-search {
|
.list-search {
|
||||||
padding: $spacer-3 $spacer-4;
|
padding: $spacer-3 $spacer-4;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ const frappe = require('frappejs');
|
|||||||
const DataTable = require('frappe-datatable');
|
const DataTable = require('frappe-datatable');
|
||||||
const controls = require('frappejs/client/view/controls');
|
const controls = require('frappejs/client/view/controls');
|
||||||
const Modal = require('frappejs/client/ui/modal');
|
const Modal = require('frappejs/client/ui/modal');
|
||||||
|
const utils = require('./utils');
|
||||||
|
|
||||||
module.exports = class ModelTable {
|
module.exports = class ModelTable {
|
||||||
constructor({doctype, parent, layout, parentControl, getRowData,
|
constructor({doctype, parent, layout, parentControl, getRowData,
|
||||||
@ -26,27 +27,7 @@ module.exports = class ModelTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getColumns() {
|
getColumns() {
|
||||||
return this.getTableFields().map(field => {
|
return utils.convertFieldsToDatatableColumns(this.getTableFields(), this.layout);
|
||||||
if (!field.width) {
|
|
||||||
if (this.layout==='ratio') {
|
|
||||||
field.width = 1;
|
|
||||||
} else if (this.layout==='fixed') {
|
|
||||||
field.width = 120;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
id: field.fieldname,
|
|
||||||
field: field,
|
|
||||||
content: field.label,
|
|
||||||
editable: true,
|
|
||||||
sortable: false,
|
|
||||||
resizable: true,
|
|
||||||
dropdown: false,
|
|
||||||
width: field.width,
|
|
||||||
align: ['Int', 'Float', 'Currency'].includes(field.fieldtype) ? 'right' : 'left',
|
|
||||||
format: (value) => frappe.format(value, field)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getTableFields() {
|
getTableFields() {
|
||||||
|
28
client/ui/utils.js
Normal file
28
client/ui/utils.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
const frappe = require('frappejs');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
convertFieldsToDatatableColumns(fields, layout = 'fixed') {
|
||||||
|
return fields.map(field => {
|
||||||
|
if (!field.width) {
|
||||||
|
if (layout==='ratio') {
|
||||||
|
field.width = 1;
|
||||||
|
} else if (layout==='fixed') {
|
||||||
|
field.width = 120;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: field.fieldname || frappe.slug(field.label),
|
||||||
|
field: field,
|
||||||
|
content: field.label,
|
||||||
|
editable: true,
|
||||||
|
sortable: false,
|
||||||
|
resizable: true,
|
||||||
|
dropdown: false,
|
||||||
|
width: field.width,
|
||||||
|
align: ['Int', 'Float', 'Currency'].includes(field.fieldtype) ? 'right' : 'left',
|
||||||
|
format: (value) => frappe.format(value, field)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -70,6 +70,9 @@ class BaseControl {
|
|||||||
makeLabel(labelClass = null) {
|
makeLabel(labelClass = null) {
|
||||||
this.labelElement = frappe.ui.add('label', labelClass, this.inputContainer, this.label);
|
this.labelElement = frappe.ui.add('label', labelClass, this.inputContainer, this.label);
|
||||||
this.labelElement.setAttribute('for', this.id);
|
this.labelElement.setAttribute('for', this.id);
|
||||||
|
if (this.inline) {
|
||||||
|
this.labelElement.classList.add("sr-only");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
makeInput(inputClass='form-control') {
|
makeInput(inputClass='form-control') {
|
||||||
@ -83,6 +86,9 @@ class BaseControl {
|
|||||||
if (!this.onlyInput) {
|
if (!this.onlyInput) {
|
||||||
this.makeDescription();
|
this.makeDescription();
|
||||||
}
|
}
|
||||||
|
if (this.placeholder) {
|
||||||
|
this.input.setAttribute('placeholder', this.placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +85,8 @@ module.exports = class Page extends Observable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.parent.activePage = this;
|
this.parent.activePage = this;
|
||||||
|
|
||||||
|
frappe.desk.toggleCenter(this.fullPage ? false : true);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderError(title, message) {
|
renderError(title, message) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
input: './src/index.js',
|
input: './www/index.js',
|
||||||
output: {
|
output: {
|
||||||
file: './dist/js/bundle.js',
|
file: './www/dist/js/bundle.js',
|
||||||
format: 'iife',
|
format: 'iife',
|
||||||
name: 'desk',
|
name: 'desk',
|
||||||
globals: ['io', 'nunjucks'] // for socketio client, which is imported directly
|
globals: ['io', 'nunjucks'] // for socketio client, which is imported directly
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
input: './node_modules/frappejs/client/style/style.scss',
|
input: './node_modules/frappejs/client/style/style.scss',
|
||||||
output: {
|
output: {
|
||||||
file: './dist/css/style.css',
|
file: './www/dist/css/style.css',
|
||||||
format: 'cjs'
|
format: 'cjs'
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -18,7 +18,7 @@ require.extensions['.html'] = function (module, filename) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
async start({backend, connectionParams, models}) {
|
async start({backend, connectionParams, models, staticPath = './'}) {
|
||||||
await this.init();
|
await this.init();
|
||||||
|
|
||||||
if (models) {
|
if (models) {
|
||||||
@ -31,7 +31,7 @@ module.exports = {
|
|||||||
// app
|
// app
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
app.use(express.static('./'));
|
app.use(express.static(staticPath));
|
||||||
|
|
||||||
// socketio
|
// socketio
|
||||||
io.on('connection', function (socket) {
|
io.on('connection', function (socket) {
|
||||||
|
@ -1,18 +1,9 @@
|
|||||||
const frappe = require('frappejs');
|
const frappe = require('frappejs');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
asyncHandler(fn) {
|
|
||||||
return (req, res, next) => Promise.resolve(fn(req, res, next))
|
|
||||||
.catch((err) => {
|
|
||||||
console.log(err);
|
|
||||||
// handle error
|
|
||||||
res.status(err.status_code || 500).send({error: err.message});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(app) {
|
setup(app) {
|
||||||
// get list
|
// get list
|
||||||
app.get('/api/resource/:doctype', this.asyncHandler(async function(request, response) {
|
app.get('/api/resource/:doctype', frappe.asyncHandler(async function(request, response) {
|
||||||
for (let key of ['fields', 'filters']) {
|
for (let key of ['fields', 'filters']) {
|
||||||
if (request.query[key]) {
|
if (request.query[key]) {
|
||||||
request.query[key] = JSON.parse(request.query[key]);
|
request.query[key] = JSON.parse(request.query[key]);
|
||||||
@ -33,7 +24,7 @@ module.exports = {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// create
|
// create
|
||||||
app.post('/api/resource/:doctype', this.asyncHandler(async function(request, response) {
|
app.post('/api/resource/:doctype', frappe.asyncHandler(async function(request, response) {
|
||||||
let data = request.body;
|
let data = request.body;
|
||||||
data.doctype = request.params.doctype;
|
data.doctype = request.params.doctype;
|
||||||
let doc = frappe.newDoc(data);
|
let doc = frappe.newDoc(data);
|
||||||
@ -43,7 +34,7 @@ module.exports = {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// update
|
// update
|
||||||
app.put('/api/resource/:doctype/:name', this.asyncHandler(async function(request, response) {
|
app.put('/api/resource/:doctype/:name', frappe.asyncHandler(async function(request, response) {
|
||||||
let data = request.body;
|
let data = request.body;
|
||||||
let doc = await frappe.getDoc(request.params.doctype, request.params.name);
|
let doc = await frappe.getDoc(request.params.doctype, request.params.name);
|
||||||
Object.assign(doc, data);
|
Object.assign(doc, data);
|
||||||
@ -54,26 +45,26 @@ module.exports = {
|
|||||||
|
|
||||||
|
|
||||||
// get document
|
// get document
|
||||||
app.get('/api/resource/:doctype/:name', this.asyncHandler(async function(request, response) {
|
app.get('/api/resource/:doctype/:name', frappe.asyncHandler(async function(request, response) {
|
||||||
let doc = await frappe.getDoc(request.params.doctype, request.params.name);
|
let doc = await frappe.getDoc(request.params.doctype, request.params.name);
|
||||||
return response.json(doc.getValidDict());
|
return response.json(doc.getValidDict());
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// get value
|
// get value
|
||||||
app.get('/api/resource/:doctype/:name/:fieldname', this.asyncHandler(async function(request, response) {
|
app.get('/api/resource/:doctype/:name/:fieldname', frappe.asyncHandler(async function(request, response) {
|
||||||
let value = await frappe.db.getValue(request.params.doctype, request.params.name, request.params.fieldname);
|
let value = await frappe.db.getValue(request.params.doctype, request.params.name, request.params.fieldname);
|
||||||
return response.json({value: value});
|
return response.json({value: value});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// delete
|
// delete
|
||||||
app.delete('/api/resource/:doctype/:name', this.asyncHandler(async function(request, response) {
|
app.delete('/api/resource/:doctype/:name', frappe.asyncHandler(async function(request, response) {
|
||||||
let doc = await frappe.getDoc(request.params.doctype, request.params.name)
|
let doc = await frappe.getDoc(request.params.doctype, request.params.name)
|
||||||
await doc.delete();
|
await doc.delete();
|
||||||
return response.json({});
|
return response.json({});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// delete many
|
// delete many
|
||||||
app.delete('/api/resource/:doctype', this.asyncHandler(async function(request, response) {
|
app.delete('/api/resource/:doctype', frappe.asyncHandler(async function(request, response) {
|
||||||
let names = request.body;
|
let names = request.body;
|
||||||
for (let name of names) {
|
for (let name of names) {
|
||||||
let doc = await frappe.getDoc(request.params.doctype, name);
|
let doc = await frappe.getDoc(request.params.doctype, name);
|
||||||
|
@ -4,8 +4,10 @@ Array.prototype.equals = function( array ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
slug(text) {
|
slug(str) {
|
||||||
return text.toLowerCase().replace(/ /g, '_');
|
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(letter, index) {
|
||||||
|
return index == 0 ? letter.toLowerCase() : letter.toUpperCase();
|
||||||
|
}).replace(/\s+/g, '');
|
||||||
},
|
},
|
||||||
|
|
||||||
getRandomString() {
|
getRandomString() {
|
||||||
@ -42,6 +44,23 @@ module.exports = {
|
|||||||
: match;
|
: match;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
|
getQueryString(params) {
|
||||||
|
if (!params) return '';
|
||||||
|
return Object.keys(params)
|
||||||
|
.map(k => params[k] != null ? encodeURIComponent(k) + '=' + encodeURIComponent(params[k]) : null)
|
||||||
|
.filter(v => v)
|
||||||
|
.join('&');
|
||||||
|
},
|
||||||
|
|
||||||
|
asyncHandler(fn) {
|
||||||
|
return (req, res, next) => Promise.resolve(fn(req, res, next))
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
// handle error
|
||||||
|
res.status(err.status_code || 500).send({error: err.message});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
Loading…
Reference in New Issue
Block a user