2
0
mirror of https://github.com/frappe/books.git synced 2025-02-12 00:49:28 +00:00

fix: Formula

- Trigger applyChange on parent if child is changed
- roundFloats after applyChange
- formulaDependsOn
- process formula value in getValueFromFormula
This commit is contained in:
Faris Ansari 2019-12-02 17:50:21 +05:30
parent 254b810fd0
commit 16ad083426

View File

@ -1,6 +1,7 @@
const frappe = require('frappejs'); const frappe = require('frappejs');
const Observable = require('frappejs/utils/observable'); const Observable = require('frappejs/utils/observable');
const naming = require('./naming'); const naming = require('./naming');
const { round } = require('frappejs/utils/numberFormat');
module.exports = class BaseDocument extends Observable { module.exports = class BaseDocument extends Observable {
constructor(data) { constructor(data) {
@ -86,25 +87,23 @@ module.exports = class BaseDocument extends Observable {
} else { } else {
this[fieldname] = await this.validateField(fieldname, value); this[fieldname] = await this.validateField(fieldname, value);
} }
// always run applyChange from the parentdoc
if (this.meta.isChild && this.parentdoc) {
await this.parentdoc.applyChange(this.parentfield);
} else {
await this.applyChange(fieldname); await this.applyChange(fieldname);
} }
} }
}
async applyChange(fieldname) { async applyChange(fieldname) {
if (await this.applyFormula()) { await this.applyFormula(fieldname);
// multiple changes this.roundFloats();
await this.trigger('change', { await this.trigger('change', {
doc: this, doc: this,
changed: fieldname changed: fieldname
}); });
} else {
// no other change, trigger control refresh
await this.trigger('change', {
doc: this,
fieldname: fieldname,
changed: fieldname
});
}
} }
setDefaults() { setDefaults() {
@ -180,18 +179,13 @@ module.exports = class BaseDocument extends Observable {
for (let field of this.meta.getValidFields()) { for (let field of this.meta.getValidFields()) {
let value = this[field.fieldname]; let value = this[field.fieldname];
if (Array.isArray(value)) { if (Array.isArray(value)) {
value = value.map(doc => doc.getValidDict ? doc.getValidDict() : doc); value = value.map(doc => (doc.getValidDict ? doc.getValidDict() : doc));
} }
data[field.fieldname] = value; data[field.fieldname] = value;
} }
return data; return data;
} }
getFullDict() {
let data = this.getValidDict();
return data;
}
setStandardValues() { setStandardValues() {
// set standard values on server-side only // set standard values on server-side only
if (frappe.isServer) { if (frappe.isServer) {
@ -313,12 +307,13 @@ module.exports = class BaseDocument extends Observable {
} }
} }
async applyFormula() { async applyFormula(fieldname) {
if (!this.meta.hasFormula()) { if (!this.meta.hasFormula()) {
return false; return false;
} }
let doc = this; let doc = this;
let changed = false;
// children // children
for (let tablefield of this.meta.getTableFields()) { for (let tablefield of this.meta.getTableFields()) {
@ -330,9 +325,11 @@ module.exports = class BaseDocument extends Observable {
for (let row of this[tablefield.fieldname]) { for (let row of this[tablefield.fieldname]) {
for (let field of formulaFields) { for (let field of formulaFields) {
if (shouldApplyFormula(field, row)) { if (shouldApplyFormula(field, row)) {
const val = await field.formula(row, doc); let val = await this.getValueFromFormula(field, row);
if (val !== false && val !== undefined) { let previousVal = row[field.fieldname];
if (val !== undefined && previousVal !== val) {
row[field.fieldname] = val; row[field.fieldname] = val;
changed = true;
} }
} }
} }
@ -343,24 +340,28 @@ module.exports = class BaseDocument extends Observable {
// parent or child row // parent or child row
for (let field of this.meta.getFormulaFields()) { for (let field of this.meta.getFormulaFields()) {
if (shouldApplyFormula(field, doc)) { if (shouldApplyFormula(field, doc)) {
let val; let previousVal = doc[field.fieldname];
if (this.meta.isChild) { let val = await this.getValueFromFormula(field, doc);
val = await field.formula(doc, this.parentdoc); if (val !== undefined && previousVal !== val) {
} else {
val = await field.formula(doc);
}
if (val !== false && val !== undefined) {
doc[field.fieldname] = val; doc[field.fieldname] = val;
changed = true;
} }
} }
} }
return true; return changed;
function shouldApplyFormula(field, doc) { function shouldApplyFormula(field, doc) {
if (field.readOnly) { if (field.readOnly) {
return true; return true;
} }
if (
fieldname &&
field.formulaDependsOn &&
field.formulaDependsOn.includes(fieldname)
) {
return true;
}
if (!frappe.isServer || frappe.isElectron) { if (!frappe.isServer || frappe.isElectron) {
if (doc[field.fieldname] == null || doc[field.fieldname] == '') { if (doc[field.fieldname] == null || doc[field.fieldname] == '') {
@ -371,6 +372,57 @@ module.exports = class BaseDocument extends Observable {
} }
} }
async getValueFromFormula(field, doc) {
let value;
if (doc.meta.isChild) {
value = await field.formula(doc, doc.parentdoc);
} else {
value = await field.formula(doc);
}
if (value === undefined) {
return;
}
if (['Float', 'Currency'].includes(field.fieldtype)) {
value = round(value, field.precision || 2);
}
if (field.fieldtype === 'Table' && Array.isArray(value)) {
value = value.map(row => {
let doc = this._initChild(row, field.fieldname);
doc.roundFloats();
return doc;
});
}
return value;
}
roundFloats() {
let fields = this.meta
.getValidFields()
.filter(df => ['Float', 'Currency', 'Table'].includes(df.fieldtype));
for (let df of fields) {
let value = this[df.fieldname];
if (value == null) {
continue;
}
// child
if (Array.isArray(value)) {
value.map(row => row.roundFloats());
continue;
}
// field
let roundedValue = round(value, df.precision);
if (roundedValue && value !== roundedValue) {
this[df.fieldname] = roundedValue;
}
}
}
async setName() { async setName() {
await naming.setName(this); await naming.setName(this);
} }