2
0
mirror of https://github.com/frappe/books.git synced 2024-09-20 19:29:02 +00:00
books/model/document.js

305 lines
8.6 KiB
JavaScript
Raw Normal View History

2018-01-16 06:09:17 +00:00
const frappe = require('frappejs');
2018-02-13 11:54:57 +00:00
const Observable = require('frappejs/utils/observable');
2018-03-05 16:45:21 +00:00
const naming = require('./naming');
2018-01-12 12:25:07 +00:00
2018-02-13 11:54:57 +00:00
module.exports = class BaseDocument extends Observable {
2018-01-12 12:25:07 +00:00
constructor(data) {
2018-02-13 11:54:57 +00:00
super();
2018-03-27 13:55:26 +00:00
this.fetchValuesCache = {};
2018-03-05 16:45:21 +00:00
this.flags = {};
2018-01-12 12:25:07 +00:00
this.setup();
Object.assign(this, data);
2018-03-27 13:55:26 +00:00
// clear fetch-values cache
frappe.db.on('change', (params) => this.fetchValuesCache[`${params.doctype}:${params.name}`] = {});
2018-01-12 12:25:07 +00:00
}
setup() {
2018-02-13 11:54:57 +00:00
// add listeners
2018-01-12 12:25:07 +00:00
}
2018-02-12 12:01:31 +00:00
get meta() {
if (!this._meta) {
this._meta = frappe.getMeta(this.doctype);
}
return this._meta;
}
async getSettings() {
if (!this._settings) {
this._settings = await frappe.getSingle(this.meta.settings);
}
return this._settings;
}
// set value and trigger change
async set(fieldname, value) {
if (this[fieldname] !== value) {
this._dirty = true;
this[fieldname] = await this.validateField(fieldname, value);
await this.applyChange(fieldname);
}
}
async applyChange(fieldname) {
if (await this.applyFormula()) {
2018-02-08 11:45:32 +00:00
// multiple changes
await this.trigger('change', { doc: this });
} else {
// no other change, trigger control refresh
await this.trigger('change', { doc: this, fieldname: fieldname });
2018-02-08 11:45:32 +00:00
}
2018-01-12 12:25:07 +00:00
}
2018-02-12 12:01:31 +00:00
setDefaults() {
for (let field of this.meta.fields) {
if (this[field.fieldname]===null || this[field.fieldname]===undefined) {
if (field.fieldtype === 'Date') {
this[field.fieldname] = (new Date()).toISOString().substr(0, 10);
} else if(field.default) {
2018-02-14 12:50:56 +00:00
this[field.fieldname] = field.default;
}
2018-02-12 12:01:31 +00:00
}
}
}
2018-02-08 11:45:32 +00:00
setKeywords() {
2018-01-12 12:25:07 +00:00
let keywords = [];
for (let fieldname of this.meta.getKeywordFields()) {
2018-01-12 12:25:07 +00:00
keywords.push(this[fieldname]);
}
this.keywords = keywords.join(', ');
}
append(key, document) {
if (!this[key]) {
this[key] = [];
}
2018-02-08 11:45:32 +00:00
this[key].push(this.initDoc(document));
2018-01-12 12:25:07 +00:00
}
2018-02-08 11:45:32 +00:00
initDoc(data) {
2018-01-12 12:25:07 +00:00
if (data.prototype instanceof Document) {
return data;
} else {
2018-02-01 09:29:25 +00:00
return new Document(data);
2018-01-12 12:25:07 +00:00
}
}
2018-02-08 11:45:32 +00:00
async validateField(key, value) {
let field = this.meta.getField(key);
2018-01-31 13:04:46 +00:00
if (field && field.fieldtype == 'Select') {
2018-02-13 11:54:57 +00:00
return this.meta.validateSelect(field, value);
2018-01-12 12:25:07 +00:00
}
return value;
2018-01-12 12:25:07 +00:00
}
2018-02-08 11:45:32 +00:00
getValidDict() {
2018-02-01 11:07:36 +00:00
let data = {};
for (let field of this.meta.getValidFields()) {
2018-02-01 11:07:36 +00:00
data[field.fieldname] = this[field.fieldname];
2018-01-12 12:25:07 +00:00
}
2018-02-01 11:07:36 +00:00
return data;
2018-01-12 12:25:07 +00:00
}
2018-03-05 16:45:21 +00:00
getFullDict() {
let data = this.getValidDict();
return data;
}
2018-02-08 11:45:32 +00:00
setStandardValues() {
// set standard values on server-side only
if (frappe.isServer) {
2018-03-05 16:45:21 +00:00
let now = (new Date()).toISOString();
if (!this.submitted) this.submitted = 0;
if (!this.owner) {
this.owner = frappe.session.user;
this.creation = now;
}
2018-03-29 14:28:56 +00:00
this.modifiedBy = frappe.session.user;
this.modified = now;
2018-01-12 12:25:07 +00:00
}
}
async load() {
let data = await frappe.db.get(this.doctype, this.name);
if (data.name) {
2018-02-08 11:45:32 +00:00
this.syncValues(data);
if (this.meta.isSingle) {
this.setDefaults();
}
} else {
throw new frappe.errors.NotFound(`Not Found: ${this.doctype} ${this.name}`);
}
2018-01-12 12:25:07 +00:00
}
2018-02-08 11:45:32 +00:00
syncValues(data) {
this.clearValues();
2018-02-01 11:07:36 +00:00
Object.assign(this, data);
this._dirty = false;
this.trigger('change', {doc: this});
2018-02-01 11:07:36 +00:00
}
2018-02-08 11:45:32 +00:00
clearValues() {
for (let field of this.meta.getValidFields()) {
2018-02-01 11:07:36 +00:00
if(this[field.fieldname]) {
delete this[field.fieldname];
}
}
}
2018-02-08 11:45:32 +00:00
setChildIdx() {
2018-02-07 13:23:52 +00:00
// renumber children
for (let field of this.meta.getValidFields()) {
2018-02-07 13:23:52 +00:00
if (field.fieldtype==='Table') {
for(let i=0; i < (this[field.fieldname] || []).length; i++) {
this[field.fieldname][i].idx = i;
}
}
}
}
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) {
2018-03-05 16:45:21 +00:00
this.flags.submitAction = true;
}
if (currentDoc.submitted && !this.submitted) {
2018-03-05 16:45:21 +00:00
this.flags.revertAction = true;
}
2018-03-05 16:45:21 +00:00
}
}
async applyFormula() {
if (!this.meta.hasFormula()) {
2018-02-08 11:45:32 +00:00
return false;
}
2018-02-09 12:55:55 +00:00
let doc = this;
2018-02-08 11:45:32 +00:00
// children
for (let tablefield of this.meta.getTableFields()) {
let formulaFields = frappe.getMeta(tablefield.childtype).getFormulaFields();
if (formulaFields.length) {
// for each row
2018-02-09 12:55:55 +00:00
for (let row of this[tablefield.fieldname]) {
2018-02-08 11:45:32 +00:00
for (let field of formulaFields) {
2018-03-08 13:31:22 +00:00
const val = await field.formula(row, doc);
if (val !== false) {
row[field.fieldname] = val;
}
2018-02-08 11:45:32 +00:00
}
}
}
}
// parent
for (let field of this.meta.getFormulaFields()) {
2018-03-08 13:31:22 +00:00
const val = await field.formula(doc);
if (val !== false) {
doc[field.fieldname] = val;
}
2018-02-08 11:45:32 +00:00
}
return true;
}
2018-02-07 13:23:52 +00:00
async commit() {
// re-run triggers
2018-02-08 11:45:32 +00:00
this.setStandardValues();
this.setKeywords();
this.setChildIdx();
await this.applyFormula();
await this.trigger('validate');
2018-02-07 13:23:52 +00:00
}
async insert() {
2018-03-07 10:37:58 +00:00
await naming.setName(this);
2018-02-07 13:23:52 +00:00
await this.commit();
await this.trigger('beforeInsert');
2018-03-05 16:45:21 +00:00
const data = await frappe.db.insert(this.doctype, this.getValidDict());
this.syncValues(data);
await this.trigger('afterInsert');
await this.trigger('afterSave');
2018-02-01 11:07:36 +00:00
return this;
}
2018-02-07 13:23:52 +00:00
async update() {
await this.compareWithCurrentDoc();
2018-02-07 13:23:52 +00:00
await this.commit();
await this.trigger('beforeUpdate');
2018-03-05 16:45:21 +00:00
// before submit
if (this.flags.submitAction) await this.trigger('beforeSubmit');
if (this.flags.revertAction) await this.trigger('beforeRevert');
const data = await frappe.db.update(this.doctype, this.getValidDict());
this.syncValues(data);
await this.trigger('afterUpdate');
await this.trigger('afterSave');
2018-03-05 16:45:21 +00:00
// after submit
if (this.flags.submitAction) await this.trigger('afterSubmit');
if (this.flags.revertAction) await this.trigger('afterRevert');
2018-02-01 11:07:36 +00:00
return this;
2018-01-12 12:25:07 +00:00
}
async delete() {
await this.trigger('before_delete');
await frappe.db.delete(this.doctype, this.name);
await this.trigger('after_delete');
}
2018-03-05 16:45:21 +00:00
async submit() {
this.submitted = 1;
this.update();
}
async revert() {
this.submitted = 0;
this.update();
}
// trigger methods on the class if they match
// with the trigger name
2018-02-13 11:54:57 +00:00
async trigger(event, params) {
if (this[event]) {
await this[event](params);
2018-01-12 12:25:07 +00:00
}
2018-02-13 11:54:57 +00:00
await super.trigger(event, params);
2018-01-12 12:25:07 +00:00
}
2018-02-09 12:55:55 +00:00
// helper functions
getSum(tablefield, childfield) {
2018-02-21 09:43:21 +00:00
return this[tablefield].map(d => (d[childfield] || 0)).reduce((a, b) => a + b, 0);
2018-02-09 12:55:55 +00:00
}
async getFrom(doctype, name, fieldname) {
if (!name) return '';
2018-03-27 13:55:26 +00:00
let _values = this.fetchValuesCache[`${doctype}:${name}`] || (this.fetchValuesCache[`${doctype}:${name}`] = {});
2018-03-07 10:37:58 +00:00
if (!_values[fieldname]) {
_values[fieldname] = await frappe.db.getValue(doctype, name, fieldname);
2018-02-09 12:55:55 +00:00
}
2018-03-07 10:37:58 +00:00
return _values[fieldname];
2018-02-09 12:55:55 +00:00
}
2018-01-24 11:52:05 +00:00
};