mirror of
https://github.com/frappe/books.git
synced 2024-12-23 11:29:03 +00:00
table control
This commit is contained in:
parent
107ff988e8
commit
4af2b1394d
@ -1,6 +1,6 @@
|
|||||||
const frappe = require('frappejs');
|
const frappe = require('frappejs');
|
||||||
const sqlite3 = require('sqlite3').verbose();
|
const sqlite3 = require('sqlite3').verbose();
|
||||||
const debug = false;
|
const debug = true;
|
||||||
|
|
||||||
class sqliteDatabase {
|
class sqliteDatabase {
|
||||||
constructor({ db_path }) {
|
constructor({ db_path }) {
|
||||||
@ -86,7 +86,13 @@ class sqliteDatabase {
|
|||||||
// load children
|
// load children
|
||||||
let table_fields = frappe.get_meta(doctype).get_table_fields();
|
let table_fields = frappe.get_meta(doctype).get_table_fields();
|
||||||
for (let field of table_fields) {
|
for (let field of table_fields) {
|
||||||
doc[fieldname] = await this.get_all({ doctype: field.childtype, fields: ["*"], filters: { parent: doc.name } });
|
doc[field.fieldname] = await this.get_all({
|
||||||
|
doctype: field.childtype,
|
||||||
|
fields: ["*"],
|
||||||
|
filters: { parent: doc.name },
|
||||||
|
order_by: 'idx',
|
||||||
|
order: 'asc'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
@ -225,7 +231,9 @@ class sqliteDatabase {
|
|||||||
fields: [fieldname],
|
fields: [fieldname],
|
||||||
filters: filters,
|
filters: filters,
|
||||||
start: 0,
|
start: 0,
|
||||||
limit: 1
|
limit: 1,
|
||||||
|
order_by: 'name',
|
||||||
|
order: 'asc'
|
||||||
});
|
});
|
||||||
return row.length ? row[0][fieldname] : null;
|
return row.length ? row[0][fieldname] : null;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ module.exports = class FormPage extends Page {
|
|||||||
// if name is different after saving, change the route
|
// if name is different after saving, change the route
|
||||||
this.form.on('submit', async (params) => {
|
this.form.on('submit', async (params) => {
|
||||||
let route = frappe.router.get_route();
|
let route = frappe.router.get_route();
|
||||||
if (!(route && route[2] === this.form.doc.name)) {
|
if (this.form.doc.name && !(route && route[2] === this.form.doc.name)) {
|
||||||
await frappe.router.set_route('edit', this.form.doc.doctype, this.form.doc.name);
|
await frappe.router.set_route('edit', this.form.doc.doctype, this.form.doc.name);
|
||||||
this.form.show_alert('Added', 'success');
|
this.form.show_alert('Added', 'success');
|
||||||
}
|
}
|
||||||
|
@ -65,3 +65,20 @@ mark {
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.data-table-col .edit-cell {
|
||||||
|
padding: 0px !important;
|
||||||
|
|
||||||
|
input, textarea {
|
||||||
|
border-radius: none;
|
||||||
|
margin: none;
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.awesomplete > ul {
|
||||||
|
position: fixed;
|
||||||
|
left: auto;
|
||||||
|
width: auto;
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
const frappe = require('frappejs');
|
const frappe = require('frappejs');
|
||||||
|
|
||||||
class BaseControl {
|
class BaseControl {
|
||||||
constructor(docfield, form) {
|
constructor({field, parent, form}) {
|
||||||
Object.assign(this, docfield);
|
Object.assign(this, field);
|
||||||
|
this.parent = parent;
|
||||||
this.form = form;
|
this.form = form;
|
||||||
|
|
||||||
if (!this.fieldname) {
|
if (!this.fieldname) {
|
||||||
this.fieldname = frappe.slug(this.label);
|
this.fieldname = frappe.slug(this.label);
|
||||||
}
|
}
|
||||||
this.parent = form.form;
|
if (!this.parent) {
|
||||||
|
this.parent = this.form.form;
|
||||||
|
}
|
||||||
if (this.setup) {
|
if (this.setup) {
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
@ -30,12 +34,16 @@ class BaseControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
make() {
|
make() {
|
||||||
if (!this.form_group) {
|
if (!this.input) {
|
||||||
|
if (!this.only_input) {
|
||||||
this.make_form_group();
|
this.make_form_group();
|
||||||
this.make_label();
|
this.make_label();
|
||||||
|
}
|
||||||
this.make_input();
|
this.make_input();
|
||||||
this.set_input_name();
|
this.set_input_name();
|
||||||
|
if (!this.only_input) {
|
||||||
this.make_description();
|
this.make_description();
|
||||||
|
}
|
||||||
this.bind_change_event();
|
this.bind_change_event();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,10 +58,14 @@ class BaseControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
make_input() {
|
make_input() {
|
||||||
this.input = frappe.ui.add('input', 'form-control', this.form_group);
|
this.input = frappe.ui.add('input', 'form-control', this.get_input_parent());
|
||||||
this.input.setAttribute('autocomplete', 'off');
|
this.input.setAttribute('autocomplete', 'off');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_input_parent() {
|
||||||
|
return this.form_group || this.parent;
|
||||||
|
}
|
||||||
|
|
||||||
set_input_name() {
|
set_input_name() {
|
||||||
this.input.setAttribute('name', this.fieldname);
|
this.input.setAttribute('name', this.fieldname);
|
||||||
}
|
}
|
||||||
@ -100,8 +112,13 @@ class BaseControl {
|
|||||||
let value = await this.parse(this.get_input_value());
|
let value = await this.parse(this.get_input_value());
|
||||||
value = await this.validate(value);
|
value = await this.validate(value);
|
||||||
if (this.doc[this.fieldname] !== value) {
|
if (this.doc[this.fieldname] !== value) {
|
||||||
|
if (this.doc.set) {
|
||||||
await this.doc.set(this.fieldname, value);
|
await this.doc.set(this.fieldname, value);
|
||||||
}
|
}
|
||||||
|
if (this.parent_control) {
|
||||||
|
await this.parent_control.doc.set(this.fieldname, this.parent_control.get_input_value());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
disable() {
|
disable() {
|
||||||
@ -111,6 +128,10 @@ class BaseControl {
|
|||||||
enable() {
|
enable() {
|
||||||
this.input.removeAttribute('disabled');
|
this.input.removeAttribute('disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_focus() {
|
||||||
|
this.input.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = BaseControl;
|
module.exports = BaseControl;
|
@ -5,16 +5,17 @@ const control_classes = {
|
|||||||
Link: require('./link'),
|
Link: require('./link'),
|
||||||
Float: require('./float'),
|
Float: require('./float'),
|
||||||
Currency: require('./currency'),
|
Currency: require('./currency'),
|
||||||
Password: require('./password')
|
Password: require('./password'),
|
||||||
|
Table: require('./table')
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
get_control_class(fieldtype) {
|
get_control_class(fieldtype) {
|
||||||
return control_classes[fieldtype];
|
return control_classes[fieldtype];
|
||||||
},
|
},
|
||||||
make_control(field, parent) {
|
make_control({field, form, parent}) {
|
||||||
const control_class = this.get_control_class(field.fieldtype);
|
const control_class = this.get_control_class(field.fieldtype);
|
||||||
let control = new control_class(field, parent);
|
let control = new control_class({field:field, form:form, parent:parent});
|
||||||
control.make();
|
control.make();
|
||||||
return control;
|
return control;
|
||||||
}
|
}
|
||||||
|
86
client/view/controls/table.js
Normal file
86
client/view/controls/table.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
const frappe = require('frappejs');
|
||||||
|
const BaseControl = require('./base');
|
||||||
|
const DataTable = require('frappe-datatable');
|
||||||
|
const controls = require('./index');
|
||||||
|
|
||||||
|
class TableControl extends BaseControl {
|
||||||
|
make() {
|
||||||
|
if (!this.datatable) {
|
||||||
|
this.wrapper = frappe.ui.add('div', 'datatable-wrapper', this.get_input_parent());
|
||||||
|
this.datatable = new DataTable(this.wrapper, {
|
||||||
|
columns: this.get_columns(),
|
||||||
|
data: this.get_table_data(),
|
||||||
|
takeAvailableSpace: true,
|
||||||
|
enableClusterize: true,
|
||||||
|
editing: this.get_table_input.bind(this),
|
||||||
|
|
||||||
|
});
|
||||||
|
this.datatable.datatableWrapper.style = 'height: 300px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_input_value() {
|
||||||
|
return this.doc[this.fieldname];
|
||||||
|
}
|
||||||
|
|
||||||
|
set_input_value(value) {
|
||||||
|
this.datatable.refresh(this.get_table_data(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
get_table_data(value) {
|
||||||
|
return value || this.get_default_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
get_table_input(colIndex, rowIndex, value, parent) {
|
||||||
|
let field = this.datatable.getColumn(colIndex).field;
|
||||||
|
field.only_input = true;
|
||||||
|
const control = controls.make_control({field: field, parent: parent});
|
||||||
|
|
||||||
|
return {
|
||||||
|
initValue: (value, rowIndex, column) => {
|
||||||
|
control.parent_control = this;
|
||||||
|
control.doc = this.doc[this.fieldname][rowIndex];
|
||||||
|
control.set_focus();
|
||||||
|
return control.set_input_value(value);
|
||||||
|
},
|
||||||
|
setValue: (value, rowIndex, column) => {
|
||||||
|
return control.set_input_value(value);
|
||||||
|
},
|
||||||
|
getValue: () => {
|
||||||
|
return control.get_input_value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_columns() {
|
||||||
|
return this.get_child_fields().map(field => {
|
||||||
|
return {
|
||||||
|
id: field.fieldname,
|
||||||
|
field: field,
|
||||||
|
content: field.label,
|
||||||
|
editable: true,
|
||||||
|
width: 120,
|
||||||
|
align: ['Int', 'Float', 'Currency'].includes(field.fieldtype) ? 'right' : 'left',
|
||||||
|
format: (value) => frappe.format(value, field)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get_child_fields() {
|
||||||
|
return frappe.get_meta(this.childtype).fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_default_data() {
|
||||||
|
// build flat table
|
||||||
|
if (!this.doc) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (!this.doc[this.fieldname]) {
|
||||||
|
this.doc[this.fieldname] = [{}];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.doc[this.fieldname];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = TableControl;
|
@ -2,7 +2,7 @@ const BaseControl = require('./base');
|
|||||||
|
|
||||||
class TextControl extends BaseControl {
|
class TextControl extends BaseControl {
|
||||||
make_input() {
|
make_input() {
|
||||||
this.input = frappe.ui.add('textarea', 'form-control', this.form_group);
|
this.input = frappe.ui.add('textarea', 'form-control', this.get_input_parent());
|
||||||
}
|
}
|
||||||
make() {
|
make() {
|
||||||
super.make();
|
super.make();
|
||||||
|
@ -28,11 +28,11 @@ module.exports = class BaseForm extends Observable {
|
|||||||
this.make_toolbar();
|
this.make_toolbar();
|
||||||
|
|
||||||
this.form = frappe.ui.add('form', null, this.body);
|
this.form = frappe.ui.add('form', null, this.body);
|
||||||
for(let df of this.meta.fields) {
|
for(let field of this.meta.fields) {
|
||||||
if (controls.get_control_class(df.fieldtype)) {
|
if (controls.get_control_class(field.fieldtype)) {
|
||||||
let control = controls.make_control(df, this);
|
let control = controls.make_control({field: field, form: this});
|
||||||
this.controls_list.push(control);
|
this.controls_list.push(control);
|
||||||
this.controls[df.fieldname] = control;
|
this.controls[field.fieldname] = control;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
"node-fetch": "^1.7.3",
|
"node-fetch": "^1.7.3",
|
||||||
"popper.js": "^1.12.9",
|
"popper.js": "^1.12.9",
|
||||||
"sqlite3": "^3.1.13",
|
"sqlite3": "^3.1.13",
|
||||||
"walk": "^2.3.9"
|
"walk": "^2.3.9",
|
||||||
|
"clusterize.js": "^0.18.0",
|
||||||
|
"sortablejs": "^1.7.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -3,6 +3,13 @@ let utils = {};
|
|||||||
Object.assign(utils, require('./number_format'));
|
Object.assign(utils, require('./number_format'));
|
||||||
|
|
||||||
Object.assign(utils, {
|
Object.assign(utils, {
|
||||||
|
format(value, field) {
|
||||||
|
if (field.fieldtype==='Currency') {
|
||||||
|
return frappe.format_number(value);
|
||||||
|
} else {
|
||||||
|
return value + '';
|
||||||
|
}
|
||||||
|
},
|
||||||
slug(text) {
|
slug(text) {
|
||||||
return text.toLowerCase().replace(/ /g, '_');
|
return text.toLowerCase().replace(/ /g, '_');
|
||||||
},
|
},
|
||||||
|
23
yarn.lock
23
yarn.lock
@ -309,10 +309,6 @@ boom@5.x.x:
|
|||||||
dependencies:
|
dependencies:
|
||||||
hoek "4.x.x"
|
hoek "4.x.x"
|
||||||
|
|
||||||
bootstrap@4.0.0-beta.3:
|
|
||||||
version "4.0.0-beta.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.0.0-beta.3.tgz#60f0660bc3d121176514b361f6f83201c7ff8874"
|
|
||||||
|
|
||||||
bootstrap@^4.0.0:
|
bootstrap@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.0.0.tgz#ceb03842c145fcc1b9b4e15da2a05656ba68469a"
|
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.0.0.tgz#ceb03842c145fcc1b9b4e15da2a05656ba68469a"
|
||||||
@ -582,6 +578,10 @@ clone@~0.1.9:
|
|||||||
version "0.1.19"
|
version "0.1.19"
|
||||||
resolved "https://registry.yarnpkg.com/clone/-/clone-0.1.19.tgz#613fb68639b26a494ac53253e15b1a6bd88ada85"
|
resolved "https://registry.yarnpkg.com/clone/-/clone-0.1.19.tgz#613fb68639b26a494ac53253e15b1a6bd88ada85"
|
||||||
|
|
||||||
|
clusterize.js@^0.18.0:
|
||||||
|
version "0.18.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/clusterize.js/-/clusterize.js-0.18.1.tgz#a286a9749bd1fa9c2fe21b7fabd8780a590dd836"
|
||||||
|
|
||||||
co@^4.6.0:
|
co@^4.6.0:
|
||||||
version "4.6.0"
|
version "4.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
||||||
@ -1388,17 +1388,6 @@ forwarded@~0.1.2:
|
|||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
||||||
|
|
||||||
frappejs@^0.0.1:
|
|
||||||
version "0.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/frappejs/-/frappejs-0.0.1.tgz#8926c6043327535aa11683c1f7831b2cfb8ed414"
|
|
||||||
dependencies:
|
|
||||||
body-parser "^1.18.2"
|
|
||||||
bootstrap "4.0.0-beta.3"
|
|
||||||
express "^4.16.2"
|
|
||||||
node-fetch "^1.7.3"
|
|
||||||
sqlite3 "^3.1.13"
|
|
||||||
walk "^2.3.9"
|
|
||||||
|
|
||||||
fresh@0.5.2:
|
fresh@0.5.2:
|
||||||
version "0.5.2"
|
version "0.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||||
@ -3930,6 +3919,10 @@ sort-keys@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-plain-obj "^1.0.0"
|
is-plain-obj "^1.0.0"
|
||||||
|
|
||||||
|
sortablejs@^1.7.0:
|
||||||
|
version "1.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.7.0.tgz#80a2b2370abd568e1cec8c271131ef30a904fa28"
|
||||||
|
|
||||||
source-map@^0.4.2:
|
source-map@^0.4.2:
|
||||||
version "0.4.4"
|
version "0.4.4"
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
|
||||||
|
Loading…
Reference in New Issue
Block a user