You've already forked openaccounting-web
forked from cybercinch/openaccounting-web
initial commit
This commit is contained in:
56
src/app/reconcile/reconcile-modal.html
Normal file
56
src/app/reconcile/reconcile-modal.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Reconcile</h4>
|
||||
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-6">Inflows</div>
|
||||
<div class="col-6">Outflows</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 inflows">
|
||||
<div class="container-fluid">
|
||||
<div *ngFor="let item of inflows" class="row">
|
||||
<div class="col-3">{{item.tx.date | date:"M/d/y"}}</div>
|
||||
<div class="col-4">{{item.tx.description}}</div>
|
||||
<div class="col-3">{{item.amount | currencyFormat:account.precision:account.currency}}</div>
|
||||
<div class="col-2">
|
||||
<input type="checkbox" (click)="toggleReconciled(item)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 outflows">
|
||||
<div class="container-fluid">
|
||||
<div *ngFor="let item of outflows" class="row">
|
||||
<div class="col-3">{{item.tx.date | date:"M/d/y"}}</div>
|
||||
<div class="col-4">{{item.tx.description}}</div>
|
||||
<div class="col-3">{{item.amount | currencyFormat:account.precision:account.currency}}</div>
|
||||
<div class="col-2">
|
||||
<input type="checkbox" (click)="toggleReconciled(item)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-4">
|
||||
<div class="col-4 offset-8">
|
||||
Balance: {{balance | currencyFormat:account.precision:account.currency}}<br>
|
||||
Reconciled: {{reconciled | currencyFormat:account.precision:account.currency}}<br>
|
||||
Difference: {{(balance - reconciled) | currencyFormat:account.precision:account.currency}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p *ngIf="error" class="error">{{error.message}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" (click)="activeModal.dismiss()">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" (click)="save()">Complete</button>
|
||||
</div>
|
||||
5
src/app/reconcile/reconcile-modal.scss
Normal file
5
src/app/reconcile/reconcile-modal.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
.inflows,
|
||||
.outflows {
|
||||
height: 300px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
226
src/app/reconcile/reconcile-modal.ts
Normal file
226
src/app/reconcile/reconcile-modal.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Logger } from '../core/logger';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Transaction, Split } from '../shared/transaction';
|
||||
import { Account, AccountTree } from '../shared/account';
|
||||
import { Org } from '../shared/org';
|
||||
import { AppError } from '../shared/error';
|
||||
import {
|
||||
FormControl,
|
||||
FormGroup,
|
||||
FormArray,
|
||||
Validators,
|
||||
FormBuilder,
|
||||
AbstractControl
|
||||
} from '@angular/forms';
|
||||
import { Util } from '../shared/util';
|
||||
import { OrgService } from '../core/org.service';
|
||||
import { TransactionService } from '../core/transaction.service';
|
||||
import { SessionService } from '../core/session.service';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/observable/from';
|
||||
import 'rxjs/add/operator/mergeMap';
|
||||
import { Reconciliation } from './reconciliation';
|
||||
|
||||
class TxItem {
|
||||
tx: Transaction;
|
||||
amount: number;
|
||||
splitIndex: number;
|
||||
reconciled: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'reconcile-modal',
|
||||
templateUrl: './reconcile-modal.html',
|
||||
styleUrls: ['./reconcile-modal.scss']
|
||||
})
|
||||
export class ReconcileModal {
|
||||
|
||||
public account: Account;
|
||||
public reconciliation: Reconciliation;
|
||||
public items: TxItem[];
|
||||
public inflows: TxItem[];
|
||||
public outflows: TxItem[];
|
||||
public form: FormGroup;
|
||||
public balance: number;
|
||||
public reconciled: number;
|
||||
public error: AppError;
|
||||
|
||||
constructor(
|
||||
public activeModal: NgbActiveModal,
|
||||
private log: Logger,
|
||||
private txService: TransactionService,
|
||||
private sessionService: SessionService,
|
||||
private fb: FormBuilder
|
||||
) {}
|
||||
|
||||
setData(account: Account, rec: Reconciliation) {
|
||||
this.account = account;
|
||||
|
||||
this.inflows = [];
|
||||
this.outflows = [];
|
||||
this.reconciliation = rec;
|
||||
|
||||
this.balance = rec.endBalance;
|
||||
this.reconciled = rec.startBalance;
|
||||
|
||||
let txs$ = this.txService.getTransactionsByAccount(this.account.id);
|
||||
let newTxs$ = this.txService.getNewTransactionsByAccount(this.account.id);
|
||||
let deletedTxs$ = this.txService.getDeletedTransactionsByAccount(this.account.id);
|
||||
|
||||
txs$.mergeMap(txs => txs).concat(newTxs$)
|
||||
.filter(tx => {
|
||||
let data = tx.getData();
|
||||
let reconciled = true;
|
||||
|
||||
let reconciledSplits = Object.keys(data.reconciledSplits || []).map(index => parseInt(index));
|
||||
tx.splits.forEach((split, index) => {
|
||||
if(split.accountId === this.account.id && reconciledSplits.indexOf(index) === -1) {
|
||||
reconciled = false;
|
||||
}
|
||||
});
|
||||
|
||||
return !reconciled;
|
||||
})
|
||||
.subscribe(tx => {
|
||||
// insert tx into list
|
||||
this.addTransaction(tx);
|
||||
});
|
||||
|
||||
deletedTxs$.subscribe(tx => {
|
||||
this.removeTransaction(tx);
|
||||
// remove tx from list
|
||||
});
|
||||
}
|
||||
|
||||
addTransaction(tx: Transaction) {
|
||||
tx.splits.forEach((split, index) => {
|
||||
if(split.accountId !== this.account.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let item = new TxItem();
|
||||
item.tx = tx;
|
||||
item.amount = Math.abs(split.amount);
|
||||
item.splitIndex = index;
|
||||
item.reconciled = false;
|
||||
|
||||
if(split.amount >= 0) {
|
||||
this.inflows.push(item);
|
||||
} else {
|
||||
this.outflows.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
this.sort();
|
||||
}
|
||||
|
||||
removeTransaction(tx: Transaction) {
|
||||
for(let i = 0; i < this.inflows.length; i++) {
|
||||
let item = this.inflows[i];
|
||||
|
||||
if(item.tx.id === tx.id) {
|
||||
this.inflows.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 0; i < this.outflows.length; i++) {
|
||||
let item = this.outflows[i];
|
||||
|
||||
if(item.tx.id === tx.id) {
|
||||
this.outflows.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort() {
|
||||
this.inflows.sort((a, b) => {
|
||||
let dateDiff = a.tx.date.getTime() - b.tx.date.getTime();
|
||||
|
||||
if(dateDiff) {
|
||||
return dateDiff;
|
||||
}
|
||||
|
||||
let insertedDiff = a.tx.inserted.getTime() - b.tx.inserted.getTime();
|
||||
|
||||
if(insertedDiff) {
|
||||
return insertedDiff;
|
||||
}
|
||||
});
|
||||
|
||||
this.outflows.sort((a, b) => {
|
||||
let dateDiff = a.tx.date.getTime() - b.tx.date.getTime();
|
||||
|
||||
if(dateDiff) {
|
||||
return dateDiff;
|
||||
}
|
||||
|
||||
let insertedDiff = a.tx.inserted.getTime() - b.tx.inserted.getTime();
|
||||
|
||||
if(insertedDiff) {
|
||||
return insertedDiff;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggleReconciled(item: TxItem) {
|
||||
item.reconciled = !item.reconciled;
|
||||
|
||||
let data = item.tx.getData();
|
||||
|
||||
if(item.reconciled) {
|
||||
if(!data.reconciledSplits) {
|
||||
data.reconciledSplits = {};
|
||||
}
|
||||
|
||||
data.reconciledSplits[item.splitIndex] = this.reconciliation.endDate;
|
||||
|
||||
if(this.account.debitBalance) {
|
||||
this.reconciled += item.tx.splits[item.splitIndex].amount;
|
||||
} else {
|
||||
this.reconciled -= item.tx.splits[item.splitIndex].amount;
|
||||
}
|
||||
} else {
|
||||
if(!data.reconciledSplits) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete data.reconciledSplits[item.splitIndex];
|
||||
if(this.account.debitBalance) {
|
||||
this.reconciled -= item.tx.splits[item.splitIndex].amount;
|
||||
} else {
|
||||
this.reconciled += item.tx.splits[item.splitIndex].amount;
|
||||
}
|
||||
}
|
||||
|
||||
item.tx.setData(data);
|
||||
}
|
||||
|
||||
save() {
|
||||
if(this.balance !== this.reconciled) {
|
||||
this.error = new AppError('Reconciled amount doesn\'t match balance');
|
||||
return;
|
||||
}
|
||||
|
||||
this.sessionService.setLoading(true);
|
||||
|
||||
let txs = this.inflows.filter(item => item.reconciled).map(item => item.tx);
|
||||
|
||||
txs = txs.concat(this.outflows.filter(item => item.reconciled).map(item => item.tx));
|
||||
|
||||
Observable.from(txs).mergeMap(tx => {
|
||||
let oldId = tx.id;
|
||||
tx.id = Util.newGuid();
|
||||
|
||||
return this.txService.putTransaction(oldId, tx);
|
||||
}, 8).subscribe(tx => {
|
||||
this.log.debug('Saved tx ' + tx.id);
|
||||
}, err => {
|
||||
this.error = err;
|
||||
this.sessionService.setLoading(false);
|
||||
}, () => {
|
||||
this.sessionService.setLoading(false);
|
||||
this.activeModal.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
62
src/app/reconcile/reconcile.html
Normal file
62
src/app/reconcile/reconcile.html
Normal file
@@ -0,0 +1,62 @@
|
||||
<h1>Reconcile Account</h1>
|
||||
|
||||
<div class="section">
|
||||
<h2>Select Account</h2>
|
||||
|
||||
<form class="form-inline" [formGroup]="accountForm" (ngSubmit)="onChooseAccount()">
|
||||
<div class="form-group mx-sm-3 mb-2">
|
||||
<label for="accountId" class="sr-only">Account</label>
|
||||
<select class="form-control" id="accountId" formControlName="accountId">
|
||||
<option *ngFor="let account of selectAccounts" [value]="account.id">
|
||||
{{account.fullName | slice:0:50}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary mb-2">Select Account</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2>New Reconciliation</h2>
|
||||
|
||||
<form [formGroup]="newReconcile" (ngSubmit)="startReconcile()">
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label">Start Date</label>
|
||||
<div class="col-sm-9">
|
||||
<input formControlName="startDate" id="startDate" type="date" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="currency" class="col-sm-3 col-form-label">Beginning Balance</label>
|
||||
<div class="col-sm-9">
|
||||
<input formControlName="startBalance" id="startBalance" type="text" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-sm-3 col-form-label">End Date</label>
|
||||
<div class="col-sm-9">
|
||||
<input formControlName="endDate" id="endDate" type="date" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="currency" class="col-sm-3 col-form-label">Ending Balance</label>
|
||||
<div class="col-sm-9">
|
||||
<input formControlName="endBalance" id="endBalance" type="text" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
<p *ngIf="error" class="error">{{error.message}}</p>
|
||||
<button type="submit" class="btn btn-primary" [disabled]="!newReconcile.valid">Start Reconciliation</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2>Past Reconciliations</h2>
|
||||
|
||||
<div *ngFor="let rec of pastReconciliations">
|
||||
Period: {{rec.startDate | date:"M/d/y"}} - {{rec.endDate | date:"M/d/y"}}<br>
|
||||
Beginning Balance: {{rec.startBalance | currencyFormat:account.precision:account.currency}}<br>
|
||||
Ending Balance: {{rec.endBalance | currencyFormat:account.precision:account.currency}}<br>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
27
src/app/reconcile/reconcile.module.ts
Normal file
27
src/app/reconcile/reconcile.module.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { AppRoutingModule } from '../app-routing.module';
|
||||
|
||||
import { ReconcilePage } from './reconcile';
|
||||
import { ReconcileModal } from './reconcile-modal';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
ReconcilePage,
|
||||
ReconcileModal
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
NgbModule,
|
||||
ReactiveFormsModule,
|
||||
SharedModule,
|
||||
AppRoutingModule
|
||||
],
|
||||
providers: [],
|
||||
entryComponents: [ReconcileModal]
|
||||
})
|
||||
export class ReconcileModule { }
|
||||
226
src/app/reconcile/reconcile.ts
Normal file
226
src/app/reconcile/reconcile.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Logger } from '../core/logger';
|
||||
import { Router } from '@angular/router';
|
||||
import {
|
||||
FormGroup,
|
||||
FormControl,
|
||||
Validators,
|
||||
FormBuilder,
|
||||
AbstractControl,
|
||||
ValidationErrors
|
||||
} from '@angular/forms';
|
||||
import { AccountService } from '../core/account.service';
|
||||
import { OrgService } from '../core/org.service';
|
||||
import { TransactionService } from '../core/transaction.service';
|
||||
import { Account, AccountApi, AccountTree } from '../shared/account';
|
||||
import { Transaction } from '../shared/transaction';
|
||||
import { AppError } from '../shared/error';
|
||||
import { Util } from '../shared/util';
|
||||
import { NgbModal, ModalDismissReasons } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ReconcileModal } from './reconcile-modal';
|
||||
import { Reconciliation } from './reconciliation';
|
||||
|
||||
@Component({
|
||||
selector: 'app-reconcile',
|
||||
templateUrl: 'reconcile.html'
|
||||
})
|
||||
export class ReconcilePage {
|
||||
|
||||
public accountForm: FormGroup;
|
||||
public newReconcile: FormGroup;
|
||||
public selectAccounts: any[];
|
||||
public account: Account;
|
||||
public pastReconciliations: Reconciliation[];
|
||||
public unreconciledTxs: Transaction[];
|
||||
public error: AppError;
|
||||
private accountTree: AccountTree;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private log: Logger,
|
||||
private accountService: AccountService,
|
||||
private orgService: OrgService,
|
||||
private txService: TransactionService,
|
||||
private fb: FormBuilder,
|
||||
private modalService: NgbModal) {
|
||||
|
||||
let org = this.orgService.getCurrentOrg();
|
||||
this.accountForm = fb.group({
|
||||
'accountId': [null, Validators.required]
|
||||
});
|
||||
|
||||
this.newReconcile = fb.group({
|
||||
'startDate': ['', Validators.required],
|
||||
'startBalance': [{value: 0, disabled: true}, Validators.required],
|
||||
'endDate': ['', Validators.required],
|
||||
'endBalance': [0, Validators.required]
|
||||
});
|
||||
|
||||
this.accountService.getAccountTree().subscribe(tree => {
|
||||
this.accountTree = tree;
|
||||
this.selectAccounts = tree.getFlattenedAccounts();
|
||||
});
|
||||
}
|
||||
|
||||
onChooseAccount() {
|
||||
let account = this.accountTree.accountMap[this.accountForm.value.accountId];
|
||||
|
||||
if(!account) {
|
||||
this.error = new AppError('Invalid account');
|
||||
return;
|
||||
}
|
||||
|
||||
this.account = account;
|
||||
|
||||
this.processTransactions();
|
||||
}
|
||||
|
||||
startReconcile() {
|
||||
let value = this.newReconcile.getRawValue();
|
||||
|
||||
let rec = new Reconciliation();
|
||||
rec.startDate = Util.getDateFromLocalDateString(value.startDate);
|
||||
rec.endDate = Util.getDateFromLocalDateString(value.endDate);
|
||||
rec.startBalance = Math.round(parseFloat(value.startBalance) * Math.pow(10, this.account.precision));
|
||||
rec.endBalance = Math.round(parseFloat(value.endBalance) * Math.pow(10, this.account.precision));
|
||||
|
||||
this.log.debug(rec);
|
||||
|
||||
let modal = this.modalService.open(ReconcileModal, {size: 'lg'});
|
||||
|
||||
modal.componentInstance.setData(this.account, rec, this.unreconciledTxs);
|
||||
|
||||
modal.result.then((result) => {
|
||||
this.log.debug('reconcile modal save');
|
||||
this.pastReconciliations.unshift(rec);
|
||||
|
||||
this.newReconcile.patchValue(
|
||||
{
|
||||
startDate: Util.getLocalDateString(rec.endDate),
|
||||
startBalance: rec.endBalance / Math.pow(10, this.account.precision),
|
||||
endBalance: 0,
|
||||
endDate: ''
|
||||
}
|
||||
);
|
||||
}, (reason) => {
|
||||
this.log.debug('cancel reconcile modal');
|
||||
});
|
||||
}
|
||||
|
||||
processTransactions() {
|
||||
// Get all transactions for account
|
||||
// Figure out reconciliations
|
||||
// startDate is date of first transaction
|
||||
// add up reconciled splits for given endDate to get endBalance
|
||||
// sort by most recent first
|
||||
// most recent endDate is used for startDate
|
||||
// most recent endBalance is used for startBalance
|
||||
// guess at next endDate
|
||||
|
||||
this.unreconciledTxs = [];
|
||||
this.pastReconciliations = [];
|
||||
|
||||
this.txService.getTransactionsByAccount(this.account.id).subscribe(txs => {
|
||||
let reconcileMap: {[date: number]: Reconciliation} = {};
|
||||
|
||||
let firstStartDate: Date = null;
|
||||
let firstEndDate: Date = null;
|
||||
|
||||
txs.forEach(tx => {
|
||||
if(!firstStartDate || (!firstEndDate && tx.date < firstStartDate)) {
|
||||
firstStartDate = tx.date;
|
||||
}
|
||||
|
||||
let data = tx.getData();
|
||||
|
||||
if(!data.reconciledSplits) {
|
||||
this.unreconciledTxs.push(tx);
|
||||
return;
|
||||
}
|
||||
|
||||
let reconciled = true;
|
||||
let splitIndexes = Object.keys(data.reconciledSplits).map(index => parseInt(index));
|
||||
|
||||
tx.splits.forEach((split, index) => {
|
||||
if(split.accountId !== this.account.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(splitIndexes.indexOf(index) === -1) {
|
||||
reconciled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let endDate = new Date(data.reconciledSplits[index]);
|
||||
|
||||
if(!firstEndDate || endDate < firstEndDate) {
|
||||
firstEndDate = endDate;
|
||||
firstStartDate = new Date(tx.date);
|
||||
}
|
||||
|
||||
if(endDate.getTime() === firstEndDate.getTime() && tx.date < firstStartDate) {
|
||||
firstStartDate = new Date(tx.date);
|
||||
}
|
||||
|
||||
if(!reconcileMap[endDate.getTime()]) {
|
||||
reconcileMap[endDate.getTime()] = new Reconciliation();
|
||||
reconcileMap[endDate.getTime()].endDate = endDate;
|
||||
reconcileMap[endDate.getTime()].net = 0;
|
||||
}
|
||||
|
||||
let r = reconcileMap[endDate.getTime()];
|
||||
|
||||
if(this.account.debitBalance) {
|
||||
r.net += split.amount;
|
||||
} else {
|
||||
r.net -= split.amount;
|
||||
}
|
||||
});
|
||||
|
||||
if(!reconciled) {
|
||||
this.unreconciledTxs.push(tx);
|
||||
}
|
||||
});
|
||||
|
||||
// Figure out starting date, beginning balance and ending balance
|
||||
let dates = Object.keys(reconcileMap).sort((a, b) => {
|
||||
return parseInt(a) - parseInt(b);
|
||||
}).map(time => {
|
||||
return new Date(parseInt(time));
|
||||
});
|
||||
|
||||
if(!dates.length) {
|
||||
if(firstStartDate) {
|
||||
this.newReconcile.patchValue({startDate: Util.getLocalDateString(firstStartDate)});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let firstRec = reconcileMap[dates[0].getTime()];
|
||||
firstRec.startDate = firstStartDate;
|
||||
firstRec.startBalance = 0;
|
||||
firstRec.endBalance = firstRec.net;
|
||||
|
||||
this.pastReconciliations.unshift(firstRec);
|
||||
|
||||
let lastRec = firstRec;
|
||||
|
||||
for(let i = 1; i < dates.length; i++) {
|
||||
let rec = reconcileMap[dates[i].getTime()];
|
||||
rec.startDate = new Date(lastRec.endDate);
|
||||
rec.startBalance = lastRec.endBalance;
|
||||
rec.endBalance = rec.startBalance + rec.net;
|
||||
this.pastReconciliations.unshift(rec);
|
||||
lastRec = rec;
|
||||
}
|
||||
|
||||
this.newReconcile.patchValue(
|
||||
{
|
||||
startDate: Util.getLocalDateString(lastRec.endDate),
|
||||
startBalance: lastRec.endBalance / Math.pow(10, this.account.precision)
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
7
src/app/reconcile/reconciliation.ts
Normal file
7
src/app/reconcile/reconciliation.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export class Reconciliation {
|
||||
startDate: Date;
|
||||
startBalance: number;
|
||||
endDate: Date;
|
||||
endBalance: number;
|
||||
net: number;
|
||||
}
|
||||
Reference in New Issue
Block a user