implementierung der ersten schritte eine komplett-suite

This commit is contained in:
Torsten Schulz (local)
2026-06-19 15:47:32 +02:00
parent 111b37b287
commit 542fae089c
62 changed files with 11924 additions and 669 deletions

View File

@@ -0,0 +1,94 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
const ClubAccount = sequelize.define('ClubAccount', {
clubId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'club_id',
},
name: {
type: DataTypes.STRING(160),
allowNull: false,
},
accountHolder: {
type: DataTypes.STRING(255),
allowNull: true,
field: 'account_holder',
},
bankName: {
type: DataTypes.STRING(160),
allowNull: true,
field: 'bank_name',
},
iban: {
type: DataTypes.STRING(34),
allowNull: true,
},
bic: {
type: DataTypes.STRING(11),
allowNull: true,
},
accountType: {
type: DataTypes.ENUM('bank', 'cash', 'virtual'),
allowNull: false,
defaultValue: 'bank',
field: 'account_type',
},
usageType: {
type: DataTypes.ENUM('general', 'membership_fees', 'donations', 'expenses', 'reserve', 'petty_cash'),
allowNull: false,
defaultValue: 'general',
field: 'usage_type',
},
currencyCode: {
type: DataTypes.STRING(3),
allowNull: false,
defaultValue: 'EUR',
field: 'currency_code',
},
allowSepaCollections: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
field: 'allow_sepa_collections',
},
allowOutgoingPayments: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true,
field: 'allow_outgoing_payments',
},
isDefault: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
field: 'is_default',
},
status: {
type: DataTypes.ENUM('active', 'inactive', 'archived'),
allowNull: false,
defaultValue: 'active',
},
notes: {
type: DataTypes.TEXT,
allowNull: true,
},
archivedAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'archived_at',
},
sortOrder: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
field: 'sort_order',
},
}, {
tableName: 'club_accounts',
underscored: true,
timestamps: true,
});
export default ClubAccount;

View File

@@ -0,0 +1,78 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
const ClubPaymentClaim = sequelize.define('ClubPaymentClaim', {
clubId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'club_id'
},
memberId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'member_id'
},
feeRuleId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'fee_rule_id'
},
claimType: {
type: DataTypes.STRING(32),
allowNull: false,
defaultValue: 'membership_fee',
field: 'claim_type'
},
status: {
type: DataTypes.ENUM('open', 'partially_paid', 'paid', 'written_off', 'cancelled'),
allowNull: false,
defaultValue: 'open'
},
dueOn: {
type: DataTypes.DATEONLY,
allowNull: false,
field: 'due_on'
},
amountCents: {
type: DataTypes.BIGINT,
allowNull: false,
field: 'amount_cents'
},
currencyCode: {
type: DataTypes.STRING(3),
allowNull: false,
defaultValue: 'EUR',
field: 'currency_code'
},
reminderLevel: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
field: 'reminder_level'
},
lastReminderAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'last_reminder_at'
},
notes: {
type: DataTypes.TEXT,
allowNull: true
},
settledAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'settled_at'
},
archivedAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'archived_at'
}
}, {
tableName: 'club_payment_claims',
underscored: true,
timestamps: true
});
export default ClubPaymentClaim;

View File

