ini
This commit is contained in:
53
src/controller/models/Account.ts
Normal file
53
src/controller/models/Account.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
// Account model definition
|
||||
import { Sequelize, DataTypes, Model, Optional, Attributes, CreateOptions, ModelStatic } from 'sequelize';
|
||||
import { HookReturn } from 'sequelize/types/hooks';
|
||||
|
||||
import { Transaction } from './Transaction'; // Import the Transaction model
|
||||
|
||||
interface AccountAttributes {
|
||||
AccountID: number;
|
||||
Name: string;
|
||||
// Other account fields...
|
||||
}
|
||||
|
||||
interface AccountCreationAttributes extends Optional<AccountAttributes, 'AccountID'> { }
|
||||
|
||||
class Account extends Model<AccountAttributes, AccountCreationAttributes> implements AccountAttributes {
|
||||
public AccountID!: number;
|
||||
public Name!: string;
|
||||
|
||||
static initialize(sequelize: Sequelize): void {
|
||||
Account.init(
|
||||
{
|
||||
AccountID: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
},
|
||||
Name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
// Other account fields...
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
modelName: 'Account',
|
||||
hooks: {
|
||||
afterCreate: (attributes: Account, options: CreateOptions<AccountAttributes>) => {
|
||||
console.log(attributes)
|
||||
},
|
||||
beforeCreate(attributes: Account, options: CreateOptions<AccountAttributes>) {
|
||||
console.log(attributes)
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static associate(models: { Transaction: typeof Transaction }): void {
|
||||
Account.hasMany(models.Transaction, { foreignKey: 'AccountID' }); // An account can have multiple transactions
|
||||
}
|
||||
}
|
||||
|
||||
export { Account };
|
||||
54
src/controller/models/Category.ts
Normal file
54
src/controller/models/Category.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Sequelize, DataTypes, Model, Optional } from 'sequelize';
|
||||
import { User } from './User';
|
||||
import { Transaction } from './Transaction';
|
||||
|
||||
// Category attributes interface
|
||||
interface CategoryAttributes {
|
||||
CategoryID: number;
|
||||
Name: string;
|
||||
Description: string | null;
|
||||
// Other fields...
|
||||
}
|
||||
|
||||
// Category creation attributes (optional fields)
|
||||
interface CategoryCreationAttributes extends Optional<CategoryAttributes, 'CategoryID'> {}
|
||||
|
||||
// Define the Category model
|
||||
class Category extends Model<CategoryAttributes, CategoryCreationAttributes> implements CategoryAttributes {
|
||||
public CategoryID!: number;
|
||||
public Name!: string;
|
||||
public Description!: string | null;
|
||||
// Other fields...
|
||||
|
||||
static initialize(sequelize: Sequelize): void {
|
||||
Category.init(
|
||||
{
|
||||
CategoryID: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
},
|
||||
Name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
Description: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
// Other fields...
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
modelName: 'Category',
|
||||
// Other model options...
|
||||
}
|
||||
);
|
||||
}
|
||||
static associate(models: { User: typeof User; Transaction: typeof Transaction }): void {
|
||||
Category.belongsToMany(models.Transaction,{through:"TransactionCategory"}); // A category can have multiple transactions
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {Category };
|
||||
114
src/controller/models/MonthlyReport.ts
Normal file
114
src/controller/models/MonthlyReport.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
// MonthlyReport model definition
|
||||
import { Sequelize, DataTypes, Model, Optional, Op, fn, col } from 'sequelize';
|
||||
import moment from 'moment';
|
||||
import { Transaction } from './Transaction'; // Assuming you have a Transaction model
|
||||
import { Account } from './Account';
|
||||
|
||||
interface MonthlyReportAttributes {
|
||||
MonthlyReportID: number;
|
||||
Month: number;
|
||||
Year: number;
|
||||
TotalAmount: number;
|
||||
|
||||
// Other report fields...
|
||||
AccountID: number;
|
||||
}
|
||||
|
||||
interface MonthlyReportCreationAttributes extends Optional<MonthlyReportAttributes, 'MonthlyReportID'> { }
|
||||
|
||||
class MonthlyReport extends Model<MonthlyReportAttributes, MonthlyReportCreationAttributes> implements MonthlyReportAttributes {
|
||||
public MonthlyReportID!: number;
|
||||
public Month!: number;
|
||||
public Year!: number;
|
||||
public TotalAmount!: number;
|
||||
public AccountID!: number;
|
||||
|
||||
static initialize(sequelize: Sequelize): void {
|
||||
MonthlyReport.init(
|
||||
{
|
||||
MonthlyReportID: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
},
|
||||
Month: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
Year: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
TotalAmount: {
|
||||
type: DataTypes.FLOAT,
|
||||
allowNull: false,
|
||||
},
|
||||
AccountID: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
}
|
||||
// Other report fields...
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
modelName: 'MonthlyReport',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static associate(models: { Transaction: typeof Transaction, Account: typeof Account }): void {
|
||||
MonthlyReport.hasMany(models.Transaction, { foreignKey: 'MonthlyReportID' }); // A report can have multiple transactions
|
||||
MonthlyReport.belongsTo(models.Account)
|
||||
}
|
||||
|
||||
static async generateMonthlyReport(month: number, year: number, AccountID: number|null = null): Promise<void> {
|
||||
try {
|
||||
moment.locale("de")
|
||||
|
||||
|
||||
let m = moment();
|
||||
m.set({year:year,month:month-1,day:15});
|
||||
let startDate = moment(m).startOf('month');
|
||||
let endDate = moment(m).endOf('month');
|
||||
// Fetch transaction data and calculate total amount
|
||||
let transactions = null;
|
||||
if(AccountID==null){
|
||||
transactions = await Transaction.findAll({
|
||||
where: {
|
||||
Date: {
|
||||
[Op.between]: [startDate, endDate],
|
||||
}
|
||||
},
|
||||
});
|
||||
}else{
|
||||
transactions = await Transaction.findAll({
|
||||
where: {
|
||||
Date: {
|
||||
[Op.between]: [startDate, endDate],
|
||||
},
|
||||
AccountID: AccountID
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(transactions)
|
||||
|
||||
const totalAmount = transactions.reduce((total, transaction) => total + transaction.Amount, 0);
|
||||
console.log(totalAmount)
|
||||
// Create a MonthlyReport entry with the generated data
|
||||
await MonthlyReport.create({
|
||||
Month: month,
|
||||
Year: year,
|
||||
TotalAmount: totalAmount,
|
||||
AccountID: 0
|
||||
});
|
||||
|
||||
console.log(`Monthly report generated for ${month}/${year}`);
|
||||
} catch (err) {
|
||||
console.error('Error generating monthly report:', err);
|
||||
} finally {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { MonthlyReport };
|
||||
129
src/controller/models/Transaction.ts
Normal file
129
src/controller/models/Transaction.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { Sequelize, DataTypes, Model, Optional, Attributes, CreateOptions, ModelStatic } from 'sequelize';
|
||||
import { User } from './User';
|
||||
import { Category } from './Category';
|
||||
import { Account } from './Account';
|
||||
import { HookReturn } from 'sequelize/types/hooks';
|
||||
import { MonthlyReport } from './MonthlyReport';
|
||||
|
||||
|
||||
|
||||
// Transaction attributes interface
|
||||
interface TransactionAttributes {
|
||||
TransactionID: number;
|
||||
Date: Date;
|
||||
Description: string | null;
|
||||
UserID: number;
|
||||
Amount: number;
|
||||
Type: 'income' | 'expense' | 'transfer';
|
||||
AccountID: number;
|
||||
// Other fields...
|
||||
|
||||
}
|
||||
|
||||
// Transaction creation attributes (optional fields)
|
||||
interface TransactionCreationAttributes extends Optional<TransactionAttributes, 'TransactionID'> { }
|
||||
|
||||
// Define the Transaction model
|
||||
class Transaction extends Model<TransactionAttributes, TransactionCreationAttributes> implements TransactionAttributes {
|
||||
public TransactionID!: number;
|
||||
public Date!: Date;
|
||||
public Description!: string | null;
|
||||
public Amount!: number;
|
||||
public Type!: 'income' | 'expense' | 'transfer';
|
||||
// Other fields...
|
||||
|
||||
public UserID!: number;
|
||||
public AccountID!: number;
|
||||
public CategoryID!: number;
|
||||
|
||||
static initialize(sequelize: Sequelize): void {
|
||||
Transaction.init(
|
||||
{
|
||||
TransactionID: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
},
|
||||
Date: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: new Date()
|
||||
},
|
||||
Description: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
UserID: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
AccountID: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 1,
|
||||
},
|
||||
Amount: {
|
||||
type: DataTypes.FLOAT,
|
||||
allowNull: false,
|
||||
},
|
||||
Type: {
|
||||
type: DataTypes.ENUM('income', 'expense','transfer'),
|
||||
allowNull: false,
|
||||
},
|
||||
// Other fields...
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
modelName: 'Transaction',
|
||||
// Other model options...
|
||||
hooks: {
|
||||
afterCreate: (attributes: Transaction, options: CreateOptions<TransactionAttributes>) => {
|
||||
console.log(attributes)
|
||||
},
|
||||
beforeCreate(attributes: Transaction, options: CreateOptions<TransactionAttributes>) {
|
||||
if (attributes.Type == "expense") {
|
||||
attributes.Amount = -0 - attributes.Amount;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
static associate(models: { User: typeof User; Category: typeof Category; Account: typeof Account; MonthlyReport: typeof MonthlyReport }): void {
|
||||
Transaction.belongsTo(models.User); // A transaction belongs to a user
|
||||
Transaction.belongsTo(models.Category, { foreignKey: 'CategoryID' }); // A transaction belongs to a category
|
||||
Transaction.belongsTo(models.Account, { foreignKey: 'AccountID' }); // A transaction belongs to an account
|
||||
}
|
||||
|
||||
static async getTotalAmountForUser(userId: number): Promise<number> {
|
||||
const result = await Transaction.sum('Amount', { where: { UserID: userId } });
|
||||
return result || 0;
|
||||
}
|
||||
|
||||
static async transferAmountFromTo(userID: number, fromAccountID: number = 1, toAccountID: number = 1, Amount: number = 0){
|
||||
console.log(`User: ${userID}, fromAccount: ${fromAccountID}, toAccount${toAccountID}, Amount: ${Amount}`)
|
||||
await Transaction.create({
|
||||
Amount:-Amount,
|
||||
Date:new Date(),
|
||||
Type:"transfer",
|
||||
//@ts-ignore
|
||||
UserID: userID, // Associate the transaction with the user
|
||||
//@ts-ignore
|
||||
CategoryID: 1,
|
||||
AccountID: fromAccountID
|
||||
})
|
||||
await Transaction.create({
|
||||
Amount:Amount,
|
||||
Date:new Date(),
|
||||
Type:"transfer",
|
||||
//@ts-ignore
|
||||
UserID: userID, // Associate the transaction with the user
|
||||
//@ts-ignore
|
||||
CategoryID: 1,
|
||||
AccountID: toAccountID
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export { Transaction };
|
||||
62
src/controller/models/User.ts
Normal file
62
src/controller/models/User.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { Sequelize, DataTypes, Model, Optional } from 'sequelize';
|
||||
import { Category } from './Category';
|
||||
import { Transaction } from './Transaction';
|
||||
|
||||
// User attributes interface
|
||||
interface UserAttributes {
|
||||
UserID: number;
|
||||
Name: string;
|
||||
Email: string;
|
||||
Password: string;
|
||||
// Other fields...
|
||||
}
|
||||
|
||||
// User creation attributes (optional fields)
|
||||
interface UserCreationAttributes extends Optional<UserAttributes, 'UserID'> { }
|
||||
|
||||
// Define the User model
|
||||
class User extends Model<UserAttributes, UserCreationAttributes> implements UserAttributes {
|
||||
public UserID!: number;
|
||||
public Name!: string;
|
||||
public Email!: string;
|
||||
public Password!: string;
|
||||
|
||||
// Other fields...
|
||||
|
||||
static initialize(sequelize: Sequelize): void {
|
||||
User.init(
|
||||
{
|
||||
UserID: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
},
|
||||
Name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
Email: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
},
|
||||
Password: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
// Other fields...
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
modelName: 'User',
|
||||
// Other model options...
|
||||
}
|
||||
);
|
||||
}
|
||||
static associate(models: { Category: typeof Category; Transaction: typeof Transaction }): void {
|
||||
User.hasMany(models.Transaction, { foreignKey: 'UserID' });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export { User };
|
||||
4
src/controller/models/index.ts
Normal file
4
src/controller/models/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { Category } from "./Category";
|
||||
export { Transaction } from "./Transaction";
|
||||
export { User } from "./User";
|
||||
export { Account } from "./Account";
|
||||
109
src/controller/router.ts
Normal file
109
src/controller/router.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
// server.ts
|
||||
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';
|
||||
|
||||
export class Router {
|
||||
app: any;
|
||||
constructor(Path = "", PORT = 3000) {
|
||||
let app = express();
|
||||
|
||||
// Set the view engine to Pug
|
||||
app.set('view engine', 'pug');
|
||||
app.set('views', './views'); // Set the views directory
|
||||
console.log(Path)
|
||||
app.use('/assets', express.static(Path));
|
||||
|
||||
app.get('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const users = await User.findAll();
|
||||
const categories = await Category.findAll();
|
||||
const transactions = await Transaction.findAll();
|
||||
|
||||
res.render('index', {
|
||||
title: 'Home',
|
||||
userCount: users.length,
|
||||
categoryCount: categories.length,
|
||||
transactionCount: transactions.length,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).send('Internal Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/users', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const users = await User.findAll();
|
||||
res.render('users', { title: 'Users', users });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
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');
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/transactions', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const transactions = await Transaction.findAll({
|
||||
include: Account
|
||||
});
|
||||
console.log(transactions)
|
||||
res.render('transactions/transactions', { title: 'Transactions', transactions });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).send('Internal Server Error');
|
||||
}
|
||||
});
|
||||
app.get('/transactions/acc/:acc', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const transactions = await Transaction.findAll({
|
||||
include: Account,
|
||||
where: {
|
||||
AccountID: req.params.acc
|
||||
}
|
||||
});
|
||||
console.log(transactions)
|
||||
res.render('transactions/transactions', { title: 'Transactions', transactions });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).send('Internal Server Error');
|
||||
}
|
||||
});
|
||||
app.get('/transactions/cat/:cat', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const transactions = await Transaction.findAll({
|
||||
include: [Account, Category],
|
||||
where: {
|
||||
//@ts-ignore
|
||||
CategoryID: req.params.cat
|
||||
}
|
||||
});
|
||||
console.log(transactions)
|
||||
res.render('transactions/transactions', { title: 'Transactions', transactions });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).send('Internal Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server is running on port ${PORT}`);
|
||||
});
|
||||
this.app = app;
|
||||
}
|
||||
}
|
||||
|
||||
5
src/controller/storage.ts
Normal file
5
src/controller/storage.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export class Storage{
|
||||
static Path=""
|
||||
static __filename: string;
|
||||
static __dirname: string;
|
||||
}
|
||||
7
src/database.ts
Normal file
7
src/database.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Sequelize, DataTypes, Model, Optional } from 'sequelize';
|
||||
export { Category, Transaction, User, Account } from './controller/models';
|
||||
|
||||
// Set associations between models (similarly as before)
|
||||
|
||||
// Sync models with the database (similarly as before)
|
||||
|
||||
120
src/index.ts
Normal file
120
src/index.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { Sequelize, DataTypes, Model, Optional } from 'sequelize';
|
||||
import { Account, Category, Transaction, User } from './controller/models';
|
||||
import { Router } from './controller/router';
|
||||
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { Storage } from './controller/storage';
|
||||
import { MonthlyReport } from './controller/models/MonthlyReport';
|
||||
|
||||
|
||||
Storage.__dirname = __dirname
|
||||
|
||||
|
||||
|
||||
let init = async () => {
|
||||
// Initialize Sequelize instance and define database connection
|
||||
const sequelize = new Sequelize('sqlite::memory:', {
|
||||
host: 'localhost',
|
||||
dialect: 'sqlite', // Change this to your database dialect
|
||||
// Other options...
|
||||
});
|
||||
|
||||
|
||||
User.initialize(sequelize);
|
||||
|
||||
Category.initialize(sequelize);
|
||||
|
||||
Transaction.initialize(sequelize);
|
||||
|
||||
|
||||
Account.initialize(sequelize);
|
||||
|
||||
MonthlyReport.initialize(sequelize);
|
||||
// Set up associations between models
|
||||
User.associate({ Transaction, Category });
|
||||
Category.associate({ User, Transaction });
|
||||
Transaction.associate({ User, Category, Account, MonthlyReport });
|
||||
Account.associate({Transaction})
|
||||
|
||||
MonthlyReport.associate({ Transaction, Account });
|
||||
|
||||
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)
|
||||
|
||||
new Router(path.join(__dirname,'../public'));
|
||||
|
||||
console.log(path.join(__dirname,'../public'))
|
||||
|
||||
MonthlyReport.generateMonthlyReport(12,2023,2)
|
||||
}
|
||||
|
||||
init();
|
||||
Reference in New Issue
Block a user