Intermediate: Merge pull request '#25-cleanup-&-enhancements' (#26) from #25-cleanup-&-enhancements into main

Reviewed-on: Ehrenamt/member-administration-server#26
This commit is contained in:
Julian Krauser 2025-01-02 17:37:18 +00:00
commit 9afb205da5
31 changed files with 188 additions and 50 deletions

View file

@ -16,4 +16,5 @@ MAIL_HOST = mail_hoststring
MAIL_PORT = mail_portnumber MAIL_PORT = mail_portnumber
MAIL_SECURE (true|false) // true for port 465, fals for other ports MAIL_SECURE (true|false) // true for port 465, fals for other ports
CLUB_NAME = clubname CLUB_NAME = clubname
CLUB_WEBSITE = https://my-club-website-url

View file

@ -37,6 +37,7 @@ services:
- MAIL_PORT=<port> - MAIL_PORT=<port>
- MAIL_SECURE=<boolean> - MAIL_SECURE=<boolean>
- CLUB_NAME=<tobemodified> - CLUB_NAME=<tobemodified>
- CLUB_WEBSITE=<tobemodified>
volumes: volumes:
- <volume|local path>:/app/export - <volume|local path>:/app/export
networks: networks:

View file

@ -1,7 +1,7 @@
import { dataSource } from "../data-source"; import { dataSource } from "../data-source";
import { award } from "../entity/award"; import { award } from "../entity/award";
import { member } from "../entity/member";
import { memberAwards } from "../entity/memberAwards"; import { memberAwards } from "../entity/memberAwards";
import { user } from "../entity/user";
import InternalException from "../exceptions/internalException"; import InternalException from "../exceptions/internalException";
import { CreateMemberAwardCommand, DeleteMemberAwardCommand, UpdateMemberAwardCommand } from "./memberAwardCommand"; import { CreateMemberAwardCommand, DeleteMemberAwardCommand, UpdateMemberAwardCommand } from "./memberAwardCommand";
@ -21,8 +21,8 @@ export default abstract class MemberAwardCommandHandler {
note: createMemberAward.note, note: createMemberAward.note,
date: createMemberAward.date, date: createMemberAward.date,
member: await dataSource member: await dataSource
.getRepository(user) .getRepository(member)
.createQueryBuilder("user") .createQueryBuilder("member")
.where("id = :id", { id: createMemberAward.memberId }) .where("id = :id", { id: createMemberAward.memberId })
.getOneOrFail(), .getOneOrFail(),
award: await dataSource award: await dataSource

View file

@ -6,6 +6,7 @@ export interface CreateMemberCommand {
lastname: string; lastname: string;
nameaffix: string; nameaffix: string;
birthdate: Date; birthdate: Date;
internalId?: string;
} }
export interface UpdateMemberCommand { export interface UpdateMemberCommand {
@ -15,6 +16,7 @@ export interface UpdateMemberCommand {
lastname: string; lastname: string;
nameaffix: string; nameaffix: string;
birthdate: Date; birthdate: Date;
internalId?: string;
} }
export interface UpdateMemberNewsletterCommand { export interface UpdateMemberNewsletterCommand {

View file

@ -27,6 +27,7 @@ export default abstract class MemberCommandHandler {
lastname: createMember.lastname, lastname: createMember.lastname,
nameaffix: createMember.nameaffix, nameaffix: createMember.nameaffix,
birthdate: createMember.birthdate, birthdate: createMember.birthdate,
internalId: createMember.internalId,
}) })
.execute() .execute()
.then((result) => { .then((result) => {
@ -53,6 +54,7 @@ export default abstract class MemberCommandHandler {
lastname: updateMember.lastname, lastname: updateMember.lastname,
nameaffix: updateMember.nameaffix, nameaffix: updateMember.nameaffix,
birthdate: updateMember.birthdate, birthdate: updateMember.birthdate,
internalId: updateMember.internalId,
}) })
.where("id = :id", { id: updateMember.id }) .where("id = :id", { id: updateMember.id })
.execute() .execute()

View file

@ -1,7 +1,7 @@
import { dataSource } from "../data-source"; import { dataSource } from "../data-source";
import { executivePosition } from "../entity/executivePosition"; import { executivePosition } from "../entity/executivePosition";
import { member } from "../entity/member";
import { memberExecutivePositions } from "../entity/memberExecutivePositions"; import { memberExecutivePositions } from "../entity/memberExecutivePositions";
import { user } from "../entity/user";
import InternalException from "../exceptions/internalException"; import InternalException from "../exceptions/internalException";
import { import {
CreateMemberExecutivePositionCommand, CreateMemberExecutivePositionCommand,
@ -25,8 +25,8 @@ export default abstract class MemberExecutivePositionCommandHandler {
start: createMemberExecutivePosition.start, start: createMemberExecutivePosition.start,
end: createMemberExecutivePosition.end, end: createMemberExecutivePosition.end,
member: await dataSource member: await dataSource
.getRepository(user) .getRepository(member)
.createQueryBuilder("user") .createQueryBuilder("member")
.where("id = :id", { id: createMemberExecutivePosition.memberId }) .where("id = :id", { id: createMemberExecutivePosition.memberId })
.getOneOrFail(), .getOneOrFail(),
executivePosition: await dataSource executivePosition: await dataSource

View file

@ -1,13 +1,13 @@
import { dataSource } from "../data-source"; import { dataSource } from "../data-source";
import { qualification } from "../entity/qualification"; import { qualification } from "../entity/qualification";
import { memberQualifications } from "../entity/memberQualifications"; import { memberQualifications } from "../entity/memberQualifications";
import { user } from "../entity/user";
import InternalException from "../exceptions/internalException"; import InternalException from "../exceptions/internalException";
import { import {
CreateMemberQualificationCommand, CreateMemberQualificationCommand,
DeleteMemberQualificationCommand, DeleteMemberQualificationCommand,
UpdateMemberQualificationCommand, UpdateMemberQualificationCommand,
} from "./memberQualificationCommand"; } from "./memberQualificationCommand";
import { member } from "../entity/member";
export default abstract class MemberQualificationCommandHandler { export default abstract class MemberQualificationCommandHandler {
/** /**
@ -24,8 +24,8 @@ export default abstract class MemberQualificationCommandHandler {
note: createMemberQualification.note, note: createMemberQualification.note,
start: createMemberQualification.start, start: createMemberQualification.start,
member: await dataSource member: await dataSource
.getRepository(user) .getRepository(member)
.createQueryBuilder("user") .createQueryBuilder("member")
.where("id = :id", { id: createMemberQualification.memberId }) .where("id = :id", { id: createMemberQualification.memberId })
.getOneOrFail(), .getOneOrFail(),
qualification: await dataSource qualification: await dataSource

View file

@ -1,5 +1,4 @@
export interface CreateMembershipCommand { export interface CreateMembershipCommand {
internalId?: string;
start: Date; start: Date;
memberId: number; memberId: number;
statusId: number; statusId: number;
@ -7,7 +6,6 @@ export interface CreateMembershipCommand {
export interface UpdateMembershipCommand { export interface UpdateMembershipCommand {
id: number; id: number;
internalId?: string;
start: Date; start: Date;
end?: Date; end?: Date;
terminationReason?: string; terminationReason?: string;

View file

@ -1,18 +1,18 @@
import { dataSource } from "../data-source"; import { dataSource } from "../data-source";
import { member } from "../entity/member";
import { membership } from "../entity/membership"; import { membership } from "../entity/membership";
import { membershipStatus } from "../entity/membershipStatus"; import { membershipStatus } from "../entity/membershipStatus";
import { user } from "../entity/user";
import InternalException from "../exceptions/internalException"; import InternalException from "../exceptions/internalException";
import { CreateMembershipCommand, DeleteMembershipCommand, UpdateMembershipCommand } from "./membershipCommand"; import { CreateMembershipCommand, DeleteMembershipCommand, UpdateMembershipCommand } from "./membershipCommand";
export default abstract class MembershipCommandHandler { export default abstract class MembershipCommandHandler {
/** /**
* @description create membership * @description create membership
* @param CreateMembershipCommand * @param {CreateMembershipCommand} createMembership
* @returns {Promise<number>} * @returns {Promise<number>}
*/ */
static async create(createMembership: CreateMembershipCommand): Promise<number> { static async create(createMembership: CreateMembershipCommand): Promise<number> {
let insertid = -1; let insertId = -1;
return await dataSource return await dataSource
.transaction(async (manager) => { .transaction(async (manager) => {
await manager await manager
@ -20,11 +20,10 @@ export default abstract class MembershipCommandHandler {
.insert() .insert()
.into(membership) .into(membership)
.values({ .values({
internalId: createMembership.internalId,
start: createMembership.start, start: createMembership.start,
member: await dataSource member: await dataSource
.getRepository(user) .getRepository(member)
.createQueryBuilder("user") .createQueryBuilder("member")
.where("id = :id", { id: createMembership.memberId }) .where("id = :id", { id: createMembership.memberId })
.getOneOrFail(), .getOneOrFail(),
status: await dataSource status: await dataSource
@ -35,7 +34,7 @@ export default abstract class MembershipCommandHandler {
}) })
.execute() .execute()
.then((result) => { .then((result) => {
insertid = result.identifiers[0].id; insertId = result.identifiers[0].id;
}); });
await manager await manager
@ -43,14 +42,15 @@ export default abstract class MembershipCommandHandler {
.update(membership) .update(membership)
.set({ .set({
end: createMembership.start, end: createMembership.start,
terminationReason: "beendet durch neuen Eintrag.",
}) })
.where("end IS NULL") .where("end IS NULL")
.andWhere("memberId = :memberId", { memberId: createMembership.memberId }) .andWhere("memberId = :memberId", { memberId: createMembership.memberId })
.andWhere("id <> :id", { id: insertid }) .andWhere("id <> :id", { id: insertId })
.execute(); .execute();
}) })
.then(() => { .then(() => {
return insertid; return insertId;
}) })
.catch((err) => { .catch((err) => {
throw new InternalException("Failed creating membership", err); throw new InternalException("Failed creating membership", err);
@ -59,7 +59,7 @@ export default abstract class MembershipCommandHandler {
/** /**
* @description update membership * @description update membership
* @param UpdateMembershipCommand * @param {UpdateMembershipCommand} updateMembership
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async update(updateMembership: UpdateMembershipCommand): Promise<void> { static async update(updateMembership: UpdateMembershipCommand): Promise<void> {
@ -67,7 +67,6 @@ export default abstract class MembershipCommandHandler {
.createQueryBuilder() .createQueryBuilder()
.update(membership) .update(membership)
.set({ .set({
internalId: updateMembership.internalId,
start: updateMembership.start, start: updateMembership.start,
end: updateMembership.end, end: updateMembership.end,
terminationReason: updateMembership.terminationReason, terminationReason: updateMembership.terminationReason,
@ -88,16 +87,16 @@ export default abstract class MembershipCommandHandler {
/** /**
* @description delete membership * @description delete membership
* @param DeleteMembershipCommand * @param {DeleteMembershipCommand} deleteMembership
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async delete(deletMembership: DeleteMembershipCommand): Promise<void> { static async delete(deleteMembership: DeleteMembershipCommand): Promise<void> {
return await dataSource return await dataSource
.createQueryBuilder() .createQueryBuilder()
.delete() .delete()
.from(membership) .from(membership)
.where("id = :id", { id: deletMembership.id }) .where("id = :id", { id: deleteMembership.id })
.andWhere("memberId = :memberId", { memberId: deletMembership.memberId }) .andWhere("memberId = :memberId", { memberId: deleteMembership.memberId })
.execute() .execute()
.then(() => {}) .then(() => {})
.catch((err) => { .catch((err) => {

View file

@ -3,4 +3,6 @@ export interface UpdateTemplateUsageCommand {
headerId: number | null; headerId: number | null;
bodyId: number | null; bodyId: number | null;
footerId: number | null; footerId: number | null;
headerHeight: number | null;
footerHeight: number | null;
} }

View file

@ -17,6 +17,8 @@ export default abstract class TemplateUsageCommandHandler {
headerId: updateTemplateUsage.headerId, headerId: updateTemplateUsage.headerId,
bodyId: updateTemplateUsage.bodyId, bodyId: updateTemplateUsage.bodyId,
footerId: updateTemplateUsage.footerId, footerId: updateTemplateUsage.footerId,
headerHeight: updateTemplateUsage.headerHeight,
footerHeight: updateTemplateUsage.footerHeight,
}) })
.where("scope = :scope", { scope: updateTemplateUsage.scope }) .where("scope = :scope", { scope: updateTemplateUsage.scope })
.execute() .execute()

View file

@ -228,6 +228,7 @@ export async function createMember(req: Request, res: Response): Promise<any> {
const lastname = req.body.lastname; const lastname = req.body.lastname;
const nameaffix = req.body.nameaffix; const nameaffix = req.body.nameaffix;
const birthdate = req.body.birthdate; const birthdate = req.body.birthdate;
const internalId = req.body.internalId;
let createMember: CreateMemberCommand = { let createMember: CreateMemberCommand = {
salutation, salutation,
@ -235,6 +236,7 @@ export async function createMember(req: Request, res: Response): Promise<any> {
lastname, lastname,
nameaffix, nameaffix,
birthdate, birthdate,
internalId,
}; };
let memberId = await MemberCommandHandler.create(createMember); let memberId = await MemberCommandHandler.create(createMember);
@ -249,12 +251,10 @@ export async function createMember(req: Request, res: Response): Promise<any> {
*/ */
export async function addMembershipToMember(req: Request, res: Response): Promise<any> { export async function addMembershipToMember(req: Request, res: Response): Promise<any> {
const memberId = parseInt(req.params.memberId); const memberId = parseInt(req.params.memberId);
const internalId = req.body.internalId;
const start = req.body.start; const start = req.body.start;
const statusId = req.body.statusId; const statusId = req.body.statusId;
let createMembership: CreateMembershipCommand = { let createMembership: CreateMembershipCommand = {
internalId,
start, start,
memberId, memberId,
statusId, statusId,
@ -394,6 +394,7 @@ export async function updateMemberById(req: Request, res: Response): Promise<any
const lastname = req.body.lastname; const lastname = req.body.lastname;
const nameaffix = req.body.nameaffix; const nameaffix = req.body.nameaffix;
const birthdate = req.body.birthdate; const birthdate = req.body.birthdate;
const internalId = req.body.internalId;
let updateMember: UpdateMemberCommand = { let updateMember: UpdateMemberCommand = {
id: memberId, id: memberId,
@ -402,6 +403,7 @@ export async function updateMemberById(req: Request, res: Response): Promise<any
lastname, lastname,
nameaffix, nameaffix,
birthdate, birthdate,
internalId,
}; };
await MemberCommandHandler.update(updateMember); await MemberCommandHandler.update(updateMember);
@ -417,7 +419,6 @@ export async function updateMemberById(req: Request, res: Response): Promise<any
export async function updateMembershipOfMember(req: Request, res: Response): Promise<any> { export async function updateMembershipOfMember(req: Request, res: Response): Promise<any> {
const memberId = parseInt(req.params.memberId); const memberId = parseInt(req.params.memberId);
const recordId = parseInt(req.params.recordId); const recordId = parseInt(req.params.recordId);
const internalId = req.body.internalId;
const start = req.body.start; const start = req.body.start;
const end = req.body.end || null; const end = req.body.end || null;
const terminationReason = req.body.terminationReason; const terminationReason = req.body.terminationReason;
@ -425,7 +426,6 @@ export async function updateMembershipOfMember(req: Request, res: Response): Pro
let updateMembership: UpdateMembershipCommand = { let updateMembership: UpdateMembershipCommand = {
id: recordId, id: recordId,
internalId,
start, start,
end, end,
terminationReason, terminationReason,

View file

@ -78,12 +78,16 @@ export async function updateTemplateUsage(req: Request, res: Response): Promise<
const headerId = req.body.headerId ?? null; const headerId = req.body.headerId ?? null;
const bodyId = req.body.bodyId ?? null; const bodyId = req.body.bodyId ?? null;
const footerId = req.body.footerId ?? null; const footerId = req.body.footerId ?? null;
const headerHeight = req.body.headerHeight ?? null;
const footerHeight = req.body.footerHeight ?? null;
let updateTemplateUsage: UpdateTemplateUsageCommand = { let updateTemplateUsage: UpdateTemplateUsageCommand = {
scope: scope, scope: scope,
headerId: headerId, headerId: headerId,
bodyId: bodyId, bodyId: bodyId,
footerId: footerId, footerId: footerId,
headerHeight: headerHeight,
footerHeight: footerHeight,
}; };
await TemplateUsageCommandHandler.update(updateTemplateUsage); await TemplateUsageCommandHandler.update(updateTemplateUsage);

View file

@ -61,6 +61,8 @@ import { newsletterRecipients } from "./entity/newsletterRecipients";
import { Newsletter1735118780511 } from "./migrations/1735118780511-newsletter"; import { Newsletter1735118780511 } from "./migrations/1735118780511-newsletter";
import { newsletterConfig } from "./entity/newsletterConfig"; import { newsletterConfig } from "./entity/newsletterConfig";
import { NewsletterConfig1735207446910 } from "./migrations/1735207446910-newsletterConfig"; import { NewsletterConfig1735207446910 } from "./migrations/1735207446910-newsletterConfig";
import { TemplateMargins1735733514043 } from "./migrations/1735733514043-templateMargins";
import { InternalId1735822722235 } from "./migrations/1735822722235-internalId";
const dataSource = new DataSource({ const dataSource = new DataSource({
type: DB_TYPE as any, type: DB_TYPE as any,
@ -132,6 +134,8 @@ const dataSource = new DataSource({
TemplateUsage1734949173739, TemplateUsage1734949173739,
Newsletter1735118780511, Newsletter1735118780511,
NewsletterConfig1735207446910, NewsletterConfig1735207446910,
TemplateMargins1735733514043,
InternalId1735822722235,
], ],
migrationsRun: true, migrationsRun: true,
migrationsTransactionMode: "each", migrationsTransactionMode: "each",

View file

@ -39,6 +39,9 @@ export class member {
@Column({ type: "date" }) @Column({ type: "date" })
birthdate: Date; birthdate: Date;
@Column({ type: "varchar", length: 255, unique: true, nullable: true })
internalId?: string;
@OneToMany(() => communication, (communications) => communications.member) @OneToMany(() => communication, (communications) => communications.member)
communications: communication[]; communications: communication[];

View file

@ -16,6 +16,12 @@ export class memberAwards {
@Column({ type: "date" }) @Column({ type: "date" })
date: Date; date: Date;
@Column()
memberId: number;
@Column()
awardId: number;
@ManyToOne(() => member, (member) => member.awards, { @ManyToOne(() => member, (member) => member.awards, {
nullable: false, nullable: false,
onDelete: "CASCADE", onDelete: "CASCADE",

View file

@ -16,6 +16,12 @@ export class memberExecutivePositions {
@Column({ type: "date", nullable: true }) @Column({ type: "date", nullable: true })
end?: Date; end?: Date;
@Column()
memberId: number;
@Column()
executivePositionId: number;
@ManyToOne(() => member, (member) => member.awards, { @ManyToOne(() => member, (member) => member.awards, {
nullable: false, nullable: false,
onDelete: "CASCADE", onDelete: "CASCADE",

View file

@ -19,6 +19,12 @@ export class memberQualifications {
@Column({ type: "varchar", length: 255, nullable: true }) @Column({ type: "varchar", length: 255, nullable: true })
terminationReason?: string; terminationReason?: string;
@Column()
memberId: number;
@Column()
qualificationId: number;
@ManyToOne(() => member, (member) => member.awards, { @ManyToOne(() => member, (member) => member.awards, {
nullable: false, nullable: false,
onDelete: "CASCADE", onDelete: "CASCADE",

View file

@ -7,6 +7,9 @@ export class membership {
@PrimaryColumn({ generated: "increment", type: "int" }) @PrimaryColumn({ generated: "increment", type: "int" })
id: number; id: number;
/**
* @deprecated
*/
@Column({ type: "varchar", length: 255, unique: true, nullable: true }) @Column({ type: "varchar", length: 255, unique: true, nullable: true })
internalId?: string; internalId?: string;
@ -19,6 +22,12 @@ export class membership {
@Column({ type: "varchar", length: 255, nullable: true }) @Column({ type: "varchar", length: 255, nullable: true })
terminationReason?: string; terminationReason?: string;
@Column()
memberId: number;
@Column()
statusId: number;
@ManyToOne(() => member, (member) => member.memberships, { @ManyToOne(() => member, (member) => member.memberships, {
nullable: false, nullable: false,
onDelete: "CASCADE", onDelete: "CASCADE",

View file

@ -7,15 +7,21 @@ export class templateUsage {
@PrimaryColumn({ type: "varchar", length: 255 }) @PrimaryColumn({ type: "varchar", length: 255 })
scope: PermissionModule; scope: PermissionModule;
@Column({ type: "number", nullable: true }) @Column({ type: "int", nullable: true })
headerId: number | null; headerId: number | null;
@Column({ type: "number", nullable: true }) @Column({ type: "int", nullable: true })
bodyId: number | null; bodyId: number | null;
@Column({ type: "number", nullable: true }) @Column({ type: "int", nullable: true })
footerId: number | null; footerId: number | null;
@Column({ type: "int", nullable: true })
headerHeight: number | null;
@Column({ type: "int", nullable: true })
footerHeight: number | null;
@ManyToOne(() => template, { @ManyToOne(() => template, {
nullable: true, nullable: true,
onDelete: "RESTRICT", onDelete: "RESTRICT",

View file

@ -17,6 +17,7 @@ export default abstract class MemberFactory {
lastname: record?.lastname, lastname: record?.lastname,
nameaffix: record?.nameaffix, nameaffix: record?.nameaffix,
birthdate: record?.birthdate, birthdate: record?.birthdate,
internalId: record.internalId,
firstMembershipEntry: record?.firstMembershipEntry firstMembershipEntry: record?.firstMembershipEntry
? MembershipFactory.mapToSingle(record.firstMembershipEntry) ? MembershipFactory.mapToSingle(record.firstMembershipEntry)
: null, : null,

View file

@ -10,7 +10,6 @@ export default abstract class MembershipFactory {
public static mapToSingle(record: membership): MembershipViewModel { public static mapToSingle(record: membership): MembershipViewModel {
return { return {
id: record.id, id: record.id,
internalId: record.internalId,
start: record.start, start: record.start,
end: record.end, end: record.end,
terminationReason: record.terminationReason, terminationReason: record.terminationReason,

View file

@ -13,6 +13,8 @@ export default abstract class TemplateUsageFactory {
header: record.header ? { id: record.header.id, template: record.header.template } : null, header: record.header ? { id: record.header.id, template: record.header.template } : null,
body: record.body ? { id: record.body.id, template: record.body.template } : null, body: record.body ? { id: record.body.id, template: record.body.template } : null,
footer: record.footer ? { id: record.footer.id, template: record.footer.template } : null, footer: record.footer ? { id: record.footer.id, template: record.footer.template } : null,
headerHeight: record.headerHeight,
footerHeight: record.footerHeight,
}; };
} }

View file

@ -11,7 +11,6 @@ export abstract class PdfExport {
filename = null, filename = null,
data = {}, data = {},
saveToDisk = true, saveToDisk = true,
margins = { top: "15mm", bottom: "15mm" },
folder = "", folder = "",
}: { }: {
template: PermissionModule; template: PermissionModule;
@ -19,12 +18,11 @@ export abstract class PdfExport {
filename?: string; filename?: string;
data?: any; data?: any;
saveToDisk?: boolean; saveToDisk?: boolean;
margins?: { top: string; bottom: string };
folder?: string; folder?: string;
}) { }) {
if (folder != "") FileSystemHelper.createFolder(folder); if (folder != "") FileSystemHelper.createFolder(folder);
const { header, footer, body } = await TemplateHelper.renderFileForModule({ const { header, footer, body, headerMargin, footerMargin } = await TemplateHelper.renderFileForModule({
module: template, module: template,
headerData: data, headerData: data,
bodyData: data, bodyData: data,
@ -46,8 +44,8 @@ export abstract class PdfExport {
format: "A4", format: "A4",
printBackground: false, printBackground: false,
margin: { margin: {
top: margins.top, top: (headerMargin ?? 15) + "mm",
bottom: margins.bottom, bottom: (footerMargin ?? 15) + "mm",
left: "10mm", left: "10mm",
right: "10mm", right: "10mm",
}, },

View file

@ -1,7 +1,7 @@
import TemplateService from "../service/templateService"; import TemplateService from "../service/templateService";
import { PermissionModule } from "../type/permissionTypes"; import { PermissionModule } from "../type/permissionTypes";
import TemplateUsageService from "../service/templateUsageService"; import TemplateUsageService from "../service/templateUsageService";
import Handlebars from "handlebars"; import Handlebars, { template } from "handlebars";
import { FileSystemHelper } from "./fileSystemHelper"; import { FileSystemHelper } from "./fileSystemHelper";
export abstract class TemplateHelper { export abstract class TemplateHelper {
@ -39,27 +39,27 @@ export abstract class TemplateHelper {
headerData?: any; headerData?: any;
bodyData?: any; bodyData?: any;
footerData?: any; footerData?: any;
}): Promise<{ header: string; body: string; footer: string; margins?: { top: string; bottom: string } }> { }): Promise<{ header: string; body: string; footer: string; headerMargin?: number; footerMargin?: number }> {
const moduleTemplates = await TemplateUsageService.getByScope(module); const moduleTemplate = await TemplateUsageService.getByScope(module);
let header = `<h1 style="font-size:10px; text-align:center; width:100%;">${title}</h1>`; let header = `<h1 style="font-size:10px; text-align:center; width:100%;">${title}</h1>`;
let footer = ""; let footer = "";
let body = ""; let body = "";
if (moduleTemplates.headerId) { if (moduleTemplate.headerId) {
header = await this.getTemplateFromStore(moduleTemplates.headerId); header = await this.getTemplateFromStore(moduleTemplate.headerId);
header = this.applyDataToTemplate(header, { title, ...headerData }); header = this.applyDataToTemplate(header, { title, ...headerData });
} }
if (moduleTemplates.footerId) { if (moduleTemplate.footerId) {
footer = await this.getTemplateFromStore(moduleTemplates.footerId); footer = await this.getTemplateFromStore(moduleTemplate.footerId);
} else { } else {
footer = this.getTemplateFromFile(module + ".footer"); footer = this.getTemplateFromFile(module + ".footer");
} }
footer = this.applyDataToTemplate(footer, footerData); footer = this.applyDataToTemplate(footer, footerData);
if (moduleTemplates.bodyId) { if (moduleTemplate.bodyId) {
body = await this.getTemplateFromStore(moduleTemplates.bodyId); body = await this.getTemplateFromStore(moduleTemplate.bodyId);
} else { } else {
body = this.getTemplateFromFile(module + ".body"); body = this.getTemplateFromFile(module + ".body");
} }
@ -69,6 +69,8 @@ export abstract class TemplateHelper {
header, header,
footer, footer,
body, body,
headerMargin: moduleTemplate.headerHeight,
footerMargin: moduleTemplate.footerHeight,
}; };
} }
} }

View file

@ -0,0 +1,35 @@
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
import { DB_TYPE } from "../env.defaults";
export class TemplateMargins1735733514043 implements MigrationInterface {
name = "TemplateMargins1735733514043";
public async up(queryRunner: QueryRunner): Promise<void> {
const variableType_int = DB_TYPE == "mysql" ? "int" : "integer";
await queryRunner.addColumn(
"template_usage",
new TableColumn({
name: "headerHeight",
type: variableType_int,
default: null,
isNullable: true,
})
);
await queryRunner.addColumn(
"template_usage",
new TableColumn({
name: "footerHeight",
type: variableType_int,
default: null,
isNullable: true,
})
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropColumn("template_usage", "footerHeight");
await queryRunner.dropColumn("template_usage", "headerHeight");
}
}

View file

@ -0,0 +1,47 @@
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
import { membership } from "../entity/membership";
import { member } from "../entity/member";
export class InternalId1735822722235 implements MigrationInterface {
name = "InternalId1735822722235";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.addColumn(
"member",
new TableColumn({
name: "internalId",
type: "varchar",
length: "255",
default: null,
isNullable: true,
isUnique: true,
})
);
let memberships = await queryRunner.manager.getRepository(membership).find();
console.log(memberships);
let internalIds = memberships.reduce<{ [key: number]: Array<string> }>((acc, cur) => {
let memberId = cur.memberId;
let setIds = acc[memberId] ?? [];
if (cur?.internalId) {
setIds.push(cur.internalId);
}
acc[memberId] = setIds;
return acc;
}, {});
console.log(internalIds);
for (const [id, value] of Object.entries(internalIds)) {
const ids = value.filter((v) => v != null).join(", ");
if (ids) {
let m = await queryRunner.manager.getRepository(member).findOneByOrFail({ id: parseInt(id) });
m.internalId = ids;
await queryRunner.manager.getRepository(member).save(m);
}
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropColumn("member", "internalId");
}
}

View file

@ -14,6 +14,7 @@ export default abstract class MembershipService {
.createQueryBuilder("membership") .createQueryBuilder("membership")
.leftJoinAndSelect("membership.status", "membershipStatus") .leftJoinAndSelect("membership.status", "membershipStatus")
.where("membership.memberId = :memberId", { memberId: memberId }) .where("membership.memberId = :memberId", { memberId: memberId })
.orderBy("membership.start", "DESC")
.getMany() .getMany()
.then((res) => { .then((res) => {
return res; return res;

View file

@ -9,6 +9,7 @@ export interface MemberViewModel {
lastname: string; lastname: string;
nameaffix: string; nameaffix: string;
birthdate: Date; birthdate: Date;
internalId?: string;
firstMembershipEntry?: MembershipViewModel; firstMembershipEntry?: MembershipViewModel;
lastMembershipEntry?: MembershipViewModel; lastMembershipEntry?: MembershipViewModel;
sendNewsletter?: CommunicationViewModel; sendNewsletter?: CommunicationViewModel;

View file

@ -1,6 +1,5 @@
export interface MembershipViewModel { export interface MembershipViewModel {
id: number; id: number;
internalId?: string;
start: Date; start: Date;
end?: Date; end?: Date;
terminationReason?: string; terminationReason?: string;

View file

@ -5,4 +5,6 @@ export interface TemplateUsageViewModel {
header: { id: number; template: string } | null; header: { id: number; template: string } | null;
body: { id: number; template: string } | null; body: { id: number; template: string } | null;
footer: { id: number; template: string } | null; footer: { id: number; template: string } | null;
headerHeight: number | null;
footerHeight: number | null;
} }