diff --git a/src/controller/admin/club/memberController.ts b/src/controller/admin/club/memberController.ts index 9ef0bf4..c0b2cd7 100644 --- a/src/controller/admin/club/memberController.ts +++ b/src/controller/admin/club/memberController.ts @@ -92,6 +92,18 @@ export async function getMembersByIds(req: Request, res: Response): Promise }); } +/** + * @description get member latest inserted InternalId + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getMemberLastInternalId(req: Request, res: Response): Promise { + let latest = await MemberService.getLatestInternalId(); + + res.send(latest); +} + /** * @description get member by id * @param req {Request} Express req object diff --git a/src/data-source.ts b/src/data-source.ts index 2fabc96..d35eaa6 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -1,6 +1,7 @@ import "dotenv/config"; import "reflect-metadata"; import { DataSource } from "typeorm"; +import { DB_HOST, DB_NAME, DB_PASSWORD, DB_PORT, DB_TYPE, DB_USERNAME } from "./env.defaults"; import { user } from "./entity/management/user"; import { refresh } from "./entity/refresh"; @@ -43,6 +44,7 @@ import { newsletterConfig } from "./entity/configuration/newsletterConfig"; import { webapi } from "./entity/management/webapi"; import { webapiPermission } from "./entity/management/webapi_permission"; import { salutation } from "./entity/configuration/salutation"; +import { setting } from "./entity/management/setting"; import { BackupAndResetDatabase1738166124200 } from "./migrations/1738166124200-BackupAndResetDatabase"; import { CreateSchema1738166167472 } from "./migrations/1738166167472-CreateSchema"; @@ -50,9 +52,8 @@ import { TemplatesAndProtocolSort1742549956787 } from "./migrations/174254995678 import { QueryToUUID1742922178643 } from "./migrations/1742922178643-queryToUUID"; import { NewsletterColumnType1744351418751 } from "./migrations/1744351418751-newsletterColumnType"; import { QueryUpdatedAt1744795756230 } from "./migrations/1744795756230-QueryUpdatedAt"; -import { setting } from "./entity/management/setting"; import { SettingsFromEnv1745059495808 } from "./migrations/1745059495808-settingsFromEnv"; -import { DB_HOST, DB_NAME, DB_PASSWORD, DB_PORT, DB_TYPE, DB_USERNAME } from "./env.defaults"; +import { MemberCreatedAt1746006549262 } from "./migrations/1746006549262-memberCreatedAt"; const dataSource = new DataSource({ type: DB_TYPE as any, @@ -115,6 +116,7 @@ const dataSource = new DataSource({ NewsletterColumnType1744351418751, QueryUpdatedAt1744795756230, SettingsFromEnv1745059495808, + MemberCreatedAt1746006549262, ], migrationsRun: true, migrationsTransactionMode: "each", diff --git a/src/entity/club/member/member.ts b/src/entity/club/member/member.ts index 1678e39..e724155 100644 --- a/src/entity/club/member/member.ts +++ b/src/entity/club/member/member.ts @@ -1,4 +1,14 @@ -import { Column, ColumnType, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne, PrimaryColumn } from "typeorm"; +import { + Column, + ColumnType, + CreateDateColumn, + Entity, + JoinColumn, + ManyToOne, + OneToMany, + OneToOne, + PrimaryColumn, +} from "typeorm"; import { membership } from "./membership"; import { memberAwards } from "./memberAwards"; import { memberQualifications } from "./memberQualifications"; @@ -30,6 +40,9 @@ export class member { @Column() salutationId: number; + @CreateDateColumn() + createdAt: Date; + @ManyToOne(() => salutation, (salutation) => salutation.members, { nullable: false, onDelete: "RESTRICT", diff --git a/src/factory/admin/club/newsletter/newsletter.ts b/src/factory/admin/club/newsletter/newsletter.ts index 39c19e3..6cc9685 100644 --- a/src/factory/admin/club/newsletter/newsletter.ts +++ b/src/factory/admin/club/newsletter/newsletter.ts @@ -18,7 +18,6 @@ export default abstract class NewsletterFactory { newsletterSignatur: record.newsletterSignatur, isSent: record.isSent, recipientsByQueryId: record?.recipientsByQuery ? record.recipientsByQuery.id : null, - recipientsByQuery: record?.recipientsByQuery ? QueryStoreFactory.mapToSingle(record.recipientsByQuery) : null, }; } diff --git a/src/migrations/1746006549262-memberCreatedAt.ts b/src/migrations/1746006549262-memberCreatedAt.ts new file mode 100644 index 0000000..08f7669 --- /dev/null +++ b/src/migrations/1746006549262-memberCreatedAt.ts @@ -0,0 +1,21 @@ +import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; +import { getTypeByORM, getDefaultByORM } from "./ormHelper"; + +export class MemberCreatedAt1746006549262 implements MigrationInterface { + name = "MemberCreatedAt1746006549262"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.addColumn( + "member", + new TableColumn({ + name: "createdAt", + ...getTypeByORM("datetime", false, 6), + default: getDefaultByORM("currentTimestamp", 6), + }) + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropColumn("member", "createdAt"); + } +} diff --git a/src/routes/admin/club/member.ts b/src/routes/admin/club/member.ts index efd6e05..804b595 100644 --- a/src/routes/admin/club/member.ts +++ b/src/routes/admin/club/member.ts @@ -20,6 +20,7 @@ import { getExecutivePositionByMemberAndRecord, getExecutivePositionsByMember, getMemberById, + getMemberLastInternalId, getMemberPrintoutById, getMembersByIds, getMembershipByMemberAndRecord, @@ -43,6 +44,10 @@ router.get("/", async (req: Request, res: Response) => { await getAllMembers(req, res); }); +router.get("/last/internalId", async (req: Request, res: Response) => { + await getMemberLastInternalId(req, res); +}); + router.post("/ids", async (req: Request, res: Response) => { await getMembersByIds(req, res); }); diff --git a/src/service/club/member/memberService.ts b/src/service/club/member/memberService.ts index cba1e47..5149114 100644 --- a/src/service/club/member/memberService.ts +++ b/src/service/club/member/memberService.ts @@ -1,4 +1,4 @@ -import { Brackets, Like, SelectQueryBuilder } from "typeorm"; +import { Brackets, Like, Not, SelectQueryBuilder } from "typeorm"; import { dataSource } from "../../../data-source"; import { member } from "../../../entity/club/member/member"; import DatabaseActionException from "../../../exceptions/databaseActionException"; @@ -29,9 +29,12 @@ export default abstract class MemberService { let searchBits = search.split(" "); if (searchBits.length < 2) { - query = query.where(`member.firstname LIKE :searchQuery OR member.lastname LIKE :searchQuery`, { - searchQuery: `%${searchBits[0]}%`, - }); + query = query.where( + `member.firstname LIKE :searchQuery OR member.lastname LIKE :searchQuery OR member.internalId LIKE :searchQuery`, + { + searchQuery: `%${searchBits[0]}%`, + } + ); } else { searchBits .flatMap((v, i) => searchBits.slice(i + 1).map((w) => [v, w])) @@ -155,6 +158,28 @@ export default abstract class MemberService { }); } + /** + * @description get latest inserted memberId + * @returns {Promise} + */ + static async getLatestInternalId(): Promise { + return await dataSource + .getRepository(member) + .createQueryBuilder("member") + .where("member.internalId IS NOT NULL") + .andWhere({ internalId: Not("") }) + .orderBy("member.createdAt", "DESC") + .addOrderBy("member.internalId", "DESC") + .limit(1) + .getOne() + .then((res) => { + return res?.internalId ?? ""; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "memberId", err); + }); + } + /** * @description apply member joins to query * @returns {SelectQueryBuilder} diff --git a/src/viewmodel/admin/club/newsletter/newsletter.models.ts b/src/viewmodel/admin/club/newsletter/newsletter.models.ts index 37bf30c..3a1c47f 100644 --- a/src/viewmodel/admin/club/newsletter/newsletter.models.ts +++ b/src/viewmodel/admin/club/newsletter/newsletter.models.ts @@ -1,5 +1,3 @@ -import { QueryStoreViewModel } from "../../configuration/queryStore.models"; - export interface NewsletterViewModel { id: number; title: string; @@ -9,5 +7,4 @@ export interface NewsletterViewModel { newsletterSignatur: string; isSent: boolean; recipientsByQueryId?: string; - recipientsByQuery?: QueryStoreViewModel; }