diff --git a/src/app/transaction/new.html b/src/app/transaction/new.html
new file mode 100644
index 0000000..ba1a78e
--- /dev/null
+++ b/src/app/transaction/new.html
@@ -0,0 +1,177 @@
+
New Transaction
+
+
\ No newline at end of file
diff --git a/src/app/transaction/new.scss b/src/app/transaction/new.scss
new file mode 100644
index 0000000..eeaa310
--- /dev/null
+++ b/src/app/transaction/new.scss
@@ -0,0 +1,16 @@
+@import '../../sass/variables';
+
+.accordion {
+ .btn-link {
+ color: $black;
+ }
+ .card {
+ margin: 0;
+ }
+ .btn-group-toggle {
+ .btn-primary {
+ background-color: transparent;
+ color: $blue;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/transaction/new.ts b/src/app/transaction/new.ts
new file mode 100644
index 0000000..263496c
--- /dev/null
+++ b/src/app/transaction/new.ts
@@ -0,0 +1,292 @@
+import { Component, ViewEncapsulation, ViewChild } from '@angular/core';
+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 { Account, AccountApi, AccountTree } from '../shared/account';
+import { Util } from '../shared/util';
+import { AppError } from '../shared/error';
+
+@Component({
+ selector: 'app-txnew',
+ templateUrl: 'new.html',
+ styleUrls: ['./new.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class NewTransactionPage {
+
+ public form: FormGroup;
+ public type: string;
+ public typeDescription: string;
+ public secondDescription: string;
+ public firstAccountPrimary: string;
+ public selectAccounts: any[];
+ public error: AppError;
+ public numAccountsShown: number;
+ public expenseAccounts: any[] = [];
+ public incomeAccounts: any[] = [];
+ public paymentAccounts: any[] = [];
+ public assetAccounts: any[] = [];
+ public expenseAccountsAll: any[] = [];
+ public incomeAccountsAll: any[] = [];
+ public paymentAccountsAll: any[] = [];
+ public assetAccountsAll: any[] = [];
+ private accountTree: AccountTree;
+ @ViewChild('acc') acc: any;
+
+ constructor(
+ private router: Router,
+ private accountService: AccountService,
+ private orgService: OrgService,
+ private fb: FormBuilder) {
+
+ this.numAccountsShown = 3;
+
+ let org = this.orgService.getCurrentOrg();
+
+ let dateString = Util.getLocalDateString(new Date());
+ this.form = fb.group({
+ 'type': ['', Validators.required],
+ 'firstAccountPrimary': [null, Validators.required],
+ 'firstAccountSecondary': [null],
+ 'secondAccountPrimary': [null, Validators.required],
+ 'secondAccountSecondary': [null],
+ 'amount': [null, Validators.required],
+ 'description': [''],
+ 'date': [dateString, Validators.required]
+ });
+
+ this.accountService.getAccountTree().subscribe(tree => {
+ this.accountTree = tree;
+ this.selectAccounts = tree.getFlattenedAccounts().filter(account => {
+ let isAsset = account.fullName.match(/^Assets/);
+ let isLiability = account.fullName.match(/^Liabilities/);
+ return !account.children.length && (isAsset || isLiability);
+ });
+
+ this.getExpenseAccounts();
+ this.getIncomeAccounts();
+ this.getPaymentAccounts();
+ this.getAssetAccounts();
+ });
+
+ this.form.get('type').valueChanges.subscribe(val => {
+ if(val === 'openingBalance') {
+ this.acc.collapse('toggle-1');
+ this.acc.expand('toggle-2');
+ this.form.patchValue({description: 'Opening Balance'});
+ }
+ })
+
+ this.form.get('firstAccountPrimary').valueChanges.subscribe(val => {
+ if(val === 'other') {
+ return;
+ }
+
+ this.acc.collapse('toggle-1');
+ this.acc.expand('toggle-2');
+ });
+
+ this.form.get('firstAccountSecondary').valueChanges.subscribe(val => {
+ this.acc.collapse('toggle-1');
+ this.acc.expand('toggle-2');
+ });
+
+ this.form.get('secondAccountPrimary').valueChanges.subscribe(val => {
+ if(val === 'other') {
+ return;
+ }
+
+ this.acc.collapse('toggle-2');
+ this.acc.expand('toggle-3');
+ this.acc.expand('toggle-4');
+ });
+
+ this.form.get('secondAccountSecondary').valueChanges.subscribe(val => {
+ this.acc.collapse('toggle-2');
+ this.acc.expand('toggle-3');
+ this.acc.expand('toggle-4');
+ });
+ }
+
+ onSubmit() {
+ let account = new AccountApi(this.form.value);
+ account.id = Util.newGuid();
+ let parentAccount = this.accountTree.accountMap[account.parent];
+
+ if(!parentAccount) {
+ this.error = new AppError('Invalid parent account');
+ return;
+ }
+
+ let org = this.orgService.getCurrentOrg();
+ account.orgId = org.id;
+ account.debitBalance = parentAccount.debitBalance;
+ account.currency = account.currency || parentAccount.currency;
+ account.precision = account.precision !== null ? account.precision : parentAccount.precision;
+
+ this.accountService.newAccount(account)
+ .subscribe(
+ account => {
+ this.router.navigate(['/accounts']);
+ },
+ err => {
+ this.error = err;
+ }
+ );
+ }
+
+ getExpenseAccounts() {
+ // Get most used expense accounts
+
+ let expenseAccounts = this.accountTree.getAccountAtoms(
+ this.accountTree.getAccountByName('Expenses', 1)
+ );
+
+ this.processAccounts('expenseAccounts', expenseAccounts, 2);
+ }
+
+ getIncomeAccounts() {
+ // Get most used income accounts
+
+ let incomeAccounts = this.accountTree.getAccountAtoms(
+ this.accountTree.getAccountByName('Income', 1)
+ );
+
+ this.processAccounts('incomeAccounts', incomeAccounts, 2);
+ }
+
+ getPaymentAccounts() {
+ // Get most used asset / liability accounts
+
+ let assetAccounts = this.accountTree.getAccountAtoms(
+ this.accountTree.getAccountByName('Assets', 1)
+ );
+
+ let liabilityAccounts = this.accountTree.getAccountAtoms(
+ this.accountTree.getAccountByName('Liabilities', 1)
+ );
+
+ let paymentAccounts = assetAccounts.concat(liabilityAccounts);
+
+ this.processAccounts('paymentAccounts', paymentAccounts, 3);
+ }
+
+ getAssetAccounts() {
+ // Get most used asset accounts
+
+ let assetAccounts = this.accountTree.getAccountAtoms(
+ this.accountTree.getAccountByName('Assets', 1)
+ );
+
+ this.processAccounts('assetAccounts', assetAccounts, 3);
+ }
+
+ processAccounts(variable, data, depth) {
+ data.sort((a, b) => {
+ return b.recentTxCount - a.recentTxCount;
+ });
+
+ let dataWithLabels = data.map((account, i) => {
+ return {
+ id: account.id,
+ name: account.name,
+ label: this.accountTree.getAccountLabel(account, depth),
+ hidden: i < this.numAccountsShown ? false: true
+ }
+ });
+
+ let firstAccounts = dataWithLabels.slice(0, this.numAccountsShown);
+ let nextAccounts = dataWithLabels.slice(this.numAccountsShown);
+
+ nextAccounts.sort((a, b) => {
+ let aAlpha = a.label.charCodeAt(0) >= 65 && a.label.charCodeAt(0) <= 122;
+ let bAlpha = b.label.charCodeAt(0) >= 65 && b.label.charCodeAt(0) <= 122;
+
+ if(!aAlpha && bAlpha) {
+ return 1;
+ }
+
+ if(aAlpha && !bAlpha) {
+ return -1;
+ }
+
+ if(a.label > b.label) {
+ return 1;
+ }
+
+ if(a.label < b.label) {
+ return -1;
+ }
+
+ return 0;
+ });
+
+ this[variable] = firstAccounts;
+ this[variable + 'All'] = nextAccounts;
+ }
+
+ getToggle1Title() {
+ let account = this.getFirstAccount();
+
+ let str = 'What type of transaction? ';
+
+ let type = this.form.value.type;
+
+ if(type) {
+ str += type.charAt(0).toUpperCase() + type.substr(1);
+ if(account) {
+ str += ' (' + account.name + ')';
+ }
+ }
+
+ return str;
+ }
+
+ getTitle() {
+ let account = this.getSecondAccount();
+
+ if(this.form.value.type === 'income') {
+ return 'Where was the money sent? ' + (account ? account.name : '');
+ } else if(this.form.value.type === 'openingBalance') {
+ return 'What account? ' + (account ? account.name : '');
+ }
+
+ return 'How did you pay? ' + (account ? account.name : '');
+ }
+
+ getFirstAccount() {
+ if(!this.accountTree) {
+ return null;
+ }
+
+ if(this.form.value.firstAccountPrimary && this.form.value.firstAccountPrimary !== 'other') {
+ return this.accountTree.accountMap[this.form.value.firstAccountPrimary];
+ }
+
+ return this.accountTree.accountMap[this.form.value.firstAccountSecondary];
+ }
+
+ getSecondAccount() {
+ if(!this.accountTree) {
+ return null;
+ }
+
+ if(this.form.value.secondAccountPrimary && this.form.value.secondAccountPrimary !== 'other') {
+ return this.accountTree.accountMap[this.form.value.secondAccountPrimary];
+ }
+
+ return this.accountTree.accountMap[this.form.value.secondAccountSecondary];
+ }
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/app/transaction/transaction.module.ts b/src/app/transaction/transaction.module.ts
index 8acc922..42e0262 100644
--- a/src/app/transaction/transaction.module.ts
+++ b/src/app/transaction/transaction.module.ts
@@ -8,6 +8,7 @@ import { AppRoutingModule } from '../app-routing.module';
import { AdvancedEdit } from './advancededit';
import { Autocomplete } from './autocomplete';
import { Breadcrumbs } from './breadcrumbs';
+import { NewTransactionPage } from './new';
@NgModule({
@@ -15,7 +16,8 @@ import { Breadcrumbs } from './breadcrumbs';
TxListPage,
AdvancedEdit,
Autocomplete,
- Breadcrumbs
+ Breadcrumbs,
+ NewTransactionPage
],
imports: [
BrowserModule,
diff --git a/src/sass/variables.scss b/src/sass/variables.scss
index 9a9cf97..50a71f2 100644
--- a/src/sass/variables.scss
+++ b/src/sass/variables.scss
@@ -1,4 +1,5 @@
/* Variables */
+$black: #466e9a;
$blue: #08f;
$positive: #0a0;
$negative: #c00;
\ No newline at end of file