#3-calendar #11

Merged
jkeffects merged 7 commits from #3-calendar into main 2024-11-07 10:10:01 +00:00
48 changed files with 2672 additions and 9 deletions
Showing only changes of commit 98eb870385 - Show all commits

1
.gitignore vendored
View file

@ -130,3 +130,4 @@ dist
.yarn/install-state.gz
.pnp.*
export

808
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "member-administration-server",
"version": "0.0.2",
"version": "0.0.3",
"description": "Feuerwehr/Verein Mitgliederverwaltung Server",
"main": "dist/index.js",
"scripts": {
@ -34,6 +34,7 @@
"mysql": "^2.18.1",
"node-schedule": "^2.1.1",
"nodemailer": "^6.9.14",
"pdf-creator-node": "^2.3.5",
"qrcode": "^1.5.4",
"reflect-metadata": "^0.2.2",
"socket.io": "^4.7.5",

View file

@ -0,0 +1,6 @@
export interface SynchronizeProtocolAgendaCommand {
id?: number;
topic: string;
context: string;
protocolId: number;
}

View file

@ -0,0 +1,49 @@
import { dataSource } from "../data-source";
import { protocolAgenda } from "../entity/protocolAgenda";
import InternalException from "../exceptions/internalException";
import { SynchronizeProtocolAgendaCommand } from "./protocolAgendaCommand";
export default abstract class ProtocolAgendaCommandHandler {
/**
* @description create protocolAgenda
* @param {number}
* @returns {Promise<number>}
*/
static async create(protocolId: number): Promise<number> {
return await dataSource
.createQueryBuilder()
.insert()
.into(protocolAgenda)
.values({
topic: "",
context: "",
protocolId,
})
.execute()
.then((result) => {
return result.identifiers[0].id;
})
.catch((err) => {
throw new InternalException("Failed creating protocol", err);
});
}
/**
* @description sync protocolAgenda
* @param {Array<SynchronizeProtocolAgendaCommand>}
* @returns {Promise<void>}
*/
static async sync(syncProtocolAgenda: Array<SynchronizeProtocolAgendaCommand>): Promise<void> {
return await dataSource
.createQueryBuilder()
.insert()
.into(protocolAgenda)
.values(syncProtocolAgenda)
.orUpdate(["topic", "context"], ["id"])
.execute()
.then(() => {})
.catch((err) => {
throw new InternalException("Failed creating protocol", err);
});
}
}

View file

@ -0,0 +1,13 @@
export interface CreateProtocolCommand {
title: string;
date: Date;
}
export interface SynchronizeProtocolCommand {
id: number;
title: string;
date: Date;
starttime: Date;
endtime: Date;
summary: string;
}

View file

@ -0,0 +1,53 @@
import { dataSource } from "../data-source";
import { protocol } from "../entity/protocol";
import InternalException from "../exceptions/internalException";
import { CreateProtocolCommand, SynchronizeProtocolCommand } from "./protocolCommand";
export default abstract class ProtocolCommandHandler {
/**
* @description create protocol
* @param CreateProtocolCommand
* @returns {Promise<number>}
*/
static async create(createProtocol: CreateProtocolCommand): Promise<number> {
return await dataSource
.createQueryBuilder()
.insert()
.into(protocol)
.values({
title: createProtocol.title,
date: createProtocol.date,
})
.execute()
.then((result) => {
return result.identifiers[0].id;
})
.catch((err) => {
throw new InternalException("Failed creating protocol", err);
});
}
/**
* @description sync protocol
* @param SynchronizeProtocolCommand
* @returns {Promise<void>}
*/
static async sync(syncProtocol: SynchronizeProtocolCommand): Promise<void> {
return await dataSource
.createQueryBuilder()
.update(protocol)
.set({
title: syncProtocol.title,
date: syncProtocol.date,
starttime: syncProtocol.starttime,
endtime: syncProtocol.endtime,
summary: syncProtocol.summary,
})
.where("id = :id", { id: syncProtocol.id })
.execute()
.then(() => {})
.catch((err) => {
throw new InternalException("Failed creating protocol", err);
});
}
}

View file

@ -0,0 +1,6 @@
export interface SynchronizeProtocolDecisionCommand {
id?: number;
topic: string;
context: string;
protocolId: number;
}

View file

@ -0,0 +1,48 @@
import { dataSource } from "../data-source";
import { protocolDecision } from "../entity/protocolDecision";
import InternalException from "../exceptions/internalException";
import { SynchronizeProtocolDecisionCommand } from "./protocolDecisionCommand";
export default abstract class ProtocolDecisionCommandHandler {
/**
* @description create protocolDecision
* @param {number}
* @returns {Promise<number>}
*/
static async create(protocolId: number): Promise<number> {
return await dataSource
.createQueryBuilder()
.insert()
.into(protocolDecision)
.values({
topic: "",
context: "",
protocolId,
})
.execute()
.then((result) => {
return result.identifiers[0].id;
})
.catch((err) => {
throw new InternalException("Failed creating protocol", err);
});
}
/**
* @description sync protocolDecision
* @param {Array<SynchronizeProtocolDecisionCommand>}
* @returns {Promise<void>}
*/
static async sync(syncProtocolDecisions: Array<SynchronizeProtocolDecisionCommand>): Promise<void> {
return await dataSource
.createQueryBuilder()
.insert()
.into(protocolDecision)
.values(syncProtocolDecisions)
.orUpdate(["topic", "context"], ["id"])
.execute()
.then(() => {})
.catch((err) => {
throw new InternalException("Failed creating protocol", err);
});
}
}

View file

@ -0,0 +1,4 @@
export interface SynchronizeProtocolPresenceCommand {
memberIds: Array<number>;
protocolId: number;
}

View file

@ -0,0 +1,69 @@
import { DeleteResult, EntityManager, InsertResult } from "typeorm";
import { dataSource } from "../data-source";
import { protocolPresence } from "../entity/protocolPresence";
import InternalException from "../exceptions/internalException";
import ProtocolPresenceService from "../service/protocolPrecenseService";
import { SynchronizeProtocolPresenceCommand } from "./protocolPresenceCommand";
export default abstract class ProtocolPresenceCommandHandler {
/**
* @description sync protocolPresence
* @param {SynchronizeProtocolPresenceCommand}
* @returns {Promise<void>}
*/
static async sync(syncProtocolPresences: SynchronizeProtocolPresenceCommand): Promise<void> {
let currentPresence = (await ProtocolPresenceService.getAll(syncProtocolPresences.protocolId)).map(
(r) => r.memberId
);
return await dataSource.manager
.transaction(async (manager) => {
let newMembers = syncProtocolPresences.memberIds.filter((r) => !currentPresence.includes(r));
let removeMembers = currentPresence.filter((r) => !syncProtocolPresences.memberIds.includes(r));
if (newMembers.length != 0) {
await this.syncPresenceAdd(manager, syncProtocolPresences.protocolId, newMembers);
}
if (removeMembers.length != 0) {
await this.syncPresenceRemove(manager, syncProtocolPresences.protocolId, removeMembers);
}
})
.then(() => {})
.catch((err) => {
throw new InternalException("Failed saving protocol presence", err);
});
}
private static async syncPresenceAdd(
manager: EntityManager,
protocolId: number,
memberIds: Array<number>
): Promise<InsertResult> {
return await manager
.createQueryBuilder()
.insert()
.into(protocolPresence)
.values(
memberIds.map((m) => ({
protocolId,
memberId: m,
}))
)
.execute();
}
private static async syncPresenceRemove(
manager: EntityManager,
protocolId: number,
memberIds: Array<number>
): Promise<DeleteResult> {
return await manager
.createQueryBuilder()
.delete()
.from(protocolPresence)
.where("memberId IN (:...ids)", { ids: memberIds })
.andWhere("protocolId = :protocolId", { protocolId })
.execute();
}
}

View file

@ -0,0 +1,6 @@
export interface CreateProtocolPrintoutCommand {
title: string;
iteration: number;
filename: string;
protocolId: number;
}

View file

@ -0,0 +1,31 @@
import { dataSource } from "../data-source";
import { protocolPrintout } from "../entity/protocolPrintout";
import InternalException from "../exceptions/internalException";
import { CreateProtocolPrintoutCommand } from "./protocolPrintoutCommand";
export default abstract class ProtocolPrintoutCommandHandler {
/**
* @description create protocolPrintout
* @param {number}
* @returns {Promise<number>}
*/
static async create(printout: CreateProtocolPrintoutCommand): Promise<number> {
return await dataSource
.createQueryBuilder()
.insert()
.into(protocolPrintout)
.values({
title: printout.title,
iteration: printout.iteration,
filename: printout.filename,
protocolId: printout.protocolId,
})
.execute()
.then((result) => {
return result.identifiers[0].id;
})
.catch((err) => {
throw new InternalException("Failed creating protocol", err);
});
}
}

View file

@ -0,0 +1,9 @@
export interface SynchronizeProtocolVotingCommand {
id: number;
topic: string;
context: string;
favour: number;
abstain: number;
against: number;
protocolId: number;
}

View file

@ -0,0 +1,48 @@
import { dataSource } from "../data-source";
import { protocolVoting } from "../entity/protocolVoting";
import InternalException from "../exceptions/internalException";
import { SynchronizeProtocolVotingCommand } from "./protocolVotingCommand";
export default abstract class ProtocolVotingCommandHandler {
/**
* @description create protocolVoting
* @param {number}
* @returns {Promise<number>}
*/
static async create(protocolId: number): Promise<number> {
return await dataSource
.createQueryBuilder()
.insert()
.into(protocolVoting)
.values({
topic: "",
context: "",
protocolId,
})
.execute()
.then((result) => {
return result.identifiers[0].id;
})
.catch((err) => {
throw new InternalException("Failed creating protocol", err);
});
}
/**
* @description sync protocolVoting
* @param {Array<SynchronizeProtocolVotingCommand>}
* @returns {Promise<void>}
*/
static async sync(syncProtocolVotings: Array<SynchronizeProtocolVotingCommand>): Promise<void> {
return await dataSource
.createQueryBuilder()
.insert()
.into(protocolVoting)
.values(syncProtocolVotings)
.orUpdate(["topic", "context", "favour", "abstain", "against"], ["id"])
.execute()
.then(() => {})
.catch((err) => {
throw new InternalException("Failed creating protocol", err);
});
}
}

View file

@ -0,0 +1,386 @@
import { Request, Response } from "express";
import ProtocolService from "../../service/protocolService";
import ProtocolFactory from "../../factory/admin/protocol";
import ProtocolAgendaService from "../../service/protocolAgendaService";
import ProtocolAgendaFactory from "../../factory/admin/protocolAgenda";
import ProtocolDecisionService from "../../service/protocolDecisionService";
import ProtocolDecisionFactory from "../../factory/admin/protocolDecision";
import ProtocolPresenceService from "../../service/protocolPrecenseService";
import ProtocolPresenceFactory from "../../factory/admin/protocolPresence";
import ProtocolVotingService from "../../service/protocolVotingService";
import ProtocolVotingFactory from "../../factory/admin/protocolVoting";
import { CreateProtocolCommand, SynchronizeProtocolCommand } from "../../command/protocolCommand";
import ProtocolCommandHandler from "../../command/protocolCommandHandler";
import { SynchronizeProtocolAgendaCommand } from "../../command/protocolAgendaCommand";
import ProtocolAgendaCommandHandler from "../../command/protocolAgendaCommandHandler";
import { ProtocolAgendaViewModel } from "../../viewmodel/admin/protocolAgenda.models";
import ProtocolDecisionCommandHandler from "../../command/protocolDecisionCommandHandler";
import { ProtocolDecisionViewModel } from "../../viewmodel/admin/protocolDecision.models";
import ProtocolPresenceCommandHandler from "../../command/protocolPresenceCommandHandler";
import { SynchronizeProtocolPresenceCommand } from "../../command/protocolPresenceCommand";
import { SynchronizeProtocolDecisionCommand } from "../../command/protocolDecisionCommand";
import { SynchronizeProtocolVotingCommand } from "../../command/protocolVotingCommand";
import { ProtocolVotingViewModel } from "../../viewmodel/admin/protocolVoting.models";
import ProtocolVotingCommandHandler from "../../command/protocolVotingCommandHandler";
import { PdfExport } from "../../helpers/pdfExport";
import ProtocolPrintoutService from "../../service/protocolPrintoutService";
import ProtocolPrintoutFactory from "../../factory/admin/protocolPrintout";
import { CreateProtocolPrintoutCommand } from "../../command/protocolPrintoutCommand";
import ProtocolPrintoutCommandHandler from "../../command/protocolPrintoutCommandHandler";
/**
* @description get all protocols
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function getAllProtocols(req: Request, res: Response): Promise<any> {
let offset = parseInt((req.query.offset as string) ?? "0");
let count = parseInt((req.query.count as string) ?? "25");
let [protocols, total] = await ProtocolService.getAll(offset, count);
res.json({
protocols: ProtocolFactory.mapToBase(protocols),
total: total,
offset: offset,
count: count,
});
}
/**
* @description get protocol by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function getProtocolById(req: Request, res: Response): Promise<any> {
let id = parseInt(req.params.id);
let protocol = await ProtocolService.getById(id);
res.json(ProtocolFactory.mapToSingle(protocol));
}
/**
* @description get protocol agenda by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function getProtocolAgendaById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let agenda = await ProtocolAgendaService.getAll(protocolId);
res.json(ProtocolAgendaFactory.mapToBase(agenda));
}
/**
* @description get protocol decisions by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function getProtocolDecisonsById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let decisions = await ProtocolDecisionService.getAll(protocolId);
res.json(ProtocolDecisionFactory.mapToBase(decisions));
}
/**
* @description get protocol precense by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function getProtocolPrecenseById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let presence = await ProtocolPresenceService.getAll(protocolId);
res.json(ProtocolPresenceFactory.mapToBase(presence));
}
/**
* @description get protocol votings by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function getProtocolVotingsById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let votings = await ProtocolVotingService.getAll(protocolId);
res.json(ProtocolVotingFactory.mapToBase(votings));
}
/**
* @description get protocol printouts by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function getProtocolPrintoutsById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let printouts = await ProtocolPrintoutService.getAll(protocolId);
res.json(ProtocolPrintoutFactory.mapToBase(printouts));
}
/**
* @description get protocol printout by id and print
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function getProtocolPrintoutByIdAndPrint(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let printoutId = parseInt(req.params.printoutId);
let printout = await ProtocolPrintoutService.getById(printoutId, protocolId);
res.sendFile(process.cwd() + `/export/${printout.filename}.pdf`, {
headers: {
"Content-Type": "application/pdf",
},
});
}
/**
* @description create protocol
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function createProtocol(req: Request, res: Response): Promise<any> {
let title = req.body.title;
let date = req.body.date;
let createProtocol: CreateProtocolCommand = {
title,
date,
};
let id = await ProtocolCommandHandler.create(createProtocol);
res.send(id);
}
/**
* @description create protocol agenda by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function createProtocolAgendaById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let agenda = await ProtocolAgendaCommandHandler.create(protocolId);
res.send(agenda);
}
/**
* @description create protocol decisions by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function createProtocolDecisonsById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let decision = await ProtocolDecisionCommandHandler.create(protocolId);
res.send(decision);
}
/**
* @description create protocol votings by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function createProtocolVotingsById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let voting = await ProtocolVotingCommandHandler.create(protocolId);
res.send(voting);
}
/**
* @description create protocol printout by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function createProtocolPrintoutById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let protocol = await ProtocolService.getById(protocolId);
let agenda = await ProtocolAgendaService.getAll(protocolId);
let decisions = await ProtocolDecisionService.getAll(protocolId);
let presence = await ProtocolPresenceService.getAll(protocolId);
let votings = await ProtocolVotingService.getAll(protocolId);
let iteration = await ProtocolPrintoutService.getCount(protocolId);
let title = `Sitzungsprotokoll - ${new Date(protocol.date).toLocaleDateString("de-DE", {
day: "2-digit",
month: "long",
year: "numeric",
})}`;
let filename = `P_${protocol.title.replace(/[^a-zA-Z0-9]/g, "")}_${iteration + 1}_${new Date().toLocaleDateString()}`;
await PdfExport.renderFile({
template: "protocol.template.html",
title,
filename,
data: {
title: protocol.title,
summary: protocol.summary,
iteration: iteration + 1,
date: new Date(protocol.date).toLocaleDateString("de-DE", {
weekday: "long",
day: "2-digit",
month: "2-digit",
year: "numeric",
}),
start: protocol.starttime,
end: protocol.endtime,
agenda,
decisions,
presence: presence.map((p) => p.member),
votings,
},
});
let printout: CreateProtocolPrintoutCommand = {
title,
iteration: iteration + 1,
filename,
protocolId,
};
await ProtocolPrintoutCommandHandler.create(printout);
res.sendStatus(204);
}
/**
* @description synchronize protocol by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function synchronizeProtocolById(req: Request, res: Response): Promise<any> {
let id = parseInt(req.params.id);
let title = req.body.title;
let date = req.body.date;
let starttime = req.body.starttime;
let endtime = req.body.endtime;
let summary = req.body.summary;
let syncProtocol: SynchronizeProtocolCommand = {
id,
title,
date,
starttime,
endtime,
summary,
};
await ProtocolCommandHandler.sync(syncProtocol);
res.sendStatus(204);
}
/**
* @description synchronize protocol agenda by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function synchronizeProtocolAgendaById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let agenda = req.body.agenda as Array<ProtocolAgendaViewModel>;
let syncAgenda: Array<SynchronizeProtocolAgendaCommand> = agenda.map(
(a: ProtocolAgendaViewModel): SynchronizeProtocolAgendaCommand => ({
id: a.id ?? null,
topic: a.topic,
context: a.context,
protocolId,
})
);
await ProtocolAgendaCommandHandler.sync(syncAgenda);
res.sendStatus(204);
}
/**
* @description synchronize protocol decisions by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function synchronizeProtocolDecisonsById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let decisions = req.body.decisions as Array<ProtocolDecisionViewModel>;
let syncDecision: Array<SynchronizeProtocolDecisionCommand> = decisions.map(
(d: ProtocolDecisionViewModel): SynchronizeProtocolDecisionCommand => ({
id: d.id ?? null,
topic: d.topic,
context: d.context,
protocolId,
})
);
await ProtocolDecisionCommandHandler.sync(syncDecision);
res.sendStatus(204);
}
/**
* @description synchronize protocol votings by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function synchronizeProtocolVotingsById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let votings = req.body.votings as Array<ProtocolVotingViewModel>;
let syncVoting: Array<SynchronizeProtocolVotingCommand> = votings.map(
(d: ProtocolVotingViewModel): SynchronizeProtocolVotingCommand => ({
id: d.id ?? null,
topic: d.topic,
context: d.context,
favour: d.favour,
abstain: d.abstain,
against: d.abstain,
protocolId,
})
);
await ProtocolVotingCommandHandler.sync(syncVoting);
res.sendStatus(204);
}
/**
* @description synchronize protocol precense by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function synchronizeProtocolPrecenseById(req: Request, res: Response): Promise<any> {
let protocolId = parseInt(req.params.protocolId);
let presence = req.body.presence as Array<number>;
let syncPresence: SynchronizeProtocolPresenceCommand = {
memberIds: presence,
protocolId,
};
await ProtocolPresenceCommandHandler.sync(syncPresence);
res.sendStatus(204);
}

View file

@ -29,6 +29,13 @@ import { memberQualifications } from "./entity/memberQualifications";
import { membership } from "./entity/membership";
import { Memberdata1726301836849 } from "./migrations/1726301836849-memberdata";
import { CommunicationFields1727439800630 } from "./migrations/1727439800630-communicationFields";
import { protocol } from "./entity/protocol";
import { protocolAgenda } from "./entity/protocolAgenda";
import { protocolDecision } from "./entity/protocolDecision";
import { protocolPresence } from "./entity/protocolPresence";
import { protocolVoting } from "./entity/protocolVoting";
import { protocolPrintout } from "./entity/protocolPrintout";
import { Protocol1729347911107 } from "./migrations/1729347911107-protocol";
import { calendar } from "./entity/calendar";
import { calendarType } from "./entity/calendarType";
import { Calendar1729947763295 } from "./migrations/1729947763295-calendar";
@ -61,6 +68,12 @@ const dataSource = new DataSource({
memberExecutivePositions,
memberQualifications,
membership,
protocol,
protocolAgenda,
protocolDecision,
protocolPresence,
protocolVoting,
protocolPrintout,
calendar,
calendarType,
],
@ -73,6 +86,7 @@ const dataSource = new DataSource({
MemberBaseData1725435669492,
Memberdata1726301836849,
CommunicationFields1727439800630,
Protocol1729347911107,
Calendar1729947763295,
],
migrationsRun: true,

22
src/entity/protocol.ts Normal file
View file

@ -0,0 +1,22 @@
import { Column, Entity, PrimaryColumn } from "typeorm";
@Entity()
export class protocol {
@PrimaryColumn({ generated: "increment", type: "int" })
id: number;
@Column({ type: "varchar", length: 255 })
title: string;
@Column({ type: "date" })
date: Date;
@Column({ type: "time", nullable: true })
starttime: Date;
@Column({ type: "time", nullable: true })
endtime: Date;
@Column({ type: "text", nullable: true })
summary: string;
}

View file

@ -0,0 +1,24 @@
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { protocol } from "./protocol";
@Entity()
export class protocolAgenda {
@PrimaryGeneratedColumn("increment")
id: number;
@Column({ type: "varchar", length: 255 })
topic: string;
@Column({ type: "text", default: "" })
context: string;
@Column()
protocolId: number;
@ManyToOne(() => protocol, {
nullable: false,
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
protocol: protocol;
}

View file

@ -0,0 +1,24 @@
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { protocol } from "./protocol";
@Entity()
export class protocolDecision {
@PrimaryGeneratedColumn("increment")
id: number;
@Column({ type: "varchar", length: 255 })
topic: string;
@Column({ type: "text", default: "" })
context: string;
@Column()
protocolId: number;
@ManyToOne(() => protocol, {
nullable: false,
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
protocol: protocol;
}

View file

@ -0,0 +1,26 @@
import { Column, Entity, ManyToOne, PrimaryColumn } from "typeorm";
import { protocol } from "./protocol";
import { member } from "./member";
@Entity()
export class protocolPresence {
@PrimaryColumn()
memberId: number;
@PrimaryColumn()
protocolId: number;
@ManyToOne(() => member, {
nullable: false,
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
member: member;
@ManyToOne(() => protocol, {
nullable: false,
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
protocol: protocol;
}

View file

@ -0,0 +1,30 @@
import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { protocol } from "./protocol";
@Entity()
export class protocolPrintout {
@PrimaryGeneratedColumn("increment")
id: number;
@Column({ type: "varchar", length: 255 })
title: string;
@Column({ type: "int" })
iteration: number;
@Column({ type: "varchar", length: 255 })
filename: string;
@CreateDateColumn()
createdAt: Date;
@Column()
protocolId: number;
@ManyToOne(() => protocol, {
nullable: false,
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
protocol: protocol;
}

View file

@ -0,0 +1,33 @@
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { protocol } from "./protocol";
@Entity()
export class protocolVoting {
@PrimaryGeneratedColumn("increment")
id: number;
@Column({ type: "varchar", length: 255 })
topic: string;
@Column({ type: "text", default: "" })
context: string;
@Column({ type: "int", default: 0 })
favour: number;
@Column({ type: "int", default: 0 })
abstain: number;
@Column({ type: "int", default: 0 })
against: number;
@Column()
protocolId: number;
@ManyToOne(() => protocol, {
nullable: false,
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
protocol: protocol;
}

View file

@ -0,0 +1,29 @@
import { protocol } from "../../entity/protocol";
import { ProtocolViewModel } from "../../viewmodel/admin/protocol.models";
export default abstract class ProtocolFactory {
/**
* @description map record to protocol
* @param {protocol} record
* @returns {ProtocolViewModel}
*/
public static mapToSingle(record: protocol): ProtocolViewModel {
return {
id: record.id,
title: record.title,
date: record.date,
starttime: record.starttime,
endtime: record.endtime,
summary: record.summary,
};
}
/**
* @description map records to protocol
* @param {Array<protocol>} records
* @returns {Array<ProtocolViewModel>}
*/
public static mapToBase(records: Array<protocol>): Array<ProtocolViewModel> {
return records.map((r) => this.mapToSingle(r));
}
}

