From d6a13d657ba265e42436fb6a7aba0b9b724df991 Mon Sep 17 00:00:00 2001
From: Julian Krauser <jkrauser209@gmail.com>
Date: Sat, 15 Mar 2025 09:05:22 +0100
Subject: [PATCH 1/4] member base template

---
 src/templates/member.body.template.html   | 49 +++++++++++++++++++++++
 src/templates/member.footer.template.html |  3 ++
 2 files changed, 52 insertions(+)
 create mode 100644 src/templates/member.body.template.html
 create mode 100644 src/templates/member.footer.template.html

diff --git a/src/templates/member.body.template.html b/src/templates/member.body.template.html
new file mode 100644
index 0000000..9129600
--- /dev/null
+++ b/src/templates/member.body.template.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>Mitglied</title>
+  </head>
+  <body>
+    <h1>{{member.lastname}} {{member.firstname}}{{#if member.nameaffix}} - {{member.nameaffix}}{{/if}}</h1>
+    <p>Mitglieds-Ausdruck Stand {{today}}</p>
+    <br />
+
+    <h2>Agenda</h2>
+    {{#each ---}}
+    <div>
+      <h3></h3>
+      <span></span>
+    </div>
+    <br />
+    {{/each}}
+  </body>
+  <style>
+    h2,
+    h3,
+    p,
+    span,
+    ul,
+    li {
+      padding: 0;
+      margin: 0;
+    }
+
+    h1,
+    h2 {
+      color: #990b00;
+    }
+
+    h2 {
+      margin-bottom: 5px;
+    }
+
+    table,
+    th,
+    td {
+      border: 1px solid black;
+      border-collapse: collapse;
+      text-align: start;
+    }
+  </style>
+</html>
diff --git a/src/templates/member.footer.template.html b/src/templates/member.footer.template.html
new file mode 100644
index 0000000..7a3190b
--- /dev/null
+++ b/src/templates/member.footer.template.html
@@ -0,0 +1,3 @@
+<div style="font-size: 10px; text-align: center; width: 100%; color: #888">
+  Seite <span class="pageNumber"></span> von <span class="totalPages"></span>
+</div>
-- 
2.45.3


From 2f72cc392562c54fec0c0d0c7f4e214dcfdc3298 Mon Sep 17 00:00:00 2001
From: Julian Krauser <jkrauser209@gmail.com>
Date: Sun, 16 Mar 2025 17:42:51 +0100
Subject: [PATCH 2/4] template + route & controller

---
 src/controller/admin/club/memberController.ts | 38 ++++++++++++
 src/routes/admin/club/member.ts               |  5 ++
 src/templates/member.body.template.html       | 62 +++++++++++++++++--
 3 files changed, 99 insertions(+), 6 deletions(-)

diff --git a/src/controller/admin/club/memberController.ts b/src/controller/admin/club/memberController.ts
index 91c51f8..86bea81 100644
--- a/src/controller/admin/club/memberController.ts
+++ b/src/controller/admin/club/memberController.ts
@@ -118,6 +118,44 @@ export async function getMemberStatisticsById(req: Request, res: Response): Prom
   res.json(MemberFactory.mapToMemberStatistic(member));
 }
 
+/**
+ * @description get member printout by id
+ * @param req {Request} Express req object
+ * @param res {Response} Express res object
+ * @returns {Promise<*>}
+ */
+export async function getMemberPrintoutById(req: Request, res: Response): Promise<any> {
+  const memberId = req.params.id;
+  let member = await MemberService.getById(memberId);
+  let memberships = await MembershipService.getAll(memberId);
+  let awards = await MemberAwardService.getAll(memberId);
+  let qualifications = await MemberQualificationService.getAll(memberId);
+  let positions = await MemberExecutivePositionService.getAll(memberId);
+  let communications = await CommunicationService.getAll(memberId);
+
+  let pdf = await PdfExport.renderFile({
+    title: "Mitglieder-Ausdruck",
+    template: "member",
+    saveToDisk: false,
+    data: {
+      member,
+      memberships,
+      awards,
+      qualifications,
+      positions,
+      communications,
+    },
+  });
+
+  let pdfbuffer = Buffer.from(pdf);
+
+  res.setHeader("Content-Type", "application/pdf");
+  res.setHeader("Content-Length", pdfbuffer.byteLength);
+  res.setHeader("Content-Disposition", "inline; filename=preview.pdf");
+
+  res.send(pdfbuffer);
+}
+
 /**
  * @description get memberships by member
  * @param req {Request} Express req object
diff --git a/src/routes/admin/club/member.ts b/src/routes/admin/club/member.ts
index 5f9d032..541487e 100644
--- a/src/routes/admin/club/member.ts
+++ b/src/routes/admin/club/member.ts
@@ -21,6 +21,7 @@ import {
   getExecutivePositionByMemberAndRecord,
   getExecutivePositionsByMember,
   getMemberById,
+  getMemberPrintoutById,
   getMembersByIds,
   getMembershipByMemberAndRecord,
   getMembershipsByMember,
@@ -55,6 +56,10 @@ router.get("/:id/statistics", async (req: Request, res: Response) => {
   await getMemberStatisticsById(req, res);
 });
 
+router.get("/:id/print", async (req: Request, res: Response) => {
+  await getMemberPrintoutById(req, res);
+});
+
 router.get("/print/namelist", async (req: Request, res: Response) => {
   await createMemberPrintoutList(req, res);
 });
diff --git a/src/templates/member.body.template.html b/src/templates/member.body.template.html
index 9129600..a777c2a 100644
--- a/src/templates/member.body.template.html
+++ b/src/templates/member.body.template.html
@@ -5,18 +5,68 @@
     <title>Mitglied</title>
   </head>
   <body>
-    <h1>{{member.lastname}} {{member.firstname}}{{#if member.nameaffix}} - {{member.nameaffix}}{{/if}}</h1>
+    <h1>
+      {{member.salutation}} {{member.lastname}} {{member.firstname}}{{#if member.nameaffix}} -
+      {{member.nameaffix}}{{/if}}
+    </h1>
     <p>Mitglieds-Ausdruck Stand {{today}}</p>
     <br />
 
-    <h2>Agenda</h2>
-    {{#each ---}}
+    <p>geboren: {{member.birthdate}}</p>
+    <p>eingetreten: {{member.firstMembershipEntry.start}}</p>
+    {{#if member.lastMembershipEntry}}
+    <p>ausgetreten: {{member.lastMembershipEntry.end}}</p>
+    {{/if}} {{#if memberships.length}}
+    <br />
+    <h2>Mitgliedschaften</h2>
+    {{#each memberships}}
     <div>
-      <h3></h3>
-      <span></span>
+      <h3>{{this.status.status}}: {{this.start}} - {{this.end ?? 'heute'}}</h3>
+      {{#if this.terminationReason}}
+      <p>beendet, weil:{{this.terminationReason}}</p>
+      {{/if}}
     </div>
     <br />
-    {{/each}}
+    {{/each}} {{/if}} {{#if memberships.length}}
+    <br />
+    <h2>Vereinsämter</h2>
+    {{#each positions}}
+    <div>
+      <h3>{{this.executivePosition.position}}: {{this.start}} - {{this.end ?? 'heute'}}</h3>
+      {{#if this.note}}
+      <p>Notiz: {{this.note}}</p>
+      {{/if}}
+    </div>
+    <br />
+    {{/each}} {{/if}} {{#if awards.length}}
+    <br />
+    <h2>Auszeichnungen</h2>
+    {{#each awards}}
+    <div>
+      <h3>{{this.award.award}}: {{this.date}}</h3>
+      {{#if this.given}}
+      <p>wurde vergeben</p>
+      {{else}}
+      <p>wurde verwehrt / zurückgewiesen</p>
+      {{/if}} {{#if this.note}}
+      <p>Notiz: {{this.note}}</p>
+      {{/if}}
+    </div>
+    <br />
+    {{/each}} {{/if}} {{#if qualifications.length}}
+    <br />
+    <h2>Qualifikationen</h2>
+    {{#each qualifications}}
+    <div>
+      <h3>{{this.qualification.qualification}}: {{this.date}}</h3>
+      {{#if this.terminationReason}}
+      <p>beendet, weil:{{this.terminationReason}}</p>
+      {{/if}} {{/if}} {{#if this.note}}
+      <p>Notiz: {{this.note}}</p>
+      {{/if}}
+    </div>
+    <br />
+    {{/each}} {{/if}}
   </body>
   <style>
     h2,
-- 
2.45.3


From de5e4afffb3538b25034ac2138b57de3e706cded Mon Sep 17 00:00:00 2001
From: Julian Krauser <jkrauser209@gmail.com>
Date: Mon, 17 Mar 2025 11:56:17 +0100
Subject: [PATCH 3/4] demo data & template usage

---
 src/controller/admin/club/memberController.ts |   6 +
 src/data-source.ts                            |   3 +-
 src/demodata/member.data.ts                   | 138 ++++++++++++++++++
 src/helpers/demoDataHelper.ts                 |   7 +-
 .../1742207245862-memberPrintoutTemplates.ts  |  20 +++
 src/templates/member.body.template.html       |  10 +-
 6 files changed, 176 insertions(+), 8 deletions(-)
 create mode 100644 src/demodata/member.data.ts
 create mode 100644 src/migrations/1742207245862-memberPrintoutTemplates.ts

diff --git a/src/controller/admin/club/memberController.ts b/src/controller/admin/club/memberController.ts
index 86bea81..b44f690 100644
--- a/src/controller/admin/club/memberController.ts
+++ b/src/controller/admin/club/memberController.ts
@@ -138,6 +138,12 @@ export async function getMemberPrintoutById(req: Request, res: Response): Promis
     template: "member",
     saveToDisk: false,
     data: {
+      today: new Date().toLocaleDateString("de-DE", {
+        weekday: "long",
+        day: "2-digit",
+        month: "2-digit",
+        year: "numeric",
+      }),
       member,
       memberships,
       awards,
diff --git a/src/data-source.ts b/src/data-source.ts
index 6babcb1..87af7a0 100644
--- a/src/data-source.ts
+++ b/src/data-source.ts
@@ -47,6 +47,7 @@ import { salutation } from "./entity/configuration/salutation";
 
 import { BackupAndResetDatabase1738166124200 } from "./migrations/1738166124200-BackupAndResetDatabase";
 import { CreateSchema1738166167472 } from "./migrations/1738166167472-CreateSchema";
+import { MemberPrintoutTemplates1742207245862 } from "./migrations/1742207245862-memberPrintoutTemplates";
 
 const dataSource = new DataSource({
   type: DB_TYPE as any,
@@ -100,7 +101,7 @@ const dataSource = new DataSource({
     webapi,
     webapiPermission,
   ],
-  migrations: [BackupAndResetDatabase1738166124200, CreateSchema1738166167472],
+  migrations: [BackupAndResetDatabase1738166124200, CreateSchema1738166167472, MemberPrintoutTemplates1742207245862],
   migrationsRun: true,
   migrationsTransactionMode: "each",
   subscribers: [],
diff --git a/src/demodata/member.data.ts b/src/demodata/member.data.ts
new file mode 100644
index 0000000..150ae00
--- /dev/null
+++ b/src/demodata/member.data.ts
@@ -0,0 +1,138 @@
+import { communication } from "../entity/club/member/communication";
+import { member } from "../entity/club/member/member";
+import { memberAwards } from "../entity/club/member/memberAwards";
+import { memberExecutivePositions } from "../entity/club/member/memberExecutivePositions";
+import { memberQualifications } from "../entity/club/member/memberQualifications";
+import { membership } from "../entity/club/member/membership";
+
+export const memberDemoData: {
+  member: Partial<member>;
+  today: string;
+  memberships: Array<Partial<membership>>;
+  awards: Array<Partial<memberAwards>>;
+  qualifications: Array<Partial<memberQualifications>>;
+  positions: Array<Partial<memberExecutivePositions>>;
+  communications: Array<Partial<communication>>;
+} = {
+  today: "Montag, 17.03.2025",
+  member: {
+    id: "2fe205f8-8ae8-4218-839f-af3456d3f39d",
+    firstname: "Julian",
+    lastname: "Krauser",
+    nameaffix: "",
+    //@ts-ignore
+    birthdate: "2003-09-20",
+    internalId: "1312",
+    salutationId: 47,
+    salutation: { id: 47, salutation: "Herr", members: [] },
+    firstMembershipEntry: {
+      id: 8681,
+      //@ts-ignore
+      start: "2017-11-13",
+      end: null,
+      terminationReason: null,
+      memberId: "2fe205f8-8ae8-4218-839f-af3456d3f39d",
+      statusId: 34,
+      status: { id: 34, status: "aktiv", memberships: [] },
+      //@ts-ignore
+      member: {},
+    },
+    lastMembershipEntry: {
+      id: 8681,
+      //@ts-ignore
+      start: "2017-11-13",
+      end: null,
+      terminationReason: null,
+      memberId: "2fe205f8-8ae8-4218-839f-af3456d3f39d",
+      statusId: 34,
+      status: { id: 34, status: "aktiv", memberships: [] },
+      //@ts-ignore
+      member: {},
+    },
+    preferredCommunication: [
+      {
+        id: 7031,
+        preferred: true,
+        isSMSAlarming: false,
+        isSendNewsletter: true,
+        mobile: "",
+        email: "julian.krauser@jk-effects.com",
+        postalCode: "",
+        city: "",
+        street: "",
+        streetNumber: 0,
+        streetNumberAddition: "",
+        memberId: "2fe205f8-8ae8-4218-839f-af3456d3f39d",
+        typeId: 46,
+        type: { id: 46, type: "Email", useColumns: ["email"], communications: [] },
+        // @ts-ignore
+        member: {},
+      },
+    ],
+    smsAlarming: [],
+    sendNewsletter: {
+      id: 7031,
+      preferred: true,
+      isSMSAlarming: false,
+      isSendNewsletter: true,
+      mobile: "",
+      email: "julian.krauser@jk-effects.com                             ",
+      postalCode: "",
+      city: "",
+      street: "",
+      streetNumber: 0,
+      streetNumberAddition: "",
+      memberId: "2fe205f8-8ae8-4218-839f-af3456d3f39d",
+      typeId: 46,
+      type: { id: 46, type: "Email", useColumns: ["email"], communications: [] },
+      // @ts-ignore
+      member: {},
+    },
+  },
+  memberships: [
+    {
+      id: 8681,
+      //@ts-ignore
+      start: "2017-11-13",
+      end: null,
+      terminationReason: null,
+      memberId: "2fe205f8-8ae8-4218-839f-af3456d3f39d",
+      statusId: 34,
+      status: { id: 34, status: "aktiv", memberships: [] },
+    },
+  ],
+  awards: [],
+  qualifications: [],
+  positions: [
+    {
+      id: 696,
+      note: "",
+      //@ts-ignore
+      start: "2025-01-06",
+      end: null,
+      memberId: "2fe205f8-8ae8-4218-839f-af3456d3f39d",
+      executivePositionId: 192,
+      executivePosition: { id: 192, position: "Schriftführer", members: [] },
+    },
+  ],
+  communications: [
+    {
+      id: 7031,
+      preferred: true,
+      isSMSAlarming: false,
+      isSendNewsletter: true,
+      mobile: "",
+      email: "julian.krauser@jk-effects.com",
+      postalCode: "",
+      city: "",
+      street: "",
+      streetNumber: 0,
+      streetNumberAddition: "",
+      memberId: "2fe205f8-8ae8-4218-839f-af3456d3f39d",
+      typeId: 46,
+      //@ts-ignore
+      member: {},
+      type: { id: 46, type: "Email", useColumns: ["email"], communications: [] },
+    },
+  ],
+};
diff --git a/src/helpers/demoDataHelper.ts b/src/helpers/demoDataHelper.ts
index 3187ca1..45020a4 100644
--- a/src/helpers/demoDataHelper.ts
+++ b/src/helpers/demoDataHelper.ts
@@ -1,10 +1,11 @@
 import { newsletterDemoData } from "../demodata/newsletter.data";
 import { protocolDemoData } from "../demodata/protocol.data";
 import { PermissionModule } from "../type/permissionTypes";
-import {memberlistDemoData} from "../demodata/member.list.data";
+import { memberlistDemoData } from "../demodata/member.list.data";
+import { memberDemoData } from "../demodata/member.data";
 
 export abstract class DemoDataHelper {
-  static getData(scope: `${PermissionModule}`|`${PermissionModule}.${string}`) {
+  static getData(scope: `${PermissionModule}` | `${PermissionModule}.${string}`) {
     switch (scope) {
       case "protocol":
         return protocolDemoData;
@@ -12,6 +13,8 @@ export abstract class DemoDataHelper {
         return newsletterDemoData;
       case "member.list":
         return memberlistDemoData;
+      case "member":
+        return memberDemoData;
       default:
         return {};
     }
diff --git a/src/migrations/1742207245862-memberPrintoutTemplates.ts b/src/migrations/1742207245862-memberPrintoutTemplates.ts
new file mode 100644
index 0000000..a799d78
--- /dev/null
+++ b/src/migrations/1742207245862-memberPrintoutTemplates.ts
@@ -0,0 +1,20 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+import { templateUsage } from "../entity/configuration/templateUsage";
+
+export class MemberPrintoutTemplates1742207245862 implements MigrationInterface {
+  name = "MemberPrintoutTemplates1742207245862";
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.manager
+      .createQueryBuilder()
+      .insert()
+      .into(templateUsage)
+      .values([{ scope: "member" }])
+      .orUpdate(["headerId", "bodyId", "footerId", "headerHeight", "footerHeight"], ["scope"])
+      .execute();
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.manager.createQueryBuilder().delete().from(templateUsage).where({ scope: "member" }).execute();
+  }
+}
diff --git a/src/templates/member.body.template.html b/src/templates/member.body.template.html
index a777c2a..5d499ae 100644
--- a/src/templates/member.body.template.html
+++ b/src/templates/member.body.template.html
@@ -6,7 +6,7 @@
   </head>
   <body>
     <h1>
-      {{member.salutation}} {{member.lastname}} {{member.firstname}}{{#if member.nameaffix}} -
+      {{member.salutation.salutation}} {{member.lastname}} {{member.firstname}}{{#if member.nameaffix}} -
       {{member.nameaffix}}{{/if}}
     </h1>
     <p>Mitglieds-Ausdruck Stand {{today}}</p>
@@ -14,14 +14,14 @@
 
     <p>geboren: {{member.birthdate}}</p>
     <p>eingetreten: {{member.firstMembershipEntry.start}}</p>
-    {{#if member.lastMembershipEntry}}
+    {{#if member.lastMembershipEntry?.end}}
     <p>ausgetreten: {{member.lastMembershipEntry.end}}</p>
     {{/if}} {{#if memberships.length}}
     <br />
     <h2>Mitgliedschaften</h2>
     {{#each memberships}}
     <div>
-      <h3>{{this.status.status}}: {{this.start}} - {{this.end ?? 'heute'}}</h3>
+      <h3>{{this.status.status}}: {{this.start}} - {{#if this.end}}{{this.end}}{{else}}heute{{/if}}</h3>
       {{#if this.terminationReason}}
       <p>beendet, weil:{{this.terminationReason}}</p>
       {{/if}}
@@ -32,7 +32,7 @@
     <h2>Vereinsämter</h2>
     {{#each positions}}
     <div>
-      <h3>{{this.executivePosition.position}}: {{this.start}} - {{this.end ?? 'heute'}}</h3>
+      <h3>{{this.executivePosition.position}}: {{this.start}} - {{#if this.end}}{{this.end}}{{else}}heute{{/if}}</h3>
       {{#if this.note}}
       <p>Notiz: {{this.note}}</p>
       {{/if}}
@@ -61,7 +61,7 @@
       <h3>{{this.qualification.qualification}}: {{this.date}}</h3>
       {{#if this.terminationReason}}
       <p>beendet, weil:{{this.terminationReason}}</p>
-      {{/if}} {{/if}} {{#if this.note}}
+      {{/if}} {{#if this.note}}
       <p>Notiz: {{this.note}}</p>
       {{/if}}
     </div>
-- 
2.45.3


From 14957c2abc24bb1caec43c63852dda56c2b71d0c Mon Sep 17 00:00:00 2001
From: Julian Krauser <jkrauser209@gmail.com>
Date: Tue, 18 Mar 2025 10:26:05 +0100
Subject: [PATCH 4/4] template improvements & handlebars helpers

---
 src/controller/admin/club/memberController.ts |  7 +----
 src/helpers/templateHelper.ts                 | 27 +++++++++++++++++++
 src/templates/member.body.template.html       | 23 +++++++++-------
 3 files changed, 41 insertions(+), 16 deletions(-)

diff --git a/src/controller/admin/club/memberController.ts b/src/controller/admin/club/memberController.ts
index b44f690..a31e514 100644
--- a/src/controller/admin/club/memberController.ts
+++ b/src/controller/admin/club/memberController.ts
@@ -138,12 +138,7 @@ export async function getMemberPrintoutById(req: Request, res: Response): Promis
     template: "member",
     saveToDisk: false,
     data: {
-      today: new Date().toLocaleDateString("de-DE", {
-        weekday: "long",
-        day: "2-digit",
-        month: "2-digit",
-        year: "numeric",
-      }),
+      today: new Date(),
       member,
       memberships,
       awards,
diff --git a/src/helpers/templateHelper.ts b/src/helpers/templateHelper.ts
index a8a14b1..af1c832 100644
--- a/src/helpers/templateHelper.ts
+++ b/src/helpers/templateHelper.ts
@@ -4,6 +4,33 @@ import TemplateUsageService from "../service/configuration/templateUsageService"
 import Handlebars, { template } from "handlebars";
 import { FileSystemHelper } from "./fileSystemHelper";
 
+Handlebars.registerHelper("date", function (aString) {
+  return new Date(aString).toLocaleDateString("de-DE", {
+    day: "2-digit",
+    month: "2-digit",
+    year: "numeric",
+  });
+});
+
+Handlebars.registerHelper("longdate", function (aString) {
+  return new Date(aString).toLocaleDateString("de-DE", {
+    weekday: "long",
+    day: "2-digit",
+    month: "2-digit",
+    year: "numeric",
+  });
+});
+
+Handlebars.registerHelper("datetime", function (aString) {
+  return new Date(aString).toLocaleDateString("de-DE", {
+    day: "2-digit",
+    month: "2-digit",
+    year: "numeric",
+    hour: "2-digit",
+    minute: "2-digit",
+  });
+});
+
 export abstract class TemplateHelper {
   static getTemplateFromFile(template: string) {
     return FileSystemHelper.readTemplateFile(`/src/templates/${template}.template.html`);
diff --git a/src/templates/member.body.template.html b/src/templates/member.body.template.html
index 5d499ae..67f8821 100644
--- a/src/templates/member.body.template.html
+++ b/src/templates/member.body.template.html
@@ -6,33 +6,36 @@
   </head>
   <body>
     <h1>
-      {{member.salutation.salutation}} {{member.lastname}} {{member.firstname}}{{#if member.nameaffix}} -
+      {{member.salutation.salutation}} {{member.lastname}}, {{member.firstname}}{{#if member.nameaffix}} -
       {{member.nameaffix}}{{/if}}
     </h1>
-    <p>Mitglieds-Ausdruck Stand {{today}}</p>
+    <p>Mitglieds-Ausdruck Stand {{longdate today}}</p>
     <br />
 
-    <p>geboren: {{member.birthdate}}</p>
-    <p>eingetreten: {{member.firstMembershipEntry.start}}</p>
+    <p>geboren: {{date member.birthdate}}</p>
+    <p>eingetreten: {{date member.firstMembershipEntry.start}}</p>
     {{#if member.lastMembershipEntry?.end}}
-    <p>ausgetreten: {{member.lastMembershipEntry.end}}</p>
+    <p>ausgetreten: {{date member.lastMembershipEntry.end}}</p>
     {{/if}} {{#if memberships.length}}
     <br />
     <h2>Mitgliedschaften</h2>
     {{#each memberships}}
     <div>
-      <h3>{{this.status.status}}: {{this.start}} - {{#if this.end}}{{this.end}}{{else}}heute{{/if}}</h3>
+      <h3>{{this.status.status}}: {{date this.start}} bis {{#if this.end}}{{date this.end}}{{else}}heute{{/if}}</h3>
       {{#if this.terminationReason}}
       <p>beendet, weil:{{this.terminationReason}}</p>
       {{/if}}
     </div>
     <br />
-    {{/each}} {{/if}} {{#if memberships.length}}
+    {{/each}} {{/if}} {{#if positions.length}}
     <br />
     <h2>Vereinsämter</h2>
     {{#each positions}}
     <div>
-      <h3>{{this.executivePosition.position}}: {{this.start}} - {{#if this.end}}{{this.end}}{{else}}heute{{/if}}</h3>
+      <h3>
+        {{this.executivePosition.position}}: {{date this.start}} bis {{#if this.end}}{{date
+        this.end}}{{else}}heute{{/if}}
+      </h3>
       {{#if this.note}}
       <p>Notiz: {{this.note}}</p>
       {{/if}}
@@ -43,7 +46,7 @@
     <h2>Auszeichnungen</h2>
     {{#each awards}}
     <div>
-      <h3>{{this.award.award}}: {{this.date}}</h3>
+      <h3>{{this.award.award}}: {{date this.date}}</h3>
       {{#if this.given}}
       <p>wurde vergeben</p>
       {{else}}
@@ -58,7 +61,7 @@
     <h2>Qualifikationen</h2>
     {{#each qualifications}}
     <div>
-      <h3>{{this.qualification.qualification}}: {{this.date}}</h3>
+      <h3>{{this.qualification.qualification}}: {{date this.date}}</h3>
       {{#if this.terminationReason}}
       <p>beendet, weil:{{this.terminationReason}}</p>
       {{/if}} {{#if this.note}}
-- 
2.45.3