diff --git a/src/command/club/member/memberCommand.ts b/src/command/club/member/memberCommand.ts index a05e284..126b87d 100644 --- a/src/command/club/member/memberCommand.ts +++ b/src/command/club/member/memberCommand.ts @@ -1,7 +1,5 @@ -import { Salutation } from "../../../enums/salutation"; - export interface CreateMemberCommand { - salutation: Salutation; + salutationId: number; firstname: string; lastname: string; nameaffix: string; @@ -11,7 +9,7 @@ export interface CreateMemberCommand { export interface UpdateMemberCommand { id: number; - salutation: Salutation; + salutationId: number; firstname: string; lastname: string; nameaffix: string; diff --git a/src/command/club/member/memberCommandHandler.ts b/src/command/club/member/memberCommandHandler.ts index 2bea5b5..569e835 100644 --- a/src/command/club/member/memberCommandHandler.ts +++ b/src/command/club/member/memberCommandHandler.ts @@ -21,7 +21,7 @@ export default abstract class MemberCommandHandler { .insert() .into(member) .values({ - salutation: createMember.salutation, + salutationId: createMember.salutationId, firstname: createMember.firstname, lastname: createMember.lastname, nameaffix: createMember.nameaffix, @@ -50,7 +50,7 @@ export default abstract class MemberCommandHandler { .createQueryBuilder() .update(member) .set({ - salutation: updateMember.salutation, + salutationId: updateMember.salutationId, firstname: updateMember.firstname, lastname: updateMember.lastname, nameaffix: updateMember.nameaffix, diff --git a/src/command/settings/salutation/salutationCommand.ts b/src/command/settings/salutation/salutationCommand.ts new file mode 100644 index 0000000..955f1b8 --- /dev/null +++ b/src/command/settings/salutation/salutationCommand.ts @@ -0,0 +1,12 @@ +export interface CreateSalutationCommand { + salutation: string; +} + +export interface UpdateSalutationCommand { + id: number; + salutation: string; +} + +export interface DeleteSalutationCommand { + id: number; +} diff --git a/src/command/settings/salutation/salutationCommandHandler.ts b/src/command/settings/salutation/salutationCommandHandler.ts new file mode 100644 index 0000000..78f7c69 --- /dev/null +++ b/src/command/settings/salutation/salutationCommandHandler.ts @@ -0,0 +1,69 @@ +import { dataSource } from "../../../data-source"; +import { salutation } from "../../../entity/settings/salutation"; +import InternalException from "../../../exceptions/internalException"; +import { CreateSalutationCommand, DeleteSalutationCommand, UpdateSalutationCommand } from "./salutationCommand"; + +export default abstract class SalutationCommandHandler { + /** + * @description create salutation + * @param {CreateSalutationCommand} createSalutation + * @returns {Promise} + */ + static async create(createSalutation: CreateSalutationCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(salutation) + .values({ + salutation: createSalutation.salutation, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new InternalException("Failed creating salutation", err); + }); + } + + /** + * @description update salutation + * @param {UpdateSalutationCommand} updateSalutation + * @returns {Promise} + */ + static async update(updateSalutation: UpdateSalutationCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(salutation) + .set({ + salutation: updateSalutation.salutation, + }) + .where("id = :id", { id: updateSalutation.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new InternalException("Failed updating salutation", err); + }); + } + + /** + * @description delete salutation + * @param {DeleteSalutationCommand} deleteSalutation + * @returns {Promise} + */ + static async delete(deleteSalutation: DeleteSalutationCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(salutation) + .where("id = :id", { id: deleteSalutation.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new InternalException( + `Failed deleting salutation ${err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""}`, + err + ); + }); + } +} diff --git a/src/controller/admin/club/memberController.ts b/src/controller/admin/club/memberController.ts index 4970ead..ec99ef3 100644 --- a/src/controller/admin/club/memberController.ts +++ b/src/controller/admin/club/memberController.ts @@ -285,7 +285,7 @@ export async function createMemberPrintoutList(req: Request, res: Response): Pro * @returns {Promise<*>} */ export async function createMember(req: Request, res: Response): Promise { - const salutation = req.body.salutation; + const salutationId = parseInt(req.body.salutationId); const firstname = req.body.firstname; const lastname = req.body.lastname; const nameaffix = req.body.nameaffix; @@ -293,7 +293,7 @@ export async function createMember(req: Request, res: Response): Promise { const internalId = req.body.internalId; let createMember: CreateMemberCommand = { - salutation, + salutationId, firstname, lastname, nameaffix, @@ -453,7 +453,7 @@ export async function addCommunicationToMember(req: Request, res: Response): Pro */ export async function updateMemberById(req: Request, res: Response): Promise { const memberId = parseInt(req.params.id); - const salutation = req.body.salutation; + const salutationId = parseInt(req.body.salutationId); const firstname = req.body.firstname; const lastname = req.body.lastname; const nameaffix = req.body.nameaffix; @@ -462,7 +462,7 @@ export async function updateMemberById(req: Request, res: Response): Promise} + */ +export async function getAllSalutations(req: Request, res: Response): Promise { + let salutations = await SalutationService.getAll(); + + res.json(SalutationFactory.mapToBase(salutations)); +} + +/** + * @description get salutation by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getSalutationById(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + let salutation = await SalutationService.getById(id); + + res.json(SalutationFactory.mapToSingle(salutation)); +} + +/** + * @description create new salutation + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createSalutation(req: Request, res: Response): Promise { + const salutation = req.body.salutation; + + let createSalutation: CreateSalutationCommand = { + salutation: salutation, + }; + await SalutationCommandHandler.create(createSalutation); + + res.sendStatus(204); +} + +/** + * @description update salutation + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateSalutation(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + const salutation = req.body.salutation; + + let updateSalutation: UpdateSalutationCommand = { + id: id, + salutation: salutation, + }; + await SalutationCommandHandler.update(updateSalutation); + + res.sendStatus(204); +} + +/** + * @description delete salutation + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function deleteSalutation(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + + let deleteSalutation: DeleteSalutationCommand = { + id: id, + }; + await SalutationCommandHandler.delete(deleteSalutation); + + res.sendStatus(204); +} diff --git a/src/data-source.ts b/src/data-source.ts index 8ba9056..c016ca4 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -71,6 +71,9 @@ import { ProtocolPresenceExcuse1737287798828 } from "./migrations/1737287798828- import { webapi } from "./entity/user/webapi"; import { webapiPermission } from "./entity/user/webapi_permission"; import { AddWebapiTokens1737453096674 } from "./migrations/1737453096674-addwebapiTokens"; +import { salutation } from "./entity/settings/salutation"; +import { SalutationAsTable1737796878058 } from "./migrations/1737796878058-salutationAsTable"; +import { UpdateViews1737800468938 } from "./migrations/1737800468938-updateViews"; const dataSource = new DataSource({ type: DB_TYPE as any, @@ -96,6 +99,7 @@ const dataSource = new DataSource({ executivePosition, membershipStatus, qualification, + salutation, member, memberAwards, memberExecutivePositions, @@ -152,6 +156,8 @@ const dataSource = new DataSource({ FinishInternalIdTransfer1736505324488, ProtocolPresenceExcuse1737287798828, AddWebapiTokens1737453096674, + SalutationAsTable1737796878058, + UpdateViews1737800468938, ], migrationsRun: true, migrationsTransactionMode: "each", diff --git a/src/demodata/newsletter.data.ts b/src/demodata/newsletter.data.ts index 8729264..20f13d1 100644 --- a/src/demodata/newsletter.data.ts +++ b/src/demodata/newsletter.data.ts @@ -1,6 +1,5 @@ import { calendar } from "../entity/club/calendar"; import { member } from "../entity/club/member/member"; -import { Salutation } from "../enums/salutation"; export const newsletterDemoData: { title: string; @@ -18,7 +17,9 @@ export const newsletterDemoData: { } > >; - recipient: Partial; + recipient: Partial< + Omit & { salutation: string; street: string; streetNumber: string; streetNumberAdd: string } + >; } = { title: "Beispiel Newsletter Daten", description: "Zusammenfassung der Demodaten.", @@ -63,7 +64,7 @@ export const newsletterDemoData: { recipient: { firstname: "Julian", lastname: "Krauser", - salutation: Salutation.sir, + salutation: "Herr", nameaffix: "", street: "Straße", streetNumber: "Hausnummer", diff --git a/src/entity/club/member/member.ts b/src/entity/club/member/member.ts index 1defb1f..e5b27c2 100644 --- a/src/entity/club/member/member.ts +++ b/src/entity/club/member/member.ts @@ -1,32 +1,16 @@ import { Column, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne, PrimaryColumn } from "typeorm"; -import { Salutation } from "../../../enums/salutation"; import { membership } from "./membership"; import { memberAwards } from "./memberAwards"; import { memberQualifications } from "./memberQualifications"; import { memberExecutivePositions } from "./memberExecutivePositions"; import { communication } from "./communication"; -import { CommunicationViewModel } from "../../../viewmodel/admin/club/member/communication.models"; +import { salutation } from "../../settings/salutation"; @Entity() export class member { @PrimaryColumn({ generated: "increment", type: "int" }) id: number; - @Column({ - type: "varchar", - length: "255", - default: Salutation.none.toString(), - transformer: { - to(value: Salutation) { - return value.toString(); - }, - from(value: string) { - return Salutation[value as keyof typeof Salutation]; - }, - }, - }) - salutation: Salutation; - @Column({ type: "varchar", length: 255 }) firstname: string; @@ -42,6 +26,9 @@ export class member { @Column({ type: "varchar", length: 255, unique: true, nullable: true }) internalId?: string; + @Column() + salutationId: number; + @OneToMany(() => communication, (communications) => communications.member) communications: communication[]; @@ -53,6 +40,9 @@ export class member { @JoinColumn() sendNewsletter?: communication; + @ManyToOne(() => salutation, (salutation) => salutation.members) + salutation: salutation; + @OneToMany(() => membership, (membership) => membership.member) memberships: membership[]; diff --git a/src/entity/settings/salutation.ts b/src/entity/settings/salutation.ts new file mode 100644 index 0000000..634921b --- /dev/null +++ b/src/entity/settings/salutation.ts @@ -0,0 +1,15 @@ +import { Column, Entity, OneToMany, PrimaryColumn } from "typeorm"; +import { memberAwards } from "../club/member/memberAwards"; +import { member } from "../club/member/member"; + +@Entity() +export class salutation { + @PrimaryColumn({ generated: "increment", type: "int" }) + id: number; + + @Column({ type: "varchar", length: 255, unique: true }) + salutation: string; + + @OneToMany(() => member, (member) => member.salutation) + members: member[]; +} diff --git a/src/enums/salutation.ts b/src/enums/salutation.ts deleted file mode 100644 index ba2fce4..0000000 --- a/src/enums/salutation.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum Salutation { - sir = "sir", - madam = "madam", - other = "other", - none = "none", -} diff --git a/src/factory/admin/club/member/member.ts b/src/factory/admin/club/member/member.ts index 116a0c8..e934025 100644 --- a/src/factory/admin/club/member/member.ts +++ b/src/factory/admin/club/member/member.ts @@ -1,6 +1,7 @@ import { member } from "../../../../entity/club/member/member"; import { MemberStatisticsViewModel, MemberViewModel } from "../../../../viewmodel/admin/club/member/member.models"; import { memberView } from "../../../../views/memberView"; +import SalutationFactory from "../../settings/salutation"; import CommunicationFactory from "./communication"; import MembershipFactory from "./membership"; @@ -13,7 +14,7 @@ export default abstract class MemberFactory { public static mapToSingle(record: member): MemberViewModel { return { id: record?.id, - salutation: record?.salutation, + salutation: SalutationFactory.mapToSingle(record?.salutation), firstname: record?.firstname, lastname: record?.lastname, nameaffix: record?.nameaffix, diff --git a/src/factory/admin/settings/salutation.ts b/src/factory/admin/settings/salutation.ts new file mode 100644 index 0000000..65ac3dd --- /dev/null +++ b/src/factory/admin/settings/salutation.ts @@ -0,0 +1,25 @@ +import { salutation } from "../../../entity/settings/salutation"; +import { SalutationViewModel } from "../../../viewmodel/admin/settings/salutation.models"; + +export default abstract class SalutationFactory { + /** + * @description map record to salutation + * @param {salutation} record + * @returns {SalutationViewModel} + */ + public static mapToSingle(record: salutation): SalutationViewModel { + return { + id: record.id, + salutation: record.salutation, + }; + } + + /** + * @description map records to salutation + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/helpers/dynamicQueryBuilder.ts b/src/helpers/dynamicQueryBuilder.ts index f0962d3..1c39f80 100644 --- a/src/helpers/dynamicQueryBuilder.ts +++ b/src/helpers/dynamicQueryBuilder.ts @@ -11,6 +11,7 @@ export default abstract class DynamicQueryBuilder { "executivePosition", "membershipStatus", "qualification", + "salutation", "member", "memberAwards", "memberExecutivePositions", diff --git a/src/helpers/newsletterHelper.ts b/src/helpers/newsletterHelper.ts index cfdeb76..1e458c1 100644 --- a/src/helpers/newsletterHelper.ts +++ b/src/helpers/newsletterHelper.ts @@ -100,7 +100,7 @@ export abstract class NewsletterHelper { recipient: { firstname: recipient.firstname, lastname: recipient.lastname, - salutation: recipient.salutation, + salutation: recipient.salutation.salutation, nameaffix: recipient.nameaffix, ...(showAdress ? { diff --git a/src/migrations/1734520998539-memberDataViews.ts b/src/migrations/1734520998539-memberDataViews.ts index 4e0df38..b29b3e2 100644 --- a/src/migrations/1734520998539-memberDataViews.ts +++ b/src/migrations/1734520998539-memberDataViews.ts @@ -8,105 +8,105 @@ export class MemberDataViews1734520998539 implements MigrationInterface { name = "MemberDataViews1734520998539"; public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createView( - new View({ - name: "member_view", - expression: (datasource: DataSource) => - datasource - .getRepository(member) - .createQueryBuilder("member") - .select("member.id", "id") - .addSelect("member.salutation", "salutation") - .addSelect("member.firstname", "firstname") - .addSelect("member.lastname", "lastname") - .addSelect("member.nameaffix", "nameaffix") - .addSelect("member.birthdate", "birthdate") - .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") - .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear"), - }), - true - ); - await queryRunner.createView( - new View({ - name: "member_executive_positions_view", - expression: (datasource: DataSource) => - datasource - .getRepository(memberExecutivePositions) - .createQueryBuilder("memberExecutivePositions") - .select("executivePosition.id", "positionId") - .addSelect("executivePosition.position", "position") - .addSelect("member.id", "memberId") - .addSelect("member.salutation", "memberSalutation") - .addSelect("member.firstname", "memberFirstname") - .addSelect("member.lastname", "memberLastname") - .addSelect("member.nameaffix", "memberNameaffix") - .addSelect("member.birthdate", "memberBirthdate") - .addSelect( - "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", - "durationInDays" - ) - .leftJoin("memberExecutivePositions.executivePosition", "executivePosition") - .leftJoin("memberExecutivePositions.member", "member") - .groupBy("executivePosition.id"), - }), - true - ); - await queryRunner.createView( - new View({ - name: "member_qualifications_view", - expression: (datasource: DataSource) => - datasource - .getRepository(memberQualifications) - .createQueryBuilder("memberQualifications") - .select("qualification.id", "qualificationId") - .addSelect("qualification.qualification", "qualification") - .addSelect("member.id", "memberId") - .addSelect("member.salutation", "memberSalutation") - .addSelect("member.firstname", "memberFirstname") - .addSelect("member.lastname", "memberLastname") - .addSelect("member.nameaffix", "memberNameaffix") - .addSelect("member.birthdate", "memberBirthdate") - .addSelect( - "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", - "durationInDays" - ) - .leftJoin("memberQualifications.qualification", "qualification") - .leftJoin("memberQualifications.member", "member") - .groupBy("qualification.id"), - }), - true - ); - await queryRunner.createView( - new View({ - name: "membership_view", - expression: (datasource: DataSource) => - datasource - .getRepository(membership) - .createQueryBuilder("membership") - .select("status.id", "statusId") - .addSelect("status.status", "status") - .addSelect("member.id", "memberId") - .addSelect("member.salutation", "memberSalutation") - .addSelect("member.firstname", "memberFirstname") - .addSelect("member.lastname", "memberLastname") - .addSelect("member.nameaffix", "memberNameaffix") - .addSelect("member.birthdate", "memberBirthdate") - .addSelect( - "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", - "durationInDays" - ) - .leftJoin("membership.status", "status") - .leftJoin("membership.member", "member") - .groupBy("status.id"), - }), - true - ); + // await queryRunner.createView( + // new View({ + // name: "member_view", + // expression: (datasource: DataSource) => + // datasource + // .getRepository(member) + // .createQueryBuilder("member") + // .select("member.id", "id") + // .addSelect("member.salutation", "salutation") + // .addSelect("member.firstname", "firstname") + // .addSelect("member.lastname", "lastname") + // .addSelect("member.nameaffix", "nameaffix") + // .addSelect("member.birthdate", "birthdate") + // .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") + // .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear"), + // }), + // true + // ); + // await queryRunner.createView( + // new View({ + // name: "member_executive_positions_view", + // expression: (datasource: DataSource) => + // datasource + // .getRepository(memberExecutivePositions) + // .createQueryBuilder("memberExecutivePositions") + // .select("executivePosition.id", "positionId") + // .addSelect("executivePosition.position", "position") + // .addSelect("member.id", "memberId") + // .addSelect("member.salutation", "memberSalutation") + // .addSelect("member.firstname", "memberFirstname") + // .addSelect("member.lastname", "memberLastname") + // .addSelect("member.nameaffix", "memberNameaffix") + // .addSelect("member.birthdate", "memberBirthdate") + // .addSelect( + // "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", + // "durationInDays" + // ) + // .leftJoin("memberExecutivePositions.executivePosition", "executivePosition") + // .leftJoin("memberExecutivePositions.member", "member") + // .groupBy("executivePosition.id"), + // }), + // true + // ); + // await queryRunner.createView( + // new View({ + // name: "member_qualifications_view", + // expression: (datasource: DataSource) => + // datasource + // .getRepository(memberQualifications) + // .createQueryBuilder("memberQualifications") + // .select("qualification.id", "qualificationId") + // .addSelect("qualification.qualification", "qualification") + // .addSelect("member.id", "memberId") + // .addSelect("member.salutation", "memberSalutation") + // .addSelect("member.firstname", "memberFirstname") + // .addSelect("member.lastname", "memberLastname") + // .addSelect("member.nameaffix", "memberNameaffix") + // .addSelect("member.birthdate", "memberBirthdate") + // .addSelect( + // "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", + // "durationInDays" + // ) + // .leftJoin("memberQualifications.qualification", "qualification") + // .leftJoin("memberQualifications.member", "member") + // .groupBy("qualification.id"), + // }), + // true + // ); + // await queryRunner.createView( + // new View({ + // name: "membership_view", + // expression: (datasource: DataSource) => + // datasource + // .getRepository(membership) + // .createQueryBuilder("membership") + // .select("status.id", "statusId") + // .addSelect("status.status", "status") + // .addSelect("member.id", "memberId") + // .addSelect("member.salutation", "memberSalutation") + // .addSelect("member.firstname", "memberFirstname") + // .addSelect("member.lastname", "memberLastname") + // .addSelect("member.nameaffix", "memberNameaffix") + // .addSelect("member.birthdate", "memberBirthdate") + // .addSelect( + // "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", + // "durationInDays" + // ) + // .leftJoin("membership.status", "status") + // .leftJoin("membership.member", "member") + // .groupBy("status.id"), + // }), + // true + // ); } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropView("membership_view"); - await queryRunner.dropView("member_qualifications_view"); - await queryRunner.dropView("member_executive_positions_view"); - await queryRunner.dropView("member_view"); + // await queryRunner.dropView("membership_view"); + // await queryRunner.dropView("member_qualifications_view"); + // await queryRunner.dropView("member_executive_positions_view"); + // await queryRunner.dropView("member_view"); } } diff --git a/src/migrations/1736084198860-extendViewValues.ts b/src/migrations/1736084198860-extendViewValues.ts index 9816a83..4a024c9 100644 --- a/src/migrations/1736084198860-extendViewValues.ts +++ b/src/migrations/1736084198860-extendViewValues.ts @@ -8,106 +8,104 @@ export class ExtendViewValues1736084198860 implements MigrationInterface { name = "ExtendViewValues1736084198860"; public async up(queryRunner: QueryRunner): Promise { - await queryRunner.dropView("membership_view"); - await queryRunner.dropView("member_view"); - - await queryRunner.createView( - new View({ - name: "member_view", - expression: (datasource: DataSource) => - datasource - .getRepository(member) - .createQueryBuilder("member") - .select("member.id", "id") - .addSelect("member.salutation", "salutation") - .addSelect("member.firstname", "firstname") - .addSelect("member.lastname", "lastname") - .addSelect("member.nameaffix", "nameaffix") - .addSelect("member.birthdate", "birthdate") - .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") - .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear") - .addSelect("CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, member.birthdate, CURDATE())))", "exactAge"), - }), - true - ); - await queryRunner.createView( - new View({ - name: "membership_view", - expression: (datasource: DataSource) => - datasource - .getRepository(membership) - .createQueryBuilder("membership") - .select("status.id", "statusId") - .addSelect("status.status", "status") - .addSelect("member.id", "memberId") - .addSelect("member.salutation", "memberSalutation") - .addSelect("member.firstname", "memberFirstname") - .addSelect("member.lastname", "memberLastname") - .addSelect("member.nameaffix", "memberNameaffix") - .addSelect("member.birthdate", "memberBirthdate") - .addSelect( - "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", - "durationInDays" - ) - .addSelect( - "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))))", - "durationInYears" - ) - .leftJoin("membership.status", "status") - .leftJoin("membership.member", "member") - .groupBy("status.id") - .addGroupBy("member.id"), - }), - true - ); + // await queryRunner.dropView("membership_view"); + // await queryRunner.dropView("member_view"); + // await queryRunner.createView( + // new View({ + // name: "member_view", + // expression: (datasource: DataSource) => + // datasource + // .getRepository(member) + // .createQueryBuilder("member") + // .select("member.id", "id") + // .addSelect("member.salutation", "salutation") + // .addSelect("member.firstname", "firstname") + // .addSelect("member.lastname", "lastname") + // .addSelect("member.nameaffix", "nameaffix") + // .addSelect("member.birthdate", "birthdate") + // .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") + // .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear") + // .addSelect("CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, member.birthdate, CURDATE())))", "exactAge"), + // }), + // true + // ); + // await queryRunner.createView( + // new View({ + // name: "membership_view", + // expression: (datasource: DataSource) => + // datasource + // .getRepository(membership) + // .createQueryBuilder("membership") + // .select("status.id", "statusId") + // .addSelect("status.status", "status") + // .addSelect("member.id", "memberId") + // .addSelect("member.salutation", "memberSalutation") + // .addSelect("member.firstname", "memberFirstname") + // .addSelect("member.lastname", "memberLastname") + // .addSelect("member.nameaffix", "memberNameaffix") + // .addSelect("member.birthdate", "memberBirthdate") + // .addSelect( + // "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", + // "durationInDays" + // ) + // .addSelect( + // "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))))", + // "durationInYears" + // ) + // .leftJoin("membership.status", "status") + // .leftJoin("membership.member", "member") + // .groupBy("status.id") + // .addGroupBy("member.id"), + // }), + // true + // ); } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropView("membership_view"); - await queryRunner.dropView("member_view"); - - await queryRunner.createView( - new View({ - name: "member_view", - expression: (datasource: DataSource) => - datasource - .getRepository(member) - .createQueryBuilder("member") - .select("member.id", "id") - .addSelect("member.salutation", "salutation") - .addSelect("member.firstname", "firstname") - .addSelect("member.lastname", "lastname") - .addSelect("member.nameaffix", "nameaffix") - .addSelect("member.birthdate", "birthdate") - .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") - .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear"), - }), - true - ); - await queryRunner.createView( - new View({ - name: "membership_view", - expression: (datasource: DataSource) => - datasource - .getRepository(membership) - .createQueryBuilder("membership") - .select("status.id", "statusId") - .addSelect("status.status", "status") - .addSelect("member.id", "memberId") - .addSelect("member.salutation", "memberSalutation") - .addSelect("member.firstname", "memberFirstname") - .addSelect("member.lastname", "memberLastname") - .addSelect("member.nameaffix", "memberNameaffix") - .addSelect("member.birthdate", "memberBirthdate") - .addSelect( - "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", - "durationInDays" - ) - .leftJoin("membership.status", "status") - .leftJoin("membership.member", "member") - .groupBy("status.id"), - }), - true - ); + // await queryRunner.dropView("membership_view"); + // await queryRunner.dropView("member_view"); + // await queryRunner.createView( + // new View({ + // name: "member_view", + // expression: (datasource: DataSource) => + // datasource + // .getRepository(member) + // .createQueryBuilder("member") + // .select("member.id", "id") + // .addSelect("member.salutation", "salutation") + // .addSelect("member.firstname", "firstname") + // .addSelect("member.lastname", "lastname") + // .addSelect("member.nameaffix", "nameaffix") + // .addSelect("member.birthdate", "birthdate") + // .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") + // .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear"), + // }), + // true + // ); + // await queryRunner.createView( + // new View({ + // name: "membership_view", + // expression: (datasource: DataSource) => + // datasource + // .getRepository(membership) + // .createQueryBuilder("membership") + // .select("status.id", "statusId") + // .addSelect("status.status", "status") + // .addSelect("member.id", "memberId") + // .addSelect("member.salutation", "memberSalutation") + // .addSelect("member.firstname", "memberFirstname") + // .addSelect("member.lastname", "memberLastname") + // .addSelect("member.nameaffix", "memberNameaffix") + // .addSelect("member.birthdate", "memberBirthdate") + // .addSelect( + // "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", + // "durationInDays" + // ) + // .leftJoin("membership.status", "status") + // .leftJoin("membership.member", "member") + // .groupBy("status.id"), + // }), + // true + // ); } } diff --git a/src/migrations/1737796878058-salutationAsTable.ts b/src/migrations/1737796878058-salutationAsTable.ts new file mode 100644 index 0000000..df84a91 --- /dev/null +++ b/src/migrations/1737796878058-salutationAsTable.ts @@ -0,0 +1,91 @@ +import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm"; +import { DB_TYPE } from "../env.defaults"; +import { member } from "../entity/club/member/member"; +import { salutation } from "../entity/settings/salutation"; + +export class SalutationAsTable1737796878058 implements MigrationInterface { + name = "SalutationAsTable1737796878058"; + + public async up(queryRunner: QueryRunner): Promise { + const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; + + await queryRunner.createTable( + new Table({ + name: "salutation", + columns: [ + { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "salutation", type: "varchar", length: "255", isUnique: true, isNullable: false }, + ], + }), + true + ); + + // ! has to be sql. Else the column would be dropped and created - resulting in data loss. + await queryRunner.query( + `ALTER TABLE \`member\` CHANGE \`salutation\` \`salutationId\` varchar(255) NOT NULL DEFAULT ''` + ); + + // ! has to be sql. Else no data is returned. + const existing_salutations = await queryRunner.query( + "SELECT DISTINCT salutationId FROM `member` `member` GROUP BY salutationId" + ); + + for (let s of existing_salutations.map((s: any) => s.salutationId) as Array) { + await queryRunner.manager.getRepository(salutation).save({ salutation: s }); + } + + const salutations = await queryRunner.manager.getRepository(salutation).find(); + + for (let salutation of salutations) { + await queryRunner.manager + .getRepository(member) + .createQueryBuilder("member") + .update({ salutationId: salutation.id }) + .where({ salutationId: salutation.salutation }) + .execute(); + } + + await queryRunner.query( + `ALTER TABLE \`member\` CHANGE \`salutationId\` \`salutationId\` ${variableType_int} NOT NULL` + ); + + await queryRunner.createForeignKey( + "member", + new TableForeignKey({ + columnNames: ["salutationId"], + referencedColumnNames: ["id"], + referencedTableName: "salutation", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) + ); + } + + public async down(queryRunner: QueryRunner): Promise { + const table = await queryRunner.getTable("member"); + const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("salutationId") !== -1); + await queryRunner.dropForeignKey("member", foreignKey); + + // ! has to be sql. Else the column would be dropped and created - resulting in data loss. + await queryRunner.query( + `ALTER TABLE \`member\` CHANGE \`salutationId\` \`salutationId\` varchar(255) NOT NULL DEFAULT ''` + ); + + const salutations = await queryRunner.manager.getRepository(salutation).find(); + + for (let salutation of salutations) { + await queryRunner.manager + .getRepository(member) + .createQueryBuilder("member") + .update({ salutationId: salutation.salutation as unknown as number }) + .where({ salutationId: salutation.id }) + .execute(); + } + + await queryRunner.query( + `ALTER TABLE \`member\` CHANGE \`salutationId\` \`salutation\` varchar(255) NOT NULL DEFAULT 'none'` + ); + + await queryRunner.dropTable("webapi"); + } +} diff --git a/src/migrations/1737800468938-updateViews.ts b/src/migrations/1737800468938-updateViews.ts new file mode 100644 index 0000000..64587af --- /dev/null +++ b/src/migrations/1737800468938-updateViews.ts @@ -0,0 +1,229 @@ +import { DataSource, MigrationInterface, QueryRunner, View } from "typeorm"; +import { member } from "../entity/club/member/member"; +import { memberExecutivePositions } from "../entity/club/member/memberExecutivePositions"; +import { memberQualifications } from "../entity/club/member/memberQualifications"; +import { membership } from "../entity/club/member/membership"; + +export class UpdateViews1737800468938 implements MigrationInterface { + name = "UpdateViews1737800468938"; + + public async up(queryRunner: QueryRunner): Promise { + // await queryRunner.dropView("membership_view"); + // await queryRunner.dropView("member_executive_positions_view"); + // await queryRunner.dropView("member_qualifications_view"); + // await queryRunner.dropView("member_view"); + + await queryRunner.createView( + new View({ + name: "member_view", + expression: (datasource: DataSource) => + datasource + .getRepository(member) + .createQueryBuilder("member") + .select("member.id", "id") + .addSelect("member.firstname", "firstname") + .addSelect("member.lastname", "lastname") + .addSelect("member.nameaffix", "nameaffix") + .addSelect("member.birthdate", "birthdate") + .addSelect("salutation.salutation", "salutation") + .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") + .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear") + .addSelect("CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, member.birthdate, CURDATE())))", "exactAge") + .leftJoin("member.salutation", "salutation"), + }), + true + ); + await queryRunner.createView( + new View({ + name: "member_executive_positions_view", + expression: (datasource: DataSource) => + datasource + .getRepository(memberExecutivePositions) + .createQueryBuilder("memberExecutivePositions") + .select("executivePosition.id", "positionId") + .addSelect("executivePosition.position", "position") + .addSelect("member.id", "memberId") + .addSelect("member.firstname", "memberFirstname") + .addSelect("member.lastname", "memberLastname") + .addSelect("member.nameaffix", "memberNameaffix") + .addSelect("member.birthdate", "memberBirthdate") + .addSelect("salutation.salutation", "memberSalutation") + .addSelect( + "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", + "durationInDays" + ) + .leftJoin("memberExecutivePositions.executivePosition", "executivePosition") + .leftJoin("memberExecutivePositions.member", "member") + .leftJoin("member.salutation", "salutation") + .groupBy("executivePosition.id") + .addGroupBy("member.id"), + }), + true + ); + await queryRunner.createView( + new View({ + name: "member_qualifications_view", + expression: (datasource: DataSource) => + datasource + .getRepository(memberQualifications) + .createQueryBuilder("memberQualifications") + .select("qualification.id", "qualificationId") + .addSelect("qualification.qualification", "qualification") + .addSelect("member.id", "memberId") + .addSelect("member.firstname", "memberFirstname") + .addSelect("member.lastname", "memberLastname") + .addSelect("member.nameaffix", "memberNameaffix") + .addSelect("member.birthdate", "memberBirthdate") + .addSelect("salutation.salutation", "memberSalutation") + .addSelect( + "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", + "durationInDays" + ) + .leftJoin("memberQualifications.qualification", "qualification") + .leftJoin("memberQualifications.member", "member") + .leftJoin("member.salutation", "salutation") + .groupBy("qualification.id") + .addGroupBy("member.id"), + }), + true + ); + await queryRunner.createView( + new View({ + name: "membership_view", + expression: (datasource: DataSource) => + datasource + .getRepository(membership) + .createQueryBuilder("membership") + .select("status.id", "statusId") + .addSelect("status.status", "status") + .addSelect("member.id", "memberId") + .addSelect("member.firstname", "memberFirstname") + .addSelect("member.lastname", "memberLastname") + .addSelect("member.nameaffix", "memberNameaffix") + .addSelect("member.birthdate", "memberBirthdate") + .addSelect("salutation.salutation", "memberSalutation") + .addSelect( + "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", + "durationInDays" + ) + .addSelect( + "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))))", + "durationInYears" + ) + .leftJoin("membership.status", "status") + .leftJoin("membership.member", "member") + .leftJoin("member.salutation", "salutation") + .groupBy("status.id") + .addGroupBy("member.id"), + }), + true + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropView("membership_view"); + await queryRunner.dropView("member_executive_positions_view"); + await queryRunner.dropView("member_qualifications_view"); + await queryRunner.dropView("member_view"); + + await queryRunner.createView( + new View({ + name: "member_view", + expression: (datasource: DataSource) => + datasource + .getRepository(member) + .createQueryBuilder("member") + .select("member.id", "id") + .addSelect("member.salutation", "salutation") + .addSelect("member.firstname", "firstname") + .addSelect("member.lastname", "lastname") + .addSelect("member.nameaffix", "nameaffix") + .addSelect("member.birthdate", "birthdate") + .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") + .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear") + .addSelect("CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, member.birthdate, CURDATE())))", "exactAge"), + }), + true + ); + await queryRunner.createView( + new View({ + name: "member_executive_positions_view", + expression: (datasource: DataSource) => + datasource + .getRepository(memberExecutivePositions) + .createQueryBuilder("memberExecutivePositions") + .select("executivePosition.id", "positionId") + .addSelect("executivePosition.position", "position") + .addSelect("member.id", "memberId") + .addSelect("member.salutation", "memberSalutation") + .addSelect("member.firstname", "memberFirstname") + .addSelect("member.lastname", "memberLastname") + .addSelect("member.nameaffix", "memberNameaffix") + .addSelect("member.birthdate", "memberBirthdate") + .addSelect( + "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", + "durationInDays" + ) + .leftJoin("memberExecutivePositions.executivePosition", "executivePosition") + .leftJoin("memberExecutivePositions.member", "member") + .groupBy("executivePosition.id"), + }), + true + ); + await queryRunner.createView( + new View({ + name: "member_qualifications_view", + expression: (datasource: DataSource) => + datasource + .getRepository(memberQualifications) + .createQueryBuilder("memberQualifications") + .select("qualification.id", "qualificationId") + .addSelect("qualification.qualification", "qualification") + .addSelect("member.id", "memberId") + .addSelect("member.salutation", "memberSalutation") + .addSelect("member.firstname", "memberFirstname") + .addSelect("member.lastname", "memberLastname") + .addSelect("member.nameaffix", "memberNameaffix") + .addSelect("member.birthdate", "memberBirthdate") + .addSelect( + "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", + "durationInDays" + ) + .leftJoin("memberQualifications.qualification", "qualification") + .leftJoin("memberQualifications.member", "member") + .groupBy("qualification.id"), + }), + true + ); + await queryRunner.createView( + new View({ + name: "membership_view", + expression: (datasource: DataSource) => + datasource + .getRepository(membership) + .createQueryBuilder("membership") + .select("status.id", "statusId") + .addSelect("status.status", "status") + .addSelect("member.id", "memberId") + .addSelect("member.salutation", "memberSalutation") + .addSelect("member.firstname", "memberFirstname") + .addSelect("member.lastname", "memberLastname") + .addSelect("member.nameaffix", "memberNameaffix") + .addSelect("member.birthdate", "memberBirthdate") + .addSelect( + "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", + "durationInDays" + ) + .addSelect( + "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))))", + "durationInYears" + ) + .leftJoin("membership.status", "status") + .leftJoin("membership.member", "member") + .groupBy("status.id") + .addGroupBy("member.id"), + }), + true + ); + } +} diff --git a/src/routes/admin/index.ts b/src/routes/admin/index.ts index 9aa24ce..016db3e 100644 --- a/src/routes/admin/index.ts +++ b/src/routes/admin/index.ts @@ -6,6 +6,7 @@ import communicationType from "./settings/communicationType"; import executivePosition from "./settings/executivePosition"; import membershipStatus from "./settings/membershipStatus"; import qualification from "./settings/qualification"; +import salutation from "./settings/salutation"; import calendarType from "./settings/calendarType"; import queryStore from "./settings/queryStore"; import template from "./settings/template"; @@ -43,6 +44,7 @@ router.use( membershipStatus ); router.use("/qualification", PermissionHelper.passCheckMiddleware("read", "settings", "qualification"), qualification); +router.use("/salutation", PermissionHelper.passCheckMiddleware("read", "settings", "salutation"), salutation); router.use("/calendartype", PermissionHelper.passCheckMiddleware("read", "settings", "calendar_type"), calendarType); router.use("/querystore", PermissionHelper.passCheckMiddleware("read", "settings", "query_store"), queryStore); router.use("/template", PermissionHelper.passCheckMiddleware("read", "settings", "template"), template); diff --git a/src/routes/admin/settings/salutation.ts b/src/routes/admin/settings/salutation.ts new file mode 100644 index 0000000..c4c53a0 --- /dev/null +++ b/src/routes/admin/settings/salutation.ts @@ -0,0 +1,45 @@ +import express, { Request, Response } from "express"; +import { + createSalutation, + deleteSalutation, + getAllSalutations, + getSalutationById, + updateSalutation, +} from "../../../controller/admin/settings/salutationController"; +import PermissionHelper from "../../../helpers/permissionHelper"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllSalutations(req, res); +}); + +router.get("/:id", async (req: Request, res: Response) => { + await getSalutationById(req, res); +}); + +router.post( + "/", + PermissionHelper.passCheckMiddleware("create", "settings", "salutation"), + async (req: Request, res: Response) => { + await createSalutation(req, res); + } +); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "settings", "salutation"), + async (req: Request, res: Response) => { + await updateSalutation(req, res); + } +); + +router.delete( + "/:id", + PermissionHelper.passCheckMiddleware("delete", "settings", "salutation"), + async (req: Request, res: Response) => { + await deleteSalutation(req, res); + } +); + +export default router; diff --git a/src/service/club/member/memberService.ts b/src/service/club/member/memberService.ts index 3b08f9f..35b8c1d 100644 --- a/src/service/club/member/memberService.ts +++ b/src/service/club/member/memberService.ts @@ -49,7 +49,8 @@ export default abstract class MemberService { ) .leftJoinAndSelect("preferredCommunication.type", "communicationtype_preferred") .leftJoinAndMapMany("member.smsAlarming", "member.communications", "smsAlarming", "smsAlarming.isSMSAlarming = 1") - .leftJoinAndSelect("smsAlarming.type", "communicationtype_smsAlarming"); + .leftJoinAndSelect("smsAlarming.type", "communicationtype_smsAlarming") + .leftJoinAndSelect("member.salutation", "salutation"); if (search != "") { search.split(" ").forEach((term, index) => { @@ -123,6 +124,7 @@ export default abstract class MemberService { .leftJoinAndMapMany("member.smsAlarming", "member.communications", "smsAlarming", "smsAlarming.isSMSAlarming = 1") .leftJoinAndSelect("smsAlarming.type", "communicationtype_smsAlarming") .leftJoinAndSelect("preferredCommunication.type", "communicationtype_preferred") + .leftJoinAndSelect("member.salutation", "salutation") .where("member.id = :id", { id: id }) .getOneOrFail() .then((res) => { diff --git a/src/service/settings/salutationService.ts b/src/service/settings/salutationService.ts new file mode 100644 index 0000000..70a6b4f --- /dev/null +++ b/src/service/settings/salutationService.ts @@ -0,0 +1,41 @@ +import { dataSource } from "../../data-source"; +import { salutation } from "../../entity/settings/salutation"; +import InternalException from "../../exceptions/internalException"; + +export default abstract class SalutationService { + /** + * @description get all salutations + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(salutation) + .createQueryBuilder("salutation") + .orderBy("salutation", "ASC") + .getMany() + .then((res) => { + return res; + }) + .catch((err) => { + throw new InternalException("salutations not found", err); + }); + } + + /** + * @description get salutation by id + * @returns {Promise} + */ + static async getById(id: number): Promise { + return await dataSource + .getRepository(salutation) + .createQueryBuilder("salutation") + .where("salutation.id = :id", { id: id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new InternalException("salutation not found by id", err); + }); + } +} diff --git a/src/type/permissionTypes.ts b/src/type/permissionTypes.ts index 5ba5cb4..cd37e49 100644 --- a/src/type/permissionTypes.ts +++ b/src/type/permissionTypes.ts @@ -11,6 +11,7 @@ export type PermissionModule = | "executive_position" | "communication_type" | "membership_status" + | "salutation" | "calendar_type" | "user" | "role" @@ -53,6 +54,7 @@ export const permissionModules: Array = [ "executive_position", "communication_type", "membership_status", + "salutation", "calendar_type", "user", "role", @@ -71,6 +73,7 @@ export const sectionsAndModules: SectionsAndModulesObject = { "executive_position", "communication_type", "membership_status", + "salutation", "calendar_type", "query_store", "template", diff --git a/src/viewmodel/admin/club/member/member.models.ts b/src/viewmodel/admin/club/member/member.models.ts index c28ed73..2254d68 100644 --- a/src/viewmodel/admin/club/member/member.models.ts +++ b/src/viewmodel/admin/club/member/member.models.ts @@ -1,10 +1,10 @@ -import { Salutation } from "../../../../enums/salutation"; +import { SalutationViewModel } from "../../settings/salutation.models"; import { CommunicationViewModel } from "./communication.models"; import { MembershipViewModel } from "./membership.models"; export interface MemberViewModel { id: number; - salutation: Salutation; + salutation: SalutationViewModel; firstname: string; lastname: string; nameaffix: string; @@ -19,7 +19,7 @@ export interface MemberViewModel { export interface MemberStatisticsViewModel { id: number; - salutation: Salutation; + salutation: string; firstname: string; lastname: string; nameaffix: string; diff --git a/src/viewmodel/admin/club/member/membership.models.ts b/src/viewmodel/admin/club/member/membership.models.ts index da8bffa..1db540a 100644 --- a/src/viewmodel/admin/club/member/membership.models.ts +++ b/src/viewmodel/admin/club/member/membership.models.ts @@ -1,5 +1,3 @@ -import { Salutation } from "../../../../enums/salutation"; - export interface MembershipViewModel { id: number; start: Date; @@ -15,7 +13,7 @@ export interface MembershipStatisticsViewModel { status: string; statusId: number; memberId: number; - memberSalutation: Salutation; + memberSalutation: string; memberFirstname: string; memberLastname: string; memberNameaffix: string; diff --git a/src/viewmodel/admin/settings/salutation.models.ts b/src/viewmodel/admin/settings/salutation.models.ts new file mode 100644 index 0000000..18531f5 --- /dev/null +++ b/src/viewmodel/admin/settings/salutation.models.ts @@ -0,0 +1,4 @@ +export interface SalutationViewModel { + id: number; + salutation: string; +} diff --git a/src/views/memberExecutivePositionView.ts b/src/views/memberExecutivePositionView.ts index 23722cb..c1df465 100644 --- a/src/views/memberExecutivePositionView.ts +++ b/src/views/memberExecutivePositionView.ts @@ -1,6 +1,5 @@ import { DataSource, ViewColumn, ViewEntity } from "typeorm"; import { memberExecutivePositions } from "../entity/club/member/memberExecutivePositions"; -import { Salutation } from "../enums/salutation"; @ViewEntity({ expression: (datasource: DataSource) => @@ -10,18 +9,20 @@ import { Salutation } from "../enums/salutation"; .select("executivePosition.id", "positionId") .addSelect("executivePosition.position", "position") .addSelect("member.id", "memberId") - .addSelect("member.salutation", "memberSalutation") .addSelect("member.firstname", "memberFirstname") .addSelect("member.lastname", "memberLastname") .addSelect("member.nameaffix", "memberNameaffix") .addSelect("member.birthdate", "memberBirthdate") + .addSelect("salutation.salutation", "memberSalutation") .addSelect( "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", "durationInDays" ) .leftJoin("memberExecutivePositions.executivePosition", "executivePosition") .leftJoin("memberExecutivePositions.member", "member") - .groupBy("executivePosition.id"), + .leftJoin("member.salutation", "salutation") + .groupBy("executivePosition.id") + .addGroupBy("member.id"), }) export class memberExecutivePositionsView { @ViewColumn() @@ -37,7 +38,7 @@ export class memberExecutivePositionsView { memberId: number; @ViewColumn() - memberSalutation: Salutation; + memberSalutation: string; @ViewColumn() memberFirstname: string; diff --git a/src/views/memberQualificationsView.ts b/src/views/memberQualificationsView.ts index 4ae436d..b22e05e 100644 --- a/src/views/memberQualificationsView.ts +++ b/src/views/memberQualificationsView.ts @@ -1,6 +1,5 @@ import { DataSource, ViewColumn, ViewEntity } from "typeorm"; import { memberQualifications } from "../entity/club/member/memberQualifications"; -import { Salutation } from "../enums/salutation"; @ViewEntity({ expression: (datasource: DataSource) => @@ -10,18 +9,20 @@ import { Salutation } from "../enums/salutation"; .select("qualification.id", "qualificationId") .addSelect("qualification.qualification", "qualification") .addSelect("member.id", "memberId") - .addSelect("member.salutation", "memberSalutation") .addSelect("member.firstname", "memberFirstname") .addSelect("member.lastname", "memberLastname") .addSelect("member.nameaffix", "memberNameaffix") .addSelect("member.birthdate", "memberBirthdate") + .addSelect("salutation.salutation", "memberSalutation") .addSelect( "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", "durationInDays" ) .leftJoin("memberQualifications.qualification", "qualification") .leftJoin("memberQualifications.member", "member") - .groupBy("qualification.id"), + .leftJoin("member.salutation", "salutation") + .groupBy("qualification.id") + .addGroupBy("member.id"), }) export class memberQualificationsView { @ViewColumn() @@ -37,7 +38,7 @@ export class memberQualificationsView { memberId: number; @ViewColumn() - memberSalutation: Salutation; + memberSalutation: string; @ViewColumn() memberFirstname: string; diff --git a/src/views/memberView.ts b/src/views/memberView.ts index 6a980f0..90debc3 100644 --- a/src/views/memberView.ts +++ b/src/views/memberView.ts @@ -1,6 +1,5 @@ import { DataSource, ViewColumn, ViewEntity } from "typeorm"; import { member } from "../entity/club/member/member"; -import { Salutation } from "../enums/salutation"; @ViewEntity({ expression: (datasource: DataSource) => @@ -8,21 +7,22 @@ import { Salutation } from "../enums/salutation"; .getRepository(member) .createQueryBuilder("member") .select("member.id", "id") - .addSelect("member.salutation", "salutation") .addSelect("member.firstname", "firstname") .addSelect("member.lastname", "lastname") .addSelect("member.nameaffix", "nameaffix") .addSelect("member.birthdate", "birthdate") + .addSelect("salutation.salutation", "salutation") .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear") - .addSelect("CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, member.birthdate, CURDATE())))", "exactAge"), + .addSelect("CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, member.birthdate, CURDATE())))", "exactAge") + .leftJoin("member.salutation", "salutation"), }) export class memberView { @ViewColumn() id: number; @ViewColumn() - salutation: Salutation; + salutation: string; @ViewColumn() firstname: string; diff --git a/src/views/membershipsView.ts b/src/views/membershipsView.ts index 1e40209..1c764bf 100644 --- a/src/views/membershipsView.ts +++ b/src/views/membershipsView.ts @@ -1,6 +1,5 @@ import { DataSource, ViewColumn, ViewEntity } from "typeorm"; import { membership } from "../entity/club/member/membership"; -import { Salutation } from "../enums/salutation"; @ViewEntity({ expression: (datasource: DataSource) => @@ -10,11 +9,11 @@ import { Salutation } from "../enums/salutation"; .select("status.id", "statusId") .addSelect("status.status", "status") .addSelect("member.id", "memberId") - .addSelect("member.salutation", "memberSalutation") .addSelect("member.firstname", "memberFirstname") .addSelect("member.lastname", "memberLastname") .addSelect("member.nameaffix", "memberNameaffix") .addSelect("member.birthdate", "memberBirthdate") + .addSelect("salutation.salutation", "memberSalutation") .addSelect("SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", "durationInDays") .addSelect( "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))))", @@ -22,6 +21,7 @@ import { Salutation } from "../enums/salutation"; ) .leftJoin("membership.status", "status") .leftJoin("membership.member", "member") + .leftJoin("member.salutation", "salutation") .groupBy("status.id") .addGroupBy("member.id"), }) @@ -42,7 +42,7 @@ export class membershipView { memberId: number; @ViewColumn() - memberSalutation: Salutation; + memberSalutation: string; @ViewColumn() memberFirstname: string;