diff --git a/src/command/club/member/communicationCommand.ts b/src/command/club/member/communicationCommand.ts index cc80442..f0fdd5a 100644 --- a/src/command/club/member/communicationCommand.ts +++ b/src/command/club/member/communicationCommand.ts @@ -1,7 +1,6 @@ export interface CreateCommunicationCommand { preferred: boolean; isSMSAlarming: boolean; - isSendNewsletter: boolean; mobile: string; email: string; postalCode: string; @@ -17,7 +16,6 @@ export interface UpdateCommunicationCommand { id: number; preferred: boolean; isSMSAlarming: boolean; - isSendNewsletter: boolean; mobile: string; email: string; postalCode: string; diff --git a/src/command/club/member/communicationCommandHandler.ts b/src/command/club/member/communicationCommandHandler.ts index 24402fc..ad6341c 100644 --- a/src/command/club/member/communicationCommandHandler.ts +++ b/src/command/club/member/communicationCommandHandler.ts @@ -14,44 +14,26 @@ export default abstract class CommunicationCommandHandler { * @returns {Promise} */ static async create(createCommunication: CreateCommunicationCommand): Promise { - let insertId = -1; return await dataSource - .transaction(async (manager) => { - await manager - .createQueryBuilder() - .insert() - .into(communication) - .values({ - preferred: createCommunication.preferred, - isSMSAlarming: createCommunication.isSMSAlarming, - isSendNewsletter: createCommunication.isSendNewsletter, - mobile: createCommunication.mobile, - email: createCommunication.email, - postalCode: createCommunication.postalCode, - city: createCommunication.city, - street: createCommunication.street, - streetNumber: createCommunication.streetNumber, - streetNumberAddition: createCommunication.streetNumberAddition, - memberId: createCommunication.memberId, - typeId: createCommunication.typeId, - }) - .execute() - .then((result) => { - insertId = result.identifiers[0].id; - }); - - await manager - .createQueryBuilder() - .update(communication) - .set({ - isSendNewsletter: false, - }) - .where("memberId = :memberId", { memberId: createCommunication.memberId }) - .andWhere("id <> :id", { id: insertId }) - .execute(); + .createQueryBuilder() + .insert() + .into(communication) + .values({ + preferred: createCommunication.preferred, + isSMSAlarming: createCommunication.isSMSAlarming, + mobile: createCommunication.mobile, + email: createCommunication.email, + postalCode: createCommunication.postalCode, + city: createCommunication.city, + street: createCommunication.street, + streetNumber: createCommunication.streetNumber, + streetNumberAddition: createCommunication.streetNumberAddition, + memberId: createCommunication.memberId, + typeId: createCommunication.typeId, }) - .then(() => { - return insertId; + .execute() + .then((result) => { + return result.identifiers[0].id; }) .catch((err) => { throw new InternalException("Failed creating communication", err); @@ -65,36 +47,22 @@ export default abstract class CommunicationCommandHandler { */ static async update(updateCommunication: UpdateCommunicationCommand): Promise { return await dataSource - .transaction(async (manager) => { - await manager - .createQueryBuilder() - .update(communication) - .set({ - preferred: updateCommunication.preferred, - isSMSAlarming: updateCommunication.isSMSAlarming, - isSendNewsletter: updateCommunication.isSendNewsletter, - mobile: updateCommunication.mobile, - email: updateCommunication.email, - postalCode: updateCommunication.postalCode, - city: updateCommunication.city, - street: updateCommunication.street, - streetNumber: updateCommunication.streetNumber, - streetNumberAddition: updateCommunication.streetNumberAddition, - }) - .where("id = :id", { id: updateCommunication.id }) - .andWhere("memberId = :memberId", { memberId: updateCommunication.memberId }) - .execute(); - - await manager - .createQueryBuilder() - .update(communication) - .set({ - isSendNewsletter: false, - }) - .where("memberId = :memberId", { memberId: updateCommunication.memberId }) - .andWhere("id <> :id", { id: updateCommunication.id }) - .execute(); + .createQueryBuilder() + .update(communication) + .set({ + preferred: updateCommunication.preferred, + isSMSAlarming: updateCommunication.isSMSAlarming, + mobile: updateCommunication.mobile, + email: updateCommunication.email, + postalCode: updateCommunication.postalCode, + city: updateCommunication.city, + street: updateCommunication.street, + streetNumber: updateCommunication.streetNumber, + streetNumberAddition: updateCommunication.streetNumberAddition, }) + .where("id = :id", { id: updateCommunication.id }) + .andWhere("memberId = :memberId", { memberId: updateCommunication.memberId }) + .execute() .then(() => {}) .catch((err) => { throw new InternalException("Failed updating communication", err); diff --git a/src/command/club/member/memberCommand.ts b/src/command/club/member/memberCommand.ts index 90ecee0..126b87d 100644 --- a/src/command/club/member/memberCommand.ts +++ b/src/command/club/member/memberCommand.ts @@ -17,6 +17,11 @@ export interface UpdateMemberCommand { internalId?: string; } +export interface UpdateMemberNewsletterCommand { + id: number; + communicationId: number; +} + export interface DeleteMemberCommand { id: number; } diff --git a/src/command/club/member/memberCommandHandler.ts b/src/command/club/member/memberCommandHandler.ts index 54862f7..569e835 100644 --- a/src/command/club/member/memberCommandHandler.ts +++ b/src/command/club/member/memberCommandHandler.ts @@ -2,7 +2,12 @@ import { dataSource } from "../../../data-source"; import { communication } from "../../../entity/club/member/communication"; import { member } from "../../../entity/club/member/member"; import InternalException from "../../../exceptions/internalException"; -import { CreateMemberCommand, DeleteMemberCommand, UpdateMemberCommand } from "./memberCommand"; +import { + CreateMemberCommand, + DeleteMemberCommand, + UpdateMemberCommand, + UpdateMemberNewsletterCommand, +} from "./memberCommand"; export default abstract class MemberCommandHandler { /** @@ -63,6 +68,51 @@ export default abstract class MemberCommandHandler { }); } + /** + * @description update member newsletter + * @param {UpdateMemberCommand} updateMember + * @returns {Promise} + */ + static async updateNewsletter(updateMember: UpdateMemberNewsletterCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(member) + .set({ + sendNewsletter: await dataSource + .getRepository(communication) + .createQueryBuilder("communication") + .where("id = :id", { id: updateMember.communicationId }) + .andWhere("memberId = :memberId", { memberId: updateMember.id }) + .getOneOrFail(), + }) + .where("id = :id", { id: updateMember.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new InternalException(`Failed updating member`, err); + }); + } + + /** + * @description update member newsletter to unset + * @param {number} memberId + * @returns {Promise} + */ + static async unsetNewsletter(memberId: number): Promise { + return await dataSource + .createQueryBuilder() + .update(member) + .set({ + sendNewsletter: null, + }) + .where("id = :id", { id: memberId }) + .execute() + .then(() => {}) + .catch((err) => { + throw new InternalException("Failed updating member", err); + }); + } + /** * @description delete member * @param {DeleteMemberCommand} deleteMember diff --git a/src/controller/admin/club/memberController.ts b/src/controller/admin/club/memberController.ts index 2e8698a..18c9cf5 100644 --- a/src/controller/admin/club/memberController.ts +++ b/src/controller/admin/club/memberController.ts @@ -15,6 +15,7 @@ import { CreateMemberCommand, DeleteMemberCommand, UpdateMemberCommand, + UpdateMemberNewsletterCommand, } from "../../../command/club/member/memberCommand"; import MemberCommandHandler from "../../../command/club/member/memberCommandHandler"; import { @@ -289,7 +290,7 @@ export async function createMember(req: Request, res: Response): Promise { const lastname = req.body.lastname; const nameaffix = req.body.nameaffix; const birthdate = req.body.birthdate; - const internalId = req.body.internalId; + const internalId = req.body.internalId || null; let createMember: CreateMemberCommand = { salutationId, @@ -408,7 +409,6 @@ export async function addCommunicationToMember(req: Request, res: Response): Pro const memberId = parseInt(req.params.memberId); const preferred = req.body.preferred; const isSMSAlarming = req.body.isSMSAlarming; - const isSendNewsletter = req.body.isNewsletterMain; const mobile = req.body.mobile; const email = req.body.email; const postalCode = req.body.postalCode; @@ -417,11 +417,11 @@ export async function addCommunicationToMember(req: Request, res: Response): Pro const streetNumber = req.body.streetNumber; const streetNumberAddition = req.body.streetNumberAddition; const typeId = req.body.typeId; + const isNewsletterMain = req.body.isNewsletterMain; let createCommunication: CreateCommunicationCommand = { preferred, isSMSAlarming, - isSendNewsletter, mobile, email, postalCode, @@ -434,6 +434,14 @@ export async function addCommunicationToMember(req: Request, res: Response): Pro }; let id = await CommunicationCommandHandler.create(createCommunication); + if (isNewsletterMain) { + let updateNewsletter: UpdateMemberNewsletterCommand = { + id: memberId, + communicationId: id, + }; + await MemberCommandHandler.updateNewsletter(updateNewsletter); + } + res.sendStatus(204); } @@ -587,7 +595,6 @@ export async function updateCommunicationOfMember(req: Request, res: Response): const recordId = parseInt(req.params.recordId); const preferred = req.body.preferred; const isSMSAlarming = req.body.isSMSAlarming; - const isSendNewsletter = req.body.isNewsletterMain; const mobile = req.body.mobile; const email = req.body.email; const postalCode = req.body.postalCode; @@ -595,12 +602,12 @@ export async function updateCommunicationOfMember(req: Request, res: Response): const street = req.body.street; const streetNumber = req.body.streetNumber; const streetNumberAddition = req.body.streetNumberAddition; + const isNewsletterMain = req.body.isNewsletterMain; let updateCommunication: UpdateCommunicationCommand = { id: recordId, preferred, isSMSAlarming, - isSendNewsletter, mobile, email, postalCode, @@ -612,6 +619,18 @@ export async function updateCommunicationOfMember(req: Request, res: Response): }; await CommunicationCommandHandler.update(updateCommunication); + let currentUserNewsletterMain = await MemberService.getNewsletterById(memberId); + + if (isNewsletterMain) { + let updateNewsletter: UpdateMemberNewsletterCommand = { + id: memberId, + communicationId: recordId, + }; + await MemberCommandHandler.updateNewsletter(updateNewsletter); + } else if (currentUserNewsletterMain.sendNewsletter?.id == recordId) { + await MemberCommandHandler.unsetNewsletter(memberId); + } + res.sendStatus(204); } diff --git a/src/data-source.ts b/src/data-source.ts index 851997f..c016ca4 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -74,7 +74,6 @@ import { AddWebapiTokens1737453096674 } from "./migrations/1737453096674-addweba import { salutation } from "./entity/settings/salutation"; import { SalutationAsTable1737796878058 } from "./migrations/1737796878058-salutationAsTable"; import { UpdateViews1737800468938 } from "./migrations/1737800468938-updateViews"; -import { MoveSendNewsletterFlag1737816852011 } from "./migrations/1737816852011-moveSendNewsletterFlag"; const dataSource = new DataSource({ type: DB_TYPE as any, @@ -159,7 +158,6 @@ const dataSource = new DataSource({ AddWebapiTokens1737453096674, SalutationAsTable1737796878058, UpdateViews1737800468938, - MoveSendNewsletterFlag1737816852011, ], migrationsRun: true, migrationsTransactionMode: "each", diff --git a/src/entity/club/member/communication.ts b/src/entity/club/member/communication.ts index c3c79fc..f50d1a9 100644 --- a/src/entity/club/member/communication.ts +++ b/src/entity/club/member/communication.ts @@ -13,9 +13,6 @@ export class communication { @Column({ type: "boolean", default: false }) isSMSAlarming: boolean; - @Column({ type: "boolean", default: false }) - isSendNewsletter: boolean; - @Column({ type: "varchar", length: 255, nullable: true }) mobile: string; diff --git a/src/entity/club/member/member.ts b/src/entity/club/member/member.ts index 3557d58..e5b27c2 100644 --- a/src/entity/club/member/member.ts +++ b/src/entity/club/member/member.ts @@ -32,6 +32,14 @@ export class member { @OneToMany(() => communication, (communications) => communications.member) communications: communication[]; + @OneToOne(() => communication, { + nullable: true, + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }) + @JoinColumn() + sendNewsletter?: communication; + @ManyToOne(() => salutation, (salutation) => salutation.members) salutation: salutation; @@ -51,5 +59,4 @@ export class member { lastMembershipEntry?: membership; preferredCommunication?: Array; smsAlarming?: Array; - sendNewsletter?: communication; } diff --git a/src/factory/admin/club/member/communication.ts b/src/factory/admin/club/member/communication.ts index 127ab82..702cfc5 100644 --- a/src/factory/admin/club/member/communication.ts +++ b/src/factory/admin/club/member/communication.ts @@ -20,7 +20,7 @@ export default abstract class CommunicationFactory { streetNumber: record.streetNumber, streetNumberAddition: record.streetNumberAddition, type: CommunicationTypeFactory.mapToSingle(record.type), - isNewsletterMain: record?.isSendNewsletter, + isNewsletterMain: isMain ? isMain : record?.member?.sendNewsletter?.id == record.id, isSMSAlarming: record.isSMSAlarming, }; } diff --git a/src/migrations/1737800468938-updateViews.ts b/src/migrations/1737800468938-updateViews.ts index 76dd7cc..64587af 100644 --- a/src/migrations/1737800468938-updateViews.ts +++ b/src/migrations/1737800468938-updateViews.ts @@ -12,10 +12,6 @@ export class UpdateViews1737800468938 implements MigrationInterface { // await queryRunner.dropView("member_executive_positions_view"); // await queryRunner.dropView("member_qualifications_view"); // await queryRunner.dropView("member_view"); - await queryRunner.query(`DROP VIEW IF EXISTS membership_view`); - await queryRunner.query(`DROP VIEW IF EXISTS member_executive_positions_view`); - await queryRunner.query(`DROP VIEW IF EXISTS member_qualifications_view`); - await queryRunner.query(`DROP VIEW IF EXISTS member_view`); await queryRunner.createView( new View({ diff --git a/src/migrations/1737816852011-moveSendNewsletterFlag.ts b/src/migrations/1737816852011-moveSendNewsletterFlag.ts deleted file mode 100644 index d294337..0000000 --- a/src/migrations/1737816852011-moveSendNewsletterFlag.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn, TableForeignKey, TableIndex } from "typeorm"; -import { communication } from "../entity/club/member/communication"; -import { member } from "../entity/club/member/member"; - -export class MoveSendNewsletterFlag1737816852011 implements MigrationInterface { - name = "MoveSendNewsletterFlag1737816852011"; - - public async up(queryRunner: QueryRunner): Promise { - const table = await queryRunner.getTable("member"); - const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("sendNewsletterId") !== -1); - await queryRunner.dropForeignKey("member", foreignKey); - - await queryRunner.addColumn( - "communication", - new TableColumn({ name: "isSendNewsletter", type: "tinyint", isNullable: false, default: 0 }) - ); - - // ! has to be sql. Else no data is returned. - const member_newsletter_send = await queryRunner.query("SELECT sendNewsletterId, id FROM `member` `member`"); - - for (let assigned of member_newsletter_send.map((mns: any) => ({ - id: mns.id, - sendNewsletterId: mns.sendNewsletterId, - })) as Array<{ id: number; sendNewsletterId: number }>) { - await queryRunner.manager - .getRepository(communication) - .createQueryBuilder("communication") - .update({ isSendNewsletter: true }) - .where({ memberId: assigned.id, id: assigned.sendNewsletterId }) - .execute(); - } - - await queryRunner.dropColumn("member", "sendNewsletterId"); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.addColumn( - "member", - new TableColumn({ name: "sendNewsletterId", type: "int", isNullable: true, isUnique: true, default: null }) - ); - - const member_newsletter_send = await queryRunner.manager - .getRepository(communication) - .createQueryBuilder("communication") - .where("communication.isSendNewsletter = :isSendNewsletter", { isSendNewsletter: true }) - .getMany(); - - for (let assigned of member_newsletter_send.map((mns: any) => ({ - id: mns.id, - memberId: mns.memberId, - })) as Array<{ id: number; memberId: number }>) { - await queryRunner.query("UPDATE `member` SET sendNewsletterId = ? WHERE id = ?", [ - assigned.id, - assigned.memberId, - ]); - } - - await queryRunner.createForeignKey( - "member", - new TableForeignKey({ - columnNames: ["sendNewsletterId"], - referencedColumnNames: ["id"], - referencedTableName: "communication", - onDelete: "SET NULL", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.dropColumn("communication", "isSendNewsletter"); - } -} diff --git a/src/service/club/member/communicationService.ts b/src/service/club/member/communicationService.ts index b828bc7..5ca7bdd 100644 --- a/src/service/club/member/communicationService.ts +++ b/src/service/club/member/communicationService.ts @@ -14,6 +14,7 @@ export default abstract class CommunicationService { .createQueryBuilder("communication") .leftJoinAndSelect("communication.type", "communicationType") .leftJoinAndSelect("communication.member", "member") + .leftJoinAndSelect("member.sendNewsletter", "sendNewsletter") .where("communication.memberId = :memberId", { memberId: memberId }) .orderBy("communicationType.type", "ASC") .getMany() @@ -37,6 +38,7 @@ export default abstract class CommunicationService { .createQueryBuilder("communication") .leftJoinAndSelect("communication.type", "communicationType") .leftJoinAndSelect("communication.member", "member") + .leftJoinAndSelect("member.sendNewsletter", "sendNewsletter") .where("communication.memberId = :memberId", { memberId: memberId }) .andWhere("communication.id = :recordId", { recordId: recordId }) .getOneOrFail() diff --git a/src/service/club/member/memberService.ts b/src/service/club/member/memberService.ts index 53a2aaa..9b289d9 100644 --- a/src/service/club/member/memberService.ts +++ b/src/service/club/member/memberService.ts @@ -39,6 +39,8 @@ export default abstract class MemberService { ) .leftJoinAndSelect("membership_first.status", "status_first") .leftJoinAndSelect("membership_last.status", "status_last") + .leftJoinAndSelect("member.sendNewsletter", "sendNewsletter") + .leftJoinAndSelect("sendNewsletter.type", "communicationtype") .leftJoinAndMapMany( "member.preferredCommunication", "member.communications", @@ -46,13 +48,6 @@ export default abstract class MemberService { "preferredCommunication.preferred = 1" ) .leftJoinAndSelect("preferredCommunication.type", "communicationtype_preferred") - .leftJoinAndMapOne( - "member.sendNewsletter", - "member.communications", - "sendNewsletter", - "sendNewsletter.isSendNewsletter = 1" - ) - .leftJoinAndSelect("sendNewsletter.type", "communicationtype") .leftJoinAndMapMany("member.smsAlarming", "member.communications", "smsAlarming", "smsAlarming.isSMSAlarming = 1") .leftJoinAndSelect("smsAlarming.type", "communicationtype_smsAlarming") .leftJoinAndSelect("member.salutation", "salutation"); @@ -74,7 +69,7 @@ export default abstract class MemberService { } if (ids.length != 0) { - query = query.where({ id: ids }); + query = query.where("member.id IN (:...ids)", { ids: ids }); } if (!noLimit) { @@ -117,22 +112,18 @@ export default abstract class MemberService { ) .leftJoinAndSelect("membership_first.status", "status_first") .leftJoinAndSelect("membership_last.status", "status_last") + .leftJoinAndSelect("member.sendNewsletter", "sendNewsletter") + .leftJoinAndSelect("sendNewsletter.type", "communicationtype") .leftJoinAndMapMany( "member.preferredCommunication", "member.communications", "preferredCommunication", "preferredCommunication.preferred = 1" ) - .leftJoinAndSelect("preferredCommunication.type", "communicationtype_preferred") - .leftJoinAndMapOne( - "member.sendNewsletter", - "member.communications", - "sendNewsletter", - "sendNewsletter.isSendNewsletter = 1" - ) - .leftJoinAndSelect("sendNewsletter.type", "communicationtype") + .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() @@ -194,12 +185,7 @@ export default abstract class MemberService { return await dataSource .getRepository(member) .createQueryBuilder("member") - .leftJoinAndMapOne( - "member.sendNewsletter", - "member.communications", - "sendNewsletter", - "sendNewsletter.isSendNewsletter = 1" - ) + .leftJoinAndSelect("member.sendNewsletter", "sendNewsletter") .where("member.id = :id", { id: id }) .getOneOrFail() .then((res) => {