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