diff --git a/dist/css/style.css b/dist/css/style.css
index 74899de2..d35ce32c 100644
--- a/dist/css/style.css
+++ b/dist/css/style.css
@@ -5735,9 +5735,13 @@ a.text-dark:hover, a.text-dark:focus {
background: #3d6b00;
color: inherit; }
html {
- font-size: 16px; }
+ font-size: 14px; }
.main {
border-left: 1px solid #dee2e6; }
+.sidebar .list-group-item {
+ padding: 0.5rem 1rem; }
+.sidebar .list-group-flush {
+ margin: -1rem; }
.hide {
display: none !important; }
.page-head {
@@ -5780,8 +5784,8 @@ mark {
color: inherit;
padding: 0px; }
.table-wrapper {
- margin-top: 1rem;
- margin-bottom: 1rem; }
+ margin-top: 2rem;
+ margin-bottom: 2rem; }
.table-toolbar {
margin-top: 0.5rem; }
.data-table-col .edit-cell {
diff --git a/dist/js/bundle.js b/dist/js/bundle.js
index c7061524..cc3450b8 100644
--- a/dist/js/bundle.js
+++ b/dist/js/bundle.js
@@ -173,19 +173,19 @@ var number_format = {
var frappejs = {
async init() {
if (this._initialized) return;
- this.init_config();
- this.init_globals();
+ this.initConfig();
+ this.initGlobals();
this._initialized = true;
},
- init_config() {
+ initConfig() {
this.config = {
backend: 'sqlite',
port: 8000
};
},
- init_globals() {
+ initGlobals() {
this.meta_cache = {};
this.modules = {};
this.docs = {};
@@ -194,7 +194,7 @@ var frappejs = {
};
},
- add_to_cache(doc) {
+ addToCache(doc) {
if (!this.flags.cache_docs) return;
// add to `docs` cache
@@ -206,7 +206,7 @@ var frappejs = {
}
},
- get_doc_from_cache(doctype, name) {
+ getDocFromCache(doctype, name) {
if (this.docs[doctype] && this.docs[doctype][name]) {
return this.docs[doctype][name];
}
@@ -214,12 +214,12 @@ var frappejs = {
getMeta(doctype) {
if (!this.meta_cache[doctype]) {
- this.meta_cache[doctype] = new (this.getMeta_class(doctype))();
+ this.meta_cache[doctype] = new (this.getMetaClass(doctype))();
}
return this.meta_cache[doctype];
},
- getMeta_class(doctype) {
+ getMetaClass(doctype) {
doctype = this.slug(doctype);
if (this.modules[doctype] && this.modules[doctype].Meta) {
return this.modules[doctype].Meta;
@@ -228,23 +228,23 @@ var frappejs = {
}
},
- async get_doc(doctype, name) {
- let doc = this.get_doc_from_cache(doctype, name);
+ async getDoc(doctype, name) {
+ let doc = this.getDocFromCache(doctype, name);
if (!doc) {
- let controller_class = this.get_controller_class(doctype);
+ let controller_class = this.getControllerClass(doctype);
doc = new controller_class({doctype:doctype, name: name});
await doc.load();
- this.add_to_cache(doc);
+ this.addToCache(doc);
}
return doc;
},
- new_doc(data) {
- let controller_class = this.get_controller_class(data.doctype);
+ newDoc(data) {
+ let controller_class = this.getControllerClass(data.doctype);
return new controller_class(data);
},
- get_controller_class(doctype) {
+ getControllerClass(doctype) {
doctype = this.slug(doctype);
if (this.modules[doctype] && this.modules[doctype].Document) {
return this.modules[doctype].Document;
@@ -253,16 +253,16 @@ var frappejs = {
}
},
- async get_new_doc(doctype) {
- let doc = frappe.new_doc({doctype: doctype});
+ async getNewDoc(doctype) {
+ let doc = this.newDoc({doctype: doctype});
doc.setName();
- doc.__not_inserted = true;
- this.add_to_cache(doc);
+ doc._notInserted = true;
+ this.addToCache(doc);
return doc;
},
async insert(data) {
- return await (this.new_doc(data)).insert();
+ return await (this.newDoc(data)).insert();
},
login(user='guest', user_key) {
@@ -285,12 +285,12 @@ var model = {
async get_series_next(prefix) {
let series;
try {
- series = await frappejs.get_doc('Number Series', prefix);
+ series = await frappejs.getDoc('Number Series', prefix);
} catch (e) {
if (!e.status_code || e.status_code !== 404) {
throw e;
}
- series = frappejs.new_doc({doctype: 'Number Series', name: prefix, current: 0});
+ series = frappejs.newDoc({doctype: 'Number Series', name: prefix, current: 0});
await series.insert();
}
let next = await series.next();
@@ -298,41 +298,41 @@ var model = {
},
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
}
]
};
@@ -340,6 +340,7 @@ var model = {
var document$1 = class BaseDocument {
constructor(data) {
this.handlers = {};
+ this.fetchValues = {};
this.setup();
Object.assign(this, data);
}
@@ -366,7 +367,7 @@ var document$1 = 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 {
@@ -475,12 +476,12 @@ var document$1 = 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()) {
@@ -488,16 +489,15 @@ var document$1 = 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);
}
@@ -505,30 +505,13 @@ var document$1 = 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 (frappejs.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');
}
@@ -570,6 +553,20 @@ var document$1 = 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 frappejs.db.getValue(doctype, name, fieldname);
+ }
+ return this.fetchValues[key];
+ }
};
var meta = class BaseMeta extends document$1 {
@@ -579,8 +576,8 @@ var meta = class BaseMeta extends document$1 {
this.list_options = {
fields: ['name', 'modified']
};
- if (this.setup_meta) {
- this.setup_meta();
+ if (this.setupMeta) {
+ this.setupMeta();
}
}
@@ -608,6 +605,23 @@ var meta = class BaseMeta extends document$1 {
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 (frappejs.getMeta(tablefield.childtype).getFormulaFields().length) {
+ this._hasFormulae = true;
+ break;
+ }
+ }
+ }
+ }
+ return this._hasFormulae;
+ }
+
on(key, fn) {
if (!this.event_handlers[key]) {
this.event_handlers[key] = [];
@@ -683,7 +697,7 @@ var meta = class BaseMeta extends document$1 {
}
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) {
@@ -1101,7 +1115,13 @@ var rest_client = class RESTClient {
async fetch(url, args) {
args.headers = this.getHeaders();
let response = await frappejs.fetch(url, args);
- return await response.json();
+ let data = await response.json();
+
+ if (response.status !== 200) {
+ throw Error(data.error);
+ }
+
+ return data;
}
getURL(...parts) {
@@ -1292,8 +1312,30 @@ var ui = {
};
-var router = class Router {
+var observable = class Observable {
constructor() {
+ this._handlers = {};
+ }
+
+ on(event, fn) {
+ if (!this._handlers[event]) {
+ this._handlers[event] = [];
+ }
+ this._handlers[event].push(fn);
+ }
+
+ async trigger(event, params) {
+ if (this._handlers[event]) {
+ for (let handler of this._handlers[event]) {
+ await handler(params);
+ }
+ }
+ }
+};
+
+var router = class Router extends observable {
+ constructor() {
+ super();
this.last_route = null;
this.current_page = null;
this.static_routes = [];
@@ -1395,6 +1437,7 @@ var router = class Router {
} else {
await this.match('not-found').handler({route: route});
}
+ await this.trigger('change');
}
match(route) {
@@ -1428,27 +1471,6 @@ var router = class Router {
}
};
-var observable = class Observable {
- constructor() {
- this._handlers = {};
- }
-
- on(event, fn) {
- if (!this._handlers[event]) {
- this._handlers[event] = [];
- }
- this._handlers[event].push(fn);
- }
-
- async trigger(event, params) {
- if (this._handlers[event]) {
- for (let handler of this._handlers[event]) {
- await handler(params);
- }
- }
- }
-};
-
var page = class Page extends observable {
constructor(title) {
super();
@@ -1471,7 +1493,7 @@ var page = class Page extends observable {
addButton(label, cssClass, action) {
this.head.classList.remove('hide');
- this.button = frappejs.ui.add('button', 'btn btn-sm ' + cssClass, this.head);
+ this.button = frappejs.ui.add('button', 'btn ' + cssClass, this.head);
this.button.innerHTML = label;
this.button.addEventListener('click', action);
return this.button;
@@ -1675,9 +1697,12 @@ var list = class BaseList {
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 = frappejs.slug(this.label);
@@ -1714,10 +1739,12 @@ class BaseControl {
}
this.makeInput();
this.setInputName();
+ this.setRequiredAttribute();
+ this.setDisabled();
if (!this.onlyInput) {
this.makeDescription();
}
- this.bindChangeEvent();
+ this.addChangeHandler();
}
}
@@ -1726,13 +1753,21 @@ class BaseControl {
}
makeLabel() {
- this.label_element = frappejs.ui.add('label', null, this.formGroup);
- this.label_element.textContent = this.label;
+ this.labelElement = frappejs.ui.add('label', null, this.formGroup);
+ this.labelElement.textContent = this.label;
+ this.labelElement.setAttribute('for', this.id);
}
makeInput() {
this.input = frappejs.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() {
@@ -1743,6 +1778,12 @@ class BaseControl {
this.input.setAttribute('name', this.fieldname);
}
+ setRequiredAttribute() {
+ if (this.required) {
+ this.input.required = true;
+ }
+ }
+
makeDescription() {
if (this.description) {
this.description_element = frappejs.ui.add('small', 'form-text text-muted', this.formGroup);
@@ -1777,11 +1818,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) {
@@ -1808,6 +1852,8 @@ class BaseControl {
}
}
+BaseControl.count = 0;
+
var base = BaseControl;
class DataControl extends base {
@@ -1821,7 +1867,7 @@ var data = DataControl;
class TextControl extends base {
makeInput() {
- this.input = frappe.ui.add('textarea', 'form-control', this.get_input_parent());
+ this.input = frappejs.ui.add('textarea', 'form-control', this.get_input_parent());
}
make() {
super.make();
@@ -5162,6 +5208,7 @@ var CellManager = function () {
var _this2 = this;
var focusCell = function focusCell(direction) {
+ console.log(direction);
if (!_this2.$focusedCell || _this2.$editingCell) {
return false;
}
@@ -5170,7 +5217,7 @@ var CellManager = function () {
if (direction === 'left') {
$cell = _this2.getLeftCell$($cell);
- } else if (direction === 'right') {
+ } else if (direction === 'right' || direction === 'tab') {
$cell = _this2.getRightCell$($cell);
} else if (direction === 'up') {
$cell = _this2.getAboveCell$($cell);
@@ -5207,7 +5254,7 @@ var CellManager = function () {
return true;
};
- ['left', 'right', 'up', 'down'].map(function (direction) {
+ ['left', 'right', 'up', 'down', 'tab'].map(function (direction) {
return _keyboard2.default.on(direction, function () {
return focusCell(direction);
});
@@ -5535,10 +5582,10 @@ var CellManager = function () {
}, {
key: 'deactivateEditing',
value: function deactivateEditing() {
- if (!this.$editingCell) return;
-
// keep focus on the cell so that keyboard navigation works
- this.$editingCell.focus();
+ if (this.$focusedCell) this.$focusedCell.focus();
+
+ if (!this.$editingCell) return;
this.$editingCell.classList.remove('editing');
this.$editingCell = null;
}
@@ -5790,7 +5837,9 @@ function getCellContent(cell) {
var hasDropdown = isHeader && cell.dropdown !== false;
var dropdown = hasDropdown ? '
' + (0, _columnmanager.getDropdownHTML)() + '
' : '';
- return '\n \n ' + (!cell.isHeader && cell.column.format ? cell.column.format(cell.content) : cell.content) + '\n ' + sortIndicator + '\n ' + resizeColumn + '\n ' + dropdown + '\n
\n ' + editCellHTML + '\n ';
+ var contentHTML = !cell.isHeader && cell.column.format ? cell.column.format(cell.content) : cell.content;
+
+ return '\n \n ' + contentHTML + '\n ' + sortIndicator + '\n ' + resizeColumn + '\n ' + dropdown + '\n
\n ' + editCellHTML + '\n ';
}
function getEditCellHTML() {
@@ -7438,6 +7487,7 @@ var BodyRenderer = function () {
this.bodyScrollable.innerHTML = '\n \n ' + getBodyHTML(rows) + '\n
\n ';
this.instance.setDimensions();
+ this.restoreState();
}
}, {
key: 'renderBodyWithClusterize',
@@ -7460,9 +7510,7 @@ var BodyRenderer = function () {
contentElem: (0, _dom2.default)('tbody', this.bodyScrollable),
callbacks: {
clusterChanged: function clusterChanged() {
- _this.rowmanager.highlightCheckedRows();
- _this.cellmanager.selectAreaOnClusterChanged();
- _this.cellmanager.focusCellOnClusterChanged();
+ _this.restoreState();
}
},
/* eslint-disable */
@@ -7479,6 +7527,13 @@ var BodyRenderer = function () {
this.appendRemainingData();
}
+ }, {
+ key: 'restoreState',
+ value: function restoreState() {
+ this.rowmanager.highlightCheckedRows();
+ this.cellmanager.selectAreaOnClusterChanged();
+ this.cellmanager.focusCellOnClusterChanged();
+ }
}, {
key: 'appendRemainingData',
value: function appendRemainingData() {
@@ -7618,14 +7673,14 @@ exports.default = {
desc: '↓',
none: ''
},
- freezeMessage: 'Loading...',
+ freezeMessage: '',
editing: function editing() {},
addSerialNoColumn: true,
addCheckboxColumn: false,
enableClusterize: true,
enableLogs: false,
takeAvailableSpace: false,
- loadingText: 'Loading...'
+ loadingText: ''
};
module.exports = exports['default'];
@@ -8208,7 +8263,7 @@ module.exports = {"name":"frappe-datatable","version":"0.0.1","description":"A m
/***/ })
/******/ ]);
});
-//# sourceMappingURL=frappe-datatable.js.map
+
});
unwrapExports(frappeDatatable);
@@ -24915,7 +24970,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
unwrapExports(bootstrap);
var modal = 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 = jquery(`
@@ -24935,23 +24990,23 @@ var modal = class Modal {
`).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 = jquery(``)
.appendTo(this.$modal.find('.modal-footer'))
.on('click', () => action(this));
}
- add_secondary(label, action) {
+ addSecondary(label, action) {
this.$primary = jquery(``)
.appendTo(this.$modal.find('.modal-footer'))
@@ -24966,7 +25021,7 @@ var modal = class Modal {
this.$modal.modal('hide');
}
- get_body() {
+ getBody() {
return this.$modal.find('.modal-body').get(0);
}
};
@@ -24978,15 +25033,16 @@ class TableControl extends base {
this.wrapper.innerHTML =
`
-
-
+
+
`;
this.datatable = new frappeDatatable(this.wrapper.querySelector('.datatable-wrapper'), {
columns: this.getColumns(),
data: this.getTableData(),
takeAvailableSpace: true,
- enableClusterize: true,
addCheckboxColumn: true,
editing: this.getTableInput.bind(this),
});
@@ -25023,16 +25079,19 @@ class TableControl extends base {
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$1.makeControl({field: field, parent: parent});
+ // change will be triggered by datatable
+ control.skipChangeEvent = true;
+
return {
initValue: (value, rowIndex, column) => {
control.parent_control = this;
@@ -25041,7 +25100,7 @@ class TableControl extends base {
return control.setInputValue(value);
},
setValue: async (value, rowIndex, column) => {
- // triggers change event
+ control.handleChange();
},
getValue: () => {
return control.getInputValue();
@@ -25050,21 +25109,24 @@ class TableControl extends base {
}
- getControlInModal(field, parent) {
+ getControlModal(field) {
this.modal = new modal({
title: frappejs._('Edit {0}', field.label),
body: '',
- primary_label: frappejs._('Submit'),
- primary_action: (modal$$1) => {
- this.datatable.cellmanager.submitEditing();
- modal$$1.hide();
+ primary: {
+ label: frappejs._('Submit'),
+ action: (modal$$1) => {
+ this.datatable.cellmanager.submitEditing();
+ modal$$1.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() {
@@ -25073,8 +25135,11 @@ class TableControl extends base {
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) => frappejs.format(value, field)
}
@@ -25082,7 +25147,7 @@ class TableControl extends base {
}
getChildFields() {
- return frappejs.getMeta(this.childtype).fields;
+ return frappejs.getMeta(this.childtype).fields.filter(f => f.hidden ? false : true);
}
getDefaultData() {
@@ -25145,9 +25210,15 @@ var form = class BaseForm extends observable {
this.body = frappejs.ui.add('div', 'form-body', this.parent);
this.makeToolbar();
- this.form = frappejs.ui.add('div', 'form-container', this.body);
+ this.form = frappejs.ui.add('form', 'form-container', this.body);
+ this.form.onValidate = true;
+
+ this.makeControls();
+ }
+
+ makeControls() {
for(let field of this.meta.fields) {
- if (controls$1.getControlClass(field.fieldtype)) {
+ if (!field.hidden && controls$1.getControlClass(field.fieldtype)) {
let control = controls$1.makeControl({field: field, form: this});
this.controlList.push(control);
this.controls[field.fieldname] = control;
@@ -25156,29 +25227,33 @@ var form = class BaseForm extends observable {
}
makeToolbar() {
- this.btnSubmit = this.page.addButton(frappejs._("Save"), 'btn-primary', async () => {
+ this.btnSubmit = this.page.addButton(frappejs._("Save"), 'btn-primary', async (event) => {
await this.submit();
});
- this.btnDelete = this.page.addButton(frappejs._("Delete"), 'btn-outline-secondary', async () => {
+ this.btnDelete = this.page.addButton(frappejs._("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) {
@@ -25191,14 +25266,17 @@ var form = 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();
@@ -25207,6 +25285,7 @@ var form = class BaseForm extends observable {
this.showAlert('Saved', 'success');
} catch (e) {
this.showAlert('Failed', 'danger');
+ return;
}
await this.trigger('submit');
}
@@ -25288,7 +25367,7 @@ var formpage = class FormPage extends page {
async show_doc(doctype, name) {
try {
- this.doc = await frappejs.get_doc(doctype, name);
+ this.doc = await frappejs.getDoc(doctype, name);
this.form.use(this.doc);
} catch (e) {
this.renderError(e.status_code, e.message);
@@ -25315,7 +25394,7 @@ var navbar = class Navbar {
constructor({brand_label = 'Home'} = {}) {
Object.assign(this, arguments[0]);
this.items = {};
- this.navbar = frappejs.ui.add('div', 'navbar navbar-expand-md border-bottom', document.querySelector('body'));
+ this.navbar = frappejs.ui.add('div', 'navbar navbar-expand-md border-bottom navbar-dark bg-dark', document.querySelector('body'));
this.brand = frappejs.ui.add('a', 'navbar-brand', this.navbar);
this.brand.href = '#';
@@ -25361,70 +25440,89 @@ var desk = class Desk {
this.navbar = new navbar();
this.container = frappejs.ui.add('div', 'container-fluid', body);
- this.container_row = frappejs.ui.add('div', 'row', this.container);
- this.sidebar = frappejs.ui.add('div', 'col-md-2 p-3 sidebar d-none d-md-block', this.container_row);
- this.body = frappejs.ui.add('div', 'col-md-10 p-3 main', this.container_row);
+ this.containerRow = frappejs.ui.add('div', 'row', this.container);
+ this.sidebar = frappejs.ui.add('div', 'col-md-2 p-3 sidebar d-none d-md-block', this.containerRow);
+ this.sidebarList = frappejs.ui.add('div', 'list-group list-group-flush', this.sidebar);
+ this.body = frappejs.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() {
frappejs.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 : '');
});
frappejs.router.add('list/:doctype', async (params) => {
- let page$$1 = this.getList_page(params.doctype);
+ let page$$1 = this.getListPage(params.doctype);
await page$$1.show(params);
});
frappejs.router.add('edit/:doctype/:name', async (params) => {
- let page$$1 = this.get_form_page(params.doctype);
+ let page$$1 = this.getFormPage(params.doctype);
await page$$1.show(params);
});
frappejs.router.add('new/:doctype', async (params) => {
- let doc = await frappejs.get_new_doc(params.doctype);
+ let doc = await frappejs.getNewDoc(params.doctype);
// unset the name, its local
await frappejs.router.setRoute('edit', doc.doctype, doc.name);
await doc.set('name', '');
});
+ frappejs.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 = frappejs.ui.add('a', '', frappejs.ui.add('p', null, frappejs.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 = frappejs.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);
});
}
}
@@ -25455,7 +25553,7 @@ var name = "ToDo";
var doctype = "DocType";
var is_single = 0;
var keyword_fields = ["subject","description"];
-var fields = [{"fieldname":"subject","label":"Subject","fieldtype":"Data","reqd":1},{"fieldname":"description","label":"Description","fieldtype":"Text"},{"fieldname":"status","label":"Status","fieldtype":"Select","options":["Open","Closed"],"default":"Open","reqd":1}];
+var fields = [{"fieldname":"subject","label":"Subject","fieldtype":"Data","required":1},{"fieldname":"status","label":"Status","fieldtype":"Select","options":["Open","Closed"],"default":"Open","required":1},{"fieldname":"description","label":"Description","fieldtype":"Text"}];
var todo = {
autoname: autoname,
name: name,
@@ -25478,7 +25576,7 @@ var todo$1 = Object.freeze({
var require$$0$4 = ( todo$1 && todo ) || todo$1;
class ToDoMeta extends meta {
- setup_meta() {
+ setupMeta() {
Object.assign(this, require$$0$4);
}
}
@@ -25503,7 +25601,7 @@ var name$1 = "Account";
var doctype$1 = "DocType";
var is_single$1 = 0;
var keyword_fields$1 = ["name","account_type"];
-var fields$1 = [{"fieldname":"name","label":"Account Name","fieldtype":"Data","reqd":1},{"fieldname":"parent_account","label":"Parent Account","fieldtype":"Link","target":"Account"},{"fieldname":"account_type","label":"Account Type","fieldtype":"Select","options":["Asset","Liability","Equity","Income","Expense"]}];
+var fields$1 = [{"fieldname":"name","label":"Account Name","fieldtype":"Data","required":1},{"fieldname":"parent_account","label":"Parent Account","fieldtype":"Link","target":"Account"},{"fieldname":"account_type","label":"Account Type","fieldtype":"Select","options":["Asset","Liability","Equity","Income","Expense"]}];
var account = {
name: name$1,
doctype: doctype$1,
@@ -25524,7 +25622,7 @@ var account$1 = Object.freeze({
var require$$0$5 = ( account$1 && account ) || account$1;
class AccountMeta extends meta {
- setup_meta() {
+ setupMeta() {
Object.assign(this, require$$0$5);
}
}
@@ -25553,7 +25651,7 @@ var name$2 = "Item";
var doctype$2 = "DocType";
var is_single$2 = 0;
var keyword_fields$2 = ["name","description"];
-var fields$2 = [{"fieldname":"name","label":"Item Name","fieldtype":"Data","reqd":1},{"fieldname":"description","label":"Description","fieldtype":"Text"},{"fieldname":"unit","label":"Unit","fieldtype":"Select","options":["No","Kg","Gram","Hour","Day"]},{"fieldname":"rate","label":"Rate","fieldtype":"Currency"}];
+var fields$2 = [{"fieldname":"name","label":"Item Name","fieldtype":"Data","required":1},{"fieldname":"description","label":"Description","fieldtype":"Text"},{"fieldname":"unit","label":"Unit","fieldtype":"Select","options":["No","Kg","Gram","Hour","Day"]},{"fieldname":"rate","label":"Rate","fieldtype":"Currency"}];
var item$1 = {
name: name$2,
doctype: doctype$2,
@@ -25574,7 +25672,7 @@ var item$2 = Object.freeze({
var require$$0$6 = ( item$2 && item$1 ) || item$2;
class ItemMeta extends meta {
- setup_meta() {
+ setupMeta() {
Object.assign(this, require$$0$6);
}
}
@@ -25592,7 +25690,7 @@ var doctype$3 = "DocType";
var is_single$3 = 0;
var istable = 0;
var keyword_fields$3 = ["name"];
-var fields$3 = [{"fieldname":"name","label":"Name","fieldtype":"Data","reqd":1}];
+var fields$3 = [{"fieldname":"name","label":"Name","fieldtype":"Data","required":1}];
var customer = {
name: name$3,
doctype: doctype$3,
@@ -25615,7 +25713,7 @@ var customer$1 = Object.freeze({
var require$$0$7 = ( customer$1 && customer ) || customer$1;
class CustomerMeta extends meta {
- setup_meta() {
+ setupMeta() {
Object.assign(this, require$$0$7);
}
}
@@ -25633,7 +25731,7 @@ var doctype$4 = "DocType";
var is_single$4 = 0;
var istable$1 = 0;
var keyword_fields$4 = [];
-var fields$4 = [{"fieldname":"customer","label":"Customer","fieldtype":"Link","target":"Customer","reqd":1},{"fieldname":"items","label":"Items","fieldtype":"Table","childtype":"Invoice Item","reqd":1},{"fieldname":"total","label":"Total","fieldtype":"Currency","formula":"doc.get_total()","reqd":1}];
+var fields$4 = [{"fieldname":"customer","label":"Customer","fieldtype":"Link","target":"Customer","required":1},{"fieldname":"items","label":"Items","fieldtype":"Table","childtype":"Invoice Item","required":true},{"fieldname":"total","label":"Total","fieldtype":"Currency","formula":"doc.getSum('items', 'amount')","required":true,"disabled":true}];
var invoice = {
name: name$4,
doctype: doctype$4,
@@ -25656,15 +25754,12 @@ var invoice$1 = Object.freeze({
var require$$0$8 = ( invoice$1 && invoice ) || invoice$1;
class InvoiceMeta extends meta {
- setup_meta() {
+ setupMeta() {
Object.assign(this, require$$0$8);
}
}
class Invoice extends document$1 {
- get_total() {
- return this.items.map(d => (d.amount || 0)).reduce((a, b) => a + b, 0);
- }
}
var invoice$2 = {
@@ -25677,7 +25772,7 @@ var doctype$5 = "DocType";
var is_single$5 = 0;
var is_child = 1;
var keyword_fields$5 = [];
-var fields$5 = [{"fieldname":"item","label":"Item","fieldtype":"Link","target":"Item","reqd":1},{"fieldname":"description","label":"Description","fieldtype":"Text","fetch":{"from":"item","value":"description"},"reqd":1},{"fieldname":"quantity","label":"Quantity","fieldtype":"Float","reqd":1},{"fieldname":"rate","label":"Rate","fieldtype":"Currency","reqd":1},{"fieldname":"amount","label":"Amount","fieldtype":"Currency","read_only":1,"formula":"doc.quantity * doc.rate"}];
+var fields$5 = [{"fieldname":"item","label":"Item","fieldtype":"Link","target":"Item","required":1},{"fieldname":"description","label":"Description","fieldtype":"Text","formula":"doc.getFrom('Item', row.item, 'description')","required":1},{"fieldname":"quantity","label":"Quantity","fieldtype":"Float","required":1},{"fieldname":"rate","label":"Rate","fieldtype":"Currency","required":1},{"fieldname":"amount","label":"Amount","fieldtype":"Currency","disabled":1,"formula":"row.quantity * row.rate"}];
var invoice_item = {
name: name$5,
doctype: doctype$5,
@@ -25700,7 +25795,7 @@ var invoice_item$1 = Object.freeze({
var require$$0$9 = ( invoice_item$1 && invoice_item ) || invoice_item$1;
class InvoiceItemMeta extends meta {
- setup_meta() {
+ setupMeta() {
Object.assign(this, require$$0$9);
}
}
@@ -25771,11 +25866,11 @@ client.start({
frappe.modules.todo_client = todo_client;
frappe.modules.account_client = account_client;
- frappe.desk.add_sidebar_item('ToDo', '#list/todo');
- frappe.desk.add_sidebar_item('Accounts', '#list/account');
- frappe.desk.add_sidebar_item('Items', '#list/item');
- frappe.desk.add_sidebar_item('Customers', '#list/customer');
- frappe.desk.add_sidebar_item('Invoice', '#list/invoice');
+ frappe.desk.addSidebarItem('ToDo', '#list/todo');
+ frappe.desk.addSidebarItem('Accounts', '#list/account');
+ frappe.desk.addSidebarItem('Items', '#list/item');
+ frappe.desk.addSidebarItem('Customers', '#list/customer');
+ frappe.desk.addSidebarItem('Invoice', '#list/invoice');
frappe.router.default = '#list/todo';
diff --git a/models/doctype/account/account.json b/models/doctype/account/account.json
index cb8a86fe..85111622 100644
--- a/models/doctype/account/account.json
+++ b/models/doctype/account/account.json
@@ -11,7 +11,7 @@
"fieldname": "name",
"label": "Account Name",
"fieldtype": "Data",
- "reqd": 1
+ "required": 1
},
{
"fieldname": "parent_account",
diff --git a/models/doctype/customer/customer.json b/models/doctype/customer/customer.json
index f1e68fd9..31b3340f 100644
--- a/models/doctype/customer/customer.json
+++ b/models/doctype/customer/customer.json
@@ -11,7 +11,7 @@
"fieldname": "name",
"label": "Name",
"fieldtype": "Data",
- "reqd": 1
+ "required": 1
}
]
}
\ No newline at end of file
diff --git a/models/doctype/invoice/invoice.js b/models/doctype/invoice/invoice.js
index e9be7604..bf06f029 100644
--- a/models/doctype/invoice/invoice.js
+++ b/models/doctype/invoice/invoice.js
@@ -8,9 +8,6 @@ class InvoiceMeta extends BaseMeta {
}
class Invoice extends BaseDocument {
- get_total() {
- return this.items.map(d => (d.amount || 0)).reduce((a, b) => a + b, 0);
- }
}
module.exports = {
diff --git a/models/doctype/invoice/invoice.json b/models/doctype/invoice/invoice.json
index 2ec20ab9..daee9706 100644
--- a/models/doctype/invoice/invoice.json
+++ b/models/doctype/invoice/invoice.json
@@ -10,21 +10,22 @@
"label": "Customer",
"fieldtype": "Link",
"target": "Customer",
- "reqd": 1
+ "required": 1
},
{
"fieldname": "items",
"label": "Items",
"fieldtype": "Table",
"childtype": "Invoice Item",
- "reqd": 1
+ "required": true
},
{
"fieldname": "total",
"label": "Total",
"fieldtype": "Currency",
- "formula": "doc.get_total()",
- "reqd": 1
+ "formula": "doc.getSum('items', 'amount')",
+ "required": true,
+ "disabled": true
}
]
}
\ No newline at end of file
diff --git a/models/doctype/invoice_item/invoice_item.json b/models/doctype/invoice_item/invoice_item.json
index 71a119eb..55477cfe 100644
--- a/models/doctype/invoice_item/invoice_item.json
+++ b/models/doctype/invoice_item/invoice_item.json
@@ -10,33 +10,33 @@
"label": "Item",
"fieldtype": "Link",
"target": "Item",
- "reqd": 1
+ "required": 1
},
{
"fieldname": "description",
"label": "Description",
"fieldtype": "Text",
- "fetch": {"from": "item", "value": "description"},
- "reqd": 1
+ "formula": "doc.getFrom('Item', row.item, 'description')",
+ "required": 1
},
{
"fieldname": "quantity",
"label": "Quantity",
"fieldtype": "Float",
- "reqd": 1
+ "required": 1
},
{
"fieldname": "rate",
"label": "Rate",
"fieldtype": "Currency",
- "reqd": 1
+ "required": 1
},
{
"fieldname": "amount",
"label": "Amount",
"fieldtype": "Currency",
- "read_only": 1,
- "formula": "doc.quantity * doc.rate"
+ "disabled": 1,
+ "formula": "row.quantity * row.rate"
}
]
}
\ No newline at end of file
diff --git a/models/doctype/item/item.json b/models/doctype/item/item.json
index 11329750..2534f1b6 100644
--- a/models/doctype/item/item.json
+++ b/models/doctype/item/item.json
@@ -11,7 +11,7 @@
"fieldname": "name",
"label": "Item Name",
"fieldtype": "Data",
- "reqd": 1
+ "required": 1
},
{
"fieldname": "description",
diff --git a/src/index.js b/src/index.js
index 930733e8..d078fbeb 100644
--- a/src/index.js
+++ b/src/index.js
@@ -17,11 +17,11 @@ client.start({
frappe.modules.todo_client = require('frappejs/models/doctype/todo/todo_client.js');
frappe.modules.account_client = require('../models/doctype/account/account_client.js');
- frappe.desk.add_sidebar_item('ToDo', '#list/todo');
- frappe.desk.add_sidebar_item('Accounts', '#list/account');
- frappe.desk.add_sidebar_item('Items', '#list/item');
- frappe.desk.add_sidebar_item('Customers', '#list/customer');
- frappe.desk.add_sidebar_item('Invoice', '#list/invoice');
+ frappe.desk.addSidebarItem('ToDo', '#list/todo');
+ frappe.desk.addSidebarItem('Accounts', '#list/account');
+ frappe.desk.addSidebarItem('Items', '#list/item');
+ frappe.desk.addSidebarItem('Customers', '#list/customer');
+ frappe.desk.addSidebarItem('Invoice', '#list/invoice');
frappe.router.default = '#list/todo';