From 0c44b01037dee3b3b04650ef584b26e9809f0a07 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 14 Mar 2025 13:17:25 +0100 Subject: [PATCH 1/9] fix: calendar alarms --- src/helpers/calendarHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/calendarHelper.ts b/src/helpers/calendarHelper.ts index e645d5b..7418089 100644 --- a/src/helpers/calendarHelper.ts +++ b/src/helpers/calendarHelper.ts @@ -50,7 +50,7 @@ export abstract class CalendarHelper { alarms: [ { action: "display", - description: "Erinnerung", + summary: "Erinnerung", trigger: { minutes: 30, before: true, -- 2.45.3 From d6a13d657ba265e42436fb6a7aba0b9b724df991 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 15 Mar 2025 09:05:22 +0100 Subject: [PATCH 2/9] member base template --- src/templates/member.body.template.html | 49 +++++++++++++++++++++++ src/templates/member.footer.template.html | 3 ++ 2 files changed, 52 insertions(+) create mode 100644 src/templates/member.body.template.html create mode 100644 src/templates/member.footer.template.html diff --git a/src/templates/member.body.template.html b/src/templates/member.body.template.html new file mode 100644 index 0000000..9129600 --- /dev/null +++ b/src/templates/member.body.template.html @@ -0,0 +1,49 @@ + + + + + Mitglied + + +

{{member.lastname}} {{member.firstname}}{{#if member.nameaffix}} - {{member.nameaffix}}{{/if}}

+

Mitglieds-Ausdruck Stand {{today}}

+
+ +

Agenda

+ {{#each ---}} +
+

+ +
+
+ {{/each}} + + + diff --git a/src/templates/member.footer.template.html b/src/templates/member.footer.template.html new file mode 100644 index 0000000..7a3190b --- /dev/null +++ b/src/templates/member.footer.template.html @@ -0,0 +1,3 @@ +
+ Seite von +
-- 2.45.3 From 2f72cc392562c54fec0c0d0c7f4e214dcfdc3298 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sun, 16 Mar 2025 17:42:51 +0100 Subject: [PATCH 3/9] template + route & controller --- src/controller/admin/club/memberController.ts | 38 ++++++++++++ src/routes/admin/club/member.ts | 5 ++ src/templates/member.body.template.html | 62 +++++++++++++++++-- 3 files changed, 99 insertions(+), 6 deletions(-) diff --git a/src/controller/admin/club/memberController.ts b/src/controller/admin/club/memberController.ts index 91c51f8..86bea81 100644 --- a/src/controller/admin/club/memberController.ts +++ b/src/controller/admin/club/memberController.ts @@ -118,6 +118,44 @@ export async function getMemberStatisticsById(req: Request, res: Response): Prom res.json(MemberFactory.mapToMemberStatistic(member)); } +/** + * @description get member printout by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getMemberPrintoutById(req: Request, res: Response): Promise { + const memberId = req.params.id; + let member = await MemberService.getById(memberId); + let memberships = await MembershipService.getAll(memberId); + let awards = await MemberAwardService.getAll(memberId); + let qualifications = await MemberQualificationService.getAll(memberId); + let positions = await MemberExecutivePositionService.getAll(memberId); + let communications = await CommunicationService.getAll(memberId); + + let pdf = await PdfExport.renderFile({ + title: "Mitglieder-Ausdruck", + template: "member", + saveToDisk: false, + data: { + member, + memberships, + awards, + qualifications, + positions, + communications, + }, + }); + + let pdfbuffer = Buffer.from(pdf); + + res.setHeader("Content-Type", "application/pdf"); + res.setHeader("Content-Length", pdfbuffer.byteLength); + res.setHeader("Content-Disposition", "inline; filename=preview.pdf"); + + res.send(pdfbuffer); +} + /** * @description get memberships by member * @param req {Request} Express req object diff --git a/src/routes/admin/club/member.ts b/src/routes/admin/club/member.ts index 5f9d032..541487e 100644 --- a/src/routes/admin/club/member.ts +++ b/src/routes/admin/club/member.ts @@ -21,6 +21,7 @@ import { getExecutivePositionByMemberAndRecord, getExecutivePositionsByMember, getMemberById, + getMemberPrintoutById, getMembersByIds, getMembershipByMemberAndRecord, getMembershipsByMember, @@ -55,6 +56,10 @@ router.get("/:id/statistics", async (req: Request, res: Response) => { await getMemberStatisticsById(req, res); }); +router.get("/:id/print", async (req: Request, res: Response) => { + await getMemberPrintoutById(req, res); +}); + router.get("/print/namelist", async (req: Request, res: Response) => { await createMemberPrintoutList(req, res); }); diff --git a/src/templates/member.body.template.html b/src/templates/member.body.template.html index 9129600..a777c2a 100644 --- a/src/templates/member.body.template.html +++ b/src/templates/member.body.template.html @@ -5,18 +5,68 @@ Mitglied -

{{member.lastname}} {{member.firstname}}{{#if member.nameaffix}} - {{member.nameaffix}}{{/if}}

+

+ {{member.salutation}} {{member.lastname}} {{member.firstname}}{{#if member.nameaffix}} - + {{member.nameaffix}}{{/if}} +

Mitglieds-Ausdruck Stand {{today}}


-

Agenda

- {{#each ---}} +

geboren: {{member.birthdate}}

+

eingetreten: {{member.firstMembershipEntry.start}}

+ {{#if member.lastMembershipEntry}} +

ausgetreten: {{member.lastMembershipEntry.end}}

+ {{/if}} {{#if memberships.length}} +
+

Mitgliedschaften

+ {{#each memberships}}
-

- +

{{this.status.status}}: {{this.start}} - {{this.end ?? 'heute'}}

+ {{#if this.terminationReason}} +

beendet, weil:{{this.terminationReason}}

+ {{/if}}

- {{/each}} + {{/each}} {{/if}} {{#if memberships.length}} +
+

Vereinsämter

+ {{#each positions}} +
+

{{this.executivePosition.position}}: {{this.start}} - {{this.end ?? 'heute'}}

+ {{#if this.note}} +

Notiz: {{this.note}}

+ {{/if}} +
+
+ {{/each}} {{/if}} {{#if awards.length}} +
+

Auszeichnungen

+ {{#each awards}} +
+

{{this.award.award}}: {{this.date}}

+ {{#if this.given}} +

wurde vergeben

+ {{else}} +

wurde verwehrt / zurückgewiesen

+ {{/if}} {{#if this.note}} +

Notiz: {{this.note}}

+ {{/if}} +
+
+ {{/each}} {{/if}} {{#if qualifications.length}} +
+

Qualifikationen

+ {{#each qualifications}} +
+

{{this.qualification.qualification}}: {{this.date}}

+ {{#if this.terminationReason}} +

beendet, weil:{{this.terminationReason}}

+ {{/if}} {{/if}} {{#if this.note}} +

Notiz: {{this.note}}

+ {{/if}} +
+
+ {{/each}} {{/if}} + diff --git a/src/templates/member.list.body.template.html b/src/templates/listprint.member.body.template.html similarity index 78% rename from src/templates/member.list.body.template.html rename to src/templates/listprint.member.body.template.html index cc0996c..a4e1840 100644 --- a/src/templates/member.list.body.template.html +++ b/src/templates/listprint.member.body.template.html @@ -6,15 +6,16 @@

Mitgliederliste

+

Ausdruck Stand {{longdate today}}


- {{#each member}} + {{#each list}} - + + {{/each}}
+ {{this.lastname}} {{this.firstname}}{{#if this.nameaffix}} - {{this.nameaffix}}{{/if}} - -
diff --git a/src/templates/member.list.footer.template.html b/src/templates/member.list.footer.template.html deleted file mode 100644 index 7a3190b..0000000 --- a/src/templates/member.list.footer.template.html +++ /dev/null @@ -1,3 +0,0 @@ -
- Seite von -
diff --git a/src/templates/protocol.footer.template.html b/src/templates/protocol.footer.template.html deleted file mode 100644 index 7a3190b..0000000 --- a/src/templates/protocol.footer.template.html +++ /dev/null @@ -1,3 +0,0 @@ -
- Seite von -
diff --git a/src/type/permissionTypes.ts b/src/type/permissionTypes.ts index a031c0e..e9ed138 100644 --- a/src/type/permissionTypes.ts +++ b/src/type/permissionTypes.ts @@ -6,6 +6,7 @@ export type PermissionModule = | "newsletter" | "newsletter_config" | "protocol" + | "listprint" | "qualification" | "award" | "executive_position" @@ -50,6 +51,7 @@ export const permissionModules: Array = [ "newsletter", "newsletter_config", "protocol", + "listprint", "qualification", "award", "executive_position", @@ -68,7 +70,7 @@ export const permissionModules: Array = [ ]; export const permissionTypes: Array = ["read", "create", "update", "delete"]; export const sectionsAndModules: SectionsAndModulesObject = { - club: ["member", "calendar", "newsletter", "protocol", "query"], + club: ["member", "calendar", "newsletter", "protocol", "query", "listprint"], configuration: [ "qualification", "award", -- 2.45.3 From f8a5183f1ab006e76fdbd9896d63389b284cb9e8 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 19 Mar 2025 15:19:03 +0100 Subject: [PATCH 7/9] system queries + custom template selection --- .../admin/club/listprintController.ts | 18 +++++++--- src/helpers/dynamicQueryBuilder.ts | 35 ++++++++++++++++++- src/helpers/pdfExport.ts | 1 + src/helpers/templateHelper.ts | 4 ++- src/routes/admin/club/listprint.ts | 2 +- 5 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/controller/admin/club/listprintController.ts b/src/controller/admin/club/listprintController.ts index edd4961..1159d9f 100644 --- a/src/controller/admin/club/listprintController.ts +++ b/src/controller/admin/club/listprintController.ts @@ -2,6 +2,7 @@ import { Request, Response } from "express"; import { PdfExport } from "../../../helpers/pdfExport"; import DynamicQueryBuilder from "../../../helpers/dynamicQueryBuilder"; import QueryStoreService from "../../../service/configuration/queryStoreService"; +import InternalException from "../../../exceptions/internalException"; /** * @description print list by query and template @@ -18,13 +19,19 @@ export async function printListByQueryAndTemplate(req: Request, res: Response): const headerHeight = req.body.headerHeight ?? null; const footerHeight = req.body.footerHeight ?? null; - // \/ integrate into query builder execution via shortcut - //await MemberService.getByRunningMembership(); - //await MemberService.getAll({ noLimit:true }); + let query = queryStore; + if (queryStore != "member" && queryStore != "memberByRunningMembership") { + query = (await QueryStoreService.getById(queryStore)).query; + } - let query = (await QueryStoreService.getById(queryStore)).query; + let data = await DynamicQueryBuilder.executeQuery({ + query: query.startsWith("{") ? JSON.parse(query) : query, + noLimit: true, + }); - await DynamicQueryBuilder.executeQuery({ query, noLimit: true }); + if (data.stats == "error") { + throw new InternalException("Failed executing Query"); + } let pdf = await PdfExport.renderFile({ title: title, @@ -32,6 +39,7 @@ export async function printListByQueryAndTemplate(req: Request, res: Response): saveToDisk: false, data: { today: new Date(), + list: data.rows, }, customTemplate: { headerId, diff --git a/src/helpers/dynamicQueryBuilder.ts b/src/helpers/dynamicQueryBuilder.ts index 1749016..446fc22 100644 --- a/src/helpers/dynamicQueryBuilder.ts +++ b/src/helpers/dynamicQueryBuilder.ts @@ -314,6 +314,13 @@ export default abstract class DynamicQueryBuilder { count: number; } > { + if (query == "member") { + query = memberQuery; + } + if (query == "memberByRunningMembership") { + query = memberByRunningMembershipQuery; + } + if (typeof query == "string") { const upperQuery = query.trim().toUpperCase(); if (!upperQuery.startsWith("SELECT") || /INSERT|UPDATE|DELETE|ALTER|DROP|CREATE|TRUNCATE/.test(upperQuery)) { @@ -330,7 +337,7 @@ export default abstract class DynamicQueryBuilder { return await dataSource .transaction(async (manager) => { - data = await manager.query(query); + data = await manager.query(query.toString()); throw new Error("AllwaysRollbackQuery"); }) @@ -382,3 +389,29 @@ export default abstract class DynamicQueryBuilder { } } } + +const memberQuery: DynamicQueryStructure = { + select: "*", + table: "member", + orderBy: [ + { column: "lastname", order: "ASC" }, + { column: "firstname", order: "ASC" }, + ], +}; + +const memberByRunningMembershipQuery: DynamicQueryStructure = { + select: "*", + table: "member", + join: [ + { + select: "*", + table: "membership", + where: [{ structureType: "condition", concat: "_", operation: "null", column: "end", value: "" }], + foreignColumn: "memberships", + }, + ], + orderBy: [ + { column: "lastname", order: "ASC" }, + { column: "firstname", order: "ASC" }, + ], +}; diff --git a/src/helpers/pdfExport.ts b/src/helpers/pdfExport.ts index b22f061..e59ba54 100644 --- a/src/helpers/pdfExport.ts +++ b/src/helpers/pdfExport.ts @@ -41,6 +41,7 @@ export abstract class PdfExport { })); } else { ({ header, footer, body, headerMargin, footerMargin } = await TemplateHelper.renderFileForCustom({ + module: template, customTemplate, headerData: data, bodyData: data, diff --git a/src/helpers/templateHelper.ts b/src/helpers/templateHelper.ts index 54a48fc..e75c87c 100644 --- a/src/helpers/templateHelper.ts +++ b/src/helpers/templateHelper.ts @@ -81,12 +81,14 @@ export abstract class TemplateHelper { } static async renderFileForCustom({ + module, title = "pdf-export FF Admin", headerData = {}, bodyData = {}, footerData = {}, customTemplate, }: { + module: `${PermissionModule}` | `${PermissionModule}.${string}`; title?: string; headerData?: any; bodyData?: any; @@ -118,7 +120,7 @@ export abstract class TemplateHelper { if (customTemplate.bodyId && typeof customTemplate.bodyId == "number") { body = await this.getTemplateFromStore(customTemplate.bodyId); } else { - body = this.getTemplateFromFile(module + ".body"); + body = this.getTemplateFromFile((customTemplate.bodyId || module) + ".body"); } body = this.applyDataToTemplate(body, { title, ...bodyData }); diff --git a/src/routes/admin/club/listprint.ts b/src/routes/admin/club/listprint.ts index 9bb8a7d..08e24d5 100644 --- a/src/routes/admin/club/listprint.ts +++ b/src/routes/admin/club/listprint.ts @@ -3,7 +3,7 @@ import { printListByQueryAndTemplate } from "../../../controller/admin/club/list var router = express.Router({ mergeParams: true }); -router.get("/", async (req: Request, res: Response) => { +router.post("/", async (req: Request, res: Response) => { await printListByQueryAndTemplate(req, res); }); -- 2.45.3 From 2e5b345daaccfc4ffc73d031a8b2ccb87ede916b Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 21 Mar 2025 09:46:29 +0100 Subject: [PATCH 8/9] add sorting to protocol agenda, decision and votings --- .../club/protocol/protocolAgendaCommand.ts | 1 + .../protocol/protocolAgendaCommandHandler.ts | 5 +++- .../club/protocol/protocolDecisionCommand.ts | 1 + .../protocolDecisionCommandHandler.ts | 5 +++- .../club/protocol/protocolVotingCommand.ts | 1 + .../protocol/protocolVotingCommandHandler.ts | 5 +++- .../admin/club/protocolController.ts | 23 ++++++++------- src/data-source.ts | 2 ++ src/entity/club/protocol/protocolAgenda.ts | 3 ++ src/entity/club/protocol/protocolDecision.ts | 3 ++ src/entity/club/protocol/protocolVoting.ts | 3 ++ .../admin/club/protocol/protocolAgenda.ts | 1 + .../admin/club/protocol/protocolDecision.ts | 1 + .../admin/club/protocol/protocolVoting.ts | 1 + src/helpers/backupHelper.ts | 13 +++++++-- src/migrations/1742544887410-protocolSort.ts | 29 +++++++++++++++++++ .../club/protocol/protocolAgendaService.ts | 18 ++++++++++++ .../club/protocol/protocolDecisionService.ts | 18 ++++++++++++ .../club/protocol/protocolVotingService.ts | 18 ++++++++++++ .../club/protocol/protocolAgenda.models.ts | 1 + .../club/protocol/protocolDecision.models.ts | 1 + .../club/protocol/protocolVoting.models.ts | 1 + 22 files changed, 138 insertions(+), 16 deletions(-) create mode 100644 src/migrations/1742544887410-protocolSort.ts diff --git a/src/command/club/protocol/protocolAgendaCommand.ts b/src/command/club/protocol/protocolAgendaCommand.ts index 749390d..2889122 100644 --- a/src/command/club/protocol/protocolAgendaCommand.ts +++ b/src/command/club/protocol/protocolAgendaCommand.ts @@ -2,5 +2,6 @@ export interface SynchronizeProtocolAgendaCommand { id?: number; topic: string; context: string; + sort?: number; protocolId: number; } diff --git a/src/command/club/protocol/protocolAgendaCommandHandler.ts b/src/command/club/protocol/protocolAgendaCommandHandler.ts index 02fe4f2..96dbc08 100644 --- a/src/command/club/protocol/protocolAgendaCommandHandler.ts +++ b/src/command/club/protocol/protocolAgendaCommandHandler.ts @@ -2,6 +2,7 @@ import { dataSource } from "../../../data-source"; import { protocolAgenda } from "../../../entity/club/protocol/protocolAgenda"; import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; +import ProtocolAgendaService from "../../../service/club/protocol/protocolAgendaService"; import { SynchronizeProtocolAgendaCommand } from "./protocolAgendaCommand"; export default abstract class ProtocolAgendaCommandHandler { @@ -11,6 +12,7 @@ export default abstract class ProtocolAgendaCommandHandler { * @returns {Promise} */ static async create(protocolId: number): Promise { + let count = await ProtocolAgendaService.getInstanceCount(protocolId); return await dataSource .createQueryBuilder() .insert() @@ -18,6 +20,7 @@ export default abstract class ProtocolAgendaCommandHandler { .values({ topic: "", context: "", + sort: count, protocolId, }) .execute() @@ -40,7 +43,7 @@ export default abstract class ProtocolAgendaCommandHandler { .insert() .into(protocolAgenda) .values(syncProtocolAgenda) - .orUpdate(["topic", "context"], ["id"]) + .orUpdate(["topic", "context", "sort"], ["id"]) .execute() .then(() => {}) .catch((err) => { diff --git a/src/command/club/protocol/protocolDecisionCommand.ts b/src/command/club/protocol/protocolDecisionCommand.ts index 61eb6f0..aeb2f24 100644 --- a/src/command/club/protocol/protocolDecisionCommand.ts +++ b/src/command/club/protocol/protocolDecisionCommand.ts @@ -2,5 +2,6 @@ export interface SynchronizeProtocolDecisionCommand { id?: number; topic: string; context: string; + sort?: number; protocolId: number; } diff --git a/src/command/club/protocol/protocolDecisionCommandHandler.ts b/src/command/club/protocol/protocolDecisionCommandHandler.ts index 585abf1..9425d38 100644 --- a/src/command/club/protocol/protocolDecisionCommandHandler.ts +++ b/src/command/club/protocol/protocolDecisionCommandHandler.ts @@ -2,6 +2,7 @@ import { dataSource } from "../../../data-source"; import { protocolDecision } from "../../../entity/club/protocol/protocolDecision"; import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; +import ProtocolDecisionService from "../../../service/club/protocol/protocolDecisionService"; import { SynchronizeProtocolDecisionCommand } from "./protocolDecisionCommand"; export default abstract class ProtocolDecisionCommandHandler { @@ -11,6 +12,7 @@ export default abstract class ProtocolDecisionCommandHandler { * @returns {Promise} */ static async create(protocolId: number): Promise { + let count = await ProtocolDecisionService.getInstanceCount(protocolId); return await dataSource .createQueryBuilder() .insert() @@ -18,6 +20,7 @@ export default abstract class ProtocolDecisionCommandHandler { .values({ topic: "", context: "", + sort: count, protocolId, }) .execute() @@ -39,7 +42,7 @@ export default abstract class ProtocolDecisionCommandHandler { .insert() .into(protocolDecision) .values(syncProtocolDecisions) - .orUpdate(["topic", "context"], ["id"]) + .orUpdate(["topic", "context", "sort"], ["id"]) .execute() .then(() => {}) .catch((err) => { diff --git a/src/command/club/protocol/protocolVotingCommand.ts b/src/command/club/protocol/protocolVotingCommand.ts index a707b64..617a560 100644 --- a/src/command/club/protocol/protocolVotingCommand.ts +++ b/src/command/club/protocol/protocolVotingCommand.ts @@ -5,5 +5,6 @@ export interface SynchronizeProtocolVotingCommand { favour: number; abstain: number; against: number; + sort?: number; protocolId: number; } diff --git a/src/command/club/protocol/protocolVotingCommandHandler.ts b/src/command/club/protocol/protocolVotingCommandHandler.ts index 45a4642..741c0da 100644 --- a/src/command/club/protocol/protocolVotingCommandHandler.ts +++ b/src/command/club/protocol/protocolVotingCommandHandler.ts @@ -2,6 +2,7 @@ import { dataSource } from "../../../data-source"; import { protocolVoting } from "../../../entity/club/protocol/protocolVoting"; import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; +import ProtocolVotingService from "../../../service/club/protocol/protocolVotingService"; import { SynchronizeProtocolVotingCommand } from "./protocolVotingCommand"; export default abstract class ProtocolVotingCommandHandler { @@ -11,6 +12,7 @@ export default abstract class ProtocolVotingCommandHandler { * @returns {Promise} */ static async create(protocolId: number): Promise { + let count = await ProtocolVotingService.getInstanceCount(protocolId); return await dataSource .createQueryBuilder() .insert() @@ -18,6 +20,7 @@ export default abstract class ProtocolVotingCommandHandler { .values({ topic: "", context: "", + sort: count, protocolId, }) .execute() @@ -39,7 +42,7 @@ export default abstract class ProtocolVotingCommandHandler { .insert() .into(protocolVoting) .values(syncProtocolVotings) - .orUpdate(["topic", "context", "favour", "abstain", "against"], ["id"]) + .orUpdate(["topic", "context", "favour", "abstain", "against", "sort"], ["id"]) .execute() .then(() => {}) .catch((err) => { diff --git a/src/controller/admin/club/protocolController.ts b/src/controller/admin/club/protocolController.ts index 868715f..2a6f5fb 100644 --- a/src/controller/admin/club/protocolController.ts +++ b/src/controller/admin/club/protocolController.ts @@ -257,13 +257,13 @@ export async function createProtocolPrintoutById(req: Request, res: Response): P }), start: protocol.starttime, end: protocol.endtime, - agenda, - decisions, + agenda: agenda.sort((a, b) => a.sort - b.sort), + decisions: decisions.sort((a, b) => a.sort - b.sort), presence: presence.filter((p) => !p.absent).map((p) => p.member), absent: presence.filter((p) => p.absent).map((p) => ({ ...p.member, excused: p.excused })), excused_absent: presence.filter((p) => p.absent && p.excused).map((p) => p.member), unexcused_absent: presence.filter((p) => p.absent && !p.excused).map((p) => p.member), - votings, + votings: votings.sort((a, b) => a.sort - b.sort), }, }); @@ -320,6 +320,7 @@ export async function synchronizeProtocolAgendaById(req: Request, res: Response) id: a.id ?? null, topic: a.topic, context: a.context, + sort: a.sort, protocolId, }) ); @@ -343,6 +344,7 @@ export async function synchronizeProtocolDecisonsById(req: Request, res: Respons id: d.id ?? null, topic: d.topic, context: d.context, + sort: d.sort, protocolId, }) ); @@ -362,13 +364,14 @@ export async function synchronizeProtocolVotingsById(req: Request, res: Response let votings = req.body.votings as Array; let syncVoting: Array = votings.map( - (d: ProtocolVotingViewModel): SynchronizeProtocolVotingCommand => ({ - id: d.id ?? null, - topic: d.topic, - context: d.context, - favour: d.favour, - abstain: d.abstain, - against: d.abstain, + (v: ProtocolVotingViewModel): SynchronizeProtocolVotingCommand => ({ + id: v.id ?? null, + topic: v.topic, + context: v.context, + favour: v.favour, + abstain: v.abstain, + against: v.abstain, + sort: v.sort, protocolId, }) ); diff --git a/src/data-source.ts b/src/data-source.ts index ea45dc3..37aebfd 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -49,6 +49,7 @@ import { BackupAndResetDatabase1738166124200 } from "./migrations/1738166124200- import { CreateSchema1738166167472 } from "./migrations/1738166167472-CreateSchema"; import { MemberPrintoutTemplates1742207245862 } from "./migrations/1742207245862-memberPrintoutTemplates"; import { Listprinting1742311486232 } from "./migrations/1742311486232-listprinting"; +import { ProtocolSort1742544887410 } from "./migrations/1742544887410-protocolSort"; const dataSource = new DataSource({ type: DB_TYPE as any, @@ -107,6 +108,7 @@ const dataSource = new DataSource({ CreateSchema1738166167472, MemberPrintoutTemplates1742207245862, Listprinting1742311486232, + ProtocolSort1742544887410, ], migrationsRun: true, migrationsTransactionMode: "each", diff --git a/src/entity/club/protocol/protocolAgenda.ts b/src/entity/club/protocol/protocolAgenda.ts index 7ce7556..a64bf9a 100644 --- a/src/entity/club/protocol/protocolAgenda.ts +++ b/src/entity/club/protocol/protocolAgenda.ts @@ -12,6 +12,9 @@ export class protocolAgenda { @Column({ type: "text", default: "" }) context: string; + @Column({ type: "int", default: 0 }) + sort: number; + @Column() protocolId: number; diff --git a/src/entity/club/protocol/protocolDecision.ts b/src/entity/club/protocol/protocolDecision.ts index 4978d17..e2cc4c1 100644 --- a/src/entity/club/protocol/protocolDecision.ts +++ b/src/entity/club/protocol/protocolDecision.ts @@ -12,6 +12,9 @@ export class protocolDecision { @Column({ type: "text", default: "" }) context: string; + @Column({ type: "int", default: 0 }) + sort: number; + @Column() protocolId: number; diff --git a/src/entity/club/protocol/protocolVoting.ts b/src/entity/club/protocol/protocolVoting.ts index 8e8f7a1..438e8fc 100644 --- a/src/entity/club/protocol/protocolVoting.ts +++ b/src/entity/club/protocol/protocolVoting.ts @@ -21,6 +21,9 @@ export class protocolVoting { @Column({ type: "int", default: 0 }) against: number; + @Column({ type: "int", default: 0 }) + sort: number; + @Column() protocolId: number; diff --git a/src/factory/admin/club/protocol/protocolAgenda.ts b/src/factory/admin/club/protocol/protocolAgenda.ts index aa15b75..65d2334 100644 --- a/src/factory/admin/club/protocol/protocolAgenda.ts +++ b/src/factory/admin/club/protocol/protocolAgenda.ts @@ -12,6 +12,7 @@ export default abstract class ProtocolAgendaFactory { id: record.id, topic: record.topic, context: record.context, + sort: record.sort, protocolId: record.protocolId, }; } diff --git a/src/factory/admin/club/protocol/protocolDecision.ts b/src/factory/admin/club/protocol/protocolDecision.ts index 0fbfff5..608bc2a 100644 --- a/src/factory/admin/club/protocol/protocolDecision.ts +++ b/src/factory/admin/club/protocol/protocolDecision.ts @@ -12,6 +12,7 @@ export default abstract class ProtocolDecisionFactory { id: record.id, topic: record.topic, context: record.context, + sort: record.sort, protocolId: record.protocolId, }; } diff --git a/src/factory/admin/club/protocol/protocolVoting.ts b/src/factory/admin/club/protocol/protocolVoting.ts index 2f4fa4e..49f954c 100644 --- a/src/factory/admin/club/protocol/protocolVoting.ts +++ b/src/factory/admin/club/protocol/protocolVoting.ts @@ -15,6 +15,7 @@ export default abstract class ProtocolVotingFactory { favour: record.favour, abstain: record.abstain, against: record.against, + sort: record.sort, protocolId: record.protocolId, }; } diff --git a/src/helpers/backupHelper.ts b/src/helpers/backupHelper.ts index a60e963..7727527 100644 --- a/src/helpers/backupHelper.ts +++ b/src/helpers/backupHelper.ts @@ -300,8 +300,8 @@ export default abstract class BackupHelper { .leftJoin("protocol.printouts", "printouts") .leftJoin("protocol.votings", "votings") .select(["protocol.title", "protocol.date", "protocol.starttime", "protocol.endtime", "protocol.summary"]) - .addSelect(["agendas.topic", "agendas.context"]) - .addSelect(["decisions.topic", "decisions.context"]) + .addSelect(["agendas.topic", "agendas.context", "agendas.sort"]) + .addSelect(["decisions.topic", "decisions.context", "decisions.sort"]) .addSelect(["presences.absent", "presences.excused"]) .addSelect([ ...(collectIds ? ["member.id"] : []), @@ -312,7 +312,14 @@ export default abstract class BackupHelper { "member.internalId", ]) .addSelect(["printouts.title", "printouts.iteration", "printouts.filename", "printouts.createdAt"]) - .addSelect(["votings.topic", "votings.context", "votings.favour", "votings.abstain", "votings.against"]) + .addSelect([ + "votings.topic", + "votings.context", + "votings.favour", + "votings.abstain", + "votings.against", + "votings.sort", + ]) .getMany(); } private static async getNewsletter(collectIds: boolean): Promise> { diff --git a/src/migrations/1742544887410-protocolSort.ts b/src/migrations/1742544887410-protocolSort.ts new file mode 100644 index 0000000..77f6011 --- /dev/null +++ b/src/migrations/1742544887410-protocolSort.ts @@ -0,0 +1,29 @@ +import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; +import { getDefaultByORM, getTypeByORM } from "./ormHelper"; + +export class ProtocolSort1742544887410 implements MigrationInterface { + name = "ProtocolSort1742544887410"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.addColumn( + "protocol_agenda", + new TableColumn({ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }) + ); + + await queryRunner.addColumn( + "protocol_decision", + new TableColumn({ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }) + ); + + await queryRunner.addColumn( + "protocol_voting", + new TableColumn({ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }) + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropColumn("protocol_agenda", "sort"); + await queryRunner.dropColumn("protocol_decision", "sort"); + await queryRunner.dropColumn("protocol_voting", "sort"); + } +} diff --git a/src/service/club/protocol/protocolAgendaService.ts b/src/service/club/protocol/protocolAgendaService.ts index fb8ead8..7d54168 100644 --- a/src/service/club/protocol/protocolAgendaService.ts +++ b/src/service/club/protocol/protocolAgendaService.ts @@ -39,4 +39,22 @@ export default abstract class ProtocolAgendaService { throw new DatabaseActionException("SELECT", "protocolAgenda", err); }); } + + /** + * @description get count of exisiting protocolAgenda by protocolId + * @returns {Promise} + */ + static async getInstanceCount(protocolId: number): Promise { + return await dataSource + .getRepository(protocolAgenda) + .createQueryBuilder("protocolAgenda") + .where({ protocolId }) + .getCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("COUNT", "protocolAgenda", err); + }); + } } diff --git a/src/service/club/protocol/protocolDecisionService.ts b/src/service/club/protocol/protocolDecisionService.ts index 56ac4c6..241dba2 100644 --- a/src/service/club/protocol/protocolDecisionService.ts +++ b/src/service/club/protocol/protocolDecisionService.ts @@ -39,4 +39,22 @@ export default abstract class ProtocolDecisionService { throw new DatabaseActionException("SELECT", "protocolDecision", err); }); } + + /** + * @description get count of exisiting protocolDecision by protocolId + * @returns {Promise} + */ + static async getInstanceCount(protocolId: number): Promise { + return await dataSource + .getRepository(protocolDecision) + .createQueryBuilder("protocolDecisions") + .where({ protocolId }) + .getCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("COUNT", "protocolDecision", err); + }); + } } diff --git a/src/service/club/protocol/protocolVotingService.ts b/src/service/club/protocol/protocolVotingService.ts index 2000cce..220d29e 100644 --- a/src/service/club/protocol/protocolVotingService.ts +++ b/src/service/club/protocol/protocolVotingService.ts @@ -39,4 +39,22 @@ export default abstract class ProtocolVotingService { throw new DatabaseActionException("SELECT", "protocolVoting", err); }); } + + /** + * @description get count of exisiting protocolVoting by protocolId + * @returns {Promise} + */ + static async getInstanceCount(protocolId: number): Promise { + return await dataSource + .getRepository(protocolVoting) + .createQueryBuilder("protocolVotings") + .where({ protocolId }) + .getCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("COUNT", "protocolVoting", err); + }); + } } diff --git a/src/viewmodel/admin/club/protocol/protocolAgenda.models.ts b/src/viewmodel/admin/club/protocol/protocolAgenda.models.ts index 3a59327..eed423d 100644 --- a/src/viewmodel/admin/club/protocol/protocolAgenda.models.ts +++ b/src/viewmodel/admin/club/protocol/protocolAgenda.models.ts @@ -2,5 +2,6 @@ export interface ProtocolAgendaViewModel { id: number; topic: string; context: string; + sort: number; protocolId: number; } diff --git a/src/viewmodel/admin/club/protocol/protocolDecision.models.ts b/src/viewmodel/admin/club/protocol/protocolDecision.models.ts index 4a7212c..7de33c4 100644 --- a/src/viewmodel/admin/club/protocol/protocolDecision.models.ts +++ b/src/viewmodel/admin/club/protocol/protocolDecision.models.ts @@ -2,5 +2,6 @@ export interface ProtocolDecisionViewModel { id: number; topic: string; context: string; + sort: number; protocolId: number; } diff --git a/src/viewmodel/admin/club/protocol/protocolVoting.models.ts b/src/viewmodel/admin/club/protocol/protocolVoting.models.ts index 686f423..a5bd723 100644 --- a/src/viewmodel/admin/club/protocol/protocolVoting.models.ts +++ b/src/viewmodel/admin/club/protocol/protocolVoting.models.ts @@ -5,5 +5,6 @@ export interface ProtocolVotingViewModel { favour: number; abstain: number; against: number; + sort: number; protocolId: number; } -- 2.45.3 From 2aa7ea99acf028d30ca09b1142ea965c56dfb960 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 21 Mar 2025 10:44:30 +0100 Subject: [PATCH 9/9] squash: combine template and protocol migrations --- src/data-source.ts | 12 +--- .../1742207245862-memberPrintoutTemplates.ts | 20 ------- src/migrations/1742311486232-listprinting.ts | 25 --------- src/migrations/1742544887410-protocolSort.ts | 29 ---------- .../1742549956787-templatesAndProtocolSort.ts | 55 +++++++++++++++++++ 5 files changed, 57 insertions(+), 84 deletions(-) delete mode 100644 src/migrations/1742207245862-memberPrintoutTemplates.ts delete mode 100644 src/migrations/1742311486232-listprinting.ts delete mode 100644 src/migrations/1742544887410-protocolSort.ts create mode 100644 src/migrations/1742549956787-templatesAndProtocolSort.ts diff --git a/src/data-source.ts b/src/data-source.ts index 37aebfd..aa99610 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -47,9 +47,7 @@ import { salutation } from "./entity/configuration/salutation"; import { BackupAndResetDatabase1738166124200 } from "./migrations/1738166124200-BackupAndResetDatabase"; import { CreateSchema1738166167472 } from "./migrations/1738166167472-CreateSchema"; -import { MemberPrintoutTemplates1742207245862 } from "./migrations/1742207245862-memberPrintoutTemplates"; -import { Listprinting1742311486232 } from "./migrations/1742311486232-listprinting"; -import { ProtocolSort1742544887410 } from "./migrations/1742544887410-protocolSort"; +import { TemplatesAndProtocolSort1742549956787 } from "./migrations/1742549956787-templatesAndProtocolSort"; const dataSource = new DataSource({ type: DB_TYPE as any, @@ -103,13 +101,7 @@ const dataSource = new DataSource({ webapi, webapiPermission, ], - migrations: [ - BackupAndResetDatabase1738166124200, - CreateSchema1738166167472, - MemberPrintoutTemplates1742207245862, - Listprinting1742311486232, - ProtocolSort1742544887410, - ], + migrations: [BackupAndResetDatabase1738166124200, CreateSchema1738166167472, TemplatesAndProtocolSort1742549956787], migrationsRun: true, migrationsTransactionMode: "each", subscribers: [], diff --git a/src/migrations/1742207245862-memberPrintoutTemplates.ts b/src/migrations/1742207245862-memberPrintoutTemplates.ts deleted file mode 100644 index a799d78..0000000 --- a/src/migrations/1742207245862-memberPrintoutTemplates.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; -import { templateUsage } from "../entity/configuration/templateUsage"; - -export class MemberPrintoutTemplates1742207245862 implements MigrationInterface { - name = "MemberPrintoutTemplates1742207245862"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.manager - .createQueryBuilder() - .insert() - .into(templateUsage) - .values([{ scope: "member" }]) - .orUpdate(["headerId", "bodyId", "footerId", "headerHeight", "footerHeight"], ["scope"]) - .execute(); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.manager.createQueryBuilder().delete().from(templateUsage).where({ scope: "member" }).execute(); - } -} diff --git a/src/migrations/1742311486232-listprinting.ts b/src/migrations/1742311486232-listprinting.ts deleted file mode 100644 index 6b14e61..0000000 --- a/src/migrations/1742311486232-listprinting.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; -import { templateUsage } from "../entity/configuration/templateUsage"; - -export class Listprinting1742311486232 implements MigrationInterface { - name = "Listprinting1742311486232"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.manager - .createQueryBuilder() - .delete() - .from(templateUsage) - .where({ scope: "member.list" }) - .execute(); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.manager - .createQueryBuilder() - .insert() - .into(templateUsage) - .values([{ scope: "member.list" }]) - .orUpdate(["headerId", "bodyId", "footerId", "headerHeight", "footerHeight"], ["scope"]) - .execute(); - } -} diff --git a/src/migrations/1742544887410-protocolSort.ts b/src/migrations/1742544887410-protocolSort.ts deleted file mode 100644 index 77f6011..0000000 --- a/src/migrations/1742544887410-protocolSort.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; -import { getDefaultByORM, getTypeByORM } from "./ormHelper"; - -export class ProtocolSort1742544887410 implements MigrationInterface { - name = "ProtocolSort1742544887410"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.addColumn( - "protocol_agenda", - new TableColumn({ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }) - ); - - await queryRunner.addColumn( - "protocol_decision", - new TableColumn({ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }) - ); - - await queryRunner.addColumn( - "protocol_voting", - new TableColumn({ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumn("protocol_agenda", "sort"); - await queryRunner.dropColumn("protocol_decision", "sort"); - await queryRunner.dropColumn("protocol_voting", "sort"); - } -} diff --git a/src/migrations/1742549956787-templatesAndProtocolSort.ts b/src/migrations/1742549956787-templatesAndProtocolSort.ts new file mode 100644 index 0000000..5036920 --- /dev/null +++ b/src/migrations/1742549956787-templatesAndProtocolSort.ts @@ -0,0 +1,55 @@ +import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; +import { templateUsage } from "../entity/configuration/templateUsage"; +import { getTypeByORM, getDefaultByORM } from "./ormHelper"; + +export class TemplatesAndProtocolSort1742549956787 implements MigrationInterface { + name = "TemplatesAndProtocolSort1742549956787"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.manager + .createQueryBuilder() + .insert() + .into(templateUsage) + .values([{ scope: "member" }]) + .orUpdate(["headerId", "bodyId", "footerId", "headerHeight", "footerHeight"], ["scope"]) + .execute(); + + await queryRunner.manager + .createQueryBuilder() + .delete() + .from(templateUsage) + .where({ scope: "member.list" }) + .execute(); + + await queryRunner.addColumn( + "protocol_agenda", + new TableColumn({ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }) + ); + + await queryRunner.addColumn( + "protocol_decision", + new TableColumn({ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }) + ); + + await queryRunner.addColumn( + "protocol_voting", + new TableColumn({ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }) + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropColumn("protocol_agenda", "sort"); + await queryRunner.dropColumn("protocol_decision", "sort"); + await queryRunner.dropColumn("protocol_voting", "sort"); + + await queryRunner.manager + .createQueryBuilder() + .insert() + .into(templateUsage) + .values([{ scope: "member.list" }]) + .orUpdate(["headerId", "bodyId", "footerId", "headerHeight", "footerHeight"], ["scope"]) + .execute(); + + await queryRunner.manager.createQueryBuilder().delete().from(templateUsage).where({ scope: "member" }).execute(); + } +} -- 2.45.3