2
0
mirror of https://github.com/frappe/books.git synced 2024-11-09 23:30:56 +00:00

invoice totals

This commit is contained in:
Rushabh Mehta 2018-02-08 17:16:10 +05:30
parent 36be3b98f7
commit 354813d339
4 changed files with 153 additions and 99 deletions

4
dist/css/style.css vendored
View File

@ -5735,7 +5735,7 @@ a.text-dark:hover, a.text-dark:focus {
background: #3d6b00;
color: inherit; }
html {
font-size: 14px; }
font-size: 16px; }
.main {
border-left: 1px solid #dee2e6; }
.hide {
@ -5754,6 +5754,8 @@ html {
margin-bottom: 1rem; }
.form-body .alert {
margin-top: 1rem; }
.list-row .checkbox {
margin-right: 0.5rem; }
.dropdown-menu-right {
right: 0;
left: auto; }

238
dist/js/bundle.js vendored
View File

@ -255,7 +255,7 @@ var frappejs = {
async get_new_doc(doctype) {
let doc = frappe.new_doc({doctype: doctype});
doc.set_name();
doc.setName();
doc.__not_inserted = true;
this.add_to_cache(doc);
return doc;
@ -365,11 +365,17 @@ var document$1 = class BaseDocument {
// set value and trigger change
async set(fieldname, value) {
this[fieldname] = await this.validate_field(fieldname, value);
await this.trigger('change', { doc: this, fieldname: fieldname, value: value });
this[fieldname] = await this.validateField(fieldname, value);
if (this.applyFormulae()) {
// multiple changes
await this.trigger('change', { doc: this });
} else {
// no other change, trigger control refresh
await this.trigger('change', { doc: this, fieldname: fieldname, value: value });
}
}
set_name() {
setName() {
// assign a random name by default
// override this to set a name
if (!this.name) {
@ -377,7 +383,7 @@ var document$1 = class BaseDocument {
}
}
set_keywords() {
setKeywords() {
let keywords = [];
for (let fieldname of this.meta.getKeywordFields()) {
keywords.push(this[fieldname]);
@ -396,10 +402,10 @@ var document$1 = class BaseDocument {
if (!this[key]) {
this[key] = [];
}
this[key].push(this.init_doc(document));
this[key].push(this.initDoc(document));
}
init_doc(data) {
initDoc(data) {
if (data.prototype instanceof Document) {
return data;
} else {
@ -407,15 +413,15 @@ var document$1 = class BaseDocument {
}
}
async validate_field(key, value) {
let field = this.meta.get_field(key);
async validateField(key, value) {
let field = this.meta.getField(key);
if (field && field.fieldtype == 'Select') {
return this.meta.validate_select(field, value);
}
return value;
}
get_valid_dict() {
getValidDict() {
let data = {};
for (let field of this.meta.getValidFields()) {
data[field.fieldname] = this[field.fieldname];
@ -423,7 +429,7 @@ var document$1 = class BaseDocument {
return data;
}
set_standard_values() {
setStandardValues() {
let now = new Date();
if (this.docstatus === null || this.docstatus === undefined) {
this.docstatus = 0;
@ -439,18 +445,18 @@ var document$1 = class BaseDocument {
async load() {
let data = await frappejs.db.get(this.doctype, this.name);
if (data.name) {
this.sync_values(data);
this.syncValues(data);
} else {
throw new frappejs.errors.NotFound(`Not Found: ${this.doctype} ${this.name}`);
}
}
sync_values(data) {
this.clear_values();
syncValues(data) {
this.clearValues();
Object.assign(this, data);
}
clear_values() {
clearValues() {
for (let field of this.meta.getValidFields()) {
if(this[field.fieldname]) {
delete this[field.fieldname];
@ -458,7 +464,7 @@ var document$1 = class BaseDocument {
}
}
set_child_idx() {
setChildIdx() {
// renumber children
for (let field of this.meta.getValidFields()) {
if (field.fieldtype==='Table') {
@ -469,12 +475,60 @@ var document$1 = class BaseDocument {
}
}
applyFormulae() {
if (!this.hasFormulae()) {
return false;
}
let doc;
// children
for (let tablefield of this.meta.getTableFields()) {
let formulaFields = frappejs.getMeta(tablefield.childtype).getFormulaFields();
if (formulaFields.length) {
// for each row
for (doc of this[tablefield.fieldname]) {
for (let field of formulaFields) {
doc[field.fieldname] = eval(field.formula);
}
}
}
}
// parent
doc = this;
for (let field of this.meta.getFormulaFields()) {
doc[field.fieldname] = eval(field.formula);
}
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.set_name();
this.set_standard_values();
this.set_keywords();
this.set_child_idx();
this.setName();
this.setStandardValues();
this.setKeywords();
this.setChildIdx();
this.applyFormulae();
await this.trigger('validate');
await this.trigger('commit');
}
@ -482,7 +536,7 @@ var document$1 = class BaseDocument {
async insert() {
await this.commit();
await this.trigger('before_insert');
this.sync_values(await frappejs.db.insert(this.doctype, this.get_valid_dict()));
this.syncValues(await frappejs.db.insert(this.doctype, this.getValidDict()));
await this.trigger('after_insert');
await this.trigger('after_save');
@ -492,7 +546,7 @@ var document$1 = class BaseDocument {
async update() {
await this.commit();
await this.trigger('before_update');
this.sync_values(await frappejs.db.update(this.doctype, this.get_valid_dict()));
this.syncValues(await frappejs.db.update(this.doctype, this.getValidDict()));
await this.trigger('after_update');
await this.trigger('after_save');
@ -530,7 +584,7 @@ var meta = class BaseMeta extends document$1 {
}
}
get_field(fieldname) {
getField(fieldname) {
if (!this._field_map) {
this._field_map = {};
for (let field of this.fields) {
@ -541,12 +595,19 @@ var meta = class BaseMeta extends document$1 {
}
getTableFields() {
if (!this._tableFields) {
if (this._tableFields===undefined) {
this._tableFields = this.fields.filter(field => field.fieldtype === 'Table');
}
return this._tableFields;
}
getFormulaFields() {
if (this._formulaFields===undefined) {
this._formulaFields = this.fields.filter(field => field.formula);
}
return this._formulaFields;
}
on(key, fn) {
if (!this.event_handlers[key]) {
this.event_handlers[key] = [];
@ -1647,44 +1708,44 @@ class BaseControl {
make() {
if (!this.input) {
if (!this.only_input) {
this.make_form_group();
this.make_label();
if (!this.onlyInput) {
this.makeFormGroup();
this.makeLabel();
}
this.make_input();
this.set_input_name();
if (!this.only_input) {
this.make_description();
this.makeInput();
this.setInputName();
if (!this.onlyInput) {
this.makeDescription();
}
this.bind_change_event();
this.bindChangeEvent();
}
}
make_form_group() {
this.form_group = frappejs.ui.add('div', 'form-group', this.parent);
makeFormGroup() {
this.formGroup = frappejs.ui.add('div', 'form-group', this.parent);
}
make_label() {
this.label_element = frappejs.ui.add('label', null, this.form_group);
makeLabel() {
this.label_element = frappejs.ui.add('label', null, this.formGroup);
this.label_element.textContent = this.label;
}
make_input() {
makeInput() {
this.input = frappejs.ui.add('input', 'form-control', this.get_input_parent());
this.input.setAttribute('autocomplete', 'off');
}
get_input_parent() {
return this.form_group || this.parent;
return this.formGroup || this.parent;
}
set_input_name() {
setInputName() {
this.input.setAttribute('name', this.fieldname);
}
make_description() {
makeDescription() {
if (this.description) {
this.description_element = frappejs.ui.add('small', 'form-text text-muted', this.form_group);
this.description_element = frappejs.ui.add('small', 'form-text text-muted', this.formGroup);
this.description_element.textContent = this.description;
}
}
@ -1700,7 +1761,7 @@ class BaseControl {
return value;
}
async get_parsed_value() {
async getParsedValue() {
return await this.parse(this.input.value);
}
@ -1716,11 +1777,11 @@ class BaseControl {
return value;
}
bind_change_event() {
this.input.addEventListener('change', (e) => this.handle_change(e));
bindChangeEvent() {
this.input.addEventListener('change', (e) => this.handleChange());
}
async handle_change(e) {
async handleChange() {
let value = await this.parse(this.getInputValue());
value = await this.validate(value);
if (this.doc[this.fieldname] !== value) {
@ -1728,6 +1789,7 @@ class BaseControl {
await this.doc.set(this.fieldname, value);
}
if (this.parent_control) {
this.doc[this.fieldname] = value;
await this.parent_control.doc.set(this.fieldname, this.parent_control.getInputValue());
}
}
@ -1758,7 +1820,7 @@ class DataControl extends base {
var data = DataControl;
class TextControl extends base {
make_input() {
makeInput() {
this.input = frappe.ui.add('textarea', 'form-control', this.get_input_parent());
}
make() {
@ -1770,8 +1832,8 @@ class TextControl extends base {
var text = TextControl;
class SelectControl extends base {
make_input() {
this.input = frappe.ui.add('select', 'form-control', this.form_group);
makeInput() {
this.input = frappejs.ui.add('select', 'form-control', this.formGroup);
let options = this.options;
if (typeof options==='string') {
@ -1779,7 +1841,7 @@ class SelectControl extends base {
}
for (let value of options) {
let option = frappe.ui.add('option', null, this.input);
let option = frappejs.ui.add('option', null, this.input);
option.textContent = value;
option.setAttribute('value', value);
}
@ -2346,6 +2408,10 @@ class FloatControl extends base {
}, 100);
});
}
parse(value) {
value = parseFloat(value);
return value===NaN ? 0 : value;
}
}
var float_1 = FloatControl;
@ -6544,6 +6610,9 @@ var DataTable = function () {
this.datamanager.init(data);
this.render();
this.setDimensions();
if (this.cellmanager.$focusedCell) {
this.cellmanager.$focusedCell.focus();
}
}
}, {
key: 'destroy',
@ -7137,12 +7206,6 @@ var DataManager = function () {
}
}
// update model
if (!Array.isArray(this.data[rowIndex])) {
var col = this.getColumn(colIndex);
this.data[rowIndex][col.id] = options.content;
}
return cell;
}
}, {
@ -24920,12 +24983,12 @@ class TableControl extends base {
</div>`;
this.datatable = new frappeDatatable(this.wrapper.querySelector('.datatable-wrapper'), {
columns: this.get_columns(),
data: this.get_table_data(),
columns: this.getColumns(),
data: this.getTableData(),
takeAvailableSpace: true,
enableClusterize: true,
addCheckboxColumn: true,
editing: this.get_table_input.bind(this),
editing: this.getTableInput.bind(this),
});
this.wrapper.querySelector('.btn-add').addEventListener('click', async (event) => {
@ -24949,25 +25012,25 @@ class TableControl extends base {
}
setInputValue(value) {
this.datatable.refresh(this.get_table_data(value));
this.datatable.refresh(this.getTableData(value));
}
get_table_data(value) {
return value || this.get_default_data();
getTableData(value) {
return value || this.getDefaultData();
}
get_table_input(colIndex, rowIndex, value, parent) {
getTableInput(colIndex, rowIndex, value, parent) {
let field = this.datatable.getColumn(colIndex).field;
if (field.fieldtype==='Text') {
return this.get_text_control(field);
return this.getControlInModal(field);
} else {
return this.get_control(field, parent);
return this.getControl(field, parent);
}
}
get_control(field, parent) {
field.only_input = true;
getControl(field, parent) {
field.onlyInput = true;
const control = controls$1.makeControl({field: field, parent: parent});
return {
@ -24977,8 +25040,8 @@ class TableControl extends base {
control.set_focus();
return control.setInputValue(value);
},
setValue: (value, rowIndex, column) => {
return control.setInputValue(value);
setValue: async (value, rowIndex, column) => {
// triggers change event
},
getValue: () => {
return control.getInputValue();
@ -24987,9 +25050,8 @@ class TableControl extends base {
}
get_text_control(field, parent) {
this.text_modal = new modal({
getControlInModal(field, parent) {
this.modal = new modal({
title: frappejs._('Edit {0}', field.label),
body: '',
primary_label: frappejs._('Submit'),
@ -24998,15 +25060,15 @@ class TableControl extends base {
modal$$1.hide();
}
});
this.text_modal.$modal.on('hidden.bs.modal', () => {
this.modal.$modal.on('hidden.bs.modal', () => {
this.datatable.cellmanager.deactivateEditing();
});
return this.get_control(field, this.text_modal.get_body());
return this.getControl(field, this.modal.get_body());
}
get_columns() {
return this.get_child_fields().map(field => {
getColumns() {
return this.getChildFields().map(field => {
return {
id: field.fieldname,
field: field,
@ -25019,11 +25081,11 @@ class TableControl extends base {
});
}
get_child_fields() {
getChildFields() {
return frappejs.getMeta(this.childtype).fields;
}
get_default_data() {
getDefaultData() {
// build flat table
if (!this.doc) {
return [];
@ -25119,9 +25181,15 @@ var form = class BaseForm extends observable {
// refresh value in control
this.doc.addHandler('change', (params) => {
let control = this.controls[params.fieldname];
if (control && control.getInputValue() !== control.format(params.fieldname)) {
control.setDocValue();
if (params.fieldname) {
// only single value changed
let control = this.controls[params.fieldname];
if (control && control.getInputValue() !== control.format(params.fieldname)) {
control.setDocValue();
}
} else {
// multiple values changed
this.refresh();
}
});
@ -25594,14 +25662,6 @@ class InvoiceMeta extends meta {
}
class Invoice extends document$1 {
setup() {
this.addHandler('validate');
}
validate() {
this.total = this.get_total();
}
get_total() {
return this.items.map(d => (d.amount || 0)).reduce((a, b) => a + b, 0);
}
@ -25617,7 +25677,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":"item.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","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 invoice_item = {
name: name$5,
doctype: doctype$5,

View File

@ -8,14 +8,6 @@ class InvoiceMeta extends BaseMeta {
}
class Invoice extends BaseDocument {
setup() {
this.addHandler('validate');
}
validate() {
this.total = this.get_total();
}
get_total() {
return this.items.map(d => (d.amount || 0)).reduce((a, b) => a + b, 0);
}

View File

@ -16,7 +16,7 @@
"fieldname": "description",
"label": "Description",
"fieldtype": "Text",
"fetch": "item.description",
"fetch": {"from": "item", "value": "description"},
"reqd": 1
},
{