@@ -0,0 +1,94 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
const ClubRequest = sequelize.define('ClubRequest', {
clubId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'club_id'
},
requestType: {
type: DataTypes.ENUM('contact', 'trial_training', 'membership', 'sponsoring'),
allowNull: false,
defaultValue: 'contact',
field: 'request_type'
},
status: {
type: DataTypes.ENUM('open', 'in_progress', 'waiting', 'converted', 'rejected', 'archived'),
allowNull: false,
defaultValue: 'open'
},
workflowStage: {
type: DataTypes.STRING(64),
allowNull: true,
field: 'workflow_stage'
},
priority: {
type: DataTypes.ENUM('low', 'normal', 'high', 'urgent'),
allowNull: false,
defaultValue: 'normal'
},
subject: {
type: DataTypes.STRING,
allowNull: true
},
firstName: {
type: DataTypes.STRING,
allowNull: true,
field: 'first_name'
},
lastName: {
type: DataTypes.STRING,
allowNull: true,
field: 'last_name'
},
email: {
type: DataTypes.STRING,
allowNull: true
},
phone: {
type: DataTypes.STRING,
allowNull: true
},
message: {
type: DataTypes.TEXT,
allowNull: true
},
sourceSystem: {
type: DataTypes.STRING,
allowNull: true,
field: 'source_system'
},
receivedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
field: 'received_at'
},
assignedUserId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'assigned_user_id'
},
assignedMemberId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'assigned_member_id'
},
convertedMemberId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'converted_member_id'
},
closedAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'closed_at'
}
}, {
tableName: 'club_requests',
underscored: true,
timestamps: true
});
export default ClubRequest;

View File

@@ -0,0 +1,32 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
const ClubRequestNote = sequelize.define('ClubRequestNote', {
clubRequestId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'club_request_id'
},
noteType: {
type: DataTypes.STRING(32),
allowNull: false,
defaultValue: 'internal',
field: 'note_type'
},
createdByUserId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'created_by_user_id'
},
body: {
type: DataTypes.TEXT,
allowNull: false
}
}, {
tableName: 'club_request_notes',
underscored: true,
timestamps: true,
updatedAt: false
});
export default ClubRequestNote;

View File

@@ -0,0 +1,58 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
const ClubRole = sequelize.define('ClubRole', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
clubId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'club_id',
},
roleKey: {
type: DataTypes.STRING(64),
allowNull: false,
field: 'role_key',
},
name: {
type: DataTypes.STRING(120),
allowNull: false,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
permissions: {
type: DataTypes.JSON,
allowNull: false,
defaultValue: {},
},
isSystemRole: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
field: 'is_system_role',
},
sortOrder: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
field: 'sort_order',
},
}, {
tableName: 'club_roles',
underscored: true,
timestamps: true,
indexes: [
{
unique: true,
fields: ['club_id', 'role_key'],
},
],
});
export default ClubRole;

View File

@@ -0,0 +1,64 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
const ClubSepaMandate = sequelize.define('ClubSepaMandate', {
clubId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'club_id'
},
memberId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'member_id'
},
debtorName: {
type: DataTypes.STRING,
allowNull: false,
field: 'debtor_name'
},
iban: {
type: DataTypes.STRING(34),
allowNull: false
},
bic: {
type: DataTypes.STRING(11),
allowNull: true
},
mandateReference: {
type: DataTypes.STRING(80),
allowNull: false,
field: 'mandate_reference'
},
signedOn: {
type: DataTypes.DATEONLY,
allowNull: true,
field: 'signed_on'
},
validFrom: {
type: DataTypes.DATEONLY,
allowNull: true,
field: 'valid_from'
},
revokedAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'revoked_at'
},
status: {
type: DataTypes.STRING(32),
allowNull: false,
defaultValue: 'active'
},
historyNote: {
type: DataTypes.TEXT,
allowNull: true,
field: 'history_note'
}
}, {
tableName: 'club_sepa_mandates',
underscored: true,
timestamps: true
});
export default ClubSepaMandate;

View File

