diff --git a/src/command/protocolPresenceCommand.ts b/src/command/protocolPresenceCommand.ts index b39878b..76c004a 100644 --- a/src/command/protocolPresenceCommand.ts +++ b/src/command/protocolPresenceCommand.ts @@ -1,4 +1,9 @@ export interface SynchronizeProtocolPresenceCommand { - memberIds: Array; + members: Array; protocolId: number; } + +export interface ProtocolPresenceCommand { + memberId: number; + absent: boolean; +} diff --git a/src/command/protocolPresenceCommandHandler.ts b/src/command/protocolPresenceCommandHandler.ts index b773cc6..f3acda9 100644 --- a/src/command/protocolPresenceCommandHandler.ts +++ b/src/command/protocolPresenceCommandHandler.ts @@ -1,9 +1,9 @@ -import { DeleteResult, EntityManager, InsertResult } from "typeorm"; +import { DeleteResult, EntityManager, InsertResult, UpdateResult } from "typeorm"; import { dataSource } from "../data-source"; import { protocolPresence } from "../entity/protocolPresence"; import InternalException from "../exceptions/internalException"; import ProtocolPresenceService from "../service/protocolPrecenseService"; -import { SynchronizeProtocolPresenceCommand } from "./protocolPresenceCommand"; +import { ProtocolPresenceCommand, SynchronizeProtocolPresenceCommand } from "./protocolPresenceCommand"; export default abstract class ProtocolPresenceCommandHandler { /** @@ -12,14 +12,21 @@ export default abstract class ProtocolPresenceCommandHandler { * @returns {Promise} */ static async sync(syncProtocolPresences: SynchronizeProtocolPresenceCommand): Promise { - let currentPresence = (await ProtocolPresenceService.getAll(syncProtocolPresences.protocolId)).map( - (r) => r.memberId - ); + let currentPresence = await ProtocolPresenceService.getAll(syncProtocolPresences.protocolId); return await dataSource.manager .transaction(async (manager) => { - let newMembers = syncProtocolPresences.memberIds.filter((r) => !currentPresence.includes(r)); - let removeMembers = currentPresence.filter((r) => !syncProtocolPresences.memberIds.includes(r)); + let newMembers = syncProtocolPresences.members.filter( + (r) => !currentPresence.some((cp) => cp.memberId == r.memberId) + ); + let removeMembers = currentPresence.filter( + (r) => !syncProtocolPresences.members.some((cp) => cp.memberId == r.memberId) + ); + let keptMembers = syncProtocolPresences.members.filter( + (m) => + currentPresence.some((cd) => cd.memberId == m.memberId) && + !removeMembers.some((cd) => cd.memberId == m.memberId) + ); if (newMembers.length != 0) { await this.syncPresenceAdd(manager, syncProtocolPresences.protocolId, newMembers); @@ -28,6 +35,10 @@ export default abstract class ProtocolPresenceCommandHandler { if (removeMembers.length != 0) { await this.syncPresenceRemove(manager, syncProtocolPresences.protocolId, removeMembers); } + + for (const member of keptMembers) { + await this.syncPresenceUpdate(manager, syncProtocolPresences.protocolId, member); + } }) .then(() => {}) .catch((err) => { @@ -38,7 +49,7 @@ export default abstract class ProtocolPresenceCommandHandler { private static async syncPresenceAdd( manager: EntityManager, protocolId: number, - memberIds: Array + memberIds: Array ): Promise { return await manager .createQueryBuilder() @@ -46,23 +57,39 @@ export default abstract class ProtocolPresenceCommandHandler { .into(protocolPresence) .values( memberIds.map((m) => ({ + ...m, protocolId, - memberId: m, })) ) .execute(); } + private static async syncPresenceUpdate( + manager: EntityManager, + protocolId: number, + member: ProtocolPresenceCommand + ): Promise { + return await manager + .createQueryBuilder() + .update(protocolPresence) + .set({ + absent: member.absent, + }) + .where("memberId = :memberId", { memberId: member.memberId }) + .andWhere("protocolId = :protocolId", { protocolId }) + .execute(); + } + private static async syncPresenceRemove( manager: EntityManager, protocolId: number, - memberIds: Array + members: Array ): Promise { return await manager .createQueryBuilder() .delete() .from(protocolPresence) - .where("memberId IN (:...ids)", { ids: memberIds }) + .where("memberId IN (:...ids)", { ids: members.map((m) => m.memberId) }) .andWhere("protocolId = :protocolId", { protocolId }) .execute(); } diff --git a/src/controller/admin/protocolController.ts b/src/controller/admin/protocolController.ts index 60b1e30..b77237e 100644 --- a/src/controller/admin/protocolController.ts +++ b/src/controller/admin/protocolController.ts @@ -28,6 +28,7 @@ import ProtocolPrintoutFactory from "../../factory/admin/protocolPrintout"; import { CreateProtocolPrintoutCommand } from "../../command/protocolPrintoutCommand"; import ProtocolPrintoutCommandHandler from "../../command/protocolPrintoutCommandHandler"; import { FileSystemHelper } from "../../helpers/fileSystemHelper"; +import { ProtocolPresenceViewModel } from "../../viewmodel/admin/protocolPresence.models"; /** * @description get all protocols @@ -228,7 +229,7 @@ export async function createProtocolPrintoutById(req: Request, res: Response): P let votings = await ProtocolVotingService.getAll(protocolId); let iteration = await ProtocolPrintoutService.getCount(protocolId); - let title = `Sitzungsprotokoll - ${new Date(protocol.date).toLocaleDateString("de-DE", { + let title = `${protocol.title} - ${new Date(protocol.date).toLocaleDateString("de-DE", { day: "2-digit", month: "long", year: "numeric", @@ -258,7 +259,8 @@ export async function createProtocolPrintoutById(req: Request, res: Response): P end: protocol.endtime, agenda, decisions, - presence: presence.map((p) => p.member), + presence: presence.filter((p) => !p.absent).map((p) => p.member), + absent: presence.filter((p) => p.absent).map((p) => p.member), votings, }, }); @@ -381,10 +383,13 @@ export async function synchronizeProtocolVotingsById(req: Request, res: Response */ export async function synchronizeProtocolPrecenseById(req: Request, res: Response): Promise { let protocolId = parseInt(req.params.protocolId); - let presence = req.body.presence as Array; + let presence = req.body.presence as Array; let syncPresence: SynchronizeProtocolPresenceCommand = { - memberIds: presence, + members: presence.map((p) => ({ + memberId: p.memberId, + absent: p.absent, + })), protocolId, }; await ProtocolPresenceCommandHandler.sync(syncPresence); diff --git a/src/data-source.ts b/src/data-source.ts index 8074d2d..770fe2a 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -64,6 +64,7 @@ import { NewsletterConfig1735207446910 } from "./migrations/1735207446910-newsle import { TemplateMargins1735733514043 } from "./migrations/1735733514043-templateMargins"; import { InternalId1735822722235 } from "./migrations/1735822722235-internalId"; import { PostalCode1735927918979 } from "./migrations/1735927918979-postalCode"; +import { ProtocolAbsent1736072179716 } from "./migrations/1736072179716-protocolAbsent"; const dataSource = new DataSource({ type: DB_TYPE as any, @@ -138,6 +139,7 @@ const dataSource = new DataSource({ TemplateMargins1735733514043, InternalId1735822722235, PostalCode1735927918979, + ProtocolAbsent1736072179716, ], migrationsRun: true, migrationsTransactionMode: "each", diff --git a/src/demodata/protocol.data.ts b/src/demodata/protocol.data.ts index 1feeace..9a20486 100644 --- a/src/demodata/protocol.data.ts +++ b/src/demodata/protocol.data.ts @@ -13,6 +13,7 @@ export const protocolDemoData: { agenda: Array>; decisions: Array>; presence: Array>; + absent: Array>; votings: Array>; } = { title: "Beispiel Protokoll Daten", @@ -44,6 +45,7 @@ export const protocolDemoData: { lastname: "Krauser", }, ], + absent: [], votings: [ { topic: "Abstimmung xy", diff --git a/src/entity/protocolPresence.ts b/src/entity/protocolPresence.ts index 5d80b10..c369d71 100644 --- a/src/entity/protocolPresence.ts +++ b/src/entity/protocolPresence.ts @@ -10,6 +10,9 @@ export class protocolPresence { @PrimaryColumn() protocolId: number; + @Column({ type: "boolean", default: false }) + absent: boolean; + @ManyToOne(() => member, { nullable: false, onDelete: "CASCADE", diff --git a/src/factory/admin/protocolPresence.ts b/src/factory/admin/protocolPresence.ts index 59a70ad..b597885 100644 --- a/src/factory/admin/protocolPresence.ts +++ b/src/factory/admin/protocolPresence.ts @@ -11,7 +11,7 @@ export default abstract class ProtocolPresenceFactory { public static mapToSingle(record: protocolPresence): ProtocolPresenceViewModel { return { memberId: record.member.id, - member: MemberFactory.mapToSingle(record.member), + absent: record.absent, protocolId: record.protocolId, }; } diff --git a/src/migrations/1736072179716-protocolAbsent.ts b/src/migrations/1736072179716-protocolAbsent.ts new file mode 100644 index 0000000..d6a859f --- /dev/null +++ b/src/migrations/1736072179716-protocolAbsent.ts @@ -0,0 +1,21 @@ +import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; + +export class ProtocolAbsent1736072179716 implements MigrationInterface { + name = "ProtocolAbsent1736072179716"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.addColumn( + "protocol_presence", + new TableColumn({ + name: "absent", + type: "tinyint", + default: "0", + isNullable: false, + }) + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropColumn("protocol_presence", "absent"); + } +} diff --git a/src/templates/protocol.body.template.html b/src/templates/protocol.body.template.html index 98a2a21..c37fa75 100644 --- a/src/templates/protocol.body.template.html +++ b/src/templates/protocol.body.template.html @@ -14,11 +14,9 @@

Anwesenheit ({{presence.length}})

-
    - {{#each presence}} -
  • {{this.firstname}} {{this.lastname}}
  • - {{/each}} -
+

{{#each presence}} {{this.firstname}} {{this.lastname}}{{#unless @last}}, {{/unless}} {{/each}}

+

Abwesenheit ({{absent.length}})

+

{{#each absent}} {{this.firstname}} {{this.lastname}}{{#unless @last}}, {{/unless}} {{/each}}


Agenda

{{#each agenda}} diff --git a/src/viewmodel/admin/protocolPresence.models.ts b/src/viewmodel/admin/protocolPresence.models.ts index 9c10f59..c488968 100644 --- a/src/viewmodel/admin/protocolPresence.models.ts +++ b/src/viewmodel/admin/protocolPresence.models.ts @@ -2,6 +2,6 @@ import { MemberViewModel } from "./member.models"; export interface ProtocolPresenceViewModel { memberId: number; - member: MemberViewModel; + absent: boolean; protocolId: number; }