Feature: Allow math expressions on all journal inputs and also round them properly.

Chore:
  Prettied index.html, main.ts and styles.css
  Updated Dependencies
This commit is contained in:
2020-10-07 18:41:17 +05:30
parent cefb3ebdcc
commit cfeef1795d
20 changed files with 202 additions and 120 deletions

View File

@ -4,6 +4,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup } from '@angular/forms';
import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
import { Observable, of as observableOf } from 'rxjs';
import { MathService } from '../shared/math.service';
import { Batch } from '../core/voucher';
import { BatchService } from '../core/batch.service';
@ -21,6 +22,7 @@ export class IssueDialogComponent implements OnInit {
public dialogRef: MatDialogRef<IssueDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private fb: FormBuilder,
private math: MathService,
private batchSer: BatchService,
) {
this.createForm();
@ -65,7 +67,7 @@ export class IssueDialogComponent implements OnInit {
accept(): void {
const formValue = this.form.value;
const quantity = +formValue.quantity;
const quantity = this.math.parseAmount(formValue.quantity, 2);
this.data.inventory.batch = this.batch;
this.data.inventory.product = this.batch.product;
this.data.inventory.quantity = quantity;

View File

@ -9,6 +9,8 @@ import { IssueDataSource } from './issue-datasource';
import { VoucherService } from '../core/voucher.service';
import { Batch, Inventory, Voucher } from '../core/voucher';
import * as moment from 'moment';
import { round } from 'mathjs';
import { MathService } from '../shared/math.service';
import { AuthService } from '../auth/auth.service';
import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component';
import { ToasterService } from '../core/toaster.service';
@ -49,6 +51,7 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy {
private hotkeys: HotkeysService,
private toaster: ToasterService,
private auth: AuthService,
private math: MathService,
private ser: VoucherService,
private batchSer: BatchService,
private issueGridSer: IssueGridService,
@ -123,7 +126,7 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy {
addRow() {
const formValue = this.form.get('addRow').value;
const quantity = +formValue.quantity;
const quantity = this.math.parseAmount(formValue.quantity, 2);
const isConsumption = this.form.value.source === '7b845f95-dfef-fa4a-897c-f0baf15284a3';
if (this.batch === null || quantity <= 0) {
return;
@ -175,8 +178,9 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy {
updateView() {
this.inventoryObservable.next(this.voucher.inventories);
const amount = Math.abs(
this.voucher.inventories.map((x) => x.amount).reduce((p, c) => p + c, 0),
const amount = round(
Math.abs(this.voucher.inventories.map((x) => x.amount).reduce((p, c) => p + c, 0)),
2,
);
this.form.get('amount').setValue(amount);
}

View File

@ -1,11 +1,12 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
import { Account } from '../core/account';
import { AccountService } from '../core/account.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Observable, of as observableOf } from 'rxjs';
import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
import { MathService } from '../shared/math.service';
import { Account } from '../core/account';
import { AccountService } from '../core/account.service';
@Component({
selector: 'app-journal-dialog',
@ -22,6 +23,7 @@ export class JournalDialogComponent implements OnInit {
public dialogRef: MatDialogRef<JournalDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private fb: FormBuilder,
private math: MathService,
private accountSer: AccountService,
) {
this.createForm();
@ -70,11 +72,10 @@ export class JournalDialogComponent implements OnInit {
accept(): void {
const formValue = this.form.value;
const debit = +formValue.debit;
const amount = +formValue.amount;
this.data.journal.debit = debit;
const amount = this.math.journalAmount(formValue.amount, +formValue.debit);
this.data.journal.debit = amount.debit;
this.data.journal.account = this.account;
this.data.journal.amount = amount;
this.data.journal.amount = amount.amount;
this.dialogRef.close(this.data.journal);
}
}

View File

@ -10,7 +10,8 @@ import { VoucherService } from '../core/voucher.service';
import { AccountService } from '../core/account.service';
import { DbFile, Journal, Voucher } from '../core/voucher';
import * as moment from 'moment';
import { evaluate } from 'mathjs';
import { round } from 'mathjs';
import { MathService } from '../shared/math.service';
import { AuthService } from '../auth/auth.service';
import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component';
import { ToasterService } from '../core/toaster.service';
@ -46,6 +47,7 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy {
private hotkeys: HotkeysService,
private toaster: ToasterService,
private auth: AuthService,
private math: MathService,
private ser: VoucherService,
private accountSer: AccountService,
) {
@ -131,7 +133,7 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy {
addRow() {
const formValue = this.form.get('addRow').value;
const amount = this.rowAmount(formValue.amount, +formValue.debit);
const amount = this.math.journalAmount(formValue.amount, +formValue.debit);
if (this.account === null || amount.amount === 0) {
return;
}
@ -154,22 +156,10 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy {
this.resetAddRow();
}
rowAmount(amount: string = '', debit: number): any {
try {
amount = amount.replace(new RegExp('(₹[s]*)|(,)|(s)', 'g'), '');
const val = evaluate(amount);
if (val < 0) {
debit *= -1;
}
return { debit: debit, amount: Math.abs(val) };
} catch {
return { debit: debit, amount: 0 };
}
}
resetAddRow() {
const amount = Math.abs(
this.voucher.journals.map((x) => x.debit * x.amount).reduce((p, c) => p + c),
const amount = round(
Math.abs(this.voucher.journals.map((x) => x.debit * x.amount).reduce((p, c) => p + c)),
2,
);
const debit = this.form.get('addRow').value.debit;
this.form.get('addRow').reset({

View File

@ -1,11 +1,12 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup } from '@angular/forms';
import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
import { Observable, of as observableOf } from 'rxjs';
import { MathService } from '../shared/math.service';
import { Account } from '../core/account';
import { AccountService } from '../core/account.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Observable, of as observableOf } from 'rxjs';
@Component({
selector: 'app-payment-dialog',
@ -22,6 +23,7 @@ export class PaymentDialogComponent implements OnInit {
public dialogRef: MatDialogRef<PaymentDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private fb: FormBuilder,
private math: MathService,
private accountSer: AccountService,
) {
this.createForm();
@ -68,7 +70,7 @@ export class PaymentDialogComponent implements OnInit {
accept(): void {
const formValue = this.form.value;
const amount = +formValue.amount;
const amount = this.math.parseAmount(formValue.amount, 2);
this.data.journal.account = this.account;
this.data.journal.amount = amount;
this.dialogRef.close(this.data.journal);

View File

@ -10,7 +10,8 @@ import { VoucherService } from '../core/voucher.service';
import { AccountService } from '../core/account.service';
import { DbFile, Journal, Voucher } from '../core/voucher';
import * as moment from 'moment';
import { evaluate } from 'mathjs';
import { round } from 'mathjs';
import { MathService } from '../shared/math.service';
import { AuthService } from '../auth/auth.service';
import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component';
import { ToasterService } from '../core/toaster.service';
@ -48,6 +49,7 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy {
private hotkeys: HotkeysService,
private toaster: ToasterService,
private auth: AuthService,
private math: MathService,
private ser: VoucherService,
private accountSer: AccountService,
) {
@ -133,7 +135,7 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy {
}
addRow() {
const amount = this.rowAmount(this.form.get('addRow').value.amount);
const amount = this.math.parseAmount(this.form.get('addRow').value.amount, 2);
const debit = 1;
if (this.account === null || amount <= 0) {
return;
@ -157,15 +159,15 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy {
this.resetAddRow();
this.updateView();
}
rowAmount(amount: string = ''): number {
try {
amount = amount.replace(new RegExp('(₹[s]*)|(,)|(s)', 'g'), '');
return evaluate(amount);
} catch {
return 0;
}
}
//
// rowAmount(amount: string = ''): number {
// try {
// amount = amount.replace(new RegExp('(₹[s]*)|(,)|(s)', 'g'), '');
// return round(evaluate(amount), 2);
// } catch {
// return 0;
// }
// }
resetAddRow() {
this.form.get('addRow').reset({
@ -182,7 +184,10 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy {
updateView() {
const journals = this.voucher.journals.filter((x) => x.debit === 1);
this.journalObservable.next(journals);
this.paymentJournal.amount = Math.abs(journals.map((x) => x.amount).reduce((p, c) => p + c, 0));
this.paymentJournal.amount = round(
Math.abs(journals.map((x) => x.amount).reduce((p, c) => p + c, 0)),
2,
);
this.form.get('paymentAmount').setValue(this.paymentJournal.amount);
}

View File

@ -4,6 +4,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup } from '@angular/forms';
import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
import { Observable, of as observableOf } from 'rxjs';
import { MathService } from '../shared/math.service';
import { Batch } from '../core/voucher';
import { BatchService } from '../core/batch.service';
@ -21,6 +22,7 @@ export class PurchaseReturnDialogComponent implements OnInit {
public dialogRef: MatDialogRef<PurchaseReturnDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private fb: FormBuilder,
private math: MathService,
private batchSer: BatchService,
) {
this.createForm();
@ -65,7 +67,7 @@ export class PurchaseReturnDialogComponent implements OnInit {
accept(): void {
const formValue = this.form.value;
const quantity = +formValue.quantity;
const quantity = this.math.parseAmount(formValue.quantity, 2);
this.data.inventory.batch = this.batch;
this.data.inventory.product = this.batch.product;
this.data.inventory.quantity = quantity;

View File

@ -10,6 +10,8 @@ import { VoucherService } from '../core/voucher.service';
import { AccountService } from '../core/account.service';
import { Batch, DbFile, Inventory, Voucher } from '../core/voucher';
import * as moment from 'moment';
import { round } from 'mathjs';
import { MathService } from '../shared/math.service';
import { AuthService } from '../auth/auth.service';
import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component';
import { ToasterService } from '../core/toaster.service';
@ -48,6 +50,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy
private hotkeys: HotkeysService,
private toaster: ToasterService,
private auth: AuthService,
private math: MathService,
private ser: VoucherService,
private batchSer: BatchService,
private accountSer: AccountService,
@ -135,7 +138,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy
addRow() {
const formValue = this.form.get('addRow').value;
const quantity = +formValue.quantity;
const quantity = this.math.parseAmount(formValue.quantity, 2);
if (this.batch === null || quantity <= 0 || this.batch.quantityRemaining < quantity) {
return;
}
@ -175,7 +178,10 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy
this.inventoryObservable.next(this.voucher.inventories);
this.form
.get('amount')
.setValue(Math.abs(this.voucher.inventories.map((x) => x.amount).reduce((p, c) => p + c, 0)));
.setValue(
round(Math.abs(this.voucher.inventories.map((x) => x.amount).reduce((p, c) => p + c, 0))),
2,
);
}
editRow(row: Inventory) {

View File

@ -1,9 +1,11 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
import { FormBuilder, FormGroup } from '@angular/forms';
import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
import { Observable, of as observableOf } from 'rxjs';
import { round } from 'mathjs';
import { MathService } from '../shared/math.service';
import { Product } from '../core/product';
import { ProductService } from '../product/product.service';
@ -21,6 +23,7 @@ export class PurchaseDialogComponent implements OnInit {
public dialogRef: MatDialogRef<PurchaseDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private fb: FormBuilder,
private math: MathService,
private productSer: ProductService,
) {
this.createForm();
@ -70,16 +73,16 @@ export class PurchaseDialogComponent implements OnInit {
accept(): void {
const formValue = this.form.value;
const quantity = +formValue.quantity;
const price = +formValue.price;
const tax = +formValue.tax;
const discount = +formValue.discount;
const quantity = this.math.parseAmount(formValue.quantity, 2);
const price = this.math.parseAmount(formValue.price, 2);
const tax = this.math.parseAmount(formValue.tax, 5);
const discount = this.math.parseAmount(formValue.discount, 5);
this.data.inventory.product = this.product;
this.data.inventory.quantity = quantity;
this.data.inventory.rate = price;
this.data.inventory.tax = tax;
this.data.inventory.discount = discount;
this.data.inventory.amount = quantity * price * (1 + tax) * (1 - discount);
this.data.inventory.amount = round(quantity * price * (1 + tax) * (1 - discount), 2);
this.dialogRef.close(this.data.inventory);
}
}

View File

@ -10,6 +10,8 @@ import { VoucherService } from '../core/voucher.service';
import { AccountService } from '../core/account.service';
import { DbFile, Inventory, Voucher } from '../core/voucher';
import * as moment from 'moment';
import { round } from 'mathjs';
import { MathService } from '../shared/math.service';
import { AuthService } from '../auth/auth.service';
import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component';
import { ToasterService } from '../core/toaster.service';
@ -19,6 +21,7 @@ import { ImageDialogComponent } from '../shared/image-dialog/image-dialog.compon
import { ProductService } from '../product/product.service';
import { Product } from '../core/product';
import { Hotkey, HotkeysService } from 'angular2-hotkeys';
import { conditionallyCreateMapObjectLiteral } from '@angular/compiler/src/render3/view/util';
@Component({
selector: 'app-purchase',
@ -49,6 +52,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
private hotkeys: HotkeysService,
private toaster: ToasterService,
private auth: AuthService,
private math: MathService,
private ser: VoucherService,
private productSer: ProductService,
private accountSer: AccountService,
@ -139,10 +143,10 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
addRow() {
const formValue = this.form.get('addRow').value;
const quantity = +formValue.quantity;
const price = +formValue.price;
const tax = +formValue.tax;
const discount = +formValue.discount;
const quantity = this.math.parseAmount(formValue.quantity, 2);
const price = this.math.parseAmount(formValue.price, 2);
const tax = this.math.parseAmount(formValue.tax, 5);
const discount = this.math.parseAmount(formValue.discount, 5);
if (this.product === null || quantity <= 0 || price <= 0) {
return;
}
@ -157,7 +161,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
rate: price,
tax: tax,
discount: discount,
amount: quantity * price * (1 + tax) * (1 - discount),
amount: round(quantity * price * (1 + tax) * (1 - discount), 2),
product: this.product,
batch: null,
});
@ -183,7 +187,10 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
this.inventoryObservable.next(this.voucher.inventories);
this.form
.get('amount')
.setValue(Math.abs(this.voucher.inventories.map((x) => x.amount).reduce((p, c) => p + c, 0)));
.setValue(
round(Math.abs(this.voucher.inventories.map((x) => x.amount).reduce((p, c) => p + c, 0))),
2,
);
}
editRow(row: Inventory) {

View File

@ -1,11 +1,12 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup } from '@angular/forms';
import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
import { Observable, of as observableOf } from 'rxjs';
import { MathService } from '../shared/math.service';
import { Account } from '../core/account';
import { AccountService } from '../core/account.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Observable, of as observableOf } from 'rxjs';
@Component({
selector: 'app-receipt-dialog',
@ -22,6 +23,7 @@ export class ReceiptDialogComponent implements OnInit {
public dialogRef: MatDialogRef<ReceiptDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private fb: FormBuilder,
private math: MathService,
private accountSer: AccountService,
) {
this.createForm();
@ -68,7 +70,7 @@ export class ReceiptDialogComponent implements OnInit {
accept(): void {
const formValue = this.form.value;
const amount = +formValue.amount;
const amount = this.math.parseAmount(formValue.amount, 2);
this.data.journal.account = this.account;
this.data.journal.amount = amount;
this.dialogRef.close(this.data.journal);

View File

@ -10,7 +10,8 @@ import { VoucherService } from '../core/voucher.service';
import { AccountService } from '../core/account.service';
import { DbFile, Journal, Voucher } from '../core/voucher';
import * as moment from 'moment';
import { evaluate } from 'mathjs';
import { round } from 'mathjs';
import { MathService } from '../shared/math.service';
import { AuthService } from '../auth/auth.service';
import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component';
import { ToasterService } from '../core/toaster.service';
@ -48,6 +49,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy {
private hotkeys: HotkeysService,
private toaster: ToasterService,
private auth: AuthService,
private math: MathService,
private ser: VoucherService,
private accountSer: AccountService,
) {
@ -136,7 +138,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy {
}
addRow() {
const amount = this.rowAmount(this.form.get('addRow').value.amount);
const amount = this.math.parseAmount(this.form.get('addRow').value.amount, 2);
const debit = -1;
if (this.account === null || amount <= 0) {
return;
@ -160,15 +162,15 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy {
this.resetAddRow();
this.updateView();
}
rowAmount(amount: string = ''): number {
try {
amount = amount.replace(new RegExp('(₹[s]*)|(,)|(s)', 'g'), '');
return evaluate(amount);
} catch {
return 0;
}
}
//
// rowAmount(amount: string = ''): number {
// try {
// amount = amount.replace(new RegExp('(₹[s]*)|(,)|(s)', 'g'), '');
// return round(evaluate(amount), 2);
// } catch {
// return 0;
// }
// }
resetAddRow() {
this.form.get('addRow').reset({
@ -185,7 +187,10 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy {
updateView() {
const journals = this.voucher.journals.filter((x) => x.debit === -1);
this.journalObservable.next(journals);
this.receiptJournal.amount = Math.abs(journals.map((x) => x.amount).reduce((p, c) => p + c, 0));
this.receiptJournal.amount = round(
Math.abs(journals.map((x) => x.amount).reduce((p, c) => p + c, 0)),
2,
);
this.form.get('receiptAmount').setValue(this.receiptJournal.amount);
}

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { MathService } from './math.service';
describe('MathService', () => {
let service: MathService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(MathService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,26 @@
import { Injectable } from '@angular/core';
import { evaluate, round } from 'mathjs';
@Injectable({
providedIn: 'root',
})
export class MathService {
constructor() {}
journalAmount(amount: string = '', debit: number): any {
const val = this.parseAmount(amount, 2);
if (val < 0) {
debit *= -1;
}
return { debit: debit, amount: Math.abs(val) };
}
parseAmount(amount: string = '', rounding: number = 2): number {
const cleaned = ('' + amount).replace(new RegExp('(₹[s]*)|(,)|(s)', 'g'), '');
if (cleaned === '') {
return 0;
} else {
return round(evaluate(cleaned), rounding);
}
}
}