From a6229bb77c86a0e716928fbd74f0cb7cbae6ffc2 Mon Sep 17 00:00:00 2001
From: Julian Krauser <jkrauser209@gmail.com>
Date: Wed, 30 Apr 2025 12:22:38 +0200
Subject: [PATCH] enhance: provide latest inserted internal Id

---
 src/controller/admin/club/memberController.ts | 12 +++++++
 src/data-source.ts                            |  2 ++
 src/entity/club/member/member.ts              | 15 ++++++++-
 .../1746006549262-memberCreatedAt.ts          | 21 ++++++++++++
 src/routes/admin/club/member.ts               |  5 +++
 src/service/club/member/memberService.ts      | 33 ++++++++++++++++---
 6 files changed, 83 insertions(+), 5 deletions(-)
 create mode 100644 src/migrations/1746006549262-memberCreatedAt.ts

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<any>
   });
 }
 
+/**
+ * @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<any> {
+  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 37f957d..2266f19 100644
--- a/src/data-source.ts
+++ b/src/data-source.ts
@@ -51,6 +51,7 @@ import { TemplatesAndProtocolSort1742549956787 } from "./migrations/174254995678
 import { QueryToUUID1742922178643 } from "./migrations/1742922178643-queryToUUID";
 import { NewsletterColumnType1744351418751 } from "./migrations/1744351418751-newsletterColumnType";
 import { QueryUpdatedAt1744795756230 } from "./migrations/1744795756230-QueryUpdatedAt";
+import { MemberCreatedAt1746006549262 } from "./migrations/1746006549262-memberCreatedAt";
 
 const dataSource = new DataSource({
   type: DB_TYPE as any,
@@ -111,6 +112,7 @@ const dataSource = new DataSource({
     QueryToUUID1742922178643,
     NewsletterColumnType1744351418751,
     QueryUpdatedAt1744795756230,
+    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/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<void> {
+    await queryRunner.addColumn(
+      "member",
+      new TableColumn({
+        name: "createdAt",
+        ...getTypeByORM("datetime", false, 6),
+        default: getDefaultByORM("currentTimestamp", 6),
+      })
+    );
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    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 3eae4e6..6480d58 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 { membership } from "../../../entity/club/member/membership";
@@ -31,9 +31,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]))
@@ -157,6 +160,28 @@ export default abstract class MemberService {
       });
   }
 
+  /**
+   * @description get latest inserted memberId
+   * @returns {Promise<string>}
+   */
+  static async getLatestInternalId(): Promise<string> {
+    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<member>}