View file

@ -0,0 +1,27 @@
import { protocolAgenda } from "../../entity/protocolAgenda";
import { ProtocolAgendaViewModel } from "../../viewmodel/admin/protocolAgenda.models";
export default abstract class ProtocolAgendaFactory {
/**
* @description map record to protocolAgenda
* @param {protocol} record
* @returns {ProtocolAgendaViewModel}
*/
public static mapToSingle(record: protocolAgenda): ProtocolAgendaViewModel {
return {
id: record.id,
topic: record.topic,
context: record.context,
protocolId: record.protocolId,
};
}
/**
* @description map records to protocolAgenda
* @param {Array<protocol>} records
* @returns {Array<ProtocolAgendaViewModel>}
*/
public static mapToBase(records: Array<protocolAgenda>): Array<ProtocolAgendaViewModel> {
return records.map((r) => this.mapToSingle(r));
}
}

View file

@ -0,0 +1,27 @@
import { protocolDecision } from "../../entity/protocolDecision";
import { ProtocolDecisionViewModel } from "../../viewmodel/admin/protocolDecision.models";
export default abstract class ProtocolDecisionFactory {
/**
* @description map record to protocolDecision
* @param {protocol} record
* @returns {ProtocolDecisionViewModel}
*/
public static mapToSingle(record: protocolDecision): ProtocolDecisionViewModel {
return {
id: record.id,
topic: record.topic,
context: record.context,
protocolId: record.protocolId,
};
}
/**
* @description map records to protocolDecision
* @param {Array<protocol>} records
* @returns {Array<ProtocolDecisionViewModel>}
*/
public static mapToBase(records: Array<protocolDecision>): Array<ProtocolDecisionViewModel> {
return records.map((r) => this.mapToSingle(r));
}
}

