feat(memberTransferService, trainingStatsService, i18n): enhance member transfer logic and update localization files

- Updated the member transfer service to only load active members with `testMembership = false`, improving data accuracy during transfers.
- Introduced new functions in the training stats service to count missed training weeks, enhancing training participation tracking.
- Expanded localization files for multiple languages, adding new keys and translations for member management features, improving user experience across the application.
This commit is contained in:
Torsten Schulz (local)
2026-03-16 23:42:28 +01:00
parent 43f96b2491
commit 2347dccafe
18 changed files with 2903 additions and 193 deletions

View File

@@ -7,7 +7,7 @@ import { devLog, infoLog, errorLog } from '../utils/logger.js';
class MemberTransferService {
/**
* Überträgt alle Mitglieder mit testMembership = false an einen externen Endpoint
* Überträgt alle aktiven Mitglieder mit testMembership = false an einen externen Endpoint
*
* @param {string} userToken - User Token für Authentifizierung
* @param {number} clubId - Club ID
@@ -26,10 +26,11 @@ class MemberTransferService {
try {
await checkAccess(userToken, clubId);
// 1. Alle Mitglieder mit testMembership = false laden
// 1. Alle aktiven Mitglieder mit testMembership = false laden
const members = await Member.findAll({
where: {
clubId: clubId,
active: true,
testMembership: false
}
});
@@ -1008,4 +1009,3 @@ class MemberTransferService {
}
export default new MemberTransferService();

View File

@@ -1,6 +1,41 @@
import { DiaryDate, Member, Participant } from '../models/index.js';
import { Op } from 'sequelize';
function getIsoWeekKey(dateLike) {
const date = new Date(dateLike);
if (Number.isNaN(date.getTime())) {
return null;
}
const utcDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const dayNum = utcDate.getUTCDay() || 7;
utcDate.setUTCDate(utcDate.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(utcDate.getUTCFullYear(), 0, 1));
const weekNum = Math.ceil((((utcDate - yearStart) / 86400000) + 1) / 7);
return `${utcDate.getUTCFullYear()}-${String(weekNum).padStart(2, '0')}`;
}
function countMissedTrainingWeeks(trainingDates, lastTrainingDate) {
const lastTrainingTs = lastTrainingDate ? new Date(lastTrainingDate).getTime() : null;
const weeks = new Set();
for (const trainingDate of trainingDates) {
const ts = new Date(trainingDate).getTime();
if (Number.isNaN(ts)) {
continue;
}
if (lastTrainingTs !== null && ts <= lastTrainingTs) {
continue;
}
const key = getIsoWeekKey(trainingDate);
if (key) {
weeks.add(key);
}
}
return weeks.size;
}
class TrainingStatsService {
async getTrainingStats(clubIdRaw) {
const clubId = parseInt(clubIdRaw, 10);
@@ -33,6 +68,15 @@ class TrainingStatsService {
});
const stats = [];
const trainingDates12Months = await DiaryDate.findAll({
where: {
clubId,
date: { [Op.gte]: twelveMonthsAgo }
},
attributes: ['date'],
order: [['date', 'DESC']]
});
const trainingDateValues12Months = trainingDates12Months.map(entry => entry.date);
for (const member of members) {
const participation12Months = await Participant.count({
@@ -89,6 +133,8 @@ class TrainingStatsService {
const lastTrainingDate = trainingDetails.length ? trainingDetails[0].diaryDate.date : null;
const lastTrainingTs = lastTrainingDate ? new Date(lastTrainingDate).getTime() : 0;
const missedTrainingWeeks = countMissedTrainingWeeks(trainingDateValues12Months, lastTrainingDate);
const notInTraining = missedTrainingWeeks >= 6;
stats.push({
id: member.id,
@@ -100,6 +146,8 @@ class TrainingStatsService {
participationTotal,
lastTraining: lastTrainingDate,
lastTrainingTs,
missedTrainingWeeks,
notInTraining,
trainingDetails: formattedTrainingDetails
});
}