@@ -0,0 +1,94 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
const ClubTask = sequelize.define('ClubTask', {
clubId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'club_id'
},
title: {
type: DataTypes.STRING(255),
allowNull: false
},
taskType: {
type: DataTypes.STRING(64),
allowNull: true,
field: 'task_type'
},
description: {
type: DataTypes.TEXT,
allowNull: true
},
status: {
type: DataTypes.ENUM('open', 'in_progress', 'waiting', 'done', 'cancelled', 'archived'),
allowNull: false,
defaultValue: 'open'
},
priority: {
type: DataTypes.ENUM('low', 'normal', 'high', 'urgent'),
allowNull: false,
defaultValue: 'normal'
},
dueAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'due_at'
},
remindAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'remind_at'
},
createdByUserId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'created_by_user_id'
},
assignedUserId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'assigned_user_id'
},
automationSource: {
type: DataTypes.STRING(64),
allowNull: true,
field: 'automation_source'
},
automationKey: {
type: DataTypes.STRING(255),
allowNull: true,
field: 'automation_key'
},
relatedEntityType: {
type: DataTypes.STRING(32),
allowNull: true,
field: 'related_entity_type'
},
relatedEntityId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'related_entity_id'
},
completedAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'completed_at'
},
archivedAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'archived_at'
},
sourceSnapshot: {
type: DataTypes.JSON,
allowNull: true,
field: 'source_snapshot'
}
}, {
tableName: 'club_tasks',
underscored: true,
timestamps: true
});
export default ClubTask;

View File

@@ -0,0 +1,31 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
const ClubTaskSuppression = sequelize.define('ClubTaskSuppression', {
clubId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'club_id'
},
automationKey: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'automation_key'
},
suppressionToken: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'suppression_token'
},
dismissedByUserId: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'dismissed_by_user_id'
}
}, {
tableName: 'club_task_suppressions',
underscored: true,
timestamps: true
});
export default ClubTaskSuppression;

View File

@@ -0,0 +1,44 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
const ClubUserRole = sequelize.define('ClubUserRole', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
clubId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'club_id',
},
userId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'user_id',
},
clubRoleId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'club_role_id',
},
isPrimary: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
field: 'is_primary',
},
}, {
tableName: 'club_user_roles',
underscored: true,
timestamps: true,
indexes: [
{
unique: true,
fields: ['club_id', 'user_id', 'club_role_id'],
},
],
});
export default ClubUserRole;

View File

