res).users);
+ }
+
+ getContribution(): FormGroup {
+ const date = new Date();
+ const dte = date.getFullYear() + "-" + (date.getMonth()<10?'0':'') + (date.getMonth() + 1) + "-" + (date.getDate()<10?'0':'') + date.getDate();
+
+ return this.formBuilder.group({
+ date: [dte, Validators.required],
+ contributorId: [0, Validators.required],
+ transactions: this.formBuilder.array([this.getTransaction(), this.getTransaction()])
+ });
+ }
+
+ getTransaction(): FormGroup {
+ return this.formBuilder.group({
+ amount: [null, Validators.required],
+ checkNumber: [''],
+ description: [''],
+ fundId: [1, [Validators.required, Validators.min(1), Validators.max(2)]],
+ taxYear: [(new Date()).getFullYear(), Validators.required],
+ typeId: [1, [Validators.required, Validators.min(1), Validators.max(2)]],
+ }, { validators: checkNumberValidator });
+ }
+
+ onContributorChange(event) {
+ console.log(event);
+ if (event && event.value) {
+ if (event.value === -1) {
+ // Open new user dialog
+ }
+ event = event.display;
+ }
+ const filterValue = event.toLowerCase();
+ this.filteredContributors = this.contributors.filter(option => option.display.toLowerCase().indexOf(filterValue) >= 0);
+ if (this.filteredContributors.length === 0) {
+ this.filteredContributors.push({value:-1, display:'Add New Contributor'});
+ }
+ }
+
+ contirbutorDisplayFn(contributor?: {value:number,display:string}): string | undefined {
+ return contributor ? contributor.display : undefined;
+ }
+
+ addTransaction(contributorPosition: number) {
+ const contributors = this.form.get('contributions') as FormArray;
+ const transactions = contributors.controls[contributorPosition].get('transactions') as FormArray;
+ transactions.push(this.getTransaction());
+ }
+
+ addContributor() {
+ const contributors = this.form.get('contributions') as FormArray;
+ contributors.push(this.getContribution());
+ }
+
+ deleteContributor(index: number) {
+ const contributors = this.form.get('contributions') as FormArray;
+ contributors.removeAt(index);
+ }
+
+ deleteTransaction(contributorIndex: number, transactionIndex: number) {
+ const contributors = this.form.get('contributions') as FormArray;
+ const transactions = contributors.controls[contributorIndex].get('transactions') as FormArray;
+ transactions.removeAt(transactionIndex);
+ }
+
+ contributorName(index: number) {
+ const contributors = this.form.get('contributions') as FormArray;
+ const contributor = contributors.controls[index].get('contributorId');
+ if (contributor && contributor.value.display) {
+ return contributor.value.display;
+ }
+ }
+
+ contributorTotal(index: number, fundId: number) {
+ const contributors = this.form.get('contributions') as FormArray;
+ const transactions = contributors.controls[index].get('transactions').value;
+ var sum = 0;
+ transactions.forEach(e => {
+ if (e.fundId === fundId || fundId === 0) {
+ sum += e.amount;
+ }
+ });
+ return sum;
+ }
+
+ combinedTotal(fundId: number) {
+ const contributors = this.form.get('contributions').value;
+ var sum = 0;
+ contributors.forEach(c => {
+ c.transactions.forEach(t => {
+ if (t.fundId === fundId || fundId === 0) {
+ sum += +t.amount;
+ }
+ });
+ });
+ return sum;
+ }
+
+
+}
diff --git a/Client/src/app/components/add-transaction-page/check-number-validator.ts b/Client/src/app/components/add-transaction-page/check-number-validator.ts
new file mode 100644
index 0000000..05391aa
--- /dev/null
+++ b/Client/src/app/components/add-transaction-page/check-number-validator.ts
@@ -0,0 +1,11 @@
+import { ValidatorFn, FormGroup, ValidationErrors } from '@angular/forms';
+
+export const checkNumberValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
+ const type = control.get('typeId').value;
+ const numb = control.get('checkNumber').value;
+ if (type === 2 && (!numb || numb === '')) {
+ return { 'checkNumber': true };
+ } else {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/Client/src/app/components/add-transaction-page/contribution.ts b/Client/src/app/components/add-transaction-page/contribution.ts
new file mode 100644
index 0000000..1a9006d
--- /dev/null
+++ b/Client/src/app/components/add-transaction-page/contribution.ts
@@ -0,0 +1,7 @@
+import { Transaction } from './transaction';
+
+export class Contribution {
+ date: string;
+ contributorId: number;
+ transactions: Transaction[] = [];
+}
\ No newline at end of file
diff --git a/Client/src/app/components/add-transaction-page/transaction.ts b/Client/src/app/components/add-transaction-page/transaction.ts
new file mode 100644
index 0000000..7c8fe60
--- /dev/null
+++ b/Client/src/app/components/add-transaction-page/transaction.ts
@@ -0,0 +1,10 @@
+export class Transaction {
+ date: string;
+ amount: number;
+ checkNumber: string;
+ contributorId: number;
+ description: string;
+ fundId: number;
+ taxYear: number;
+ typeId: number;
+}
\ No newline at end of file
diff --git a/Client/src/app/components/members-page/members-page.component.html b/Client/src/app/components/members-page/members-page.component.html
index fd76479..fba14e9 100644
--- a/Client/src/app/components/members-page/members-page.component.html
+++ b/Client/src/app/components/members-page/members-page.component.html
@@ -6,6 +6,9 @@
+
+
+
diff --git a/Client/src/app/components/members-page/members-page.component.ts b/Client/src/app/components/members-page/members-page.component.ts
index b378ada..3ffd05b 100644
--- a/Client/src/app/components/members-page/members-page.component.ts
+++ b/Client/src/app/components/members-page/members-page.component.ts
@@ -5,6 +5,8 @@ import { UserRights } from 'src/app/constants/user-rights';
import { MatDialog } from '@angular/material';
import { AddUserPopupComponent } from '../popups/add-user-popup/add-user-popup.component';
import { take } from 'rxjs/operators';
+import { TransactionService } from 'src/app/services/transaction.service';
+import { LoginPopupComponent } from '../popups/login-popup/login-popup.component';
@Component({
selector: 'app-members-page',
@@ -19,14 +21,14 @@ export class MembersPageComponent implements OnInit {
return this.user && this.user.canDo(UserRights.userAdd);
}
- constructor(private loginService: LoginService, private matDialog: MatDialog) { }
+ constructor(private loginService: LoginService, private matDialog: MatDialog, private tservice: TransactionService) { }
ngOnInit() {
this.loginService.isLoggedIn(true).pipe(take(1)).subscribe(r => {
this.loading = false;
console.log(r);
if (r == false) {
- this.addUserModal();
+ this.matDialog.open(LoginPopupComponent);
}
})
this.loginService.user.subscribe(u => this.user = u);
@@ -35,5 +37,4 @@ export class MembersPageComponent implements OnInit {
addUserModal() {
let dialog = this.matDialog.open(AddUserPopupComponent);
}
-
}
diff --git a/Client/src/app/constants/urls.ts b/Client/src/app/constants/urls.ts
index 0d54698..e9f3238 100644
--- a/Client/src/app/constants/urls.ts
+++ b/Client/src/app/constants/urls.ts
@@ -12,7 +12,9 @@ export const SERMON_ADD_URL = environment.baseUrl + "/api2/sermons/a/";
export const SERMON_DELETE_URL = environment.baseUrl + "/api2/sermons/a/";
export const SERMON_UPDATE_URL = environment.baseUrl + "/api2/sermons/a/";
export const SERMON_DOWNLOAD_URL = environment.baseUrl + "/api2/sermons/download/";
+export const TRANSACTION_CREATE_URL = environment.baseUrl + "/api2/transactions/a/";
export const USER_CREATE_URL = environment.baseUrl + "/api2/users/a/";
+export const USER_GET_ALL_URL = environment.baseUrl + "/api2/users/a";
export const LOGIN_URL = environment.baseUrl + '/api2/login';
export const LOGIN_VALIDATE_TOKEN = '';
export const EMAIL_URL = environment.baseUrl + "/api2/email";
diff --git a/Client/src/app/services/transaction.service.ts b/Client/src/app/services/transaction.service.ts
new file mode 100644
index 0000000..69c7a00
--- /dev/null
+++ b/Client/src/app/services/transaction.service.ts
@@ -0,0 +1,51 @@
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpErrorResponse } from '@angular/common/http';
+
+import { Observable, Subject, throwError, of } from 'rxjs';
+import { catchError, map, tap, first } from 'rxjs/operators';
+
+import { TRANSACTION_CREATE_URL } from '../constants/urls';
+
+@Injectable()
+export class TransactionService {
+
+ private options: {};
+
+ constructor(private httpClient: HttpClient){
+ this.options = {
+ withCredentials: true
+ };
+ }
+
+ create(date: Date, typeId: number, fundId: number, contributorId: number, description: string, amount: number, taxYear: number) {
+ const body = {
+ date: date,
+ typeId: typeId,
+ fundId: fundId,
+ contributorId: contributorId,
+ description: description,
+ amount: amount,
+ taxYear: taxYear
+ };
+ console.log(body);
+ return this.httpClient.post(TRANSACTION_CREATE_URL, body, this.options)
+ .pipe(tap(res => console.log(res)), catchError(this.handleError));
+ }
+
+ private handleError(error: HttpErrorResponse) {
+ if (error.error instanceof ErrorEvent) {
+ // A client-side or network error occurred. Handle it accordingly.
+ console.error('An error occurred:', error.error.message);
+ } else {
+ // The backend returned an unsuccessful response code.
+ // The response body may contain clues as to what went wrong,
+ console.error(
+ `Backend returned code ${error.status}, ` +
+ `body was: ${error.error}`);
+ console.error(error);
+ }
+ // return an observable with a user-facing error message
+ return throwError((error && error.error && error.error.message) ? error.error.message :
+ 'Something bad happened; please try again later.');
+ };
+}
diff --git a/Client/src/app/services/user.service.ts b/Client/src/app/services/user.service.ts
index e80ff89..eb1a624 100644
--- a/Client/src/app/services/user.service.ts
+++ b/Client/src/app/services/user.service.ts
@@ -4,7 +4,7 @@ import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, Subject, throwError, of } from 'rxjs';
import { catchError, map, tap, first } from 'rxjs/operators';
-import { LOGIN_URL, USER_CREATE_URL } from '../constants/urls';
+import { LOGIN_URL, USER_CREATE_URL, USER_GET_ALL_URL } from '../constants/urls';
@Injectable()
export class UserService {
@@ -17,6 +17,11 @@ export class UserService {
};
}
+ getAll(){
+ return this.httpClient.get(USER_GET_ALL_URL, this.options)
+ .pipe(tap(res => console.log(res)), catchError(this.handleError));
+ }
+
create(firstName: string, lastName: string, street: string, city: string, state: string, zip: string, country: string) {
const body = {
firstName: firstName,
diff --git a/Server/src/authentication/login.js b/Server/src/authentication/login.js
index d517fce..60318fd 100644
--- a/Server/src/authentication/login.js
+++ b/Server/src/authentication/login.js
@@ -100,6 +100,9 @@ function verifyToken(userId, tokenId, token, callback){
return;
}
user.rights = rights;
+ user.canDo = function(activity) {
+ return this.rights.includes(activity);
+ }.bind(user);
callback(null, isValid, user);
});
diff --git a/Server/src/database/transactions.js b/Server/src/database/transactions.js
new file mode 100644
index 0000000..eb612be
--- /dev/null
+++ b/Server/src/database/transactions.js
@@ -0,0 +1,41 @@
+var connectionAsync = require("./connectionasync");
+
+exports.getAll = async function() {
+ const queryResult = await connectionAsync.query('SELECT * FROM Transactions WHERE DeletedDate IS NULL;');
+ const result = [];
+ if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
+ for(var i = 0 ; i < qqueryResult.rows.length; i++) {
+ const row = queryResult.rows[i];
+ const trans = {};
+ trans.id = row.Id;
+ trans.date = row.Date;
+ trans.typeId = row.TypeId;
+ trans.checkNumber = row.CheckNumber;
+ trans.contributorId = row.ContributorId;
+ trans.fundId = row.FundId;
+ trans.description = row.Description;
+ trans.amount = row.Amount;
+ trans.taxYear = row.TaxYear;
+ result.push(trans);
+ }
+ }
+ return result;
+}
+
+exports.add = async function(date, typeId, check, contributorId, fundId, description, amount, taxYear) {
+ const newTrans = {
+ Date: date,
+ TypeId: typeId,
+ CheckNumber: check,
+ ContributorId: contributorId,
+ FundId: fundId,
+ Description: description,
+ Amount: amount,
+ TaxYear: taxYear
+ };
+ const newTransResult = await connectionAsync.nonQuery('INSERT INTO Transactions Set ?', newTrans);
+ return newTransResult;
+}
+
+
+
diff --git a/Server/src/database/users.js b/Server/src/database/users.js
index 176d368..fca79de 100644
--- a/Server/src/database/users.js
+++ b/Server/src/database/users.js
@@ -37,31 +37,46 @@ exports.getUser = function(userIdOrUserName, callback){
});
}
+exports.getAll = async function() {
+ const queryResult = await connectionAsync.query('SELECT * FROM Users WHERE DeletedDate IS NULL;');
+ const users = [];
+ if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
+ for(var i = 0; i < queryResult.rows.length; i++) {
+ users.push(rowToUser(queryResult.rows[i]));
+ }
+ }
+ return users;
+}
+
exports.getUser2 = async function(email) {
const queryResult = await connectionAsync.query('SELECT * FROM Users WHERE UserName = ? AND DeletedDate IS NULL;', [email]);
if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
const row = queryResult.rows[0];
- const user = {};
- user.id = row.Id;
- user.userName = row.UserName;
- user.password = row.Password;
- user.firstName = row.FirstName;
- user.lastName = row.LastName;
- user.street = row.Street;
- user.city = row.City;
- user.state = row.State;
- user.country = row.Country;
- user.emailVerified = row.EmailVerified;
- user.emailVerificationCode = row.EmailVerificationCode;
- user.emailVerificationCodeDate = row.EmailVerificationCodeDate;
- user.federated = row.Federated;
- user.federationCode = row.federationCode;
- return user;
+ return rowToUser(row);
} else {
return null;
}
}
+function rowToUser(row) {
+ const user = {};
+ user.id = row.Id;
+ user.userName = row.UserName;
+ user.password = row.Password;
+ user.firstName = row.FirstName;
+ user.lastName = row.LastName;
+ user.street = row.Street;
+ user.city = row.City;
+ user.state = row.State;
+ user.country = row.Country;
+ user.emailVerified = row.EmailVerified;
+ user.emailVerificationCode = row.EmailVerificationCode;
+ user.emailVerificationCodeDate = row.EmailVerificationCodeDate;
+ user.federated = row.Federated;
+ user.federationCode = row.federationCode;
+ return user;
+}
+
exports.createUser = async function(firstName, lastName, street, city, state, zip, country) {
// Check to see if email already exists
const tempUserName = firstName + " " + lastName;
diff --git a/Server/src/routes/api/api.js b/Server/src/routes/api/api.js
index 792af42..10a210b 100644
--- a/Server/src/routes/api/api.js
+++ b/Server/src/routes/api/api.js
@@ -9,6 +9,7 @@ const fs = require('fs');
router.use("/users/a", require("./require-auth"));
router.use("/sermons/a", require("./require-auth"));
router.use("/events/a", require("./require-auth"));
+router.use("/transactions/a", require("./require-auth"));
// routes
router.use("/", require("./main"));
@@ -17,6 +18,7 @@ router.use("/sermons", require("./sermons"));
router.use("/events", require("./events"));
router.use("/login", require("./login"));
router.use("/email", require("./email"));
+router.use("/transactions", require("./transactions"));
router.use('/share',require('./share'));
diff --git a/Server/src/routes/api/require-auth.js b/Server/src/routes/api/require-auth.js
index 4acf529..51a7170 100644
--- a/Server/src/routes/api/require-auth.js
+++ b/Server/src/routes/api/require-auth.js
@@ -63,6 +63,7 @@ router.use(upload.single('file'),function(req,res,next){
req.body.finalPath = finalStorage + req.file.filename;
req.body.tmpPath = req.file.destination + req.file.filename;
}
+ res.locals.user = user;
next();
});
});
diff --git a/Server/src/routes/api/transactions.js b/Server/src/routes/api/transactions.js
new file mode 100644
index 0000000..d45fd24
--- /dev/null
+++ b/Server/src/routes/api/transactions.js
@@ -0,0 +1,30 @@
+var express = require('express');
+var router = express.Router();
+var dbTransactions = require("../../database/transactions");
+
+router.post("/a/",async function(req,res) {
+ if (!res.locals.user || !res.locals.user.canDo || !res.locals.user.canDo('transactions_add')) {
+ res.status(401).json({"status":401,"message":"you are not authorized to add a transaction"});
+ return;
+ }
+ const result = await dbTransactions.add(req.body.date, req.body.typeId, req.body.check, req.body.contributorId, req.body.fundId, req.body.descriptions, req.body.amount, req.body.taxYear);
+ console.log(result);
+ console.log("new user");
+ console.log(req.body);
+ console.log(res.locals.user);
+ console.log(res.locals.user.canDo('sermons_add'));
+ console.log(res.locals.user.canDo('sermons_addd'));
+ return;
+ if (!req.body.lastName){
+ res.status(400).json({"status":400,"message":"last name is required fields in the body"});
+ return;
+ }
+ try {
+ var newUser = await dbUsers.createUser(req.body.firstName, req.body.lastName, req.body.street, req.body.city, req.body.state, req.body.zip, req.body.country);
+ res.status(201).json({"status":201,"message":"user created","user":newUser});
+ } catch (ex) {
+ res.status(500).json({"status":500,"message":ex});
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/Server/src/routes/api/users.js b/Server/src/routes/api/users.js
index 9bf3f17..0b84ea0 100644
--- a/Server/src/routes/api/users.js
+++ b/Server/src/routes/api/users.js
@@ -3,6 +3,23 @@ var router = express.Router();
var dbUsers = require("../../database/users");
+router.get("/a/", async function(req, res) {
+
+ if (!res.locals.user || !res.locals.user.canDo || !res.locals.user.canDo('user_add')) {
+ res.status(401).json({"status":401,"message":"you are not authorized to add a transaction"});
+ return;
+ }
+ const users = await dbUsers.getAll();
+ const usersRes = users.map(x => {
+ return {
+ value: x.id,
+ display: `${x.lastName} ${x.firstName}`
+ };
+ });
+ res.status(201).json({"status":201,"message":"all users","users":usersRes});
+});
+
+
router.post("/a/",async function(req,res) {
console.log("new user");
console.log(req.body);