View file

@ -0,0 +1,27 @@
import { protocolPresence } from "../../entity/protocolPresence";
import { ProtocolPresenceViewModel } from "../../viewmodel/admin/protocolPresence.models";
import MemberFactory from "./member";
export default abstract class ProtocolPresenceFactory {
/**
* @description map record to protocolPresence
* @param {protocol} record
* @returns {ProtocolPresenceViewModel}
*/
public static mapToSingle(record: protocolPresence): ProtocolPresenceViewModel {
return {
memberId: record.member.id,
member: MemberFactory.mapToSingle(record.member),
protocolId: record.protocolId,
};
}
/**
* @description map records to protocolPresence
* @param {Array<protocol>} records
* @returns {Array<ProtocolPresenceViewModel>}
*/
public static mapToBase(records: Array<protocolPresence>): Array<ProtocolPresenceViewModel> {
return records.map((r) => this.mapToSingle(r));
}
}

View file

@ -0,0 +1,28 @@
import { protocolPrintout } from "../../entity/protocolPrintout";
import { ProtocolPrintoutViewModel } from "../../viewmodel/admin/protocolPrintout.models";
export default abstract class ProtocolPrintoutFactory {
/**
* @description map record to protocolPrintout
* @param {protocol} record
* @returns {ProtocolPrintoutViewModel}
*/
public static mapToSingle(record: protocolPrintout): ProtocolPrintoutViewModel {
return {
id: record.id,
title: record.title,
iteration: record.iteration,
createdAt: record.createdAt,
protocolId: record.protocolId,
};
}
/**
* @description map records to protocolPrintout
* @param {Array<protocol>} records
* @returns {Array<ProtocolPrintoutViewModel>}
*/
public static mapToBase(records: Array<protocolPrintout>): Array<ProtocolPrintoutViewModel> {
return records.map((r) => this.mapToSingle(r));
}
}

