Compare commits

...

9 commits

Author SHA1 Message Date
32c5776fe8 Altersgrenze 2025-01-09 20:56:10 +01:00
bd13a775c4 member communication 2025-01-07 11:46:31 +01:00
93afdec274 member communication 2025-01-07 11:46:31 +01:00
2398084764 member communication 2025-01-07 11:46:31 +01:00
c8b45512c5 membership 2025-01-07 11:46:31 +01:00
c7b0adee07 membership 2025-01-07 11:46:31 +01:00
1f95f8f64d import from old database 2025-01-07 11:46:31 +01:00
3ea7c423c9 import from old database 2025-01-07 11:46:31 +01:00
3b584767f3 force exposed prod port to 5000 2025-01-07 11:46:31 +01:00
4 changed files with 590 additions and 1 deletions

View file

@ -35,12 +35,13 @@ WORKDIR /app
RUN mkdir -p /app/export RUN mkdir -p /app/export
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
ENV SERVER_PORT=5000
COPY --from=build /app/src/templates /app/src/templates COPY --from=build /app/src/templates /app/src/templates
COPY --from=build /app/dist /app/dist COPY --from=build /app/dist /app/dist
COPY --from=build /app/node_modules /app/node_modules COPY --from=build /app/node_modules /app/node_modules
COPY --from=build /app/package.json /app/package.json COPY --from=build /app/package.json /app/package.json
EXPOSE 5000 EXPOSE ${SERVER_PORT}
CMD [ "npm", "run", "start" ] CMD [ "npm", "run", "start" ]

View file

@ -30,8 +30,10 @@
"handlebars": "^4.7.8", "handlebars": "^4.7.8",
"ics": "^3.8.1", "ics": "^3.8.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"knex": "^3.1.0",
"moment": "^2.30.1", "moment": "^2.30.1",
"ms": "^2.1.3", "ms": "^2.1.3",
"mssql": "^10.0.4",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"node-schedule": "^2.1.1", "node-schedule": "^2.1.1",
"nodemailer": "^6.9.14", "nodemailer": "^6.9.14",

584
src/routes/fillfromold.ts Normal file
View file

