mirror of
https://github.com/frappe/books.git
synced 2024-11-08 23:00:56 +00:00
added submit feature and other cleanups
This commit is contained in:
parent
415389bca0
commit
b8d7294465
@ -36,7 +36,7 @@ module.exports = class Database extends Observable {
|
||||
let indexes = [];
|
||||
|
||||
for (let field of meta.getValidFields({ withChildren: false })) {
|
||||
if (this.type_map[field.fieldtype]) {
|
||||
if (this.typeMap[field.fieldtype]) {
|
||||
this.updateColumnDefinition(field, columns, indexes);
|
||||
}
|
||||
}
|
||||
@ -53,7 +53,7 @@ module.exports = class Database extends Observable {
|
||||
}
|
||||
|
||||
updateColumnDefinition(field, columns, indexes) {
|
||||
// return `${df.fieldname} ${this.type_map[df.fieldtype]} ${ ? "PRIMARY KEY" : ""} ${df.required && !df.default ? "NOT NULL" : ""} ${df.default ? `DEFAULT ${df.default}` : ""}`
|
||||
// return `${df.fieldname} ${this.typeMap[df.fieldtype]} ${ ? "PRIMARY KEY" : ""} ${df.required && !df.default ? "NOT NULL" : ""} ${df.default ? `DEFAULT ${df.default}` : ""}`
|
||||
}
|
||||
|
||||
async alterTable(doctype) {
|
||||
@ -74,7 +74,7 @@ module.exports = class Database extends Observable {
|
||||
let meta = frappe.getMeta(doctype);
|
||||
let newColumns = [];
|
||||
for (let field of meta.getValidFields({ withChildren: false })) {
|
||||
if (!tableColumns.includes(field.fieldname) && this.type_map[field.fieldtype]) {
|
||||
if (!tableColumns.includes(field.fieldname) && this.typeMap[field.fieldtype]) {
|
||||
newColumns.push(field);
|
||||
}
|
||||
}
|
||||
@ -399,7 +399,7 @@ module.exports = class Database extends Observable {
|
||||
}
|
||||
|
||||
initTypeMap() {
|
||||
this.type_map = {
|
||||
this.typeMap = {
|
||||
'Currency': 'real'
|
||||
, 'Int': 'integer'
|
||||
, 'Float': 'real'
|
||||
|
@ -119,7 +119,7 @@ module.exports = class HTTPClient extends Observable {
|
||||
}
|
||||
|
||||
initTypeMap() {
|
||||
this.type_map = {
|
||||
this.typeMap = {
|
||||
'Currency': true
|
||||
, 'Int': true
|
||||
, 'Float': true
|
||||
|
@ -11,7 +11,7 @@ module.exports = class mysqlDatabase extends Database{
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.host = host;
|
||||
this.init_type_map();
|
||||
this.init_typeMap();
|
||||
}
|
||||
|
||||
connect(db_name) {
|
||||
@ -53,7 +53,7 @@ module.exports = class mysqlDatabase extends Database{
|
||||
|
||||
|
||||
updateColumnDefinition(df, columns, indexes) {
|
||||
columns.push(`${df.fieldname} ${this.type_map[df.fieldtype]} ${df.reqd && !df.default ? "not null" : ""} ${df.default ? `default '${df.default}'` : ""}`);
|
||||
columns.push(`${df.fieldname} ${this.typeMap[df.fieldtype]} ${df.reqd && !df.default ? "not null" : ""} ${df.default ? `default '${df.default}'` : ""}`);
|
||||
}
|
||||
|
||||
async getTableColumns(doctype) {
|
||||
@ -176,8 +176,8 @@ module.exports = class mysqlDatabase extends Database{
|
||||
}
|
||||
|
||||
|
||||
init_type_map() {
|
||||
this.type_map = {
|
||||
init_typeMap() {
|
||||
this.typeMap = {
|
||||
'Currency': 'real'
|
||||
, 'Int': 'INT'
|
||||
, 'Float': 'decimal(18,6)'
|
||||
|
@ -66,7 +66,7 @@ module.exports = class sqliteDatabase extends Database {
|
||||
}
|
||||
|
||||
getColumnDefinition(field) {
|
||||
let def = `${field.fieldname} ${this.type_map[field.fieldtype]}`;
|
||||
let def = `${field.fieldname} ${this.typeMap[field.fieldtype]}`;
|
||||
if (field.fieldname==='name') {
|
||||
def += ' PRIMARY KEY NOT NULL';
|
||||
}
|
||||
@ -205,7 +205,7 @@ module.exports = class sqliteDatabase extends Database {
|
||||
}
|
||||
|
||||
initTypeMap() {
|
||||
this.type_map = {
|
||||
this.typeMap = {
|
||||
'Currency': 'real'
|
||||
, 'Int': 'integer'
|
||||
, 'Float': 'real'
|
||||
|
@ -27,11 +27,11 @@ module.exports = class FormModal extends Modal {
|
||||
doctype: this.doctype,
|
||||
parent: this.getBody(),
|
||||
container: this,
|
||||
actions: ['submit']
|
||||
actions: ['save']
|
||||
});
|
||||
|
||||
this.form.on('submit', async () => {
|
||||
await this.trigger('submit');
|
||||
this.form.on('save', async () => {
|
||||
await this.trigger('save');
|
||||
this.hide();
|
||||
});
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ module.exports = class FormPage extends Page {
|
||||
doctype: doctype,
|
||||
parent: this.body,
|
||||
container: this,
|
||||
actions: ['submit', 'delete', 'duplicate', 'settings', 'print']
|
||||
actions: ['save', 'delete', 'duplicate', 'settings', 'print']
|
||||
});
|
||||
|
||||
this.on('show', async (params) => {
|
||||
@ -21,11 +21,11 @@ module.exports = class FormPage extends Page {
|
||||
});
|
||||
|
||||
// if name is different after saving, change the route
|
||||
this.form.on('submit', async (params) => {
|
||||
this.form.on('save', async (params) => {
|
||||
let route = frappe.router.get_route();
|
||||
if (this.form.doc.name && !(route && route[2] === this.form.doc.name)) {
|
||||
await frappe.router.setRoute('edit', this.form.doc.doctype, this.form.doc.name);
|
||||
this.form.showAlert('Added', 'success');
|
||||
frappe.ui.showAlert({message: 'Added', color: 'green'});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -36,6 +36,7 @@ module.exports = class PrintPage extends Page {
|
||||
try {
|
||||
this.body.innerHTML = `<div class="print-page">${nunjucks.renderString(this.printFormat.template, context)}</div>`;
|
||||
this.setTitle(doc.name);
|
||||
if (doc.submitted) this.addTitleBadge('✓', 'Submitted');
|
||||
} catch (e) {
|
||||
this.renderError('Template Error', e);
|
||||
throw e;
|
||||
|
@ -3,6 +3,7 @@
|
||||
@import "node_modules/flatpickr/dist/flatpickr";
|
||||
@import "node_modules/flatpickr/dist/themes/airbnb";
|
||||
@import "node_modules/codemirror/lib/codemirror";
|
||||
@import "node_modules/frappe-datatable/dist/frappe-datatable";
|
||||
|
||||
$spacer-1: 0.25rem;
|
||||
$spacer-2: 0.5rem;
|
||||
@ -36,8 +37,8 @@ html {
|
||||
|
||||
.page-head {
|
||||
padding: $spacer-2 $spacer-3;
|
||||
background-color: $gray-800;
|
||||
color: $gray-100;
|
||||
background-color: $gray-100;
|
||||
border-bottom: 1px solid $gray-300;
|
||||
|
||||
.page-title {
|
||||
display: inline-block;
|
||||
@ -56,7 +57,7 @@ html {
|
||||
}
|
||||
|
||||
.form-body {
|
||||
padding: $spacer-3;
|
||||
padding: $spacer-4;
|
||||
|
||||
.form-control.font-weight-bold {
|
||||
background-color: lightyellow;
|
||||
@ -100,6 +101,15 @@ html {
|
||||
padding: $spacer-2 $spacer-3;
|
||||
}
|
||||
|
||||
.bottom-right-float {
|
||||
position: fixed;
|
||||
margin-bottom: 0px;
|
||||
bottom: $spacer-3;
|
||||
right: $spacer-3;
|
||||
max-width: 200px;
|
||||
padding: $spacer-2 $spacer-3;
|
||||
}
|
||||
|
||||
.desk-menu {
|
||||
background-color: $gray-200;
|
||||
|
||||
|
@ -41,6 +41,12 @@ module.exports = {
|
||||
|
||||
make_dropdown(label, parent, btn_class = 'btn-secondary') {
|
||||
return new Dropdown({parent: parent, label:label, btn_class:btn_class});
|
||||
}
|
||||
},
|
||||
|
||||
showAlert({message, color='yellow', timeout=4}) {
|
||||
let alert = this.add('div', 'alert alert-warning bottom-right-float', document.body);
|
||||
alert.innerHTML = `<span class='indicator ${color}'>${message}</span>`;
|
||||
frappe.sleep(timeout).then(() => alert.remove());
|
||||
return alert;
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ class BaseControl {
|
||||
} else {
|
||||
this.setDocValue();
|
||||
}
|
||||
this.setDisabled();
|
||||
}
|
||||
|
||||
renderTemplate() {
|
||||
@ -86,10 +87,12 @@ class BaseControl {
|
||||
|
||||
}
|
||||
|
||||
isDisabled() {
|
||||
return this.disabled || this.formula || (this.doc && this.doc.submitted);
|
||||
}
|
||||
|
||||
setDisabled() {
|
||||
if (this.disabled) {
|
||||
this.input.disabled = true;
|
||||
}
|
||||
this.input.disabled = this.isDisabled();
|
||||
}
|
||||
|
||||
getInputParent() {
|
||||
|
@ -22,6 +22,13 @@ class DateControl extends BaseControl {
|
||||
});
|
||||
}
|
||||
|
||||
setDisabled() {
|
||||
this.input.disabled = this.isDisabled();
|
||||
if (this.flatpickr && this.flatpickr.altInput) {
|
||||
this.flatpickr.altInput.disabled = this.isDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
setInputValue(value) {
|
||||
super.setInputValue(value);
|
||||
this.flatpickr.setDate(value);
|
||||
|
@ -39,7 +39,7 @@ class LinkControl extends BaseControl {
|
||||
formModal.form.doc.set('name', this.input.value);
|
||||
}
|
||||
|
||||
formModal.once('submit', async () => {
|
||||
formModal.once('save', async () => {
|
||||
await this.updateDocValue(formModal.form.doc.name);
|
||||
});
|
||||
}
|
||||
|
@ -55,11 +55,21 @@ class TableControl extends BaseControl {
|
||||
|
||||
setInputValue(value) {
|
||||
this.datatable.refresh(this.getTableData(value));
|
||||
}
|
||||
|
||||
setDisabled() {
|
||||
this.refreshToolbar();
|
||||
}
|
||||
|
||||
getToolbar() {
|
||||
return this.wrapper.querySelector('.table-toolbar');
|
||||
}
|
||||
|
||||
refreshToolbar() {
|
||||
this.wrapper.querySelector('.table-toolbar').classList.toggle('hide', this.disabled ? true : false);
|
||||
const toolbar = this.wrapper.querySelector('.table-toolbar');
|
||||
if (toolbar) {
|
||||
toolbar.classList.toggle('hide', this.isDisabled() ? true : false);
|
||||
}
|
||||
}
|
||||
|
||||
getTableData(value) {
|
||||
@ -68,6 +78,9 @@ class TableControl extends BaseControl {
|
||||
|
||||
getTableInput(colIndex, rowIndex, value, parent) {
|
||||
let field = this.datatable.getColumn(colIndex).field;
|
||||
if (field.disabled || field.forumla || this.isDisabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (field.fieldtype==='Text') {
|
||||
// text in modal
|
||||
@ -92,6 +105,7 @@ class TableControl extends BaseControl {
|
||||
return control.setInputValue(control.doc[column.id]);
|
||||
},
|
||||
setValue: async (value, rowIndex, column) => {
|
||||
this.doc._dirty = true;
|
||||
control.handleChange();
|
||||
},
|
||||
getValue: () => {
|
||||
@ -127,7 +141,7 @@ class TableControl extends BaseControl {
|
||||
id: field.fieldname,
|
||||
field: field,
|
||||
content: field.label,
|
||||
editable: (this.disabled || field.disabled) ? false : true,
|
||||
editable: true,
|
||||
sortable: false,
|
||||
resizable: true,
|
||||
dropdown: false,
|
||||
|
@ -43,10 +43,12 @@ module.exports = class BaseForm extends Observable {
|
||||
}
|
||||
|
||||
makeToolbar() {
|
||||
if (this.actions.includes('submit')) {
|
||||
this.container.addButton(frappe._("Save"), 'primary', async (event) => {
|
||||
await this.submit();
|
||||
})
|
||||
if (this.actions.includes('save')) {
|
||||
this.makeSaveButton();
|
||||
|
||||
if (this.meta.isSubmittable) {
|
||||
this.makeSubmitButton();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.meta.print && this.actions.includes('print')) {
|
||||
@ -67,7 +69,6 @@ module.exports = class BaseForm extends Observable {
|
||||
let menu = this.container.getDropdown(frappe._('Menu'));
|
||||
menu.addItem(frappe._('Duplicate'), async () => {
|
||||
let newDoc = await frappe.getDuplicate(this.doc);
|
||||
console.log(newDoc);
|
||||
await frappe.router.setRoute('edit', newDoc.doctype, newDoc.name);
|
||||
newDoc.set('name', '');
|
||||
});
|
||||
@ -82,13 +83,38 @@ module.exports = class BaseForm extends Observable {
|
||||
|
||||
}
|
||||
|
||||
makeSaveButton() {
|
||||
this.saveButton = this.container.addButton(frappe._("Save"), 'primary', async (event) => {
|
||||
await this.save();
|
||||
});
|
||||
this.on('change', () => {
|
||||
const show = this.doc._dirty && !this.doc.submitted;
|
||||
this.saveButton.classList.toggle('hide', !show);
|
||||
});
|
||||
}
|
||||
|
||||
makeSubmitButton() {
|
||||
this.submitButton = this.container.addButton(frappe._("Submit"), 'primary', async (event) => {
|
||||
await this.submit();
|
||||
});
|
||||
this.on('change', () => {
|
||||
const show = this.meta.isSubmittable && !this.doc._dirty && !this.doc.submitted;
|
||||
this.submitButton.classList.toggle('hide', !show);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
bindKeyboard() {
|
||||
keyboard.bindKey(this.form, 'ctrl+s', (e) => {
|
||||
if (document.activeElement) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
e.preventDefault();
|
||||
this.submit();
|
||||
if (this.doc._notInserted || this.doc._dirty) {
|
||||
this.save();
|
||||
} else {
|
||||
if (this.meta.isSubmittable && !this.doc.submitted) this.submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -101,6 +127,7 @@ module.exports = class BaseForm extends Observable {
|
||||
await this.doc.set('name', '');
|
||||
}
|
||||
this.setTitle();
|
||||
frappe._curFrm = this;
|
||||
}
|
||||
|
||||
setTitle() {
|
||||
@ -113,6 +140,7 @@ module.exports = class BaseForm extends Observable {
|
||||
} else {
|
||||
this.container.setTitle(this.doc.name);
|
||||
}
|
||||
if (this.doc.submitted) this.container.addTitleBadge('✓', frappe._('Submitted'));
|
||||
}
|
||||
|
||||
async bindEvents(doc) {
|
||||
@ -120,17 +148,16 @@ module.exports = class BaseForm extends Observable {
|
||||
// stop listening to the old doc
|
||||
this.doc.off(this.docListener);
|
||||
}
|
||||
this.clearAlert();
|
||||
this.doc = doc;
|
||||
for (let control of this.controlList) {
|
||||
control.bind(this.doc);
|
||||
}
|
||||
|
||||
this.setupChangeListener();
|
||||
this.setupDocListener();
|
||||
this.trigger('use', {doc:doc});
|
||||
}
|
||||
|
||||
setupChangeListener() {
|
||||
setupDocListener() {
|
||||
// refresh value in control
|
||||
this.docListener = (params) => {
|
||||
if (params.fieldname) {
|
||||
@ -143,10 +170,12 @@ module.exports = class BaseForm extends Observable {
|
||||
// multiple values changed
|
||||
this.refresh();
|
||||
}
|
||||
this.trigger('change');
|
||||
this.form.classList.remove('was-validated');
|
||||
};
|
||||
|
||||
this.doc.on('change', this.docListener);
|
||||
this.trigger('change');
|
||||
}
|
||||
|
||||
checkValidity() {
|
||||
@ -165,7 +194,18 @@ module.exports = class BaseForm extends Observable {
|
||||
return validity;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
for(let control of this.controlList) {
|
||||
control.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
async submit() {
|
||||
this.doc.submitted = 1;
|
||||
await this.save();
|
||||
}
|
||||
|
||||
async save() {
|
||||
if (!this.checkValidity()) {
|
||||
this.form.classList.add('was-validated');
|
||||
return;
|
||||
@ -176,44 +216,23 @@ module.exports = class BaseForm extends Observable {
|
||||
} else {
|
||||
await this.doc.update();
|
||||
}
|
||||
this.showAlert('Saved', 'success');
|
||||
frappe.ui.showAlert({message: frappe._('Saved'), color: 'green'});
|
||||
this.refresh();
|
||||
this.trigger('change');
|
||||
} catch (e) {
|
||||
this.showAlert('Failed', 'danger');
|
||||
frappe.ui.showAlert({message: frappe._('Failed'), color: 'red'});
|
||||
return;
|
||||
}
|
||||
await this.trigger('submit');
|
||||
await this.trigger('save');
|
||||
}
|
||||
|
||||
async delete() {
|
||||
try {
|
||||
await this.doc.delete();
|
||||
this.showAlert('Deleted', 'success');
|
||||
frappe.ui.showAlert({message: frappe._('Deleted'), color: 'green'});
|
||||
this.trigger('delete');
|
||||
} catch (e) {
|
||||
this.showAlert(e, 'danger');
|
||||
frappe.ui.showAlert({message: e, color: 'red'});
|
||||
}
|
||||
}
|
||||
|
||||
refresh() {
|
||||
for(let control of this.controlList) {
|
||||
control.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
showAlert(message, type, clear_after = 5) {
|
||||
this.clearAlert();
|
||||
this.alert = frappe.ui.add('div', `alert alert-${type}`, this.body);
|
||||
this.alert.textContent = message;
|
||||
setTimeout(() => {
|
||||
this.clearAlert();
|
||||
}, clear_after * 1000);
|
||||
}
|
||||
|
||||
clearAlert() {
|
||||
if (this.alert) {
|
||||
frappe.ui.remove(this.alert);
|
||||
this.alert = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -58,9 +58,11 @@ module.exports = class BaseList {
|
||||
}
|
||||
|
||||
async getData() {
|
||||
let fields = this.getFields();
|
||||
this.updateStandardFields(fields);
|
||||
return await frappe.db.getAll({
|
||||
doctype: this.doctype,
|
||||
fields: this.getFields(),
|
||||
fields: fields,
|
||||
filters: this.getFilters(),
|
||||
start: this.start,
|
||||
limit: this.pageLength + 1
|
||||
@ -68,7 +70,13 @@ module.exports = class BaseList {
|
||||
}
|
||||
|
||||
getFields() {
|
||||
return ['name'];
|
||||
return [];
|
||||
}
|
||||
|
||||
updateStandardFields(fields) {
|
||||
if (!fields.includes('name')) fields.push('name');
|
||||
if (!fields.includes('modified')) fields.push('modified');
|
||||
if (this.meta.isSubmittable && !fields.includes('submitted')) fields.push('submitted');
|
||||
}
|
||||
|
||||
async append() {
|
||||
|
@ -35,6 +35,11 @@ module.exports = class Page extends Observable {
|
||||
}
|
||||
}
|
||||
|
||||
addTitleBadge(message, title='', style='secondary') {
|
||||
this.titleElement.innerHTML += ` <span class='badge badge-${style}' title='${title}'>
|
||||
${message}</span>`;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.parent.activePage = null;
|
||||
this.wrapper.classList.add('hide');
|
||||
|
@ -12,6 +12,7 @@ class ValidationError extends BaseError {
|
||||
module.exports = {
|
||||
ValidationError: ValidationError,
|
||||
ValueError: class ValueError extends ValidationError { },
|
||||
Conflict: class Conflict extends ValidationError { },
|
||||
NotFound: class NotFound extends BaseError {
|
||||
constructor(...params) { super(404, ...params); }
|
||||
},
|
||||
|
2
index.js
2
index.js
@ -104,7 +104,7 @@ module.exports = {
|
||||
async getDuplicate(doc) {
|
||||
const newDoc = await this.getNewDoc(doc.doctype);
|
||||
for (let field of this.getMeta(doc.doctype).getValidFields()) {
|
||||
if (field.fieldname === 'name') continue;
|
||||
if (['name', 'submitted'].includes(field.fieldname)) continue;
|
||||
if (field.fieldtype === 'Table') {
|
||||
newDoc[field.fieldname] = (doc[field.fieldname] || []).map(d => {
|
||||
let newd = Object.assign({}, d);
|
||||
|
@ -128,16 +128,17 @@ module.exports = class BaseDocument extends Observable {
|
||||
}
|
||||
|
||||
setStandardValues() {
|
||||
let now = new Date();
|
||||
if (this.docstatus === null || this.docstatus === undefined) {
|
||||
this.docstatus = 0;
|
||||
// set standard values on server-side only
|
||||
if (frappe.isServer) {
|
||||
let now = new Date();
|
||||
if (!this.submitted) this.submitted = 0;
|
||||
if (!this.owner) {
|
||||
this.owner = frappe.session.user;
|
||||
this.creation = now;
|
||||
}
|
||||
this.modifieldBy = frappe.session.user;
|
||||
this.modified = now;
|
||||
}
|
||||
if (!this.owner) {
|
||||
this.owner = frappe.session.user;
|
||||
this.creation = now;
|
||||
}
|
||||
this.modifieldBy = frappe.session.user;
|
||||
this.modified = now;
|
||||
}
|
||||
|
||||
async load() {
|
||||
@ -178,6 +179,30 @@ module.exports = class BaseDocument extends Observable {
|
||||
}
|
||||
}
|
||||
|
||||
async compareWithCurrentDoc() {
|
||||
if (frappe.isServer && !this._notInserted) {
|
||||
let currentDoc = await frappe.db.get(this.doctype, this.name);
|
||||
|
||||
// check for conflict
|
||||
if (currentDoc && this.modified != currentDoc.modified) {
|
||||
throw new frappe.errors.Conflict(frappe._('Document {0} {1} has been modified after loading', [this.doctype, this.name]));
|
||||
}
|
||||
|
||||
if (this.submitted && !this.meta.isSubmittable) {
|
||||
throw new frappe.errors.ValidationError(frappe._('Document type {1} is not submittable', [this.doctype]));
|
||||
}
|
||||
|
||||
// set submit action flag
|
||||
if (this.submitted && !currentDoc.submitted) {
|
||||
this.submitAction = true;
|
||||
}
|
||||
|
||||
if (currentDoc.submitted && !this.submitted) {
|
||||
this.unSubmitAction = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async applyFormula() {
|
||||
if (!this.meta.hasFormula()) {
|
||||
return false;
|
||||
@ -219,20 +244,25 @@ module.exports = class BaseDocument extends Observable {
|
||||
|
||||
async insert() {
|
||||
await this.commit();
|
||||
await this.trigger('before_insert');
|
||||
await this.trigger('beforeInsert');
|
||||
this.syncValues(await frappe.db.insert(this.doctype, this.getValidDict()));
|
||||
await this.trigger('after_insert');
|
||||
await this.trigger('after_save');
|
||||
await this.trigger('afterInsert');
|
||||
await this.trigger('afterSave');
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
async update() {
|
||||
await this.compareWithCurrentDoc();
|
||||
await this.commit();
|
||||
await this.trigger('before_update');
|
||||
await this.trigger('beforeUpdate');
|
||||
if (this.submitAction) this.trigger('beforeSubmit');
|
||||
if (this.unSubmitAction) this.trigger('beforeUnSubmit');
|
||||
this.syncValues(await frappe.db.update(this.doctype, this.getValidDict()));
|
||||
await this.trigger('after_update');
|
||||
await this.trigger('after_save');
|
||||
await this.trigger('afterUpdate');
|
||||
await this.trigger('afterSave');
|
||||
if (this.submitAction) this.trigger('afterSubmit');
|
||||
if (this.unSubmitAction) this.trigger('afterUnSubmit');
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -243,6 +273,8 @@ module.exports = class BaseDocument extends Observable {
|
||||
await this.trigger('after_delete');
|
||||
}
|
||||
|
||||
// trigger methods on the class if they match
|
||||
// with the trigger name
|
||||
async trigger(event, params) {
|
||||
if (this[event]) {
|
||||
await this[event](params);
|
||||
|
@ -15,12 +15,12 @@ module.exports = {
|
||||
let next = await series.next()
|
||||
return prefix + next;
|
||||
},
|
||||
common_fields: [
|
||||
commonFields: [
|
||||
{
|
||||
fieldname: 'name', fieldtype: 'Data', required: 1
|
||||
}
|
||||
],
|
||||
parent_fields: [
|
||||
parentFields: [
|
||||
{
|
||||
fieldname: 'owner', fieldtype: 'Data', required: 1
|
||||
},
|
||||
@ -35,12 +35,9 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
fieldname: 'keywords', fieldtype: 'Text'
|
||||
},
|
||||
{
|
||||
fieldname: 'docstatus', fieldtype: 'Int', required: 1, default: 0
|
||||
}
|
||||
],
|
||||
child_fields: [
|
||||
childFields: [
|
||||
{
|
||||
fieldname: 'idx', fieldtype: 'Int', required: 1
|
||||
},
|
||||
|
@ -69,36 +69,40 @@ module.exports = class BaseMeta extends BaseDocument {
|
||||
}
|
||||
|
||||
getValidFields({ withChildren = true } = {}) {
|
||||
if (!this._valid_fields) {
|
||||
if (!this._validFields) {
|
||||
|
||||
this._valid_fields = [];
|
||||
this._valid_fields_withChildren = [];
|
||||
this._validFields = [];
|
||||
this._validFieldsWithChildren = [];
|
||||
|
||||
const _add = (field) => {
|
||||
this._valid_fields.push(field);
|
||||
this._valid_fields_withChildren.push(field);
|
||||
this._validFields.push(field);
|
||||
this._validFieldsWithChildren.push(field);
|
||||
}
|
||||
|
||||
const doctype_fields = this.fields.map((field) => field.fieldname);
|
||||
|
||||
// standard fields
|
||||
for (let field of model.common_fields) {
|
||||
if (frappe.db.type_map[field.fieldtype] && !doctype_fields.includes(field.fieldname)) {
|
||||
for (let field of model.commonFields) {
|
||||
if (frappe.db.typeMap[field.fieldtype] && !doctype_fields.includes(field.fieldname)) {
|
||||
_add(field);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isSubmittable) {
|
||||
_add({fieldtype:'Check', fieldname: 'submitted', label: frappe._('Submitted')})
|
||||
}
|
||||
|
||||
if (this.isChild) {
|
||||
// child fields
|
||||
for (let field of model.child_fields) {
|
||||
if (frappe.db.type_map[field.fieldtype] && !doctype_fields.includes(field.fieldname)) {
|
||||
for (let field of model.childFields) {
|
||||
if (frappe.db.typeMap[field.fieldtype] && !doctype_fields.includes(field.fieldname)) {
|
||||
_add(field);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// parent fields
|
||||
for (let field of model.parent_fields) {
|
||||
if (frappe.db.type_map[field.fieldtype] && !doctype_fields.includes(field.fieldname)) {
|
||||
for (let field of model.parentFields) {
|
||||
if (frappe.db.typeMap[field.fieldtype] && !doctype_fields.includes(field.fieldname)) {
|
||||
_add(field);
|
||||
}
|
||||
}
|
||||
@ -106,7 +110,7 @@ module.exports = class BaseMeta extends BaseDocument {
|
||||
|
||||
// doctype fields
|
||||
for (let field of this.fields) {
|
||||
let include = frappe.db.type_map[field.fieldtype];
|
||||
let include = frappe.db.typeMap[field.fieldtype];
|
||||
|
||||
if (include) {
|
||||
_add(field);
|
||||
@ -114,15 +118,15 @@ module.exports = class BaseMeta extends BaseDocument {
|
||||
|
||||
// include tables if (withChildren = True)
|
||||
if (!include && field.fieldtype === 'Table') {
|
||||
this._valid_fields_withChildren.push(field);
|
||||
this._validFieldsWithChildren.push(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (withChildren) {
|
||||
return this._valid_fields_withChildren;
|
||||
return this._validFieldsWithChildren;
|
||||
} else {
|
||||
return this._valid_fields;
|
||||
return this._validFields;
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,12 +166,13 @@ module.exports = class BaseMeta extends BaseDocument {
|
||||
|
||||
setDefaultIndicators() {
|
||||
if (!this.indicators) {
|
||||
this.indicators = {
|
||||
key: 'docstatus',
|
||||
colors: {
|
||||
0: 'gray',
|
||||
1: 'blue',
|
||||
2: 'red'
|
||||
if (this.isSubmittable) {
|
||||
this.indicators = {
|
||||
key: 'submitted',
|
||||
colors: {
|
||||
0: 'gray',
|
||||
1: 'blue'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,9 +182,13 @@ module.exports = class BaseMeta extends BaseDocument {
|
||||
if (frappe.isDirty(this.name, doc.name)) {
|
||||
return 'orange';
|
||||
} else {
|
||||
let value = doc[this.indicators.key];
|
||||
if (value) {
|
||||
return this.indicators.colors[value] || 'gray';
|
||||
if (this.indicators) {
|
||||
let value = doc[this.indicators.key];
|
||||
if (value) {
|
||||
return this.indicators.colors[value] || 'gray';
|
||||
} else {
|
||||
return 'gray';
|
||||
}
|
||||
} else {
|
||||
return 'gray';
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ module.exports = {
|
||||
// listen
|
||||
frappe.app = app;
|
||||
frappe.server = server;
|
||||
frappe.isServer = true;
|
||||
|
||||
server.listen(frappe.config.port);
|
||||
},
|
||||
|
@ -15,10 +15,10 @@ module.exports = {
|
||||
|
||||
_(text, args) {
|
||||
// should return translated text
|
||||
return this.string_replace(text, args);
|
||||
return this.stringReplace(text, args);
|
||||
},
|
||||
|
||||
string_replace(str, args) {
|
||||
stringReplace(str, args) {
|
||||
if (!Array.isArray(args)) {
|
||||
args = [args];
|
||||
}
|
||||
|
@ -1,38 +1,32 @@
|
||||
module.exports = class Observable {
|
||||
constructor() {
|
||||
this._isHot = {};
|
||||
this._eventQueue = {};
|
||||
this._observable = {
|
||||
isHot: {},
|
||||
eventQueue: {},
|
||||
listeners: {},
|
||||
onceListeners: {}
|
||||
}
|
||||
}
|
||||
|
||||
on(event, listener) {
|
||||
this._addListener('_listeners', event, listener);
|
||||
if (this._socketClient) {
|
||||
this._socketClient.on(event, listener);
|
||||
this._addListener('listeners', event, listener);
|
||||
if (this._observable.socketClient) {
|
||||
this._observable.socketClient.on(event, listener);
|
||||
}
|
||||
}
|
||||
|
||||
// remove listener
|
||||
off(event, listener) {
|
||||
for (let type of ['_listeners', '_onceListeners']) {
|
||||
let index = this[type] && this[type][event] && this[type][event].indexOf(listener);
|
||||
for (let type of ['listeners', 'onceListeners']) {
|
||||
let index = this._observable[type][event] && this._observable[type][event].indexOf(listener);
|
||||
if (index) {
|
||||
this[type][event].splice(index, 1);
|
||||
this._observable[type][event].splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
once(event, listener) {
|
||||
this._addListener('_onceListeners', event, listener);
|
||||
}
|
||||
|
||||
bindSocketClient(socket) {
|
||||
// also send events with sockets
|
||||
this._socketClient = socket;
|
||||
}
|
||||
|
||||
bindSocketServer(socket) {
|
||||
// also send events with sockets
|
||||
this._socketServer = socket;
|
||||
this._addListener('onceListeners', event, listener);
|
||||
}
|
||||
|
||||
async trigger(event, params, throttle=false) {
|
||||
@ -45,38 +39,58 @@ module.exports = class Observable {
|
||||
}
|
||||
|
||||
async _executeTriggers(event, params) {
|
||||
await this._triggerEvent('_listeners', event, params);
|
||||
await this._triggerEvent('_onceListeners', event, params);
|
||||
let response = await this._triggerEvent('listeners', event, params);
|
||||
if (response === false) return false;
|
||||
|
||||
if (this._socketServer) {
|
||||
this._socketServer.emit(event, params);
|
||||
response = await this._triggerEvent('onceListeners', event, params);
|
||||
if (response === false) return false;
|
||||
|
||||
// emit via socket
|
||||
if (this._observable.socketServer) {
|
||||
this._observable.socketServer.emit(event, params);
|
||||
}
|
||||
|
||||
// clear once-listeners
|
||||
if (this._onceListeners && this._onceListeners[event]) {
|
||||
delete this._onceListeners[event];
|
||||
if (this._observable.onceListeners && this._observable.onceListeners[event]) {
|
||||
delete this._observable.onceListeners[event];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
clearListeners() {
|
||||
this._observable.listeners = {};
|
||||
this._observable.onceListeners = {};
|
||||
}
|
||||
|
||||
bindSocketClient(socket) {
|
||||
// also send events with sockets
|
||||
this._observable.socketClient = socket;
|
||||
}
|
||||
|
||||
bindSocketServer(socket) {
|
||||
// also send events with sockets
|
||||
this._observable.socketServer = socket;
|
||||
}
|
||||
|
||||
_throttled(event, params, throttle) {
|
||||
if (this._isHot[event]) {
|
||||
if (this._observable.isHot[event]) {
|
||||
// hot, add to queue
|
||||
if (!this._eventQueue[event]) this._eventQueue[event] = [];
|
||||
this._eventQueue[event].push(params);
|
||||
if (!this._observable.eventQueue[event]) this._observable.eventQueue[event] = [];
|
||||
this._observable.eventQueue[event].push(params);
|
||||
|
||||
// aleady hot, quit
|
||||
return true;
|
||||
}
|
||||
this._isHot[event] = true;
|
||||
this._observable.isHot[event] = true;
|
||||
|
||||
// cool-off
|
||||
setTimeout(() => {
|
||||
this._isHot[event] = false;
|
||||
this._observable.isHot[event] = false;
|
||||
|
||||
// flush queue
|
||||
if (this._eventQueue[event]) {
|
||||
let _queuedParams = this._eventQueue[event];
|
||||
this._eventQueue[event] = null;
|
||||
if (this._observable.eventQueue[event]) {
|
||||
let _queuedParams = this._observable.eventQueue[event];
|
||||
this._observable.eventQueue[event] = null;
|
||||
this._executeTriggers(event, _queuedParams);
|
||||
}
|
||||
}, throttle);
|
||||
@ -84,26 +98,18 @@ module.exports = class Observable {
|
||||
return false;
|
||||
}
|
||||
|
||||
_addListener(name, event, listener) {
|
||||
if (!this[name]) {
|
||||
this[name] = {};
|
||||
_addListener(type, event, listener) {
|
||||
if (!this._observable[type][event]) {
|
||||
this._observable[type][event] = [];
|
||||
}
|
||||
if (!this[name][event]) {
|
||||
this[name][event] = [];
|
||||
}
|
||||
this[name][event].push(listener);
|
||||
this._observable[type][event].push(listener);
|
||||
}
|
||||
|
||||
async _triggerEvent(name, event, params) {
|
||||
if (this[name] && this[name][event]) {
|
||||
for (let listener of this[name][event]) {
|
||||
async _triggerEvent(type, event, params) {
|
||||
if (this._observable[type][event]) {
|
||||
for (let listener of this._observable[type][event]) {
|
||||
await listener(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearListeners() {
|
||||
this._listeners = {};
|
||||
this._onceListeners = {};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user