mirror of
https://github.com/frappe/books.git
synced 2025-01-10 18:24:40 +00:00
added fetch
This commit is contained in:
parent
bc095b5bc3
commit
3af4fb8671
@ -51,7 +51,7 @@ module.exports = class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getColumnDefinition(df) {
|
getColumnDefinition(df) {
|
||||||
return `${df.fieldname} ${this.type_map[df.fieldtype]} ${df.reqd && !df.default ? "not null" : ""} ${df.default ? `default ${df.default}` : ""}`
|
return `${df.fieldname} ${this.type_map[df.fieldtype]} ${df.required && !df.default ? "not null" : ""} ${df.default ? `default ${df.default}` : ""}`
|
||||||
}
|
}
|
||||||
|
|
||||||
async alterTable(doctype) {
|
async alterTable(doctype) {
|
||||||
|
@ -89,7 +89,13 @@ module.exports = class RESTClient {
|
|||||||
async fetch(url, args) {
|
async fetch(url, args) {
|
||||||
args.headers = this.getHeaders();
|
args.headers = this.getHeaders();
|
||||||
let response = await frappe.fetch(url, args);
|
let response = await frappe.fetch(url, args);
|
||||||
return await response.json();
|
let data = await response.json();
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw Error(data.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
getURL(...parts) {
|
getURL(...parts) {
|
||||||
|
@ -36,7 +36,7 @@ module.exports = class sqliteDatabase extends Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getColumnDefinition(df) {
|
getColumnDefinition(df) {
|
||||||
return `${df.fieldname} ${this.type_map[df.fieldtype]} ${df.reqd && !df.default ? "not null" : ""} ${df.default ? `default ${df.default}` : ""}`
|
return `${df.fieldname} ${this.type_map[df.fieldtype]} ${df.required && !df.default ? "not null" : ""} ${df.default ? `default ${df.default}` : ""}`
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTableColumns(doctype) {
|
async getTableColumns(doctype) {
|
||||||
|
@ -15,36 +15,38 @@ module.exports = class Desk {
|
|||||||
this.navbar = new Navbar();
|
this.navbar = new Navbar();
|
||||||
this.container = frappe.ui.add('div', 'container-fluid', body);
|
this.container = frappe.ui.add('div', 'container-fluid', body);
|
||||||
|
|
||||||
this.container_row = frappe.ui.add('div', 'row', this.container)
|
this.containerRow = frappe.ui.add('div', 'row', this.container)
|
||||||
this.sidebar = frappe.ui.add('div', 'col-md-2 p-3 sidebar d-none d-md-block', this.container_row);
|
this.sidebar = frappe.ui.add('div', 'col-md-2 p-3 sidebar d-none d-md-block', this.containerRow);
|
||||||
this.body = frappe.ui.add('div', 'col-md-10 p-3 main', this.container_row);
|
this.sidebarList = frappe.ui.add('div', 'list-group list-group-flush', this.sidebar);
|
||||||
|
this.body = frappe.ui.add('div', 'col-md-10 p-4 main', this.containerRow);
|
||||||
|
|
||||||
this.sidebar_items = [];
|
|
||||||
this.pages = {
|
this.pages = {
|
||||||
lists: {},
|
lists: {},
|
||||||
forms: {}
|
forms: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.init_routes();
|
this.routeItems = {};
|
||||||
|
|
||||||
|
this.initRoutes();
|
||||||
// this.search = new Search(this.nav);
|
// this.search = new Search(this.nav);
|
||||||
}
|
}
|
||||||
|
|
||||||
init_routes() {
|
initRoutes() {
|
||||||
frappe.router.add('not-found', async (params) => {
|
frappe.router.add('not-found', async (params) => {
|
||||||
if (!this.not_found_page) {
|
if (!this.notFoundPage) {
|
||||||
this.not_found_page = new Page('Not Found');
|
this.notFoundPage = new Page('Not Found');
|
||||||
}
|
}
|
||||||
await this.not_found_page.show();
|
await this.notFoundPage.show();
|
||||||
this.not_found_page.renderError('Not Found', params ? params.route : '');
|
this.notFoundPage.renderError('Not Found', params ? params.route : '');
|
||||||
})
|
})
|
||||||
|
|
||||||
frappe.router.add('list/:doctype', async (params) => {
|
frappe.router.add('list/:doctype', async (params) => {
|
||||||
let page = this.getList_page(params.doctype);
|
let page = this.getListPage(params.doctype);
|
||||||
await page.show(params);
|
await page.show(params);
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.router.add('edit/:doctype/:name', async (params) => {
|
frappe.router.add('edit/:doctype/:name', async (params) => {
|
||||||
let page = this.get_form_page(params.doctype);
|
let page = this.getFormPage(params.doctype);
|
||||||
await page.show(params);
|
await page.show(params);
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -55,30 +57,47 @@ module.exports = class Desk {
|
|||||||
await doc.set('name', '');
|
await doc.set('name', '');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frappe.router.on('change', () => {
|
||||||
|
if (this.routeItems[window.location.hash]) {
|
||||||
|
this.setActive(this.routeItems[window.location.hash]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getList_page(doctype) {
|
getListPage(doctype) {
|
||||||
if (!this.pages.lists[doctype]) {
|
if (!this.pages.lists[doctype]) {
|
||||||
this.pages.lists[doctype] = new ListPage(doctype);
|
this.pages.lists[doctype] = new ListPage(doctype);
|
||||||
}
|
}
|
||||||
return this.pages.lists[doctype];
|
return this.pages.lists[doctype];
|
||||||
}
|
}
|
||||||
|
|
||||||
get_form_page(doctype) {
|
getFormPage(doctype) {
|
||||||
if (!this.pages.forms[doctype]) {
|
if (!this.pages.forms[doctype]) {
|
||||||
this.pages.forms[doctype] = new FormPage(doctype);
|
this.pages.forms[doctype] = new FormPage(doctype);
|
||||||
}
|
}
|
||||||
return this.pages.forms[doctype];
|
return this.pages.forms[doctype];
|
||||||
}
|
}
|
||||||
|
|
||||||
add_sidebar_item(label, action) {
|
setActive(item) {
|
||||||
let item = frappe.ui.add('a', '', frappe.ui.add('p', null, frappe.desk.sidebar));
|
let className = 'list-group-item-secondary';
|
||||||
|
let activeItem = this.sidebarList.querySelector('.' + className);
|
||||||
|
if (activeItem) {
|
||||||
|
activeItem.classList.remove(className);
|
||||||
|
}
|
||||||
|
item.classList.add(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSidebarItem(label, action) {
|
||||||
|
let item = frappe.ui.add('a', 'list-group-item list-group-item-action', this.sidebarList);
|
||||||
item.textContent = label;
|
item.textContent = label;
|
||||||
if (typeof action === 'string') {
|
if (typeof action === 'string') {
|
||||||
item.href = action;
|
item.href = action;
|
||||||
|
this.routeItems[action] = item;
|
||||||
} else {
|
} else {
|
||||||
item.addEventHandler('click', () => {
|
item.addEventHandler('click', () => {
|
||||||
action();
|
action();
|
||||||
|
this.setActive(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ module.exports = class Navbar {
|
|||||||
constructor({brand_label = 'Home'} = {}) {
|
constructor({brand_label = 'Home'} = {}) {
|
||||||
Object.assign(this, arguments[0]);
|
Object.assign(this, arguments[0]);
|
||||||
this.items = {};
|
this.items = {};
|
||||||
this.navbar = frappe.ui.add('div', 'navbar navbar-expand-md border-bottom', document.querySelector('body'));
|
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);
|
||||||
this.brand.href = '#';
|
this.brand.href = '#';
|
||||||
|
@ -7,13 +7,23 @@ $spacer-3: 1rem;
|
|||||||
$spacer-4: 2rem;
|
$spacer-4: 2rem;
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
border-left: 1px solid $gray-300;
|
border-left: 1px solid $gray-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
.list-group-item {
|
||||||
|
padding: $spacer-2 $spacer-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-flush {
|
||||||
|
margin: -$spacer-3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.hide {
|
.hide {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
@ -90,8 +100,8 @@ mark {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
margin-top: $spacer-3;
|
margin-top: $spacer-4;
|
||||||
margin-bottom: $spacer-3;
|
margin-bottom: $spacer-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-toolbar {
|
.table-toolbar {
|
||||||
|
@ -2,7 +2,7 @@ const $ = require('jquery');
|
|||||||
const bootstrap = require('bootstrap');
|
const bootstrap = require('bootstrap');
|
||||||
|
|
||||||
module.exports = class Modal {
|
module.exports = class Modal {
|
||||||
constructor({ title, body, primary_label, primary_action, secondary_label, secondary_action }) {
|
constructor({ title, body, primary, secondary }) {
|
||||||
Object.assign(this, arguments[0]);
|
Object.assign(this, arguments[0]);
|
||||||
this.$modal = $(`<div class="modal" tabindex="-1" role="dialog">
|
this.$modal = $(`<div class="modal" tabindex="-1" role="dialog">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
@ -22,23 +22,23 @@ module.exports = class Modal {
|
|||||||
</div>
|
</div>
|
||||||
</div>`).appendTo(document.body);
|
</div>`).appendTo(document.body);
|
||||||
|
|
||||||
if (this.primary_label) {
|
if (this.primary) {
|
||||||
this.add_primary(this.primary_label, this.primary_action);
|
this.addPrimary(this.primary.label, this.primary.action);
|
||||||
}
|
}
|
||||||
if (this.secondary_label) {
|
if (this.secondary) {
|
||||||
this.add_secondary(this.secondary_label, this.secondary_action);
|
this.addSecondary(this.secondary.label, this.secondary.action);
|
||||||
}
|
}
|
||||||
this.show();
|
this.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
add_primary(label, action) {
|
addPrimary(label, action) {
|
||||||
this.$primary = $(`<button type="button" class="btn btn-primary">
|
this.$primary = $(`<button type="button" class="btn btn-primary">
|
||||||
${label}</button>`)
|
${label}</button>`)
|
||||||
.appendTo(this.$modal.find('.modal-footer'))
|
.appendTo(this.$modal.find('.modal-footer'))
|
||||||
.on('click', () => action(this));
|
.on('click', () => action(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
add_secondary(label, action) {
|
addSecondary(label, action) {
|
||||||
this.$primary = $(`<button type="button" class="btn btn-secondary">
|
this.$primary = $(`<button type="button" class="btn btn-secondary">
|
||||||
${label}</button>`)
|
${label}</button>`)
|
||||||
.appendTo(this.$modal.find('.modal-footer'))
|
.appendTo(this.$modal.find('.modal-footer'))
|
||||||
@ -53,7 +53,7 @@ module.exports = class Modal {
|
|||||||
this.$modal.modal('hide');
|
this.$modal.modal('hide');
|
||||||
}
|
}
|
||||||
|
|
||||||
get_body() {
|
getBody() {
|
||||||
return this.$modal.find('.modal-body').get(0);
|
return this.$modal.find('.modal-body').get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,9 +2,12 @@ const frappe = require('frappejs');
|
|||||||
|
|
||||||
class BaseControl {
|
class BaseControl {
|
||||||
constructor({field, parent, form}) {
|
constructor({field, parent, form}) {
|
||||||
|
BaseControl.count++;
|
||||||
|
|
||||||
Object.assign(this, field);
|
Object.assign(this, field);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.form = form;
|
this.form = form;
|
||||||
|
this.id = 'control-' + BaseControl.count;
|
||||||
|
|
||||||
if (!this.fieldname) {
|
if (!this.fieldname) {
|
||||||
this.fieldname = frappe.slug(this.label);
|
this.fieldname = frappe.slug(this.label);
|
||||||
@ -41,10 +44,12 @@ class BaseControl {
|
|||||||
}
|
}
|
||||||
this.makeInput();
|
this.makeInput();
|
||||||
this.setInputName();
|
this.setInputName();
|
||||||
|
this.setRequiredAttribute();
|
||||||
|
this.setDisabled();
|
||||||
if (!this.onlyInput) {
|
if (!this.onlyInput) {
|
||||||
this.makeDescription();
|
this.makeDescription();
|
||||||
}
|
}
|
||||||
this.bindChangeEvent();
|
this.addChangeHandler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,13 +58,21 @@ class BaseControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
makeLabel() {
|
makeLabel() {
|
||||||
this.label_element = frappe.ui.add('label', null, this.formGroup);
|
this.labelElement = frappe.ui.add('label', null, this.formGroup);
|
||||||
this.label_element.textContent = this.label;
|
this.labelElement.textContent = this.label;
|
||||||
|
this.labelElement.setAttribute('for', this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
makeInput() {
|
makeInput() {
|
||||||
this.input = frappe.ui.add('input', 'form-control', this.get_input_parent());
|
this.input = frappe.ui.add('input', 'form-control', this.get_input_parent());
|
||||||
this.input.setAttribute('autocomplete', 'off');
|
this.input.autocomplete = "off";
|
||||||
|
this.input.id = this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabled() {
|
||||||
|
if (this.readonly || this.disabled) {
|
||||||
|
this.input.disabled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get_input_parent() {
|
get_input_parent() {
|
||||||
@ -70,6 +83,12 @@ class BaseControl {
|
|||||||
this.input.setAttribute('name', this.fieldname);
|
this.input.setAttribute('name', this.fieldname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setRequiredAttribute() {
|
||||||
|
if (this.required) {
|
||||||
|
this.input.required = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
makeDescription() {
|
makeDescription() {
|
||||||
if (this.description) {
|
if (this.description) {
|
||||||
this.description_element = frappe.ui.add('small', 'form-text text-muted', this.formGroup);
|
this.description_element = frappe.ui.add('small', 'form-text text-muted', this.formGroup);
|
||||||
@ -104,11 +123,14 @@ class BaseControl {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bindChangeEvent() {
|
addChangeHandler() {
|
||||||
this.input.addEventListener('change', (e) => this.handleChange());
|
this.input.addEventListener('change', () => {
|
||||||
|
if (this.skipChangeEvent) return;
|
||||||
|
this.handleChange();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleChange() {
|
async handleChange(event) {
|
||||||
let value = await this.parse(this.getInputValue());
|
let value = await this.parse(this.getInputValue());
|
||||||
value = await this.validate(value);
|
value = await this.validate(value);
|
||||||
if (this.doc[this.fieldname] !== value) {
|
if (this.doc[this.fieldname] !== value) {
|
||||||
@ -135,4 +157,6 @@ class BaseControl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BaseControl.count = 0;
|
||||||
|
|
||||||
module.exports = BaseControl;
|
module.exports = BaseControl;
|
@ -11,15 +11,16 @@ class TableControl extends BaseControl {
|
|||||||
this.wrapper.innerHTML =
|
this.wrapper.innerHTML =
|
||||||
`<div class="datatable-wrapper"></div>
|
`<div class="datatable-wrapper"></div>
|
||||||
<div class="table-toolbar">
|
<div class="table-toolbar">
|
||||||
<button class="btn btn-sm btn-outline-secondary btn-add">${frappe._("Add")}</button>
|
<button type="button" class="btn btn-sm btn-outline-secondary btn-add">
|
||||||
<button class="btn btn-sm btn-outline-danger btn-remove">${frappe._("Remove")}</button>
|
${frappe._("Add")}</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary btn-remove">
|
||||||
|
${frappe._("Remove")}</button>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
this.datatable = new DataTable(this.wrapper.querySelector('.datatable-wrapper'), {
|
this.datatable = new DataTable(this.wrapper.querySelector('.datatable-wrapper'), {
|
||||||
columns: this.getColumns(),
|
columns: this.getColumns(),
|
||||||
data: this.getTableData(),
|
data: this.getTableData(),
|
||||||
takeAvailableSpace: true,
|
takeAvailableSpace: true,
|
||||||
enableClusterize: true,
|
|
||||||
addCheckboxColumn: true,
|
addCheckboxColumn: true,
|
||||||
editing: this.getTableInput.bind(this),
|
editing: this.getTableInput.bind(this),
|
||||||
});
|
});
|
||||||
@ -56,16 +57,19 @@ class TableControl extends BaseControl {
|
|||||||
let field = this.datatable.getColumn(colIndex).field;
|
let field = this.datatable.getColumn(colIndex).field;
|
||||||
|
|
||||||
if (field.fieldtype==='Text') {
|
if (field.fieldtype==='Text') {
|
||||||
return this.getControlInModal(field);
|
// text in modal
|
||||||
} else {
|
parent = this.getControlModal(field).getBody();
|
||||||
return this.getControl(field, parent);
|
|
||||||
}
|
}
|
||||||
|
return this.getControl(field, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
getControl(field, parent) {
|
getControl(field, parent) {
|
||||||
field.onlyInput = true;
|
field.onlyInput = true;
|
||||||
const control = controls.makeControl({field: field, parent: parent});
|
const control = controls.makeControl({field: field, parent: parent});
|
||||||
|
|
||||||
|
// change will be triggered by datatable
|
||||||
|
control.skipChangeEvent = true;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initValue: (value, rowIndex, column) => {
|
initValue: (value, rowIndex, column) => {
|
||||||
control.parent_control = this;
|
control.parent_control = this;
|
||||||
@ -74,7 +78,7 @@ class TableControl extends BaseControl {
|
|||||||
return control.setInputValue(value);
|
return control.setInputValue(value);
|
||||||
},
|
},
|
||||||
setValue: async (value, rowIndex, column) => {
|
setValue: async (value, rowIndex, column) => {
|
||||||
// triggers change event
|
control.handleChange();
|
||||||
},
|
},
|
||||||
getValue: () => {
|
getValue: () => {
|
||||||
return control.getInputValue();
|
return control.getInputValue();
|
||||||
@ -83,21 +87,24 @@ class TableControl extends BaseControl {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getControlInModal(field, parent) {
|
getControlModal(field) {
|
||||||
this.modal = new Modal({
|
this.modal = new Modal({
|
||||||
title: frappe._('Edit {0}', field.label),
|
title: frappe._('Edit {0}', field.label),
|
||||||
body: '',
|
body: '',
|
||||||
primary_label: frappe._('Submit'),
|
primary: {
|
||||||
primary_action: (modal) => {
|
label: frappe._('Submit'),
|
||||||
this.datatable.cellmanager.submitEditing();
|
action: (modal) => {
|
||||||
modal.hide();
|
this.datatable.cellmanager.submitEditing();
|
||||||
|
modal.hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.modal.$modal.on('hidden.bs.modal', () => {
|
this.modal.$modal.on('hidden.bs.modal', () => {
|
||||||
this.datatable.cellmanager.deactivateEditing();
|
this.datatable.cellmanager.deactivateEditing();
|
||||||
})
|
this.datatable.cellmanager.$focusedCell.focus();
|
||||||
|
});
|
||||||
|
|
||||||
return this.getControl(field, this.modal.get_body());
|
return this.modal;
|
||||||
}
|
}
|
||||||
|
|
||||||
getColumns() {
|
getColumns() {
|
||||||
@ -106,8 +113,11 @@ class TableControl extends BaseControl {
|
|||||||
id: field.fieldname,
|
id: field.fieldname,
|
||||||
field: field,
|
field: field,
|
||||||
content: field.label,
|
content: field.label,
|
||||||
editable: true,
|
|
||||||
width: 120,
|
width: 120,
|
||||||
|
editable: field.disabled ? false : true,
|
||||||
|
sortable: false,
|
||||||
|
resizable: true,
|
||||||
|
dropdown: false,
|
||||||
align: ['Int', 'Float', 'Currency'].includes(field.fieldtype) ? 'right' : 'left',
|
align: ['Int', 'Float', 'Currency'].includes(field.fieldtype) ? 'right' : 'left',
|
||||||
format: (value) => frappe.format(value, field)
|
format: (value) => frappe.format(value, field)
|
||||||
}
|
}
|
||||||
@ -115,7 +125,7 @@ class TableControl extends BaseControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getChildFields() {
|
getChildFields() {
|
||||||
return frappe.getMeta(this.childtype).fields;
|
return frappe.getMeta(this.childtype).fields.filter(f => f.hidden ? false : true);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultData() {
|
getDefaultData() {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const BaseControl = require('./base');
|
const BaseControl = require('./base');
|
||||||
|
const frappe = require('frappejs');
|
||||||
|
|
||||||
class TextControl extends BaseControl {
|
class TextControl extends BaseControl {
|
||||||
makeInput() {
|
makeInput() {
|
||||||
|
@ -24,9 +24,15 @@ module.exports = class BaseForm extends Observable {
|
|||||||
this.body = frappe.ui.add('div', 'form-body', this.parent);
|
this.body = frappe.ui.add('div', 'form-body', this.parent);
|
||||||
this.makeToolbar();
|
this.makeToolbar();
|
||||||
|
|
||||||
this.form = frappe.ui.add('div', 'form-container', this.body);
|
this.form = frappe.ui.add('form', 'form-container', this.body);
|
||||||
|
this.form.onValidate = true;
|
||||||
|
|
||||||
|
this.makeControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
makeControls() {
|
||||||
for(let field of this.meta.fields) {
|
for(let field of this.meta.fields) {
|
||||||
if (controls.getControlClass(field.fieldtype)) {
|
if (!field.hidden && controls.getControlClass(field.fieldtype)) {
|
||||||
let control = controls.makeControl({field: field, form: this});
|
let control = controls.makeControl({field: field, form: this});
|
||||||
this.controlList.push(control);
|
this.controlList.push(control);
|
||||||
this.controls[field.fieldname] = control;
|
this.controls[field.fieldname] = control;
|
||||||
@ -35,29 +41,33 @@ module.exports = class BaseForm extends Observable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
makeToolbar() {
|
makeToolbar() {
|
||||||
this.btnSubmit = this.page.addButton(frappe._("Save"), 'btn-primary', async () => {
|
this.btnSubmit = this.page.addButton(frappe._("Save"), 'btn-primary', async (event) => {
|
||||||
await this.submit();
|
await this.submit();
|
||||||
})
|
})
|
||||||
|
|
||||||
this.btnDelete = this.page.addButton(frappe._("Delete"), 'btn-outline-secondary', async () => {
|
this.btnDelete = this.page.addButton(frappe._("Delete"), 'btn-outline-secondary', async (e) => {
|
||||||
await this.doc.delete();
|
await this.doc.delete();
|
||||||
this.showAlert('Deleted', 'success');
|
this.showAlert('Deleted', 'success');
|
||||||
this.trigger('delete');
|
this.trigger('delete');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async use(doc, is_new = false) {
|
async use(doc) {
|
||||||
if (this.doc) {
|
if (this.doc) {
|
||||||
// clear handlers of outgoing doc
|
// clear handlers of outgoing doc
|
||||||
this.doc.clearHandlers();
|
this.doc.clearHandlers();
|
||||||
}
|
}
|
||||||
this.clearAlert();
|
this.clearAlert();
|
||||||
this.doc = doc;
|
this.doc = doc;
|
||||||
this.is_new = is_new;
|
|
||||||
for (let control of this.controlList) {
|
for (let control of this.controlList) {
|
||||||
control.bind(this.doc);
|
control.bind(this.doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setupChangeHandler();
|
||||||
|
this.trigger('use', {doc:doc});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupChangeHandler() {
|
||||||
// refresh value in control
|
// refresh value in control
|
||||||
this.doc.addHandler('change', (params) => {
|
this.doc.addHandler('change', (params) => {
|
||||||
if (params.fieldname) {
|
if (params.fieldname) {
|
||||||
@ -70,14 +80,17 @@ module.exports = class BaseForm extends Observable {
|
|||||||
// multiple values changed
|
// multiple values changed
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
this.form.classList.remove('was-validated');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.trigger('use', {doc:doc});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async submit() {
|
async submit() {
|
||||||
|
if (!this.form.checkValidity()) {
|
||||||
|
this.form.classList.add('was-validated');
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (this.is_new || this.doc.__not_inserted) {
|
if (this.doc._notInserted) {
|
||||||
await this.doc.insert();
|
await this.doc.insert();
|
||||||
} else {
|
} else {
|
||||||
await this.doc.update();
|
await this.doc.update();
|
||||||
@ -86,6 +99,7 @@ module.exports = class BaseForm extends Observable {
|
|||||||
this.showAlert('Saved', 'success');
|
this.showAlert('Saved', 'success');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showAlert('Failed', 'danger');
|
this.showAlert('Failed', 'danger');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
await this.trigger('submit');
|
await this.trigger('submit');
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ module.exports = class Page extends Observable {
|
|||||||
|
|
||||||
addButton(label, cssClass, action) {
|
addButton(label, cssClass, action) {
|
||||||
this.head.classList.remove('hide');
|
this.head.classList.remove('hide');
|
||||||
this.button = frappe.ui.add('button', 'btn btn-sm ' + cssClass, this.head);
|
this.button = frappe.ui.add('button', 'btn ' + cssClass, this.head);
|
||||||
this.button.innerHTML = label;
|
this.button.innerHTML = label;
|
||||||
this.button.addEventListener('click', action);
|
this.button.addEventListener('click', action);
|
||||||
return this.button;
|
return this.button;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
const frappe = require('frappejs');
|
const Observable = require('frappejs/utils/observable');
|
||||||
|
|
||||||
module.exports = class Router {
|
module.exports = class Router extends Observable {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super();
|
||||||
this.last_route = null;
|
this.last_route = null;
|
||||||
this.current_page = null;
|
this.current_page = null;
|
||||||
this.static_routes = [];
|
this.static_routes = [];
|
||||||
@ -103,6 +104,7 @@ module.exports = class Router {
|
|||||||
} else {
|
} else {
|
||||||
await this.match('not-found').handler({route: route});
|
await this.match('not-found').handler({route: route});
|
||||||
}
|
}
|
||||||
|
await this.trigger('change');
|
||||||
}
|
}
|
||||||
|
|
||||||
match(route) {
|
match(route) {
|
||||||
|
2
index.js
2
index.js
@ -84,7 +84,7 @@ module.exports = {
|
|||||||
async getNewDoc(doctype) {
|
async getNewDoc(doctype) {
|
||||||
let doc = this.newDoc({doctype: doctype});
|
let doc = this.newDoc({doctype: doctype});
|
||||||
doc.setName();
|
doc.setName();
|
||||||
doc.__not_inserted = true;
|
doc._notInserted = true;
|
||||||
this.addToCache(doc);
|
this.addToCache(doc);
|
||||||
return doc;
|
return doc;
|
||||||
},
|
},
|
||||||
|
@ -17,7 +17,7 @@ module.exports = {
|
|||||||
"fieldname": "name",
|
"fieldname": "name",
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"reqd": 1
|
"required": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`);
|
}`);
|
||||||
|
@ -3,6 +3,7 @@ const frappe = require('frappejs');
|
|||||||
module.exports = class BaseDocument {
|
module.exports = class BaseDocument {
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
this.handlers = {};
|
this.handlers = {};
|
||||||
|
this.fetchValues = {};
|
||||||
this.setup();
|
this.setup();
|
||||||
Object.assign(this, data);
|
Object.assign(this, data);
|
||||||
}
|
}
|
||||||
@ -29,7 +30,7 @@ module.exports = class BaseDocument {
|
|||||||
// set value and trigger change
|
// set value and trigger change
|
||||||
async set(fieldname, value) {
|
async set(fieldname, value) {
|
||||||
this[fieldname] = await this.validateField(fieldname, value);
|
this[fieldname] = await this.validateField(fieldname, value);
|
||||||
if (this.applyFormulae()) {
|
if (await this.applyFormulae()) {
|
||||||
// multiple changes
|
// multiple changes
|
||||||
await this.trigger('change', { doc: this });
|
await this.trigger('change', { doc: this });
|
||||||
} else {
|
} else {
|
||||||
@ -138,12 +139,12 @@ module.exports = class BaseDocument {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applyFormulae() {
|
async applyFormulae() {
|
||||||
if (!this.hasFormulae()) {
|
if (!this.meta.hasFormulae()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let doc;
|
let doc = this;
|
||||||
|
|
||||||
// children
|
// children
|
||||||
for (let tablefield of this.meta.getTableFields()) {
|
for (let tablefield of this.meta.getTableFields()) {
|
||||||
@ -151,16 +152,15 @@ module.exports = class BaseDocument {
|
|||||||
if (formulaFields.length) {
|
if (formulaFields.length) {
|
||||||
|
|
||||||
// for each row
|
// for each row
|
||||||
for (doc of this[tablefield.fieldname]) {
|
for (let row of this[tablefield.fieldname]) {
|
||||||
for (let field of formulaFields) {
|
for (let field of formulaFields) {
|
||||||
doc[field.fieldname] = eval(field.formula);
|
row[field.fieldname] = await eval(field.formula);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parent
|
// parent
|
||||||
doc = this;
|
|
||||||
for (let field of this.meta.getFormulaFields()) {
|
for (let field of this.meta.getFormulaFields()) {
|
||||||
doc[field.fieldname] = eval(field.formula);
|
doc[field.fieldname] = eval(field.formula);
|
||||||
}
|
}
|
||||||
@ -168,30 +168,13 @@ module.exports = class BaseDocument {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasFormulae() {
|
|
||||||
if (this._hasFormulae===undefined) {
|
|
||||||
this._hasFormulae = false;
|
|
||||||
if (this.meta.getFormulaFields().length) {
|
|
||||||
this._hasFormulae = true;
|
|
||||||
} else {
|
|
||||||
for (let tablefield of this.meta.getTableFields()) {
|
|
||||||
if (frappe.getMeta(tablefield.childtype).getFormulaFields().length) {
|
|
||||||
this._hasFormulae = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this._hasFormulae;
|
|
||||||
}
|
|
||||||
|
|
||||||
async commit() {
|
async commit() {
|
||||||
// re-run triggers
|
// re-run triggers
|
||||||
this.setName();
|
this.setName();
|
||||||
this.setStandardValues();
|
this.setStandardValues();
|
||||||
this.setKeywords();
|
this.setKeywords();
|
||||||
this.setChildIdx();
|
this.setChildIdx();
|
||||||
this.applyFormulae();
|
await this.applyFormulae();
|
||||||
await this.trigger('validate');
|
await this.trigger('validate');
|
||||||
await this.trigger('commit');
|
await this.trigger('commit');
|
||||||
}
|
}
|
||||||
@ -233,4 +216,18 @@ module.exports = class BaseDocument {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// helper functions
|
||||||
|
getSum(tablefield, childfield) {
|
||||||
|
return this[tablefield].map(d => (d[childfield] || 0)).reduce((a, b) => a + b, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFrom(doctype, name, fieldname) {
|
||||||
|
if (!name) return '';
|
||||||
|
let key = `${doctype}:${name}:${fieldname}`;
|
||||||
|
if (!this.fetchValues[key]) {
|
||||||
|
this.fetchValues[key] = await frappe.db.getValue(doctype, name, fieldname);
|
||||||
|
}
|
||||||
|
return this.fetchValues[key];
|
||||||
|
}
|
||||||
};
|
};
|
@ -17,41 +17,41 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
common_fields: [
|
common_fields: [
|
||||||
{
|
{
|
||||||
fieldname: 'name', fieldtype: 'Data', reqd: 1
|
fieldname: 'name', fieldtype: 'Data', required: 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
parent_fields: [
|
parent_fields: [
|
||||||
{
|
{
|
||||||
fieldname: 'owner', fieldtype: 'Link', reqd: 1, options: 'User'
|
fieldname: 'owner', fieldtype: 'Link', required: 1, options: 'User'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'modified_by', fieldtype: 'Link', reqd: 1, options: 'User'
|
fieldname: 'modified_by', fieldtype: 'Link', required: 1, options: 'User'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'creation', fieldtype: 'Datetime', reqd: 1
|
fieldname: 'creation', fieldtype: 'Datetime', required: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'modified', fieldtype: 'Datetime', reqd: 1
|
fieldname: 'modified', fieldtype: 'Datetime', required: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'keywords', fieldtype: 'Text'
|
fieldname: 'keywords', fieldtype: 'Text'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'docstatus', fieldtype: 'Int', reqd: 1, default: 0
|
fieldname: 'docstatus', fieldtype: 'Int', required: 1, default: 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
child_fields: [
|
child_fields: [
|
||||||
{
|
{
|
||||||
fieldname: 'idx', fieldtype: 'Int', reqd: 1
|
fieldname: 'idx', fieldtype: 'Int', required: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'parent', fieldtype: 'Data', reqd: 1
|
fieldname: 'parent', fieldtype: 'Data', required: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'parenttype', fieldtype: 'Link', reqd: 1, options: 'DocType'
|
fieldname: 'parenttype', fieldtype: 'Link', required: 1, options: 'DocType'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'parentfield', fieldtype: 'Data', reqd: 1
|
fieldname: 'parentfield', fieldtype: 'Data', required: 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
@ -37,6 +37,23 @@ module.exports = class BaseMeta extends BaseDocument {
|
|||||||
return this._formulaFields;
|
return this._formulaFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasFormulae() {
|
||||||
|
if (this._hasFormulae===undefined) {
|
||||||
|
this._hasFormulae = false;
|
||||||
|
if (this.getFormulaFields().length) {
|
||||||
|
this._hasFormulae = true;
|
||||||
|
} else {
|
||||||
|
for (let tablefield of this.getTableFields()) {
|
||||||
|
if (frappe.getMeta(tablefield.childtype).getFormulaFields().length) {
|
||||||
|
this._hasFormulae = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._hasFormulae;
|
||||||
|
}
|
||||||
|
|
||||||
on(key, fn) {
|
on(key, fn) {
|
||||||
if (!this.event_handlers[key]) {
|
if (!this.event_handlers[key]) {
|
||||||
this.event_handlers[key] = [];
|
this.event_handlers[key] = [];
|
||||||
@ -112,7 +129,7 @@ module.exports = class BaseMeta extends BaseDocument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getKeywordFields() {
|
getKeywordFields() {
|
||||||
return this.keyword_fields || this.meta.fields.filter(field => field.reqd).map(field => field.fieldname);
|
return this.keyword_fields || this.meta.fields.filter(field => field.required).map(field => field.fieldname);
|
||||||
}
|
}
|
||||||
|
|
||||||
validate_select(field, value) {
|
validate_select(field, value) {
|
||||||
|
@ -9,13 +9,13 @@
|
|||||||
"fieldname": "name",
|
"fieldname": "name",
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"reqd": 1
|
"required": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "current",
|
"fieldname": "current",
|
||||||
"label": "Current",
|
"label": "Current",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"reqd": 1
|
"required": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -9,7 +9,7 @@
|
|||||||
"fieldname": "name",
|
"fieldname": "name",
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"reqd": 1
|
"required": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -9,13 +9,13 @@
|
|||||||
"fieldname": "username",
|
"fieldname": "username",
|
||||||
"label": "Username",
|
"label": "Username",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"reqd": 1
|
"required": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "password",
|
"fieldname": "password",
|
||||||
"label": "Password",
|
"label": "Password",
|
||||||
"fieldtype": "Password",
|
"fieldtype": "Password",
|
||||||
"reqd": 1
|
"required": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -12,12 +12,7 @@
|
|||||||
"fieldname": "subject",
|
"fieldname": "subject",
|
||||||
"label": "Subject",
|
"label": "Subject",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"reqd": 1
|
"required": 1
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "description",
|
|
||||||
"label": "Description",
|
|
||||||
"fieldtype": "Text"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "status",
|
"fieldname": "status",
|
||||||
@ -28,7 +23,12 @@
|
|||||||
"Closed"
|
"Closed"
|
||||||
],
|
],
|
||||||
"default": "Open",
|
"default": "Open",
|
||||||
"reqd": 1
|
"required": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "description",
|
||||||
|
"label": "Description",
|
||||||
|
"fieldtype": "Text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -12,13 +12,13 @@
|
|||||||
"fieldname": "name",
|
"fieldname": "name",
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"reqd": 1
|
"required": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "full_name",
|
"fieldname": "full_name",
|
||||||
"label": "Full Name",
|
"label": "Full Name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"reqd": 1
|
"required": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "roles",
|
"fieldname": "roles",
|
||||||
|
Loading…
Reference in New Issue
Block a user