@@ -70,6 +70,15 @@ import MemberTrainingGroup from './MemberTrainingGroup.js';
import ClubDisabledPresetGroup from './ClubDisabledPresetGroup.js';
import TrainingTime from './TrainingTime.js';
import ClubVenue from './ClubVenue.js';
import ClubRequest from './ClubRequest.js';
import ClubRequestNote from './ClubRequestNote.js';
import ClubSepaMandate from './ClubSepaMandate.js';
import ClubPaymentClaim from './ClubPaymentClaim.js';
import ClubAccount from './ClubAccount.js';
import ClubTask from './ClubTask.js';
import ClubTaskSuppression from './ClubTaskSuppression.js';
import ClubRole from './ClubRole.js';
import ClubUserRole from './ClubUserRole.js';
// Official tournaments relations
OfficialTournament.hasMany(OfficialCompetition, { foreignKey: 'tournamentId', as: 'competitions' });
OfficialCompetition.belongsTo(OfficialTournament, { foreignKey: 'tournamentId', as: 'tournament' });
@@ -172,6 +181,10 @@ ClubTeam.belongsTo(League, { foreignKey: 'leagueId', as: 'league' });
Season.hasMany(ClubTeam, { foreignKey: 'seasonId', as: 'clubTeams' });
ClubTeam.belongsTo(Season, { foreignKey: 'seasonId', as: 'season' });
ClubTeam.hasMany(ClubTeamMember, { foreignKey: 'clubTeamId', as: 'lineupMembers' });
ClubTeamMember.belongsTo(ClubTeam, { foreignKey: 'clubTeamId', as: 'clubTeam' });
Member.hasMany(ClubTeamMember, { foreignKey: 'memberId', as: 'clubTeamAssignments' });
ClubTeamMember.belongsTo(Member, { foreignKey: 'memberId', as: 'member' });
// TeamDocument relationships
ClubTeam.hasMany(TeamDocument, { foreignKey: 'clubTeamId', as: 'documents' });
@@ -447,6 +460,50 @@ FriendlyMatchInvitation.hasOne(FriendlyMatchShared, {
Club.hasMany(ClubVenue, { foreignKey: 'clubId', as: 'venues' });
ClubVenue.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
Club.hasMany(ClubRequest, { foreignKey: 'clubId', as: 'clubRequests' });
ClubRequest.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
ClubRequest.hasMany(ClubRequestNote, { foreignKey: 'clubRequestId', as: 'notes' });
ClubRequestNote.belongsTo(ClubRequest, { foreignKey: 'clubRequestId', as: 'request' });
User.hasMany(ClubRequest, { foreignKey: 'assignedUserId', as: 'assignedClubRequests' });
ClubRequest.belongsTo(User, { foreignKey: 'assignedUserId', as: 'assignedUser', constraints: false });
Member.hasMany(ClubRequest, { foreignKey: 'assignedMemberId', as: 'assignedRequests' });
ClubRequest.belongsTo(Member, { foreignKey: 'assignedMemberId', as: 'assignedMember', constraints: false });
User.hasMany(ClubRequestNote, { foreignKey: 'createdByUserId', as: 'clubRequestNotes' });
ClubRequestNote.belongsTo(User, { foreignKey: 'createdByUserId', as: 'createdByUser', constraints: false });
Club.hasMany(ClubSepaMandate, { foreignKey: 'clubId', as: 'sepaMandates' });
ClubSepaMandate.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
Member.hasMany(ClubSepaMandate, { foreignKey: 'memberId', as: 'sepaMandates' });
ClubSepaMandate.belongsTo(Member, { foreignKey: 'memberId', as: 'member', constraints: false });
Club.hasMany(ClubPaymentClaim, { foreignKey: 'clubId', as: 'paymentClaims' });
ClubPaymentClaim.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
Member.hasMany(ClubPaymentClaim, { foreignKey: 'memberId', as: 'paymentClaims' });
ClubPaymentClaim.belongsTo(Member, { foreignKey: 'memberId', as: 'member', constraints: false });
Club.hasMany(ClubAccount, { foreignKey: 'clubId', as: 'accounts' });
ClubAccount.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
Club.hasMany(ClubTask, { foreignKey: 'clubId', as: 'clubTasks' });
ClubTask.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
User.hasMany(ClubTask, { foreignKey: 'createdByUserId', as: 'createdClubTasks' });
ClubTask.belongsTo(User, { foreignKey: 'createdByUserId', as: 'createdByUser', constraints: false });
User.hasMany(ClubTask, { foreignKey: 'assignedUserId', as: 'assignedClubTasksWork' });
ClubTask.belongsTo(User, { foreignKey: 'assignedUserId', as: 'assignedUser', constraints: false });
Club.hasMany(ClubTaskSuppression, { foreignKey: 'clubId', as: 'taskSuppressions' });
ClubTaskSuppression.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
User.hasMany(ClubTaskSuppression, { foreignKey: 'dismissedByUserId', as: 'dismissedTaskSuggestions' });
ClubTaskSuppression.belongsTo(User, { foreignKey: 'dismissedByUserId', as: 'dismissedByUser', constraints: false });
Club.hasMany(ClubRole, { foreignKey: 'clubId', as: 'clubRoles' });
ClubRole.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
ClubRole.hasMany(ClubUserRole, { foreignKey: 'clubRoleId', as: 'assignments' });
ClubUserRole.belongsTo(ClubRole, { foreignKey: 'clubRoleId', as: 'role' });
Club.hasMany(ClubUserRole, { foreignKey: 'clubId', as: 'clubUserRoles' });
ClubUserRole.belongsTo(Club, { foreignKey: 'clubId', as: 'club', constraints: false });
User.hasMany(ClubUserRole, { foreignKey: 'userId', as: 'clubRoleAssignments' });
ClubUserRole.belongsTo(User, { foreignKey: 'userId', as: 'user', constraints: false });
export {
User,
Log,
@@ -517,4 +574,13 @@ export {
ClubDisabledPresetGroup,
TrainingTime,
ClubVenue,
ClubRequest,
ClubRequestNote,
ClubSepaMandate,
ClubPaymentClaim,
ClubAccount,
ClubTask,
ClubTaskSuppression,
ClubRole,
ClubUserRole,
};