View file

@ -0,0 +1,30 @@
import { protocolVoting } from "../../entity/protocolVoting";
import { ProtocolVotingViewModel } from "../../viewmodel/admin/protocolVoting.models";
export default abstract class ProtocolVotingFactory {
/**
* @description map record to protocolVoting
* @param {protocol} record
* @returns {ProtocolVotingViewModel}
*/
public static mapToSingle(record: protocolVoting): ProtocolVotingViewModel {
return {
id: record.id,
topic: record.topic,
context: record.context,
favour: record.favour,
abstain: record.abstain,
against: record.against,
protocolId: record.protocolId,
};
}
/**
* @description map records to protocolVoting
* @param {Array<protocol>} records
* @returns {Array<ProtocolVotingViewModel>}
*/
public static mapToBase(records: Array<protocolVoting>): Array<ProtocolVotingViewModel> {
return records.map((r) => this.mapToSingle(r));
}
}

44
src/helpers/pdfExport.ts Normal file
View file

@ -0,0 +1,44 @@
import { readFileSync } from "fs";
import pdf, { Options } from "pdf-creator-node";
var options = (title: string = "pdf-export Mitgliederverwaltung"): Options => ({
format: "A4",
orientation: "portrait",
border: "10mm",
header: {
height: "10mm",
contents: `<h1 style="text-align: center;">${title}</h1>`,
},
footer: {
height: "5mm",
contents: {
default: '<span style="color: #444;">{{page}}</span>/<span>{{pages}}</span>',
},
},
});
export abstract class PdfExport {
static getTemplate(template: string) {
return readFileSync(process.cwd() + "/src/templates/" + template, "utf8");
}
static async renderFile({
template,
title,
filename,
data,
}: {
template: string;
title: string;
filename: string;
data: any;
}) {
let document = {
html: this.getTemplate(template),
data,
path: process.cwd() + `/export/${filename}.pdf`,
};
await pdf.create(document, options(title));
}
}

