2023.12.19.09.12
This commit is contained in:
parent
f6f9819058
commit
cafc808395
BIN
data/database.sqlite
Normal file
BIN
data/database.sqlite
Normal file
Binary file not shown.
110
less/app.less
Normal file
110
less/app.less
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
@dark: #222f3e;
|
||||||
|
@light-grey: #576574;
|
||||||
|
@light: #c8d6e5;
|
||||||
|
|
||||||
|
@green: #1dd1a1;
|
||||||
|
@yellow: #feca57;
|
||||||
|
@red: #ff6b6b;
|
||||||
|
@pink: #ff9ff3;
|
||||||
|
@purple: #5f27cd;
|
||||||
|
@cyan: #48dbfb;
|
||||||
|
@blue: #54a0ff;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--dark: @dark;
|
||||||
|
--light-grey: @light-grey;
|
||||||
|
--light: @light;
|
||||||
|
|
||||||
|
--green: @green;
|
||||||
|
--yellow: @yellow;
|
||||||
|
--red: @red;
|
||||||
|
--pink: @pink;
|
||||||
|
--purple: @purple;
|
||||||
|
--cyan: @cyan;
|
||||||
|
--blue: @blue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'robotomono-light';
|
||||||
|
src: url('/assets/font/RobotoMono-Light.ttf');
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: "robotomono-light";
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--dark);
|
||||||
|
color: var(--light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
width: 1200px;
|
||||||
|
margin: auto;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 1200px;
|
||||||
|
margin: auto;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--cyan);
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
details {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .form-group {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .form-group.half {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .form-group .text {
|
||||||
|
width: 200px;
|
||||||
|
text-align: right;
|
||||||
|
padding: 5px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .form-group .input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .form-group .input input,
|
||||||
|
form .form-group .input textarea,
|
||||||
|
form .form-group .input select {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .form-group {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"],input[type="time"],input[type="date"],select{
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
input[type="number"],input[type="time"],input[type="date"],select,input[type="text"],textarea{
|
||||||
|
background:@light-grey;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "table.less";
|
||||||
|
@import "responsive.less";
|
16
less/responsive.less
Normal file
16
less/responsive.less
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
@media (max-width: 1250px) {
|
||||||
|
|
||||||
|
.nav,
|
||||||
|
.container {
|
||||||
|
width: 95vw;
|
||||||
|
margin: auto;
|
||||||
|
padding: 1vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
form .form-group.half{
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
74
less/table.less
Normal file
74
less/table.less
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td.amount {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td.income {
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
|
||||||
|
table td.expense {
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
table td.transfer {
|
||||||
|
color: var(--pink);
|
||||||
|
}
|
||||||
|
|
||||||
|
table td.amount:after {
|
||||||
|
content: " €";
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th.middle {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
th.right {
|
||||||
|
text-align: right
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(3n+2) {
|
||||||
|
background-color: lighten(@dark, 5%);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(3n+3) {
|
||||||
|
background-color: lighten(@dark, 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr{
|
||||||
|
td:nth-child(1){
|
||||||
|
border-left:solid transparent 5px;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.fullList{
|
||||||
|
tr.transfer:nth-child(2n+1) {
|
||||||
|
td:nth-child(1) {
|
||||||
|
border-top: 1px solid @pink;
|
||||||
|
border-left: 5px solid @pink;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:nth-child(2) {
|
||||||
|
border-top: 1px solid @pink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.transfer:nth-child(2n+2) {
|
||||||
|
td:nth-child(1) {
|
||||||
|
border-left: 5px solid @pink;
|
||||||
|
border-bottom: 1px solid @pink;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:nth-child(2) {
|
||||||
|
border-bottom: 1px solid @pink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"build": "rm -rf ./build && tsc",
|
"build": "rm -rf ./build && tsc",
|
||||||
"start": "npm run build && node build/index.js"
|
"start": "npm run build && lessc less/app.less public/css/app.css && node build/index.js",
|
||||||
|
"lessc":"lessc less/app.less public/css/app.css"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
@ -1,46 +1,151 @@
|
|||||||
:root{
|
:root {
|
||||||
--dark: #222f3e;
|
--dark: #222f3e;
|
||||||
--light-grey: #576574;
|
--light-grey: #576574;
|
||||||
--light: #c8d6e5;
|
--light: #c8d6e5;
|
||||||
|
--green: #1dd1a1;
|
||||||
--green: #1dd1a1;
|
--yellow: #feca57;
|
||||||
--yellow: #feca57;
|
--red: #ff6b6b;
|
||||||
--red: #ff6b6b;
|
--pink: #ff9ff3;
|
||||||
--pink: #ff9ff3;
|
--purple: #5f27cd;
|
||||||
--purple: #5f27cd;
|
--cyan: #48dbfb;
|
||||||
--cyan: #48dbfb;
|
--blue: #54a0ff;
|
||||||
--blue: #54a0ff;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'robotomono-light';
|
font-family: 'robotomono-light';
|
||||||
src: url('/assets/font/RobotoMono-Light.ttf');
|
src: url('/assets/font/RobotoMono-Light.ttf');
|
||||||
}
|
}
|
||||||
*{
|
* {
|
||||||
font-family: "robotomono-light";
|
font-family: "robotomono-light";
|
||||||
}
|
}
|
||||||
body{
|
body {
|
||||||
background-color: var(--dark);
|
background-color: var(--dark);
|
||||||
color:var(--light);
|
color: var(--light);
|
||||||
}
|
}
|
||||||
.container{
|
.nav {
|
||||||
width: 1200px;
|
width: 1200px;
|
||||||
|
margin: auto;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
width: 1200px;
|
||||||
|
margin: auto;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: var(--cyan);
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
details {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
form .form-group {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
form .form-group.half {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
form .form-group .text {
|
||||||
|
width: 200px;
|
||||||
|
text-align: right;
|
||||||
|
padding: 5px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
form .form-group .input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
form .form-group .input input,
|
||||||
|
form .form-group .input textarea,
|
||||||
|
form .form-group .input select {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
form .form-group {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
input[type="number"],
|
||||||
|
input[type="time"],
|
||||||
|
input[type="date"],
|
||||||
|
select {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
input[type="number"],
|
||||||
|
input[type="time"],
|
||||||
|
input[type="date"],
|
||||||
|
select,
|
||||||
|
input[type="text"],
|
||||||
|
textarea {
|
||||||
|
background: #576574;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
table td.amount {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
table td.income {
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
table td.expense {
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
table td.transfer {
|
||||||
|
color: var(--pink);
|
||||||
|
}
|
||||||
|
table td.amount:after {
|
||||||
|
content: " €";
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
th.middle {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
th.right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
tr:nth-child(3n+2) {
|
||||||
|
background-color: #2b3b4e;
|
||||||
|
}
|
||||||
|
tr:nth-child(3n+3) {
|
||||||
|
background-color: #34485f;
|
||||||
|
}
|
||||||
|
tr td:nth-child(1) {
|
||||||
|
border-left: solid transparent 5px;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
table.fullList tr.transfer:nth-child(2n+1) td:nth-child(1) {
|
||||||
|
border-top: 1px solid #ff9ff3;
|
||||||
|
border-left: 5px solid #ff9ff3;
|
||||||
|
}
|
||||||
|
table.fullList tr.transfer:nth-child(2n+1) td:nth-child(2) {
|
||||||
|
border-top: 1px solid #ff9ff3;
|
||||||
|
}
|
||||||
|
table.fullList tr.transfer:nth-child(2n+2) td:nth-child(1) {
|
||||||
|
border-left: 5px solid #ff9ff3;
|
||||||
|
border-bottom: 1px solid #ff9ff3;
|
||||||
|
}
|
||||||
|
table.fullList tr.transfer:nth-child(2n+2) td:nth-child(2) {
|
||||||
|
border-bottom: 1px solid #ff9ff3;
|
||||||
|
}
|
||||||
|
@media (max-width: 1250px) {
|
||||||
|
.nav,
|
||||||
|
.container {
|
||||||
|
width: 95vw;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 10px;
|
padding: 1vw;
|
||||||
border: 1px solid;
|
}
|
||||||
}
|
}
|
||||||
table{
|
@media (max-width: 800px) {
|
||||||
|
form .form-group.half {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
table .amount{
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
table .income{
|
|
||||||
color:var(--green);
|
|
||||||
}
|
|
||||||
table .expense{
|
|
||||||
color: var(--red);
|
|
||||||
}
|
|
||||||
table .transfer{
|
|
||||||
color: var(--pink);
|
|
||||||
}
|
}
|
14
src/controller/helper/dateConversion.ts
Normal file
14
src/controller/helper/dateConversion.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import moment from "moment";
|
||||||
|
import { Transaction } from "../models";
|
||||||
|
|
||||||
|
export function convertDateArray(arr1:any):any{
|
||||||
|
let arr = [];
|
||||||
|
for (let index = 0; index < arr1.length; index++) {
|
||||||
|
let element:Transaction = arr1[index];
|
||||||
|
let e:any = element;
|
||||||
|
e.Date2 = moment(element.Date).format("YYYY-MM-DD HH:MM")
|
||||||
|
console.log(moment(element.Date).format("YYYY-MM-DD HH:MM"))
|
||||||
|
arr[index] = e;
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
@ -16,6 +16,7 @@ interface TransactionAttributes {
|
|||||||
Amount: number;
|
Amount: number;
|
||||||
Type: 'income' | 'expense' | 'transfer';
|
Type: 'income' | 'expense' | 'transfer';
|
||||||
AccountID: number;
|
AccountID: number;
|
||||||
|
Refound:boolean
|
||||||
// Other fields...
|
// Other fields...
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -30,6 +31,7 @@ class Transaction extends Model<TransactionAttributes, TransactionCreationAttrib
|
|||||||
public Description!: string | null;
|
public Description!: string | null;
|
||||||
public Amount!: number;
|
public Amount!: number;
|
||||||
public Type!: 'income' | 'expense' | 'transfer';
|
public Type!: 'income' | 'expense' | 'transfer';
|
||||||
|
public Refound!: boolean;
|
||||||
// Other fields...
|
// Other fields...
|
||||||
|
|
||||||
public UserID!: number;
|
public UserID!: number;
|
||||||
@ -70,6 +72,10 @@ class Transaction extends Model<TransactionAttributes, TransactionCreationAttrib
|
|||||||
type: DataTypes.ENUM('income', 'expense','transfer'),
|
type: DataTypes.ENUM('income', 'expense','transfer'),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
},
|
},
|
||||||
|
Refound:{
|
||||||
|
type:DataTypes.BOOLEAN,
|
||||||
|
defaultValue:false
|
||||||
|
}
|
||||||
// Other fields...
|
// Other fields...
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -100,7 +106,7 @@ class Transaction extends Model<TransactionAttributes, TransactionCreationAttrib
|
|||||||
return result || 0;
|
return result || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async transferAmountFromTo(userID: number, fromAccountID: number = 1, toAccountID: number = 1, Amount: number = 0){
|
static async transferAmountFromTo(userID: number, fromAccountID: number = 1, toAccountID: number = 1, Amount: number = 0,CategoryID:number = 1){
|
||||||
console.log(`User: ${userID}, fromAccount: ${fromAccountID}, toAccount${toAccountID}, Amount: ${Amount}`)
|
console.log(`User: ${userID}, fromAccount: ${fromAccountID}, toAccount${toAccountID}, Amount: ${Amount}`)
|
||||||
await Transaction.create({
|
await Transaction.create({
|
||||||
Amount:-Amount,
|
Amount:-Amount,
|
||||||
@ -109,7 +115,7 @@ class Transaction extends Model<TransactionAttributes, TransactionCreationAttrib
|
|||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
UserID: userID, // Associate the transaction with the user
|
UserID: userID, // Associate the transaction with the user
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
CategoryID: 1,
|
CategoryID: CategoryID,
|
||||||
AccountID: fromAccountID
|
AccountID: fromAccountID
|
||||||
})
|
})
|
||||||
await Transaction.create({
|
await Transaction.create({
|
||||||
@ -119,7 +125,7 @@ class Transaction extends Model<TransactionAttributes, TransactionCreationAttrib
|
|||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
UserID: userID, // Associate the transaction with the user
|
UserID: userID, // Associate the transaction with the user
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
CategoryID: 1,
|
CategoryID: CategoryID,
|
||||||
AccountID: toAccountID
|
AccountID: toAccountID
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@ import express, { Request, Response } from 'express';
|
|||||||
import { User, Category, Transaction, Account } from './models'; // Adjust the path to your models
|
import { User, Category, Transaction, Account } from './models'; // Adjust the path to your models
|
||||||
import pug from "pug"
|
import pug from "pug"
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { TransactionRouter } from './router/transactions';
|
||||||
|
import bodyParser from 'body-parser';
|
||||||
|
import { CategoryRouter } from './router/categories';
|
||||||
|
|
||||||
export class Router {
|
export class Router {
|
||||||
app: any;
|
app: any;
|
||||||
@ -13,6 +16,11 @@ export class Router {
|
|||||||
app.set('view engine', 'pug');
|
app.set('view engine', 'pug');
|
||||||
app.set('views', './views'); // Set the views directory
|
app.set('views', './views'); // Set the views directory
|
||||||
console.log(Path)
|
console.log(Path)
|
||||||
|
// parse application/x-www-form-urlencoded
|
||||||
|
app.use(bodyParser.urlencoded({ extended: false }))
|
||||||
|
|
||||||
|
// parse application/json
|
||||||
|
app.use(bodyParser.json())
|
||||||
app.use('/assets', express.static(Path));
|
app.use('/assets', express.static(Path));
|
||||||
|
|
||||||
app.get('/', async (req: Request, res: Response) => {
|
app.get('/', async (req: Request, res: Response) => {
|
||||||
@ -32,7 +40,8 @@ export class Router {
|
|||||||
res.status(500).send('Internal Server Error');
|
res.status(500).send('Internal Server Error');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let transactionR = TransactionRouter.routes();
|
||||||
|
app.use('/transactions', transactionR)
|
||||||
app.get('/users', async (req: Request, res: Response) => {
|
app.get('/users', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const users = await User.findAll();
|
const users = await User.findAll();
|
||||||
@ -42,24 +51,18 @@ export class Router {
|
|||||||
res.status(500).send('Internal Server Error');
|
res.status(500).send('Internal Server Error');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let categoriesR = CategoryRouter.routes();
|
||||||
app.get('/categories', async (req: Request, res: Response) => {
|
app.use('/categories', categoriesR)
|
||||||
try {
|
/*
|
||||||
const categories = await Category.findAll();
|
|
||||||
res.render('categories', { title: 'Categories', categories });
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.status(500).send('Internal Server Error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/transactions', async (req: Request, res: Response) => {
|
app.get('/transactions', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const transactions = await Transaction.findAll({
|
const transactions = await Transaction.findAll({
|
||||||
include: Account
|
include: Account
|
||||||
});
|
});
|
||||||
|
const categories = await Category.findAll({});
|
||||||
|
const accounts = await Account.findAll({});
|
||||||
console.log(transactions)
|
console.log(transactions)
|
||||||
res.render('transactions/transactions', { title: 'Transactions', transactions });
|
res.render('transactions/transactions', { title: 'Transactions', transactions, categories, accounts });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
res.status(500).send('Internal Server Error');
|
res.status(500).send('Internal Server Error');
|
||||||
@ -97,7 +100,7 @@ export class Router {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
|
0
src/controller/router/accounts.ts
Normal file
0
src/controller/router/accounts.ts
Normal file
71
src/controller/router/categories.ts
Normal file
71
src/controller/router/categories.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import express, { Request, Response } from 'express';
|
||||||
|
import { Account, Category, Transaction } from "../models";
|
||||||
|
import moment from "moment";
|
||||||
|
import { convertDateArray } from '../helper/dateConversion';
|
||||||
|
|
||||||
|
export class CategoryRouter {
|
||||||
|
static routes() {
|
||||||
|
let router = express.Router();
|
||||||
|
router.use((req, res, next) => {
|
||||||
|
console.log("TEST")
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
router.post('/addCategory',async(req,res)=>{
|
||||||
|
let name = req.body.categoryName;
|
||||||
|
let description = req.body.categoryDescription;
|
||||||
|
await Category.create({
|
||||||
|
Name:name,
|
||||||
|
Description:description
|
||||||
|
})
|
||||||
|
res.redirect("/categories");
|
||||||
|
//res.render('categories/listCategories', { title: 'Categories', categories });
|
||||||
|
|
||||||
|
})
|
||||||
|
router.get('/addCategory',async(req,res)=>{
|
||||||
|
try {
|
||||||
|
res.render('categories/formCategory', { title: 'Add Categories' });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send('Internal Server Error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
router.post('/modifyCategory/:id',async(req,res)=>{
|
||||||
|
let name = req.body.categoryName;
|
||||||
|
let description = req.body.categoryDescription;
|
||||||
|
await Category.update({
|
||||||
|
Name:name,
|
||||||
|
Description:description
|
||||||
|
},{
|
||||||
|
where:{
|
||||||
|
CategoryID:req.params.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
res.redirect("/categories");
|
||||||
|
//res.render('categories/listCategories', { title: 'Categories', categories });
|
||||||
|
|
||||||
|
})
|
||||||
|
router.get('/modifyCategory/:id',async(req,res)=>{
|
||||||
|
try {
|
||||||
|
let categoryObj = await Category.findOne({where:{
|
||||||
|
CategoryID: req.params.id
|
||||||
|
}})
|
||||||
|
res.render('categories/modifyCategory', { title: 'Add Categories',categoryObj });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send('Internal Server Error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
router.all('/', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const categories = await Category.findAll({});
|
||||||
|
const accounts = await Account.findAll({});
|
||||||
|
res.render('categories/listCategories', { title: 'Categories', categories });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send('Internal Server Error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
||||||
|
}
|
160
src/controller/router/transactions.ts
Normal file
160
src/controller/router/transactions.ts
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
import express, { Request, Response } from 'express';
|
||||||
|
import { Account, Category, Transaction } from "../models";
|
||||||
|
import moment from "moment";
|
||||||
|
import { convertDateArray } from '../helper/dateConversion';
|
||||||
|
|
||||||
|
export class TransactionRouter {
|
||||||
|
static routes() {
|
||||||
|
let router = express.Router();
|
||||||
|
router.use((req, res, next) => {
|
||||||
|
console.log("TEST")
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
router.post('/addTransaction', async (req, res, next) => {
|
||||||
|
console.log(req.body)
|
||||||
|
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
if(
|
||||||
|
req.body.date == undefined
|
||||||
|
|| req.body.Type == undefined
|
||||||
|
|| req.body.CategoryID == undefined
|
||||||
|
|| req.body.AccountID == undefined
|
||||||
|
){throw "ERROR"}
|
||||||
|
*/
|
||||||
|
let date = moment(req.body.date).set({
|
||||||
|
hour: req.body.time.split(":")[0],
|
||||||
|
minute:req.body.time.split(":")[1]
|
||||||
|
})
|
||||||
|
console.log(date.toDate())
|
||||||
|
await Transaction.create({
|
||||||
|
Amount: req.body.amount,
|
||||||
|
Date: date.toDate(),
|
||||||
|
Type: req.body.type,
|
||||||
|
//@ts-ignore
|
||||||
|
UserID: 1, // Associate the transaction with the user
|
||||||
|
//@ts-ignore
|
||||||
|
CategoryID: req.body.category,
|
||||||
|
AccountID: req.body.account
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
res.redirect("/transactions");
|
||||||
|
//next();
|
||||||
|
})
|
||||||
|
router.post('/addTransfer', async (req, res, next) => {
|
||||||
|
console.log(req.body)
|
||||||
|
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
if(
|
||||||
|
req.body.date == undefined
|
||||||
|
|| req.body.Type == undefined
|
||||||
|
|| req.body.CategoryID == undefined
|
||||||
|
|| req.body.AccountID == undefined
|
||||||
|
){throw "ERROR"}
|
||||||
|
*/
|
||||||
|
let date = moment(req.body.date).set({
|
||||||
|
hour: req.body.time.split(":")[0],
|
||||||
|
minute:req.body.time.split(":")[1]
|
||||||
|
})
|
||||||
|
console.log(date.toDate())
|
||||||
|
await Transaction.transferAmountFromTo(1,req.body.fromAccount,req.body.toAccount,req.body.amount,req.body.categoryID)
|
||||||
|
await Transaction.create({
|
||||||
|
Amount: req.body.amount,
|
||||||
|
Date: date.toDate(),
|
||||||
|
Type: req.body.type,
|
||||||
|
//@ts-ignore
|
||||||
|
UserID: 1, // Associate the transaction with the user
|
||||||
|
//@ts-ignore
|
||||||
|
CategoryID: req.body.category,
|
||||||
|
AccountID: req.body.account
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
res.redirect("/transactions");
|
||||||
|
//next();
|
||||||
|
})
|
||||||
|
router.get('/addTransaction', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const transactions = await Transaction.findAll({
|
||||||
|
include: Account
|
||||||
|
});
|
||||||
|
const categories = await Category.findAll({order:[["Name","ASC"]]});
|
||||||
|
const accounts = await Account.findAll({order:[["Name","ASC"]]});
|
||||||
|
//console.log(transactions)
|
||||||
|
res.render('transactions/formTransaction', { title: 'Transactions', transactions, categories, accounts });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send('Internal Server Error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
router.get('/addTransfer', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const transactions = await Transaction.findAll({
|
||||||
|
include: Account
|
||||||
|
});
|
||||||
|
const categories = await Category.findAll({order:[["Name","ASC"]]});
|
||||||
|
const accounts = await Account.findAll({order:[["Name","ASC"]]});
|
||||||
|
//console.log(transactions)
|
||||||
|
res.render('transactions/formTransfer', { title: 'Transactions', transactions, categories, accounts });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send('Internal Server Error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/acc/:acc', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const transactions = await Transaction.findAll({
|
||||||
|
include: [Account,Category],
|
||||||
|
where: {
|
||||||
|
AccountID: req.params.acc
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let arr = convertDateArray(transactions);
|
||||||
|
res.render('transactions/listTransactions', { title: 'Transactions', transactions:arr, cssClass:"" });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send('Internal Server Error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
router.get('/cat/:cat', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const transactions = await Transaction.findAll({
|
||||||
|
include: [Account, Category],
|
||||||
|
where: {
|
||||||
|
//@ts-ignore
|
||||||
|
CategoryID: req.params.cat
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let arr = convertDateArray(transactions);
|
||||||
|
res.render('transactions/listTransactions', { title: 'Transactions', transactions:arr, cssClass:"" });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send('Internal Server Error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
router.all('/', async (req, res) => {
|
||||||
|
try {
|
||||||
|
let transactions:any = await Transaction.findAll({
|
||||||
|
include: [Account,Category]
|
||||||
|
});
|
||||||
|
const categories = await Category.findAll({});
|
||||||
|
const accounts = await Account.findAll({});
|
||||||
|
let arr = convertDateArray(transactions);
|
||||||
|
console.log(transactions)
|
||||||
|
res.render('transactions/listTransactions', { title: 'Transactions', transactions: arr, categories, accounts, cssClass:"fullList" });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send('Internal Server Error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
||||||
|
}
|
82
src/index.ts
82
src/index.ts
@ -6,6 +6,7 @@ import path from 'path';
|
|||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { Storage } from './controller/storage';
|
import { Storage } from './controller/storage';
|
||||||
import { MonthlyReport } from './controller/models/MonthlyReport';
|
import { MonthlyReport } from './controller/models/MonthlyReport';
|
||||||
|
import { testData } from './test-data';
|
||||||
|
|
||||||
|
|
||||||
Storage.__dirname = __dirname
|
Storage.__dirname = __dirname
|
||||||
@ -14,12 +15,16 @@ Storage.__dirname = __dirname
|
|||||||
|
|
||||||
let init = async () => {
|
let init = async () => {
|
||||||
// Initialize Sequelize instance and define database connection
|
// Initialize Sequelize instance and define database connection
|
||||||
const sequelize = new Sequelize('sqlite::memory:', {
|
/*const sequelize = new Sequelize('sqlite::memory:', {
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
dialect: 'sqlite', // Change this to your database dialect
|
dialect: 'sqlite', // Change this to your database dialect
|
||||||
// Other options...
|
// Other options...
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
const sequelize = new Sequelize({
|
||||||
|
dialect: 'sqlite',
|
||||||
|
storage:`${path.join(__dirname,'../data')}/database.sqlite`
|
||||||
|
});
|
||||||
|
|
||||||
User.initialize(sequelize);
|
User.initialize(sequelize);
|
||||||
|
|
||||||
@ -41,80 +46,13 @@ let init = async () => {
|
|||||||
|
|
||||||
await sequelize.sync({ force: true });
|
await sequelize.sync({ force: true });
|
||||||
|
|
||||||
await User.create({
|
await testData(User,Category,Account,MonthlyReport)
|
||||||
Name: "Max Mustermann",
|
|
||||||
Email: "test@test.de",
|
|
||||||
Password: "sdfsf"
|
|
||||||
})
|
|
||||||
await Category.create({
|
|
||||||
Name: "Allgemein",
|
|
||||||
Description: null
|
|
||||||
})
|
|
||||||
await Category.create({
|
|
||||||
Name:"Games",
|
|
||||||
Description:"Games"
|
|
||||||
})
|
|
||||||
await Account.create({
|
|
||||||
Name:"None"
|
|
||||||
})
|
|
||||||
await Account.create({
|
|
||||||
Name:"Bar"
|
|
||||||
})
|
|
||||||
await Account.create({
|
|
||||||
Name:"Konto1"
|
|
||||||
})
|
|
||||||
|
|
||||||
let z = await Transaction.create({
|
|
||||||
Amount:50.0,
|
|
||||||
Date:new Date(),
|
|
||||||
Type:"expense",
|
|
||||||
//@ts-ignore
|
|
||||||
UserID: 1, // Associate the transaction with the user
|
|
||||||
//@ts-ignore
|
|
||||||
CategoryID: 1,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
await Transaction.create({
|
|
||||||
Amount:10.0,
|
|
||||||
Date:new Date(),
|
|
||||||
Type:"expense",
|
|
||||||
//@ts-ignore
|
|
||||||
UserID: 1, // Associate the transaction with the user
|
|
||||||
//@ts-ignore
|
|
||||||
CategoryID: 1,
|
|
||||||
})
|
|
||||||
await Transaction.create({
|
|
||||||
Amount:60.0,
|
|
||||||
Date:new Date(),
|
|
||||||
Type:"income",
|
|
||||||
//@ts-ignore
|
|
||||||
UserID: 1, // Associate the transaction with the user
|
|
||||||
//@ts-ignore
|
|
||||||
CategoryID: 1,
|
|
||||||
})
|
|
||||||
await Transaction.create({
|
|
||||||
Amount:60.0,
|
|
||||||
Date:new Date(),
|
|
||||||
Type:"income",
|
|
||||||
//@ts-ignore
|
|
||||||
UserID: 1, // Associate the transaction with the user
|
|
||||||
//@ts-ignore
|
|
||||||
CategoryID: 2,
|
|
||||||
})
|
|
||||||
|
|
||||||
//await Transaction.transferAmountFromTo(1,1,2,50);
|
|
||||||
|
|
||||||
|
|
||||||
let x = await Transaction.getTotalAmountForUser(1)
|
|
||||||
await Transaction.transferAmountFromTo(1,1,2,50);
|
|
||||||
console.log(x)
|
|
||||||
|
|
||||||
new Router(path.join(__dirname,'../public'));
|
new Router(path.join(__dirname,'../public'));
|
||||||
|
|
||||||
console.log(path.join(__dirname,'../public'))
|
//console.log(path.join(__dirname,'../public'))
|
||||||
|
|
||||||
MonthlyReport.generateMonthlyReport(12,2023,2)
|
//MonthlyReport.generateMonthlyReport(12,2023,2)
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
100
src/test-data.ts
Normal file
100
src/test-data.ts
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import { Account, Category, Transaction, User } from "./database"
|
||||||
|
import { MonthlyReport } from "./controller/models/MonthlyReport"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export async function testData(User:any,Category:any,Account:any,MonthlyReport:any){
|
||||||
|
await User.create({
|
||||||
|
Name: "Max Mustermann",
|
||||||
|
Email: "test@test.de",
|
||||||
|
Password: "sdfsf"
|
||||||
|
})
|
||||||
|
await Category.create({
|
||||||
|
Name: "Allgemein",
|
||||||
|
Description: null
|
||||||
|
})
|
||||||
|
await Category.create({
|
||||||
|
Name:"Server",
|
||||||
|
Description:"Server"
|
||||||
|
})
|
||||||
|
await Category.create({
|
||||||
|
Name:"Games",
|
||||||
|
Description:"Games"
|
||||||
|
})
|
||||||
|
await Category.create({
|
||||||
|
Name:"Server/Netcup",
|
||||||
|
Description:""
|
||||||
|
})
|
||||||
|
await Category.create({
|
||||||
|
Name:"Server/GPortal",
|
||||||
|
Description:""
|
||||||
|
})
|
||||||
|
await Category.create({
|
||||||
|
Name:"Discord",
|
||||||
|
Description:""
|
||||||
|
})
|
||||||
|
await Category.create({
|
||||||
|
Name:"Games/WarThunder"
|
||||||
|
})
|
||||||
|
await Category.create({
|
||||||
|
Name:"Games/Steam"
|
||||||
|
})
|
||||||
|
await Category.create({
|
||||||
|
Name:"Games/League"
|
||||||
|
})
|
||||||
|
await Account.create({
|
||||||
|
Name:"None"
|
||||||
|
})
|
||||||
|
await Account.create({
|
||||||
|
Name:"Bar"
|
||||||
|
})
|
||||||
|
await Account.create({
|
||||||
|
Name:"Girokonto"
|
||||||
|
})
|
||||||
|
|
||||||
|
let z = await Transaction.create({
|
||||||
|
Amount:50.0,
|
||||||
|
Date:new Date(),
|
||||||
|
Type:"expense",
|
||||||
|
//@ts-ignore
|
||||||
|
UserID: 1, // Associate the transaction with the user
|
||||||
|
//@ts-ignore
|
||||||
|
CategoryID: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
await Transaction.create({
|
||||||
|
Amount:10.0,
|
||||||
|
Date:new Date(),
|
||||||
|
Type:"expense",
|
||||||
|
//@ts-ignore
|
||||||
|
UserID: 1, // Associate the transaction with the user
|
||||||
|
//@ts-ignore
|
||||||
|
CategoryID: 1,
|
||||||
|
})
|
||||||
|
await Transaction.create({
|
||||||
|
Amount:60.0,
|
||||||
|
Date:new Date(),
|
||||||
|
Type:"income",
|
||||||
|
//@ts-ignore
|
||||||
|
UserID: 1, // Associate the transaction with the user
|
||||||
|
//@ts-ignore
|
||||||
|
CategoryID: 1,
|
||||||
|
})
|
||||||
|
await Transaction.create({
|
||||||
|
Amount:60.0,
|
||||||
|
Date:new Date(),
|
||||||
|
Type:"expense",
|
||||||
|
//@ts-ignore
|
||||||
|
UserID: 1, // Associate the transaction with the user
|
||||||
|
//@ts-ignore
|
||||||
|
CategoryID: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
await Transaction.transferAmountFromTo(1,1,2,50);
|
||||||
|
|
||||||
|
|
||||||
|
let x = await Transaction.getTotalAmountForUser(1)
|
||||||
|
await Transaction.transferAmountFromTo(1,1,2,50);
|
||||||
|
//console.log(x)
|
||||||
|
}
|
10
views/categories/categories.pug
Normal file
10
views/categories/categories.pug
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
extends ../layout/base.pug
|
||||||
|
|
||||||
|
block nav
|
||||||
|
a(href="/categories") Categories
|
||||||
|
a(href="/categories/addCategory") + Category
|
||||||
|
|
||||||
|
block headline
|
||||||
|
h1 Categories
|
||||||
|
|
||||||
|
block content
|
13
views/categories/formCategory.pug
Normal file
13
views/categories/formCategory.pug
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
extends ./categories.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
form(action="/categories/addCategory",method="POST")
|
||||||
|
.form-group
|
||||||
|
.text Name
|
||||||
|
.input
|
||||||
|
input(type="text", name="categoryName")
|
||||||
|
.form-group
|
||||||
|
.text Description
|
||||||
|
.input
|
||||||
|
textarea(name="categoryDescription", cols="30", rows="10")
|
||||||
|
button(type="submit") Add Category
|
20
views/categories/listCategories.pug
Normal file
20
views/categories/listCategories.pug
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
extends ./categories.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
table
|
||||||
|
thead
|
||||||
|
th.center ID
|
||||||
|
th Name
|
||||||
|
th Description
|
||||||
|
th Transactions
|
||||||
|
th Modify Category
|
||||||
|
tbody
|
||||||
|
each category in categories
|
||||||
|
tr
|
||||||
|
td=category.CategoryID
|
||||||
|
td=category.Name
|
||||||
|
td=category.Description
|
||||||
|
td
|
||||||
|
a(href=`/transactions/cat/${category.CategoryID}`) Transactions by Category
|
||||||
|
td
|
||||||
|
a(href=`/categories/ModifyCategory/${category.CategoryID}`) ###
|
13
views/categories/modifyCategory.pug
Normal file
13
views/categories/modifyCategory.pug
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
extends ./categories.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
form(action=`/categories/modifyCategory/${categoryObj.CategoryID}`,method="POST")
|
||||||
|
.form-group
|
||||||
|
.text Name
|
||||||
|
.input
|
||||||
|
input(type="text", name="categoryName",value=categoryObj.Name)
|
||||||
|
.form-group
|
||||||
|
.text Description
|
||||||
|
.input
|
||||||
|
textarea(name="categoryDescription", cols="30", rows="10")=categoryObj.Description
|
||||||
|
button(type="submit") Modify Category
|
5
views/layout/_navbar.pug
Normal file
5
views/layout/_navbar.pug
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
nav
|
||||||
|
a(href="/") Home
|
||||||
|
a(href="/transactions") Transactions
|
||||||
|
a(href="/categories") Categories
|
||||||
|
a(href="/accounts") Accounts
|
@ -7,6 +7,11 @@ html
|
|||||||
block additionalAssets
|
block additionalAssets
|
||||||
|
|
||||||
body
|
body
|
||||||
|
div.nav
|
||||||
|
include ./_navbar.pug
|
||||||
|
div.nav
|
||||||
|
block nav
|
||||||
|
|
||||||
div.container
|
div.container
|
||||||
block headline
|
block headline
|
||||||
|
|
||||||
|
41
views/transactions/_addTransactions.pug
Normal file
41
views/transactions/_addTransactions.pug
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
details
|
||||||
|
summary Create new transaction
|
||||||
|
|
||||||
|
form(action="/transactions",method="POST")
|
||||||
|
div.form-group
|
||||||
|
.text Description
|
||||||
|
.input
|
||||||
|
textarea(name="description", cols="30", rows="5") Test
|
||||||
|
div.form-group.half
|
||||||
|
.text Date
|
||||||
|
.input
|
||||||
|
input(type="date",name="date")
|
||||||
|
div.form-group.half
|
||||||
|
.text Time
|
||||||
|
.input
|
||||||
|
input(type="time",name="time")
|
||||||
|
div.form-group.half
|
||||||
|
.text Type
|
||||||
|
.input
|
||||||
|
select(name="type")
|
||||||
|
option(value="income") Income
|
||||||
|
option(value="expense",select) Expense
|
||||||
|
option(value="transfer") Transfer
|
||||||
|
div.form-group.half
|
||||||
|
.text Amount
|
||||||
|
.input
|
||||||
|
input(type="text",name="amount",required)
|
||||||
|
div.form-group
|
||||||
|
.text Account
|
||||||
|
.input
|
||||||
|
select(name="account")
|
||||||
|
each account in accounts
|
||||||
|
option(value=account.AccountID)=account.Name
|
||||||
|
div.form-group
|
||||||
|
.text Category
|
||||||
|
.input
|
||||||
|
select(name="category")
|
||||||
|
each category in categories
|
||||||
|
option(value=category.CategoryID,title=category.Description)=category.Name
|
||||||
|
button(type="submit") Add Transaction
|
39
views/transactions/formTransaction.pug
Normal file
39
views/transactions/formTransaction.pug
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
extends ./transactions.pug
|
||||||
|
block content
|
||||||
|
form(action="/transactions/addTransaction",method="POST")
|
||||||
|
div.form-group
|
||||||
|
.text Description
|
||||||
|
.input
|
||||||
|
textarea(name="description", cols="30", rows="5")
|
||||||
|
div.form-group.half
|
||||||
|
.text Date
|
||||||
|
.input
|
||||||
|
input(type="date",name="date")
|
||||||
|
div.form-group.half
|
||||||
|
.text Time
|
||||||
|
.input
|
||||||
|
input(type="time",name="time")
|
||||||
|
div.form-group.half
|
||||||
|
.text Type
|
||||||
|
.input
|
||||||
|
select(name="type")
|
||||||
|
option(value="income") Income
|
||||||
|
option(value="expense",select) Expense
|
||||||
|
option(value="transfer") Transfer
|
||||||
|
div.form-group.half
|
||||||
|
.text Account
|
||||||
|
.input
|
||||||
|
select(name="account")
|
||||||
|
each account in accounts
|
||||||
|
option(value=account.AccountID)=account.Name
|
||||||
|
div.form-group.half
|
||||||
|
.text Amount
|
||||||
|
.input
|
||||||
|
input(type="number",min="0",step="0.01",name="amount",required)
|
||||||
|
div.form-group.half
|
||||||
|
.text Category
|
||||||
|
.input
|
||||||
|
select(name="category")
|
||||||
|
each category in categories
|
||||||
|
option(value=category.CategoryID,title=category.Description)=category.Name
|
||||||
|
button(type="submit") Add Transaction
|
40
views/transactions/formTransfer.pug
Normal file
40
views/transactions/formTransfer.pug
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
extends ./transactions.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
form(action="/transactions/addTransfer",method="POST")
|
||||||
|
div.form-group
|
||||||
|
.text Description
|
||||||
|
.input
|
||||||
|
textarea(name="description", cols="30", rows="5")
|
||||||
|
div.form-group.half
|
||||||
|
.text Date
|
||||||
|
.input
|
||||||
|
input(type="date",name="date")
|
||||||
|
div.form-group.half
|
||||||
|
.text Time
|
||||||
|
.input
|
||||||
|
input(type="time",name="time")
|
||||||
|
|
||||||
|
div.form-group.half
|
||||||
|
.text from Account
|
||||||
|
.input
|
||||||
|
select(name="fromAccount")
|
||||||
|
each account in accounts
|
||||||
|
option(value=account.AccountID)=account.Name
|
||||||
|
div.form-group.half
|
||||||
|
.text to Account
|
||||||
|
.input
|
||||||
|
select(name="toAccount")
|
||||||
|
each account in accounts
|
||||||
|
option(value=account.AccountID)=account.Name
|
||||||
|
div.form-group.half
|
||||||
|
.text Amount
|
||||||
|
.input
|
||||||
|
input(type="number",min="0",step="0.01",name="amount",required)
|
||||||
|
div.form-group.half
|
||||||
|
.text Category
|
||||||
|
.input
|
||||||
|
select(name="category")
|
||||||
|
each category in categories
|
||||||
|
option(value=category.CategoryID,title=category.Description)=category.Name
|
||||||
|
button(type="submit") Add Transaction
|
22
views/transactions/listTransactions.pug
Normal file
22
views/transactions/listTransactions.pug
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
extends ./transactions.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
table(class=`${cssClass}`)
|
||||||
|
thead
|
||||||
|
th ID
|
||||||
|
th Date
|
||||||
|
th Description
|
||||||
|
th Category
|
||||||
|
th Account
|
||||||
|
th.right Amount
|
||||||
|
tbody
|
||||||
|
each transaction in transactions
|
||||||
|
tr(class=`${transaction.Type}`)
|
||||||
|
td=transaction.TransactionID
|
||||||
|
td=transaction.Date2
|
||||||
|
td=transaction.Description
|
||||||
|
td
|
||||||
|
a(href=`/transactions/cat/${transaction.Category.CategoryID}`)=transaction.Category.Name
|
||||||
|
td
|
||||||
|
a(href=`/transactions/acc/${transaction.Account.AccountID}`)=transaction.Account.Name
|
||||||
|
td(class=`${transaction.Type} amount` )=transaction.Amount
|
@ -1,24 +1,11 @@
|
|||||||
extends ../layout/base.pug
|
extends ../layout/base.pug
|
||||||
|
|
||||||
|
block nav
|
||||||
|
a(href="/transactions") Transactions
|
||||||
|
a(href="/transactions/addTransaction") + Transaction
|
||||||
|
a(href="/transactions/addTransfer") + Transfer
|
||||||
|
|
||||||
block headline
|
block headline
|
||||||
h1 Transactions
|
h1 Transactions
|
||||||
|
|
||||||
block content
|
block content
|
||||||
form(action="/tansactions",method="POST")
|
|
||||||
|
|
||||||
|
|
||||||
table
|
|
||||||
thead
|
|
||||||
th ID
|
|
||||||
th Date
|
|
||||||
th Description
|
|
||||||
th Account
|
|
||||||
th Amount
|
|
||||||
tbody
|
|
||||||
each transaction in transactions
|
|
||||||
tr
|
|
||||||
td=transaction.TransactionID
|
|
||||||
td=transaction.Date
|
|
||||||
td=transaction.Description
|
|
||||||
td=transaction.Account.Name
|
|
||||||
td(class=`${transaction.Type} amount` )=transaction.Amount
|
|
Loading…
Reference in New Issue
Block a user