2
0
mirror of https://github.com/frappe/books.git synced 2025-01-10 18:24:40 +00:00

added fetch

This commit is contained in:
Rushabh Mehta 2018-02-09 18:25:55 +05:30
parent bc095b5bc3
commit 3af4fb8671
23 changed files with 218 additions and 118 deletions

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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);
}); });
} }
} }

View File

@ -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 = '#';

View File

@ -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 {

View File

@ -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);
} }
} }

View File

@ -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;

View File

@ -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() {

View File

@ -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() {

View File

@ -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');
} }

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}, },

View File

@ -17,7 +17,7 @@ module.exports = {
"fieldname": "name", "fieldname": "name",
"label": "Name", "label": "Name",
"fieldtype": "Data", "fieldtype": "Data",
"reqd": 1 "required": 1
} }
] ]
}`); }`);

View File

@ -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];
}
}; };

View File

@ -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
} }
] ]
}; };

View File

@ -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) {

View File

@ -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
} }
] ]
} }

View File

@ -9,7 +9,7 @@
"fieldname": "name", "fieldname": "name",
"label": "Name", "label": "Name",
"fieldtype": "Data", "fieldtype": "Data",
"reqd": 1 "required": 1
} }
] ]
} }

View File

@ -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
} }
] ]
} }

View File

@ -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"
} }
] ]
} }

View File

@ -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",