View file

@ -0,0 +1,203 @@
import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm";
import { DB_TYPE } from "../env.defaults";
export class Protocol1729347911107 implements MigrationInterface {
name = "Protocol1729347911107";
public async up(queryRunner: QueryRunner): Promise<void> {
const variableType_int = DB_TYPE == "mysql" ? "int" : "integer";
await queryRunner.createTable(
new Table({
name: "protocol",
columns: [
{ name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" },
{ name: "title", type: "varchar", length: "255", isNullable: false },
{ name: "date", type: "date", isNullable: false },
{ name: "starttime", type: "time", isNullable: true },
{ name: "endtime", type: "time", isNullable: true },
{ name: "summary", type: "text", isNullable: true },
],
}),
true
);
await queryRunner.createTable(
new Table({
name: "protocol_agenda",
columns: [
{ name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" },
{ name: "topic", type: "varchar", length: "255", isNullable: false },
{ name: "context", type: "text", default: "''", isNullable: false },
{ name: "protocolId", type: variableType_int, isNullable: false },
],
}),
true
);
await queryRunner.createTable(
new Table({
name: "protocol_decision",
columns: [
{ name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" },
{ name: "topic", type: "varchar", length: "255", isNullable: false },
{ name: "context", type: "text", default: "''", isNullable: false },
{ name: "protocolId", type: variableType_int, isNullable: false },
],
}),
true
);
await queryRunner.createTable(
new Table({
name: "protocol_presence",
columns: [
{ name: "memberId", type: variableType_int, isPrimary: true, isNullable: false },
{ name: "protocolId", type: variableType_int, isPrimary: true, isNullable: false },
],
}),
true
);
await queryRunner.createTable(
new Table({
name: "protocol_voting",
columns: [
{ name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" },
{ name: "topic", type: "varchar", length: "255", isNullable: false },
{ name: "context", type: "text", default: "''", isNullable: false },
{ name: "favour", type: variableType_int, default: 0, isNullable: false },
{ name: "abstain", type: variableType_int, default: 0, isNullable: false },
{ name: "against", type: variableType_int, default: 0, isNullable: false },
{ name: "protocolId", type: variableType_int, isNullable: false },
],
}),
true
);
await queryRunner.createTable(
new Table({
name: "protocol_printout",
columns: [
{ name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" },
{ name: "title", type: "varchar", length: "255", isNullable: false },
{ name: "iteration", type: variableType_int, default: 1, isNullable: false },
{ name: "filename", type: "varchar", length: "255", isNullable: false },
{ name: "createdAt", type: "datetime(6)", isNullable: false, default: "CURRENT_TIMESTAMP(6)" },
{ name: "protocolId", type: variableType_int, isNullable: false },
],
}),
true
);
await queryRunner.createForeignKey(
"protocol_agenda",
new TableForeignKey({
columnNames: ["protocolId"],
referencedColumnNames: ["id"],
referencedTableName: "protocol",
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
);
await queryRunner.createForeignKey(
"protocol_decision",
new TableForeignKey({
columnNames: ["protocolId"],
referencedColumnNames: ["id"],
referencedTableName: "protocol",
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
);
await queryRunner.createForeignKey(
"protocol_voting",
new TableForeignKey({
columnNames: ["protocolId"],
referencedColumnNames: ["id"],
referencedTableName: "protocol",
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
);
await queryRunner.createForeignKey(
"protocol_presence",
new TableForeignKey({
columnNames: ["protocolId"],
referencedColumnNames: ["id"],
referencedTableName: "protocol",
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
);
await queryRunner.createForeignKey(
"protocol_presence",
new TableForeignKey({
columnNames: ["memberId"],
referencedColumnNames: ["id"],
referencedTableName: "member",
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
);
await queryRunner.createForeignKey(
"protocol_printout",
new TableForeignKey({
columnNames: ["protocolId"],
referencedColumnNames: ["id"],
referencedTableName: "protocol",
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
const tableProtocolVotings = await queryRunner.getTable("protocol_voting");
const foreignKeyProtocolVotings = tableProtocolVotings.foreignKeys.find(
(fk) => fk.columnNames.indexOf("protocolId") !== -1
);
await queryRunner.dropForeignKey("protocol_voting", foreignKeyProtocolVotings);
const tableProtocolDecisions = await queryRunner.getTable("protocol_decision");
const foreignKeyProtocolDecisions = tableProtocolDecisions.foreignKeys.find(
(fk) => fk.columnNames.indexOf("protocolId") !== -1
);
await queryRunner.dropForeignKey("protocol_decision", foreignKeyProtocolDecisions);
const tableProtocolAgenda = await queryRunner.getTable("protocol_agenda");
const foreignKeyProtocolAgenda = tableProtocolAgenda.foreignKeys.find(
(fk) => fk.columnNames.indexOf("protocolId") !== -1
);
await queryRunner.dropForeignKey("protocol_agenda", foreignKeyProtocolAgenda);
const tableProtocolPresence_protcol = await queryRunner.getTable("protocol_presence");
const foreignKeyProtocolPresence_protcol = tableProtocolPresence_protcol.foreignKeys.find(
(fk) => fk.columnNames.indexOf("protocolId") !== -1
);
await queryRunner.dropForeignKey("protocol_presence", foreignKeyProtocolPresence_protcol);
const tableProtocolPresence_member = await queryRunner.getTable("protocol_presence");
const foreignKeyProtocolPresence_member = tableProtocolPresence_member.foreignKeys.find(
(fk) => fk.columnNames.indexOf("memberId") !== -1
);
await queryRunner.dropForeignKey("protocol_presence", foreignKeyProtocolPresence_member);
const tableProtocolPrintout = await queryRunner.getTable("protocol_printout");
const foreignKeyProtocolPrintout = tableProtocolPrintout.foreignKeys.find(
(fk) => fk.columnNames.indexOf("protocolId") !== -1
);
await queryRunner.dropForeignKey("protocol_printout", foreignKeyProtocolPrintout);
await queryRunner.dropTable("protocol_printout");
await queryRunner.dropTable("protocol_voting");
await queryRunner.dropTable("protocol_presence");
await queryRunner.dropTable("protocol_decision");
await queryRunner.dropTable("protocol_agenda");
await queryRunner.dropTable("protocol");
}
}

View file

@ -8,6 +8,7 @@ import membershipStatus from "./membershipStatus";
import qualification from "./qualification";
import member from "./member";
import protocol from "./protocol";
import calendar from "./calendar";
@ -36,6 +37,7 @@ router.use("/qualification", PermissionHelper.passCheckMiddleware("read", "setti
router.use("/member", PermissionHelper.passCheckMiddleware("read", "club", "member"), member);
router.use("/protocol", PermissionHelper.passCheckMiddleware("read", "club", "protocol"), protocol);
router.use("/calendar", PermissionHelper.passCheckMiddleware("read", "club", "calendar"), calendar);
router.use("/role", PermissionHelper.passCheckMiddleware("read", "user", "role"), role);

View file

@ -0,0 +1,138 @@
import express, { Request, Response } from "express";
import {
createProtocol,
createProtocolAgendaById,
createProtocolDecisonsById,
createProtocolPrintoutById,
createProtocolVotingsById,
getAllProtocols,
getProtocolAgendaById,
getProtocolById,
getProtocolDecisonsById,
getProtocolPrecenseById,
getProtocolPrintoutByIdAndPrint,
getProtocolPrintoutsById,
getProtocolVotingsById,
synchronizeProtocolAgendaById,
synchronizeProtocolById,
synchronizeProtocolDecisonsById,
synchronizeProtocolPrecenseById,
synchronizeProtocolVotingsById,
} from "../../controller/admin/protocolController";
import PermissionHelper from "../../helpers/permissionHelper";
var router = express.Router({ mergeParams: true });
router.get("/", async (req: Request, res: Response) => {
await getAllProtocols(req, res);
});
router.get("/:id", async (req: Request, res: Response) => {
await getProtocolById(req, res);
});
router.get("/:protocolId/agenda", async (req: Request, res: Response) => {
await getProtocolAgendaById(req, res);
});
router.get("/:protocolId/decisions", async (req: Request, res: Response) => {
await getProtocolDecisonsById(req, res);
});
router.get("/:protocolId/presence", async (req: Request, res: Response) => {
await getProtocolPrecenseById(req, res);
});
router.get("/:protocolId/votings", async (req: Request, res: Response) => {
await getProtocolVotingsById(req, res);
});
router.get("/:protocolId/printouts", async (req: Request, res: Response) => {
await getProtocolPrintoutsById(req, res);
});
router.get("/:protocolId/printout/:printoutId", async (req: Request, res: Response) => {
await getProtocolPrintoutByIdAndPrint(req, res);
});
router.post(
"/",
PermissionHelper.passCheckMiddleware("create", "club", "protocol"),
async (req: Request, res: Response) => {
await createProtocol(req, res);
}
);
router.post(
"/:protocolId/agenda",
PermissionHelper.passCheckMiddleware("create", "club", "protocol"),
async (req: Request, res: Response) => {
await createProtocolAgendaById(req, res);
}
);
router.post(
"/:protocolId/decision",
PermissionHelper.passCheckMiddleware("create", "club", "protocol"),
async (req: Request, res: Response) => {
await createProtocolDecisonsById(req, res);
}
);
router.post(
"/:protocolId/voting",
PermissionHelper.passCheckMiddleware("create", "club", "protocol"),
async (req: Request, res: Response) => {
await createProtocolVotingsById(req, res);
}
);
router.post(
"/:protocolId/printout",
PermissionHelper.passCheckMiddleware("create", "club", "protocol"),
async (req: Request, res: Response) => {
await createProtocolPrintoutById(req, res);
}
);
router.patch(
"/:id/synchronize",
PermissionHelper.passCheckMiddleware("update", "club", "protocol"),
async (req: Request, res: Response) => {
await synchronizeProtocolById(req, res);
}
);
router.patch(
"/:protocolId/synchronize/agenda",
PermissionHelper.passCheckMiddleware("update", "club", "protocol"),
async (req: Request, res: Response) => {
await synchronizeProtocolAgendaById(req, res);
}
);
router.patch(
"/:protocolId/synchronize/decisions",
PermissionHelper.passCheckMiddleware("update", "club", "protocol"),
async (req: Request, res: Response) => {
await synchronizeProtocolDecisonsById(req, res);
}
);
router.patch(
"/:protocolId/synchronize/votings",
PermissionHelper.passCheckMiddleware("update", "club", "protocol"),
async (req: Request, res: Response) => {
await synchronizeProtocolVotingsById(req, res);
}
);
router.put(
"/:protocolId/synchronize/presence",
PermissionHelper.passCheckMiddleware("update", "club", "protocol"),
async (req: Request, res: Response) => {
await synchronizeProtocolPrecenseById(req, res);
}
);
export default router;

View file

@ -0,0 +1,41 @@
import { dataSource } from "../data-source";
import { protocolAgenda } from "../entity/protocolAgenda";
import InternalException from "../exceptions/internalException";
export default abstract class ProtocolAgendaService {
/**
* @description get all protocolAgendas
* @returns {Promise<Array<protocolAgenda>>}
*/
static async getAll(protocolId: number): Promise<Array<protocolAgenda>> {
return await dataSource
.getRepository(protocolAgenda)
.createQueryBuilder("protocolAgenda")
.where("protocolAgenda.protocolId = :protocolId", { protocolId })
.getMany()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocolAgendas not found", err);
});
}
/**
* @description get protocolAgenda by id
* @returns {Promise<protocolAgenda>}
*/
static async getById(id: number): Promise<protocolAgenda> {
return await dataSource
.getRepository(protocolAgenda)
.createQueryBuilder("protocolAgenda")
.where("protocolAgenda.id = :id", { id: id })
.getOneOrFail()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocolAgenda not found by id", err);
});
}
}

View file

@ -0,0 +1,41 @@
import { dataSource } from "../data-source";
import { protocolDecision } from "../entity/protocolDecision";
import InternalException from "../exceptions/internalException";
export default abstract class ProtocolDecisionService {
/**
* @description get all protocolDecisionss
* @returns {Promise<Array<protocolDecision>>}
*/
static async getAll(protocolId: number): Promise<Array<protocolDecision>> {
return await dataSource
.getRepository(protocolDecision)
.createQueryBuilder("protocolDecisions")
.where("protocolDecisions.protocolId = :protocolId", { protocolId })
.getMany()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocolDecisions not found", err);
});
}
/**
* @description get protocolDecision by id
* @returns {Promise<protocolDecision>}
*/
static async getById(id: number): Promise<protocolDecision> {
return await dataSource
.getRepository(protocolDecision)
.createQueryBuilder("protocolDecisions")
.where("protocolDecisions.id = :id", { id: id })
.getOneOrFail()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocolDecision not found by id", err);
});
}
}

View file

@ -0,0 +1,43 @@
import { dataSource } from "../data-source";
import { protocolPresence } from "../entity/protocolPresence";
import InternalException from "../exceptions/internalException";
export default abstract class ProtocolPresenceService {
/**
* @description get all protocolPresences
* @returns {Promise<Array<protocolPresence>>}
*/
static async getAll(protocolId: number): Promise<Array<protocolPresence>> {
return await dataSource
.getRepository(protocolPresence)
.createQueryBuilder("protocolPresence")
.leftJoinAndSelect("protocolPresence.member", "member")
.where("protocolPresence.protocolId = :protocolId", { protocolId })
.getMany()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocolPresence not found", err);
});
}
/**
* @description get protocolDecision by id
* @returns {Promise<protocolPresence>}
*/
static async getById(id: number): Promise<protocolPresence> {
return await dataSource
.getRepository(protocolPresence)
.createQueryBuilder("protocolPresence")
.leftJoinAndSelect("protocolPresence.member", "member")
.where("protocolPresence.id = :id", { id: id })
.getOneOrFail()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocolDecision not found by id", err);
});
}
}

View file

@ -0,0 +1,60 @@
import { dataSource } from "../data-source";
import { protocolPrintout } from "../entity/protocolPrintout";
import InternalException from "../exceptions/internalException";
export default abstract class ProtocolPrintoutService {
/**
* @description get all protocolPrintouts
* @returns {Promise<Array<protocolPrintout>>}
*/
static async getAll(protocolId: number): Promise<Array<protocolPrintout>> {
return await dataSource
.getRepository(protocolPrintout)
.createQueryBuilder("protocolPrintout")
.where("protocolPrintout.protocolId = :protocolId", { protocolId })
.getMany()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocolPrintouts not found", err);
});
}
/**
* @description get protocolPrintout by id
* @returns {Promise<protocolPrintout>}
*/
static async getById(id: number, protocolId: number): Promise<protocolPrintout> {
return await dataSource
.getRepository(protocolPrintout)
.createQueryBuilder("protocolPrintout")
.where("protocolPrintout.protocolId = :protocolId", { protocolId })
.andWhere("protocolPrintout.id = :id", { id: id })
.getOneOrFail()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocolPrintout not found by id", err);
});
}
/**
* @description get count of printouts by id
* @returns {Promise<number>}
*/
static async getCount(protocolId: number): Promise<number> {
return await dataSource
.getRepository(protocolPrintout)
.createQueryBuilder("protocolPrintout")
.where("protocolPrintout.protocolId = :protocolId", { protocolId })
.getCount()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocolPrintout not found by id", err);
});
}
}

View file

@ -0,0 +1,43 @@
import { dataSource } from "../data-source";
import { protocol } from "../entity/protocol";
import InternalException from "../exceptions/internalException";
export default abstract class ProtocolService {
/**
* @description get all protocols
* @returns {Promise<[Array<protocol>, number]>}
*/
static async getAll(offset: number = 0, count: number = 25): Promise<[Array<protocol>, number]> {
return await dataSource
.getRepository(protocol)
.createQueryBuilder("protocol")
.offset(offset)
.limit(count)
.orderBy("date", "DESC")
.getManyAndCount()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocols not found", err);
});
}
/**
* @description get protocol by id
* @returns {Promise<protocol>}
*/
static async getById(id: number): Promise<protocol> {
return await dataSource
.getRepository(protocol)
.createQueryBuilder("protocol")
.where("protocol.id = :id", { id: id })
.getOneOrFail()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocol not found by id", err);
});
}
}

