2023.12.19.09.12

This commit is contained in:
Theenoro 2023-12-19 09:12:42 +01:00
parent f6f9819058
commit cafc808395
26 changed files with 939 additions and 146 deletions

BIN
data/database.sqlite Normal file

Binary file not shown.

110
less/app.less Normal file
View 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
View 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
View 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;
}
}
}

View File

@ -6,7 +6,8 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"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": "",
"license": "ISC",

View File

@ -2,7 +2,6 @@
--dark: #222f3e;
--light-grey: #576574;
--light: #c8d6e5;
--green: #1dd1a1;
--yellow: #feca57;
--red: #ff6b6b;
@ -10,7 +9,6 @@
--purple: #5f27cd;
--cyan: #48dbfb;
--blue: #54a0ff;
}
@font-face {
font-family: 'robotomono-light';
@ -23,24 +21,131 @@ 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: #576574;
border-color: transparent;
}
table {
width: 100%;
}
table .amount{
table td.amount {
text-align: right;
}
table .income{
table td.income {
color: var(--green);
}
table .expense{
table td.expense {
color: var(--red);
}
table .transfer{
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;
padding: 1vw;
}
}
@media (max-width: 800px) {
form .form-group.half {
width: 100%;
}
}

View 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;
}

View File

@ -16,6 +16,7 @@ interface TransactionAttributes {
Amount: number;
Type: 'income' | 'expense' | 'transfer';
AccountID: number;
Refound:boolean
// Other fields...
}
@ -30,6 +31,7 @@ class Transaction extends Model<TransactionAttributes, TransactionCreationAttrib
public Description!: string | null;
public Amount!: number;
public Type!: 'income' | 'expense' | 'transfer';
public Refound!: boolean;
// Other fields...
public UserID!: number;
@ -70,6 +72,10 @@ class Transaction extends Model<TransactionAttributes, TransactionCreationAttrib
type: DataTypes.ENUM('income', 'expense','transfer'),
allowNull: false,
},
Refound:{
type:DataTypes.BOOLEAN,
defaultValue:false
}
// Other fields...
},
{
@ -100,7 +106,7 @@ class Transaction extends Model<TransactionAttributes, TransactionCreationAttrib
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}`)
await Transaction.create({
Amount:-Amount,
@ -109,7 +115,7 @@ class Transaction extends Model<TransactionAttributes, TransactionCreationAttrib
//@ts-ignore
UserID: userID, // Associate the transaction with the user
//@ts-ignore
CategoryID: 1,
CategoryID: CategoryID,
AccountID: fromAccountID
})
await Transaction.create({
@ -119,7 +125,7 @@ class Transaction extends Model<TransactionAttributes, TransactionCreationAttrib
//@ts-ignore
UserID: userID, // Associate the transaction with the user
//@ts-ignore
CategoryID: 1,
CategoryID: CategoryID,
AccountID: toAccountID
})
}

View File

@ -3,6 +3,9 @@ import express, { Request, Response } from 'express';
import { User, Category, Transaction, Account } from './models'; // Adjust the path to your models
import pug from "pug"
import path from 'path';
import { TransactionRouter } from './router/transactions';
import bodyParser from 'body-parser';
import { CategoryRouter } from './router/categories';
export class Router {
app: any;
@ -13,6 +16,11 @@ export class Router {
app.set('view engine', 'pug');
app.set('views', './views'); // Set the views directory
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.get('/', async (req: Request, res: Response) => {
@ -32,7 +40,8 @@ export class Router {
res.status(500).send('Internal Server Error');
}
});
let transactionR = TransactionRouter.routes();
app.use('/transactions', transactionR)
app.get('/users', async (req: Request, res: Response) => {
try {
const users = await User.findAll();
@ -42,24 +51,18 @@ export class Router {
res.status(500).send('Internal Server Error');
}
});
app.get('/categories', async (req: Request, res: Response) => {
try {
const categories = await Category.findAll();
res.render('categories', { title: 'Categories', categories });
} catch (err) {
console.error(err);
res.status(500).send('Internal Server Error');
}
});
let categoriesR = CategoryRouter.routes();
app.use('/categories', categoriesR)
/*
app.get('/transactions', async (req: Request, res: Response) => {
try {
const transactions = await Transaction.findAll({
include: Account
});
const categories = await Category.findAll({});
const accounts = await Account.findAll({});
console.log(transactions)
res.render('transactions/transactions', { title: 'Transactions', transactions });
res.render('transactions/transactions', { title: 'Transactions', transactions, categories, accounts });
} catch (err) {
console.error(err);
res.status(500).send('Internal Server Error');
@ -97,7 +100,7 @@ export class Router {
}
});
*/
app.listen(PORT, () => {

View File

View 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;
}
}

View 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;
}
}

View File

@ -6,6 +6,7 @@ import path from 'path';
import { fileURLToPath } from 'url';
import { Storage } from './controller/storage';
import { MonthlyReport } from './controller/models/MonthlyReport';
import { testData } from './test-data';
Storage.__dirname = __dirname
@ -14,12 +15,16 @@ Storage.__dirname = __dirname
let init = async () => {
// Initialize Sequelize instance and define database connection
const sequelize = new Sequelize('sqlite::memory:', {
/*const sequelize = new Sequelize('sqlite::memory:', {
host: 'localhost',
dialect: 'sqlite', // Change this to your database dialect
// Other options...
});
*/
const sequelize = new Sequelize({
dialect: 'sqlite',
storage:`${path.join(__dirname,'../data')}/database.sqlite`
});
User.initialize(sequelize);
@ -41,80 +46,13 @@ let init = async () => {
await sequelize.sync({ force: true });
await User.create({
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)
await testData(User,Category,Account,MonthlyReport)
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();

100
src/test-data.ts Normal file
View 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)
}

View 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

View 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

View 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}`) ###

View 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
View File

@ -0,0 +1,5 @@
nav
a(href="/") Home
a(href="/transactions") Transactions
a(href="/categories") Categories
a(href="/accounts") Accounts

View File

@ -7,6 +7,11 @@ html
block additionalAssets
body
div.nav
include ./_navbar.pug
div.nav
block nav
div.container
block headline

View 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

View 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

View 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

View 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

View File

@ -1,24 +1,11 @@
extends ../layout/base.pug
block nav
a(href="/transactions") Transactions
a(href="/transactions/addTransaction") + Transaction
a(href="/transactions/addTransfer") + Transfer
block headline
h1 Transactions
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