@ -0,0 +1,584 @@
import express from "express";
import knex from "knex";
import moment from "moment";
import AwardService from "../service/settings/awardService";
import { CreateAwardCommand, DeleteAwardCommand } from "../command/settings/award/awardCommand";
import AwardCommandHandler from "../command/settings/award/awardCommandHandler";
import MemberService from "../service/club/member/memberService";
import { CreateMemberCommand, DeleteMemberCommand } from "../command/club/member/memberCommand";
import MemberCommandHandler from "../command/club/member/memberCommandHandler";
import MemberAwardCommandHandler from "../command/club/member/memberAwardCommandHandler";
import { CreateMemberAwardCommand, DeleteMemberAwardCommand } from "../command/club/member/memberAwardCommand";
import MemberAwardService from "../service/club/member/memberAwardService";
import CommunicationService from "../service/club/member/communicationService";
import { CreateCommunicationCommand, DeleteCommunicationCommand } from "../command/club/member/communicationCommand";
import CommunicationCommandHandler from "../command/club/member/communicationCommandHandler";
import MembershipService from "../service/club/member/membershipService";
import {
CreateMembershipCommand,
DeleteMembershipCommand,
UpdateMembershipCommand,
} from "../command/club/member/membershipCommand";
import MembershipCommandHandler from "../command/club/member/membershipCommandHandler";
import MemberExecutivePositionService from "../service/club/member/memberExecutivePositionService";
import {
CreateMemberExecutivePositionCommand,
DeleteMemberExecutivePositionCommand,
} from "../command/club/member/memberExecutivePositionCommand";
import MemberExecutivePositionCommandHandler from "../command/club/member/memberExecutivePositionCommandHandler";
import CommunicationTypeService from "../service/settings/communicationTypeService";
import {
CreateCommunicationTypeCommand,
DeleteCommunicationTypeCommand,
} from "../command/settings/communicationType/communicationTypeCommand";
import CommunicationTypeCommandHandler from "../command/settings/communicationType/communicationTypeCommandHandler";
import ExecutivePositionService from "../service/settings/executivePositionService";
import {
CreateExecutivePositionCommand,
DeleteExecutivePositionCommand,
} from "../command/settings/executivePosition/executivePositionCommand";
import ExecutivePositionCommandHandler from "../command/settings/executivePosition/executivePositionCommandHandler";
import MembershipStatusService from "../service/settings/membershipStatusService";
import {
CreateMembershipStatusCommand,
DeleteMembershipStatusCommand,
} from "../command/settings/membershipStatus/membershipStatusCommand";
import MembershipStatusCommandHandler from "../command/settings/membershipStatus/membershipStatusCommandHandler";
var router = express.Router({ mergeParams: true });
router.get("/", async (req, res) => {
const dbhost = process.env.OLD_DB_HOST;
const dbport = Number(process.env.OLD_DB_PORT);
const dbuser = process.env.OLD_DB_USER;
const dbuserpass = process.env.OLD_DB_USERPASS;
const dbname = process.env.OLD_DB_NAME;
const db = knex({
client: 'mssql',
connection: {
host: dbhost,
port: dbport,
user: dbuser,
password: dbuserpass,
database: dbname,
options: {
trustServerCertificate: true,
enableArithAbort: true,
encrypt: true
}
},
debug: false,
pool: {
min: 0, max: 30
}
});
let queryResult = await db('Mitglieder');
console.log(`Read ${queryResult.length} Mitglieder`);
// Delete members
let [members, total] = await MemberService.getAll(0, 9999);
for (const m of members) {
// delete all member awards of this member
let memberAwards = await MemberAwardService.getAll(m.id);
for (const award of memberAwards) {
let deleteMemberAward: DeleteMemberAwardCommand = {
id: award.id, memberId: m.id
};
console.log(`Deleting award ${award.id} from ${m.firstname} ${m.lastname}`);
await MemberAwardCommandHandler.delete(deleteMemberAward)
}
// Delete communication from this member
let communications = await CommunicationService.getAll(m.id);
for (const comm of communications) {
let deleteCommunication: DeleteCommunicationCommand = {
id: comm.id, memberId: 0
};
console.log(`Delete communication ${comm.type} from ${m.firstname} ${m.lastname}`);
await CommunicationCommandHandler.delete(deleteCommunication);
}
// Delete membership entries
let memberships = await MembershipService.getAll(m.id);
for (const membership of memberships) {
let deleteMembership: DeleteMembershipCommand = {
id: membership.id, memberId: m.id
};
console.log(`Delete membership ${membership.id} from ${m.firstname} ${m.lastname}`);
await MembershipCommandHandler.delete(deleteMembership);
}
// Delete membership positions entries
let memberExecutivePositions = await MemberExecutivePositionService.getAll(m.id);
for (const memberPos of memberExecutivePositions) {
let deleteMemberExecutivePosition: DeleteMemberExecutivePositionCommand = {
id: memberPos.id, memberId: m.id
};
console.log(`Delete position ${deleteMemberExecutivePosition.id} from ${m.firstname} ${m.lastname}`);
await MemberExecutivePositionCommandHandler.delete(deleteMemberExecutivePosition);
}
// delete member itself
let deleteMember: DeleteMemberCommand = {
id: m.id,
};
console.log(`Delete member ${m.id}`);
await MemberCommandHandler.delete(deleteMember);
}
// Delete awards
let awards = await AwardService.getAll();
for (const award of awards) {
let deleteAward: DeleteAwardCommand = {
id: award.id,
};
console.log(`Delete award ${award.id}`);
await AwardCommandHandler.delete(deleteAward);
}
// Delete communication types
let commTypes = await CommunicationTypeService.getAll();
for (const commType of commTypes) {
let deleteCommunicationType: DeleteCommunicationTypeCommand = {
id: commType.id,
};
console.log(`Delete communication type ${commType.type}`);
await CommunicationTypeCommandHandler.delete(deleteCommunicationType);
}
// Delete postions
let positions = await ExecutivePositionService.getAll();
for (const position of positions) {
let deletePosition: DeleteExecutivePositionCommand = {
id: position.id,
};
console.log(`Delete position ${position.position}`);
await ExecutivePositionCommandHandler.delete(deletePosition);
}
// Delete membership states
let states = await MembershipStatusService.getAll();
for (const state of states) {
let deleteMembershipStatus: DeleteMembershipStatusCommand = {
id: state.id
};
console.log(`Delete membership status ${state.status}`);
await MembershipStatusCommandHandler.delete(deleteMembershipStatus);
}
// Create Awards
const sAwardsById :any = {};
const sAwardsByName :any = {};
const awardNames = ['Ehrennadel Silber', 'Ehrennadel Gold', 'Ehrenkreuz Silber', 'Ehrenkreuz Gold', 'Ehrenmitgliedschaft', 'Ehrung 25 Jahre aktiv', 'Ehrung 40 Jahre aktiv'];
for (const awardName of awardNames) {
let createAward: CreateAwardCommand = {
award: awardName,
};
const awardId = await AwardCommandHandler.create(createAward);
console.log(`Created award ${awardName} with id ${awardId}`);
sAwardsById[awardId] = awardName;
sAwardsByName[awardName] = awardId;
}
// Create communication types
const createCommunicationTypeMail: CreateCommunicationTypeCommand = {
type: "Post", useColumns: ['city','street','streetNumber','streetNumberAddition']
}
const createCommunicationTypeEMail: CreateCommunicationTypeCommand = {
type: "Email", useColumns: ['email']
}
const createCommunicationTypeSMS: CreateCommunicationTypeCommand = {
type: "SMS", useColumns: ['mobile']
}
const createCommunicationTypeWhatsApp: CreateCommunicationTypeCommand = {
type: "WhatsApp", useColumns: ['mobile']
}
const createCommTypes = [createCommunicationTypeMail, createCommunicationTypeEMail, createCommunicationTypeSMS, createCommunicationTypeWhatsApp];
const sCommTypesByName: any = {};
const sCommTypesById: any = {};
for (const createCommType of createCommTypes) {
const commTypeId = await CommunicationTypeCommandHandler.create(createCommType);
console.log(`Create communication type ${createCommType.type} with id ${commTypeId}`);
sCommTypesByName[createCommType.type] = commTypeId;
sCommTypesById[commTypeId] = createCommType.type;
}
// Create positions
const sFunktionenByOldId :any = {};
let positionsResult: any[] = await db('Funktionen');
for (const position of positionsResult) {
const posName: string = position.Name.trim();
let createExecutivePosition: CreateExecutivePositionCommand = {
position: posName,
}
sFunktionenByOldId[position.IdFunktion] = await ExecutivePositionCommandHandler.create(createExecutivePosition);
}
// create membership states
let sStatesByName : any = {};
for (const state of ['aktiv', 'passiv', 'fördernd']) {
let createMembershipStatus: CreateMembershipStatusCommand = {
status: state
}
sStatesByName[state] = await MembershipStatusCommandHandler.create(createMembershipStatus);
}
// Add all members
for (const queryResultElement of queryResult) {
const ineternalID = queryResultElement.ID
let nameAffix: string = "";
let nn = queryResultElement.Nachname.trim();
if (nn.endsWith(", jun.")) {
nameAffix = "jun.";
nn = nn.substring(0, nn.length - 6);
}
if (nn.endsWith(", sen.")) {
nameAffix = "sen.";
nn = nn.substring(0, nn.length - 6);
}
// before 1.1.2009: Altersobergrenze für Aktive: 60
const limit60 = moment('2009-01-01');
// before 1.7.2017: 63
const limit63 = moment('2017-07-01');
// from 1.7.2017: 65
const alter60 = moment(queryResultElement.Geboren).add(60, 'years');
const alter63 = moment(queryResultElement.Geboren).add(63, 'years');
const alter65 = moment(queryResultElement.Geboren).add(65, 'years');
const eintritt = moment(queryResultElement.Eingetreten);
const verstorben = moment(queryResultElement.verstorben);
let newMember: CreateMemberCommand = {
salutation: queryResultElement.Anrede.trim(),
firstname: queryResultElement.Vorname.trim(),
lastname: nn,
nameaffix: nameAffix,
birthdate: queryResultElement.Geboren ? queryResultElement.Geboren : "1900-01-01",
internalId: queryResultElement.ID,
};
const memberId = await MemberCommandHandler.create(newMember);
console.log(`Created member ${newMember.firstname} ${newMember.lastname} with id ${memberId}`);
let stateId = sStatesByName["aktiv"];
if (!queryResultElement.Eingetreten) {
throw new Error('Eingetreten missing');
}
if (queryResultElement["Übergang Passiv"] && moment(queryResultElement["Übergang Passiv"]).format('L') === moment(queryResultElement.Eingetreten).format('L')) {
stateId = sStatesByName["fördernd"];
}
let start = queryResultElement.Eingetreten;
let createMembership: CreateMembershipCommand = {
memberId: memberId, start: start, statusId: stateId,
};
let ms1 = await MembershipCommandHandler.create(createMembership);
if (queryResultElement["Übergang Passiv"] && moment(queryResultElement["Übergang Passiv"]).format('L') !== moment(queryResultElement.Eingetreten).format('L')) {
// membership transitioned to passiv
// no knowledge about active time => assume active until age of 60 or 65
let end: Date = queryResultElement["Übergang Passiv"];
let terminationReason: string = "aktiv >> passiv";
let newStart = queryResultElement["Übergang Passiv"];
let newStateId = sStatesByName["passiv"];
if (queryResultElement.Geboren == null && queryResultElement.verzogenDatum) {
terminationReason = "verzogen";
end = queryResultElement.verzogenDatum
} else {
if (queryResultElement["Übergang Passiv"] < queryResultElement.Eingetreten) {
if (alter60.isBefore(limit60)) {
terminationReason = "passiv (Altersobergrenze: 60)";
end = alter60.toDate();
} else {
if (alter63.isBefore(limit63)) {
terminationReason = "passiv (Altersobergrenze: 63)";
end = alter63.toDate();
} else {
terminationReason = "passiv (Altersobergrenze: 65)";
end = alter65.toDate();
}
}
}
}
if (end) {
newStart = end;
if (!(queryResultElement.verstorben && moment(queryResultElement.verstorben).isBefore(moment(end)))) {
// finish current membership (but only if member was alive at this time)
let updateMembership: UpdateMembershipCommand = {
end: end,
id: ms1,
memberId: memberId,
start: start,
statusId: stateId,
terminationReason: terminationReason,
};
await MembershipCommandHandler.update(updateMembership);
// create new membership with passiv state
stateId = newStateId;
start = newStart;
let createMembership: CreateMembershipCommand = {
// internalId: queryResultElement.ID,
memberId: memberId,
start: start,
statusId: stateId,
};
ms1 = await MembershipCommandHandler.create(createMembership);
}
}
}
if (!queryResultElement["Übergang Passiv"] && queryResultElement.verstorben) {
let terminationReason;
let end;
let newStart;
let newStateId;
if (alter60.isBefore(limit60) && alter60.isBefore(verstorben)) {
if (alter60.isBefore(eintritt)) {
stateId = sStatesByName["passiv"];
} else {
terminationReason = "passiv (Altersobergrenze: 60)";
end = alter60.toDate();
newStart = end;
newStateId = sStatesByName["passiv"];
}
} else {
if (alter63.isBefore(limit63) && alter63.isBefore(verstorben)) {
if (alter63.isBefore(eintritt)) {
stateId = sStatesByName["passiv"];
} else {
terminationReason = "passiv (Altersobergrenze: 63)";
end = alter63.toDate();
newStart = end;
newStateId = sStatesByName["passiv"];
}
} else {
if (alter65.isBefore(verstorben)) {
if (alter65.isBefore(eintritt)) {
stateId = sStatesByName["passiv"];
} else {
terminationReason = "passiv (Altersobergrenze: 65)";
end = alter65.toDate();
newStart = end;
newStateId = sStatesByName["passiv"];
}
} else {
end = queryResultElement.verstorben;
terminationReason = "verstorben";
}
}
}
if (end) {
let updateMembership: UpdateMembershipCommand = {
end: end,
id: ms1,
memberId: memberId,
start: start,
statusId: stateId,
terminationReason: terminationReason,
};
await MembershipCommandHandler.update(updateMembership);
if (newStart) {
// create new membership with passiv state
stateId = newStateId;
start = newStart;
let createMembership: CreateMembershipCommand = {
memberId: memberId,
start: start,
statusId: stateId,
};
ms1 = await MembershipCommandHandler.create(createMembership);
}
}
}
if (queryResultElement.Ausgetreten) {
let updateMembership: UpdateMembershipCommand = {
end: queryResultElement.Ausgetreten,
id: ms1,
memberId: memberId,
start: start,
statusId: stateId,
terminationReason: "Austritt",
};
await MembershipCommandHandler.update(updateMembership);
}
if (queryResultElement.verstorben) {
let updateMembership: UpdateMembershipCommand = {
end: queryResultElement.verstorben,
id: ms1,
memberId: memberId,
start: start,
statusId: stateId,
terminationReason: "verstorben",
};
await MembershipCommandHandler.update(updateMembership);
}
if (!queryResultElement.Ausgetreten && !queryResultElement.verstorben && queryResultElement.verzogen) {
let end = new Date('2024-12-31');
if (queryResultElement.verzogenDatum) {
end = queryResultElement.verzogenDatum;
} else {
if (queryResultElement["Übergang Passiv"]) {
end = queryResultElement["Übergang Passiv"];
}
}
let updateMembership: UpdateMembershipCommand = {
end: end,
id: ms1,
memberId: memberId,
start: start,
statusId: stateId,
terminationReason: "verzogen",
};
await MembershipCommandHandler.update(updateMembership);
}
// add member's awards
for (const awardId in sAwardsById) {
const awardName = sAwardsById[awardId];
if (queryResultElement[awardName]) {
const awardDate = queryResultElement[awardName];
console.log(`Member ${newMember.firstname} ${newMember.lastname} got award ${awardName} at ${awardDate}`);
let given = true;
let note = "";
if (awardName === "Ehrung 25 Jahre aktiv" && queryResultElement["keineEhrung25JahreAktivGrund"] !== null) {
given = false;
note = queryResultElement["keineEhrung25JahreAktivGrund"].trim();
}
if (awardName === "Ehrennadel Silber" && queryResultElement["keineEhrennadelSilberGrund"] !== null) {
given = false;
note = queryResultElement["keineEhrennadelSilberGrund"].trim();
}
if (awardName === "Ehrennadel Gold" && queryResultElement["keineEhrennadelGoldGrund"] !== null) {
given = false;
note = queryResultElement["keineEhrennadelGoldGrund"].trim();
}
let newMemberAward: CreateMemberAwardCommand = {
awardId: parseInt(awardId), date: awardDate, given: given, memberId: memberId, note: note,
};
await MemberAwardCommandHandler.create(newMemberAward);
}
}
// add member's executive positions (if there are any)
let memberPositionsResult: any[] = await db('MitgliedFunktionen').where('MitgliedFunktionen.IdMitglied', '=', ineternalID);
for (const position of memberPositionsResult) {
const start = position.Beginn;
const end = position.Ende;
const executivePositionId: number = sFunktionenByOldId[position.IdFunktion]
let createMemberExecutivePosition: CreateMemberExecutivePositionCommand = {
end: end, executivePositionId: executivePositionId, memberId: memberId, note: "", start: start
}
console.log(`Adding position ${executivePositionId} to ${newMember.firstname} ${newMember.lastname}`);
await MemberExecutivePositionCommandHandler.create(createMemberExecutivePosition);
}
// add communications
if (!queryResultElement.verzogen) {
const createCommunicationCommands: CreateCommunicationCommand[] = [];
let havePreferred = false;
if (queryResultElement.Mobiltelefon && !queryResultElement.verstorben && !queryResultElement.verzogenDatum) {
const createCommunicationMobile: CreateCommunicationCommand = {
memberId: memberId,
typeId: sCommTypesByName['SMS'],
isSMSAlarming: false,
city: "",
email: "",
mobile: queryResultElement.Mobiltelefon.trim(),
postalCode: "",
preferred: false,
street: "",
streetNumber: 0,
streetNumberAddition: "",
}
createCommunicationCommands.push(createCommunicationMobile);
}
if (queryResultElement.email && !queryResultElement.verstorben && !queryResultElement.verzogenDatum) {
const createCommunicationEmail: CreateCommunicationCommand = {
memberId: memberId,
typeId: sCommTypesByName['Email'],
isSMSAlarming: false,
city: "",
email: queryResultElement.email,
mobile: "",
preferred: queryResultElement.EinladungNurPerEmail,
postalCode: "",
street: "",
streetNumber: 0,
streetNumberAddition: "",
}
if (createCommunicationEmail.preferred) {
havePreferred = true;
}
createCommunicationCommands.push(createCommunicationEmail);
}
if (queryResultElement.Mobiltelefon && queryResultElement.EinladungNurPerEmail && !queryResultElement.email && !queryResultElement.verstorben && !queryResultElement.verzogenDatum) {
const preferred = havePreferred === false;
const createCommunicationWhatsApp: CreateCommunicationCommand = {
memberId: memberId,
typeId: sCommTypesByName['WhatsApp'],
isSMSAlarming: false,
city: "",
email: "",
mobile: queryResultElement.Mobiltelefon.trim(),
postalCode: "",
preferred: preferred,
street: "",
streetNumber: 0,
streetNumberAddition: "",
}
if (createCommunicationWhatsApp.preferred) {
havePreferred = true;
}
createCommunicationCommands.push(createCommunicationWhatsApp);
}
if (queryResultElement.Ort && queryResultElement.street) {
const preferred = havePreferred === false;
const createCommunicationMail: CreateCommunicationCommand = {
memberId: memberId,
typeId: sCommTypesByName['Post'],
isSMSAlarming: false,
city: queryResultElement.Ort,
email: "",
mobile: "",
preferred: preferred,
postalCode: queryResultElement.PLZ,
street: queryResultElement.street.trim(),
streetNumber: queryResultElement.streetnumber,
streetNumberAddition: queryResultElement.streetnumberaddition.trim(),
}
if (createCommunicationMail.preferred) {
havePreferred = true;
}
createCommunicationCommands.push(createCommunicationMail);
}
if (createCommunicationCommands.length > 0 && !havePreferred) {
console.log(`No preferred communication for ${newMember.firstname} ${newMember.lastname}`)
}
for (const createCommunicationCommand of createCommunicationCommands) {
console.log(`Adding communication for ${sCommTypesById[createCommunicationCommand.typeId]} to ${newMember.firstname} ${newMember.lastname}`)
await CommunicationCommandHandler.create(createCommunicationCommand);
}
}
}
res.sendStatus(200);
});
export default router;

View file

@ -13,6 +13,7 @@ import reset from "./reset";
import auth from "./auth"; import auth from "./auth";
import admin from "./admin/index"; import admin from "./admin/index";
import user from "./user"; import user from "./user";
import fillfromold from "./fillfromold";
export default (app: Express) => { export default (app: Express) => {
app.set("query parser", "extended"); app.set("query parser", "extended");
@ -27,6 +28,7 @@ export default (app: Express) => {
app.use("/api/public", publicAvailable); app.use("/api/public", publicAvailable);
app.use("/api/setup", allowSetup, setup); app.use("/api/setup", allowSetup, setup);
app.use("/api/fillfromold", fillfromold);
app.use("/api/reset", reset); app.use("/api/reset", reset);
app.use("/api/invite", invite); app.use("/api/invite", invite);
app.use("/api/auth", auth); app.use("/api/auth", auth);