View file

@ -0,0 +1,41 @@
import { dataSource } from "../data-source";
import { protocolVoting } from "../entity/protocolVoting";
import InternalException from "../exceptions/internalException";
export default abstract class ProtocolVotingService {
/**
* @description get all protocolVotingss
* @returns {Promise<Array<protocolVoting>>}
*/
static async getAll(protocolId: number): Promise<Array<protocolVoting>> {
return await dataSource
.getRepository(protocolVoting)
.createQueryBuilder("protocolVotings")
.where("protocolVotings.protocolId = :protocolId", { protocolId })
.getMany()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocolVotings not found", err);
});
}
/**
* @description get protocolVoting by id
* @returns {Promise<protocolVoting>}
*/
static async getById(id: number): Promise<protocolVoting> {
return await dataSource
.getRepository(protocolVoting)
.createQueryBuilder("protocolVotings")
.where("protocolVotings.id = :id", { id: id })
.getOneOrFail()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("protocolVoting not found by id", err);
});
}
}

View file

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Protokoll</title>
</head>
<body>
<h1>{{title}}</h1>
<p>Am {{date}} von {{start}} Uhr bis {{end}} Uhr</p>
<p>Ausdruck Nr {{iteration}}</p>
<br />
<p><b>Zusammenfassung:</b></p>
<p>{{{summary}}}</p>
<br />
<br />
<h2>Anwesenheit ({{presence.length}})</h2>
<ul>
{{#each presence}}
<li>{{this.firstname}} {{this.lastname}}</li>
{{/each}}
</ul>
<br />
<h2>Agenda</h2>
{{#each agenda}}
<div>
<h3>{{this.topic}}</h3>
<span>{{{this.context}}}</span>
</div>
{{/each}}
<br />
<h2>Entscheidungen</h2>
{{#each decisions}}
<div>
<h3>{{this.topic}}</h3>
<span>{{{this.context}}}</span>
</div>
{{/each}}
<br />
<h2>Abstimmungen</h2>
{{#each votings}}
<div>
<h3>{{this.topic}}</h3>
<p><b>Ergebnis:</b> dafür: {{this.favour}} | enthalten: {{this.abstain}} | dagegen: {{this.against}}</p>
<span>{{{this.context}}}</span>
</div>
{{/each}}
</body>
<style>
h2,
h3,
p,
span,
ul,
li {
padding: 0;
margin: 0;
}
h1,
h2 {
color: #990b00;
}
h2 {
margin-bottom: 5px;
}
</style>
</html>

View file

@ -4,7 +4,7 @@ export type PermissionModule =
| "member"
| "calendar"
| "newsletter"
| "protocoll"
| "protocol"
| "qualification"
| "award"
| "executive_position"
@ -40,7 +40,7 @@ export const permissionModules: Array<PermissionModule> = [
"member",
"calendar",
"newsletter",
"protocoll",
"protocol",
"qualification",
"award",
"executive_position",
@ -52,7 +52,7 @@ export const permissionModules: Array<PermissionModule> = [
];
export const permissionTypes: Array<PermissionType> = ["read", "create", "update", "delete"];
export const sectionsAndModules: SectionsAndModulesObject = {
club: ["member", "calendar", "newsletter", "protocoll"],
club: ["member", "calendar", "newsletter", "protocol"],
settings: ["qualification", "award", "executive_position", "communication", "membership_status", "calendar_type"],
user: ["user", "role"],
};

27
src/types/pdf-creator-node.d.ts vendored Normal file
View file

@ -0,0 +1,27 @@
// types/pdf-creator-node.d.ts
declare module "pdf-creator-node" {
interface Document {
html: string;
data: any;
path: string;
type?: string;
}
interface Options {
format: string;
orientation: string;
border: string;
header?: {
height: string;
contents: string;
};
footer?: {
height: string;
contents: string | { [key: string]: string | number };
};
}
function create(document: Document, options: Options): Promise<any>;
export { create, Document, Options };
}

View file

@ -0,0 +1,8 @@
export interface ProtocolViewModel {
id: number;
title: string;
date: Date;
starttime: Date;
endtime: Date;
summary: string;
}

View file

@ -0,0 +1,6 @@
export interface ProtocolAgendaViewModel {
id: number;
topic: string;
context: string;
protocolId: number;
}

View file

@ -0,0 +1,6 @@
export interface ProtocolDecisionViewModel {
id: number;
topic: string;
context: string;
protocolId: number;
}

View file

@ -0,0 +1,7 @@
import { MemberViewModel } from "./member.models";
export interface ProtocolPresenceViewModel {
memberId: number;
member: MemberViewModel;
protocolId: number;
}

View file

@ -0,0 +1,7 @@
export interface ProtocolPrintoutViewModel {
id: number;
title: string;
iteration: number;
createdAt: Date;
protocolId: number;
}

View file

@ -0,0 +1,9 @@
export interface ProtocolVotingViewModel {
id: number;
topic: string;
context: string;
favour: number;
abstain: number;
against: number;
protocolId: number;
}