2018-01-16 06:09:17 +00:00
|
|
|
const frappe = require('frappejs');
|
2018-01-12 12:25:07 +00:00
|
|
|
const controls = require('./controls');
|
2018-01-31 10:13:33 +00:00
|
|
|
const Observable = require('frappejs/utils/observable');
|
2018-02-13 10:42:44 +00:00
|
|
|
const keyboard = require('frappejs/client/ui/keyboard');
|
2018-01-12 12:25:07 +00:00
|
|
|
|
2018-01-31 10:13:33 +00:00
|
|
|
module.exports = class BaseForm extends Observable {
|
2018-02-13 10:42:44 +00:00
|
|
|
constructor({doctype, parent, submit_label='Submit', container}) {
|
2018-01-31 10:13:33 +00:00
|
|
|
super();
|
2018-02-08 09:38:47 +00:00
|
|
|
Object.assign(this, arguments[0]);
|
2018-01-12 12:25:07 +00:00
|
|
|
this.controls = {};
|
2018-02-08 09:38:47 +00:00
|
|
|
this.controlList = [];
|
2018-03-05 16:45:21 +00:00
|
|
|
this.sections = [];
|
2018-01-12 12:25:07 +00:00
|
|
|
|
2018-02-08 06:46:38 +00:00
|
|
|
this.meta = frappe.getMeta(this.doctype);
|
2018-01-24 11:52:05 +00:00
|
|
|
if (this.setup) {
|
|
|
|
this.setup();
|
|
|
|
}
|
2018-01-12 12:25:07 +00:00
|
|
|
this.make();
|
|
|
|
}
|
|
|
|
|
|
|
|
make() {
|
|
|
|
if (this.body || !this.parent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.body = frappe.ui.add('div', 'form-body', this.parent);
|
2018-02-08 09:38:47 +00:00
|
|
|
this.makeToolbar();
|
2018-01-12 12:25:07 +00:00
|
|
|
|
2018-02-09 12:55:55 +00:00
|
|
|
this.form = frappe.ui.add('form', 'form-container', this.body);
|
|
|
|
this.form.onValidate = true;
|
|
|
|
|
2018-03-05 16:45:21 +00:00
|
|
|
this.makeLayout();
|
2018-02-13 10:42:44 +00:00
|
|
|
this.bindKeyboard();
|
2018-02-09 12:55:55 +00:00
|
|
|
}
|
|
|
|
|
2018-03-05 16:45:21 +00:00
|
|
|
makeLayout() {
|
|
|
|
if (this.meta.layout) {
|
|
|
|
for (let section of this.meta.layout) {
|
|
|
|
this.makeSection(section);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.makeControls(this.meta.fields);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
makeSection(section) {
|
|
|
|
const sectionElement = frappe.ui.add('div', 'form-section', this.form);
|
|
|
|
if (section.columns) {
|
|
|
|
sectionElement.classList.add('row');
|
|
|
|
for (let column of section.columns) {
|
|
|
|
let columnElement = frappe.ui.add('div', 'col', sectionElement);
|
|
|
|
this.makeControls(this.getFieldsFromLayoutElement(column.fields), columnElement);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.makeControls(this.getFieldsFromLayoutElement(section.fields), sectionElement);
|
|
|
|
}
|
|
|
|
this.sections.push(sectionElement);
|
|
|
|
}
|
|
|
|
|
|
|
|
getFieldsFromLayoutElement(fields) {
|
|
|
|
return this.meta.fields.filter(d => fields.includes(d.fieldname));
|
|
|
|
}
|
|
|
|
|
|
|
|
makeControls(fields, parent) {
|
|
|
|
for(let field of fields) {
|
2018-02-09 12:55:55 +00:00
|
|
|
if (!field.hidden && controls.getControlClass(field.fieldtype)) {
|
2018-03-05 16:45:21 +00:00
|
|
|
let control = controls.makeControl({field: field, form: this, parent: parent});
|
2018-02-08 09:38:47 +00:00
|
|
|
this.controlList.push(control);
|
2018-02-06 17:14:07 +00:00
|
|
|
this.controls[field.fieldname] = control;
|
2018-01-12 12:25:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-08 09:38:47 +00:00
|
|
|
makeToolbar() {
|
2018-02-27 16:29:14 +00:00
|
|
|
if (this.actions.includes('save')) {
|
|
|
|
this.makeSaveButton();
|
|
|
|
|
|
|
|
if (this.meta.isSubmittable) {
|
|
|
|
this.makeSubmitButton();
|
2018-03-05 16:45:21 +00:00
|
|
|
this.makeRevertButton();
|
2018-02-27 16:29:14 +00:00
|
|
|
}
|
2018-02-13 10:42:44 +00:00
|
|
|
}
|
|
|
|
|
2018-02-21 09:38:56 +00:00
|
|
|
if (this.meta.print && this.actions.includes('print')) {
|
|
|
|
let menu = this.container.getDropdown(frappe._('Menu'));
|
|
|
|
menu.addItem(frappe._("Print"), async (e) => {
|
|
|
|
await frappe.router.setRoute('print', this.doctype, this.doc.name);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-02-14 12:50:56 +00:00
|
|
|
if (!this.meta.isSingle && this.actions.includes('delete')) {
|
|
|
|
let menu = this.container.getDropdown(frappe._('Menu'));
|
|
|
|
menu.addItem(frappe._("Delete"), async (e) => {
|
2018-02-20 09:53:38 +00:00
|
|
|
await this.delete();
|
2018-02-13 10:42:44 +00:00
|
|
|
});
|
|
|
|
}
|
2018-02-14 12:50:56 +00:00
|
|
|
|
|
|
|
if (!this.meta.isSingle && this.actions.includes('duplicate')) {
|
|
|
|
let menu = this.container.getDropdown(frappe._('Menu'));
|
|
|
|
menu.addItem(frappe._('Duplicate'), async () => {
|
|
|
|
let newDoc = await frappe.getDuplicate(this.doc);
|
|
|
|
await frappe.router.setRoute('edit', newDoc.doctype, newDoc.name);
|
|
|
|
newDoc.set('name', '');
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.meta.settings && this.actions.includes('settings')) {
|
|
|
|
let menu = this.container.getDropdown(frappe._('Menu'));
|
|
|
|
menu.addItem(frappe._('Settings...'), () => {
|
2018-02-16 13:13:46 +00:00
|
|
|
frappe.desk.showFormModal(this.meta.settings, this.meta.settings);
|
2018-02-14 12:50:56 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-02-13 10:42:44 +00:00
|
|
|
}
|
2018-01-23 08:00:29 +00:00
|
|
|
|
2018-02-27 16:29:14 +00:00
|
|
|
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);
|
|
|
|
});
|
2018-03-05 16:45:21 +00:00
|
|
|
}
|
2018-02-27 16:29:14 +00:00
|
|
|
|
2018-03-05 16:45:21 +00:00
|
|
|
makeRevertButton() {
|
|
|
|
this.revertButton = this.container.addButton(frappe._("Revert"), 'secondary', async (event) => {
|
|
|
|
await this.revert();
|
|
|
|
});
|
|
|
|
this.on('change', () => {
|
|
|
|
const show = this.meta.isSubmittable && !this.doc._dirty && this.doc.submitted;
|
|
|
|
this.revertButton.classList.toggle('hide', !show);
|
|
|
|
});
|
2018-02-27 16:29:14 +00:00
|
|
|
}
|
|
|
|
|
2018-02-13 10:42:44 +00:00
|
|
|
bindKeyboard() {
|
|
|
|
keyboard.bindKey(this.form, 'ctrl+s', (e) => {
|
|
|
|
if (document.activeElement) {
|
|
|
|
document.activeElement.blur();
|
|
|
|
}
|
|
|
|
e.preventDefault();
|
2018-02-27 16:29:14 +00:00
|
|
|
if (this.doc._notInserted || this.doc._dirty) {
|
|
|
|
this.save();
|
|
|
|
} else {
|
|
|
|
if (this.meta.isSubmittable && !this.doc.submitted) this.submit();
|
|
|
|
}
|
2018-01-12 12:25:07 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-02-13 10:42:44 +00:00
|
|
|
async setDoc(doctype, name) {
|
|
|
|
this.doc = await frappe.getDoc(doctype, name);
|
|
|
|
this.bindEvents(this.doc);
|
|
|
|
if (this.doc._notInserted && !this.doc._nameCleared) {
|
|
|
|
this.doc._nameCleared = true;
|
|
|
|
// flag so that name is cleared only once
|
|
|
|
await this.doc.set('name', '');
|
|
|
|
}
|
2018-02-15 09:53:28 +00:00
|
|
|
this.setTitle();
|
2018-02-27 16:29:14 +00:00
|
|
|
frappe._curFrm = this;
|
2018-02-15 09:53:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
setTitle() {
|
2018-02-16 13:13:46 +00:00
|
|
|
const doctypeLabel = this.doc.meta.label || this.doc.meta.name;
|
|
|
|
|
2018-03-05 16:45:21 +00:00
|
|
|
if (this.doc.meta.isSingle || this.doc.meta.naming === 'random') {
|
2018-02-16 13:13:46 +00:00
|
|
|
this.container.setTitle(doctypeLabel);
|
2018-02-15 09:53:28 +00:00
|
|
|
} else if (this.doc._notInserted) {
|
2018-02-16 13:13:46 +00:00
|
|
|
this.container.setTitle(frappe._('New {0}', doctypeLabel));
|
2018-02-15 09:53:28 +00:00
|
|
|
} else {
|
2018-02-22 07:36:28 +00:00
|
|
|
this.container.setTitle(this.doc.name);
|
2018-02-15 09:53:28 +00:00
|
|
|
}
|
2018-03-05 16:45:21 +00:00
|
|
|
if (this.doc.submitted) {
|
|
|
|
this.container.addTitleBadge('✓', frappe._('Submitted'));
|
|
|
|
}
|
2018-02-13 10:42:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async bindEvents(doc) {
|
2018-02-22 11:21:42 +00:00
|
|
|
if (this.doc && this.docListener) {
|
|
|
|
// stop listening to the old doc
|
|
|
|
this.doc.off(this.docListener);
|
2018-01-12 12:25:07 +00:00
|
|
|
}
|
|
|
|
this.doc = doc;
|
2018-02-08 09:38:47 +00:00
|
|
|
for (let control of this.controlList) {
|
2018-01-12 12:25:07 +00:00
|
|
|
control.bind(this.doc);
|
|
|
|
}
|
2018-01-25 10:04:48 +00:00
|
|
|
|
2018-02-27 16:29:14 +00:00
|
|
|
this.setupDocListener();
|
2018-02-09 12:55:55 +00:00
|
|
|
this.trigger('use', {doc:doc});
|
|
|
|
}
|
|
|
|
|
2018-02-27 16:29:14 +00:00
|
|
|
setupDocListener() {
|
2018-01-25 10:04:48 +00:00
|
|
|
// refresh value in control
|
2018-02-22 11:21:42 +00:00
|
|
|
this.docListener = (params) => {
|
2018-02-08 11:45:32 +00:00
|
|
|
if (params.fieldname) {
|
|
|
|
// only single value changed
|
|
|
|
let control = this.controls[params.fieldname];
|
|
|
|
if (control && control.getInputValue() !== control.format(params.fieldname)) {
|
2018-02-19 06:31:07 +00:00
|
|
|
control.refresh();
|
2018-02-08 11:45:32 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// multiple values changed
|
|
|
|
this.refresh();
|
2018-01-25 10:04:48 +00:00
|
|
|
}
|
2018-02-27 16:29:14 +00:00
|
|
|
this.trigger('change');
|
2018-02-09 12:55:55 +00:00
|
|
|
this.form.classList.remove('was-validated');
|
2018-02-22 11:21:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
this.doc.on('change', this.docListener);
|
2018-02-27 16:29:14 +00:00
|
|
|
this.trigger('change');
|
2018-01-12 12:25:07 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 09:42:26 +00:00
|
|
|
checkValidity() {
|
|
|
|
let validity = this.form.checkValidity();
|
|
|
|
if (validity) {
|
|
|
|
for (let control of this.controlList) {
|
|
|
|
// check validity in table
|
|
|
|
if (control.fieldtype==='Table') {
|
|
|
|
validity = control.checkValidity();
|
|
|
|
if (!validity) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return validity;
|
|
|
|
}
|
|
|
|
|
2018-02-27 16:29:14 +00:00
|
|
|
refresh() {
|
|
|
|
for(let control of this.controlList) {
|
|
|
|
control.refresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-12 12:25:07 +00:00
|
|
|
async submit() {
|
2018-02-27 16:29:14 +00:00
|
|
|
this.doc.submitted = 1;
|
|
|
|
await this.save();
|
|
|
|
}
|
|
|
|
|
2018-03-05 16:45:21 +00:00
|
|
|
async revert() {
|
|
|
|
this.doc.submitted = 0;
|
|
|
|
await this.save();
|
|
|
|
}
|
|
|
|
|
2018-02-27 16:29:14 +00:00
|
|
|
async save() {
|
2018-02-12 09:42:26 +00:00
|
|
|
if (!this.checkValidity()) {
|
2018-02-09 12:55:55 +00:00
|
|
|
this.form.classList.add('was-validated');
|
|
|
|
return;
|
|
|
|
}
|
2018-01-12 12:25:07 +00:00
|
|
|
try {
|
2018-03-05 16:45:21 +00:00
|
|
|
let oldName = this.doc.name;
|
2018-02-09 12:55:55 +00:00
|
|
|
if (this.doc._notInserted) {
|
2018-01-12 12:25:07 +00:00
|
|
|
await this.doc.insert();
|
|
|
|
} else {
|
|
|
|
await this.doc.update();
|
|
|
|
}
|
2018-02-27 16:29:14 +00:00
|
|
|
frappe.ui.showAlert({message: frappe._('Saved'), color: 'green'});
|
2018-03-05 16:45:21 +00:00
|
|
|
if (oldName !== this.doc.name) {
|
|
|
|
frappe.router.setRoute('edit', this.doctype, this.doc.name);
|
|
|
|
return;
|
|
|
|
}
|
2018-02-27 16:29:14 +00:00
|
|
|
this.refresh();
|
|
|
|
this.trigger('change');
|
2018-01-12 12:25:07 +00:00
|
|
|
} catch (e) {
|
2018-02-27 16:29:14 +00:00
|
|
|
frappe.ui.showAlert({message: frappe._('Failed'), color: 'red'});
|
2018-02-09 12:55:55 +00:00
|
|
|
return;
|
2018-01-12 12:25:07 +00:00
|
|
|
}
|
2018-02-27 16:29:14 +00:00
|
|
|
await this.trigger('save');
|
2018-01-12 12:25:07 +00:00
|
|
|
}
|
|
|
|
|
2018-02-20 09:53:38 +00:00
|
|
|
async delete() {
|
|
|
|
try {
|
|
|
|
await this.doc.delete();
|
2018-02-27 16:29:14 +00:00
|
|
|
frappe.ui.showAlert({message: frappe._('Deleted'), color: 'green'});
|
2018-02-20 09:53:38 +00:00
|
|
|
this.trigger('delete');
|
|
|
|
} catch (e) {
|
2018-02-27 16:29:14 +00:00
|
|
|
frappe.ui.showAlert({message: e, color: 'red'});
|
2018-01-31 10:13:33 +00:00
|
|
|
}
|
|
|
|
}
|
2018-01-15 11:55:31 +00:00
|
|
|
}
|