From 1ef9a0bd03e6241d53344fd61e7afdd9a28208b8 Mon Sep 17 00:00:00 2001 From: FrancoRu Date: Fri, 18 Sep 2026 13:36:47 -0300 Subject: [PATCH] Sube capa Infrastructure, Solution y archivos de configuracion del Backend --- Club12-Backend/.dockerignore | 16 + Club12-Backend/.editorconfig | 70 + Club12-Backend/Directory.Build.props | 10 + Club12-Backend/Dockerfile | 32 + .../Backup/LocalDirectoryBackupStorage.cs | 90 + .../Backup/PgDumpBackupService.cs | 97 + .../Infrastructure/Backup/ProcessRunner.cs | 64 + .../Backup/PsqlDatabaseRestoreService.cs | 68 + .../Backup/SupabaseBackupStorage.cs | 105 + .../Email/EmailTemplateLoader.cs | 40 + .../Infrastructure/Email/FluentEmailHelper.cs | 82 + .../Email/Templates/MagicLinkTemplate.html | 67 + .../Templates/PasswordResetTemplate.html | 68 + .../Templates/WelcomeSetPasswordTemplate.html | 56 + .../Identity/ApplicationUser.cs | 30 + .../Identity/IdentityAppDbContext.cs | 49 + .../Identity/IdentityAppDbContextFactory.cs | 78 + .../Identity/IdentityAuthenticationService.cs | 401 ++++ .../Infrastructure/Identity/IdentitySeeder.cs | 65 + .../Identity/IdentityUserManagementService.cs | 397 ++++ ...43537_InitialIdentityMigration.Designer.cs | 319 +++ ...20260315043537_InitialIdentityMigration.cs | 240 +++ ...316030250_changesPropsForUsers.Designer.cs | 322 +++ .../20260316030250_changesPropsForUsers.cs | 29 + ...6031655_addDateCreatedForUsers.Designer.cs | 325 +++ .../20260316031655_addDateCreatedForUsers.cs | 30 + ...16042032_addMustChangePassword.Designer.cs | 328 ++++ .../20260316042032_addMustChangePassword.cs | 28 + ...6215327_AddNewPropsForIdentity.Designer.cs | 329 ++++ .../20260326215327_AddNewPropsForIdentity.cs | 36 + ...eTournamentAndTeamManagerRoles.Designer.cs | 315 +++ ...949_RemoveTournamentAndTeamManagerRoles.cs | 40 + .../IdentityAppDbContextModelSnapshot.cs | 312 +++ .../Infrastructure/Infrastructure.csproj | 37 + ...0250316201442_InitialMigration.Designer.cs | 625 ++++++ .../20250316201442_InitialMigration.cs | 434 ++++ .../20250317005231_LatestChanges.Designer.cs | 697 +++++++ .../20250317005231_LatestChanges.cs | 179 ++ .../20250724231610_AddStageEntity.Designer.cs | 679 +++++++ .../20250724231610_AddStageEntity.cs | 293 +++ ...eAndDivisionIdInsideStageTable.Designer.cs | 685 +++++++ ...UniqueNameAndDivisionIdInsideStageTable.cs | 29 + ...0926_removeFK_DivisionIdToTeam.Designer.cs | 678 +++++++ ...0250729230926_removeFK_DivisionIdToTeam.cs | 81 + ...ocumentNumberInsidePlayerTable.Designer.cs | 674 +++++++ ...ToUniqueDocumentNumberInsidePlayerTable.cs | 93 + ...250801010149_addVenueIntoMatch.Designer.cs | 685 +++++++ .../20250801010149_addVenueIntoMatch.cs | 56 + ...eZoneToDateTimeWithoutTimeZone.Designer.cs | 685 +++++++ ...meWithTimeZoneToDateTimeWithoutTimeZone.cs | 585 ++++++ ...atchIdIntoPlayerSanctionEntity.Designer.cs | 698 +++++++ ...0120_addMatchIdIntoPlayerSanctionEntity.cs | 58 + ...6_AddChangesInTournamentEntity.Designer.cs | 729 +++++++ ...0816164236_AddChangesInTournamentEntity.cs | 132 ++ ...20260201150715_ChangesEntities.Designer.cs | 707 +++++++ .../20260201150715_ChangesEntities.cs | 139 ++ ...033209_addOrderColumnIntoStage.Designer.cs | 710 +++++++ .../20260204033209_addOrderColumnIntoStage.cs | 30 + ...5004338_removeUnneccessaryProp.Designer.cs | 705 +++++++ .../20260205004338_removeUnneccessaryProp.cs | 81 + ...8023223_AddStageTeamMatchTable.Designer.cs | 757 +++++++ .../20260218023223_AddStageTeamMatchTable.cs | 65 + .../20260315232746_cleanDB.Designer.cs | 748 +++++++ .../Migrations/20260315232746_cleanDB.cs | 474 +++++ ...6234428_changeTournamentEntity.Designer.cs | 750 +++++++ .../20260326234428_changeTournamentEntity.cs | 43 + .../20260403194406_AddScorerTable.Designer.cs | 816 ++++++++ .../20260403194406_AddScorerTable.cs | 74 + ...9150628_AddPlayerStatisticType.Designer.cs | 820 ++++++++ .../20260629150628_AddPlayerStatisticType.cs | 30 + ...260629151546_AddSanctionAppeal.Designer.cs | 838 ++++++++ .../20260629151546_AddSanctionAppeal.cs | 82 + ...AddPlayoffSeriesAndBracketName.Designer.cs | 959 +++++++++ ...16030250_AddPlayoffSeriesAndBracketName.cs | 202 ++ ...123_AddSlugToTournamentAndTeam.Designer.cs | 975 +++++++++ ...260817044123_AddSlugToTournamentAndTeam.cs | 146 ++ .../20260817064644_AddSlugToMatch.Designer.cs | 983 ++++++++++ .../20260817064644_AddSlugToMatch.cs | 98 + ...AddPlayerTeamRegistrationTable.Designer.cs | 1057 ++++++++++ ...17082125_AddPlayerTeamRegistrationTable.cs | 115 ++ ...260817115955_AddSlugToBlogPost.Designer.cs | 1065 ++++++++++ .../20260817115955_AddSlugToBlogPost.cs | 85 + ...120017_AddSlugToPlayerSanction.Designer.cs | 1073 ++++++++++ .../20260817120017_AddSlugToPlayerSanction.cs | 101 + ...820005357_AddBackupRecordTable.Designer.cs | 1112 +++++++++++ .../20260820005357_AddBackupRecordTable.cs | 47 + ...eamTournamentRegistrationTable.Designer.cs | 1132 +++++++++++ ...0738_AddTeamTournamentRegistrationTable.cs | 74 + ...SlugToDivisionStageVenuePlayer.Designer.cs | 1164 +++++++++++ ...03816_AddSlugToDivisionStageVenuePlayer.cs | 286 +++ ..._DropTournamentTeamCountLimits.Designer.cs | 1158 +++++++++++ ...828010238_DropTournamentTeamCountLimits.cs | 66 + .../20260828011954_AddMatchStatus.Designer.cs | 1162 +++++++++++ .../20260828011954_AddMatchStatus.cs | 38 + ...ivisionPointsAndPlayoffMapping.Designer.cs | 1228 ++++++++++++ ...3414_AddDivisionPointsAndPlayoffMapping.cs | 88 + ...RecordToPlayerTeamRegistration.Designer.cs | 1244 ++++++++++++ ...ddMedicalRecordToPlayerTeamRegistration.cs | 88 + ...20260828030000_AddRoundToMatch.Designer.cs | 1247 ++++++++++++ .../20260828030000_AddRoundToMatch.cs | 33 + ...dTournamentAndDivisionCategory.Designer.cs | 1257 ++++++++++++ ...031000_AddTournamentAndDivisionCategory.cs | 44 + ...000_AddSubjectToPlayerSanction.Designer.cs | 1276 ++++++++++++ ...260828040000_AddSubjectToPlayerSanction.cs | 111 ++ ...tLogBlogPublishAndJerseyNumber.Designer.cs | 1335 +++++++++++++ ...0_AddAuditLogBlogPublishAndJerseyNumber.cs | 122 ++ .../20260828054810_AddClubEntity.Designer.cs | 1394 +++++++++++++ .../20260828054810_AddClubEntity.cs | 93 + ...ddQualifiersPerGroupToDivision.Designer.cs | 1438 ++++++++++++++ ...8163513_AddQualifiersPerGroupToDivision.cs | 31 + ...0260829020604_AddTeamJerseyKit.Designer.cs | 1449 ++++++++++++++ .../20260829020604_AddTeamJerseyKit.cs | 45 + ...1_RebackfillDivisionStageSlugs.Designer.cs | 1449 ++++++++++++++ ...0829033721_RebackfillDivisionStageSlugs.cs | 81 + .../20260829044303_AddSeason.Designer.cs | 1512 ++++++++++++++ .../Migrations/20260829044303_AddSeason.cs | 93 + ...29101157_AddTeamPointDeduction.Designer.cs | 1576 +++++++++++++++ .../20260829101157_AddTeamPointDeduction.cs | 75 + ...ayerSlugsWithoutDocumentNumber.Designer.cs | 1576 +++++++++++++++ ...ackfillPlayerSlugsWithoutDocumentNumber.cs | 118 ++ ...25314_AddVenueImageAndLocation.Designer.cs | 1582 +++++++++++++++ ...20260829225314_AddVenueImageAndLocation.cs | 42 + ..._HardenMatchSeriesTeamOnDelete.Designer.cs | 1583 +++++++++++++++ ...901070931_HardenMatchSeriesTeamOnDelete.cs | 107 + ...260901193848_AddTeamStaffTable.Designer.cs | 1647 ++++++++++++++++ .../20260901193848_AddTeamStaffTable.cs | 75 + ...833_CleanupOrphanedTournaments.Designer.cs | 1647 ++++++++++++++++ ...260902050833_CleanupOrphanedTournaments.cs | 71 + ...054533_AddTargetNameToAuditLog.Designer.cs | 1651 ++++++++++++++++ .../20260902054533_AddTargetNameToAuditLog.cs | 31 + ...55555_AddWentToOvertimeToMatch.Designer.cs | 1654 ++++++++++++++++ ...20260902055555_AddWentToOvertimeToMatch.cs | 31 + ...3728_AddTeamShirtTertiaryColor.Designer.cs | 1658 ++++++++++++++++ ...0260902173728_AddTeamShirtTertiaryColor.cs | 31 + ...eamRegistrationAndStageDrawnAt.Designer.cs | 1722 ++++++++++++++++ ...DivisionTeamRegistrationAndStageDrawnAt.cs | 117 ++ ...0907015056_AddClubParentClubId.Designer.cs | 1739 +++++++++++++++++ .../20260907015056_AddClubParentClubId.cs | 57 + ...600_WidenDivisionNameMaxLength.Designer.cs | 1739 +++++++++++++++++ ...260907042600_WidenDivisionNameMaxLength.cs | 40 + .../ApplicationDBContextModelSnapshot.cs | 1736 ++++++++++++++++ .../Persistance/ApplicationDBContext.cs | 61 + .../ApplicationDBContextFactory.cs | 101 + .../AuditLogEntityConfiguration.cs | 27 + .../BackupRecordEntityConfiguration.cs | 18 + .../Configurations/BaseEntityConfiguration.cs | 36 + .../BlogPostEntityConfiguration.cs | 25 + .../Configurations/ClubEntityConfiguration.cs | 38 + .../DivisionEntityConfiguration.cs | 42 + ...visionPlayoffMappingEntityConfiguration.cs | 20 + ...sionTeamRegistrationEntityConfiguration.cs | 34 + .../MatchEntityConfiguration.cs | 55 + .../MatchSeriesEntityConfiguration.cs | 50 + .../PlayerEntityConfiguration.cs | 32 + .../PlayerSanctionEntityConfiguration.cs | 44 + .../PlayerStatisticEntityConfiguration.cs | 28 + ...ayerTeamRegistrationEntityConfiguration.cs | 49 + .../ScorerEntityConfiguration.cs | 29 + .../SeasonEntityConfiguration.cs | 27 + .../StageEntityConfiguration.cs | 32 + .../StageTeamMatchEntityConfiguration.cs | 27 + .../Configurations/TeamEntityConfiguration.cs | 38 + .../TeamPointDeductionEntityConfiguration.cs | 32 + .../TeamStaffEntityConfiguration.cs | 32 + ...urnamentRegistrationEntityConfiguration.cs | 32 + .../TournamentEntityConfiguration.cs | 30 + .../VenueEntityConfiguration.cs | 23 + .../NullableUtcDateTimeConverter.cs | 13 + .../Converters/UtcDateTimeConverter.cs | 13 + .../Persistance/EfBackupCatalog.cs | 50 + .../Persistance/EntityConstants.cs | 68 + .../Persistance/IClub12DBContext.cs | 49 + .../Seeding/Assets/ficha-medica-generica.pdf | Bin 0 -> 503180 bytes .../Seeding/DataMaintenanceService.cs | 333 ++++ .../Persistance/Seeding/DataSeeder.cs | 802 ++++++++ .../Seeding/MedicalRecordSeedBackfiller.cs | 213 ++ .../Persistance/Seeding/SampleArtwork.cs | 101 + .../Seeding/SampleTournamentBuilder.cs | 1443 ++++++++++++++ .../Repositories/AuditLogRepository.cs | 16 + .../Repositories/BlogPostRepository.cs | 16 + .../Repositories/ClubRepository.cs | 14 + .../Repositories/DivisionRepository.cs | 15 + .../DivisionTeamRegistrationRepository.cs | 14 + .../Repositories/GenericRepository.cs | 143 ++ .../Repositories/MatchRepository.cs | 45 + .../Repositories/MatchSeriesRepository.cs | 16 + .../Repositories/PlayerRepository.cs | 15 + .../Repositories/PlayerSanctionRepository.cs | 15 + .../Repositories/PlayerStatisticRepository.cs | 15 + .../PlayerTeamRegistrationRepository.cs | 14 + .../Repositories/ScorerRepository.cs | 144 ++ .../Repositories/SeasonRepository.cs | 16 + .../Repositories/StageRepository.cs | 15 + .../Repositories/StageTeamMatchRepository.cs | 16 + .../Repositories/StatisticsRepository.cs | 200 ++ .../TeamPointDeductionRepository.cs | 15 + .../Repositories/TeamRepository.cs | 14 + .../Repositories/TeamStaffRepository.cs | 15 + .../TeamTournamentRegistrationRepository.cs | 14 + .../Repositories/TournamentRepository.cs | 14 + .../Infrastructure/Repositories/UnitOfWork.cs | 147 ++ .../Repositories/VenueRepository.cs | 16 + .../Infrastructure/Storage/SupabaseHelper.cs | 217 ++ .../Storage/SupabaseMedicalRecordStorage.cs | 50 + Club12-Backend/Solution/Club12.sln | 93 + 205 files changed, 77651 insertions(+) create mode 100644 Club12-Backend/.dockerignore create mode 100644 Club12-Backend/.editorconfig create mode 100644 Club12-Backend/Directory.Build.props create mode 100644 Club12-Backend/Dockerfile create mode 100644 Club12-Backend/Infrastructure/Backup/LocalDirectoryBackupStorage.cs create mode 100644 Club12-Backend/Infrastructure/Backup/PgDumpBackupService.cs create mode 100644 Club12-Backend/Infrastructure/Backup/ProcessRunner.cs create mode 100644 Club12-Backend/Infrastructure/Backup/PsqlDatabaseRestoreService.cs create mode 100644 Club12-Backend/Infrastructure/Backup/SupabaseBackupStorage.cs create mode 100644 Club12-Backend/Infrastructure/Email/EmailTemplateLoader.cs create mode 100644 Club12-Backend/Infrastructure/Email/FluentEmailHelper.cs create mode 100644 Club12-Backend/Infrastructure/Email/Templates/MagicLinkTemplate.html create mode 100644 Club12-Backend/Infrastructure/Email/Templates/PasswordResetTemplate.html create mode 100644 Club12-Backend/Infrastructure/Email/Templates/WelcomeSetPasswordTemplate.html create mode 100644 Club12-Backend/Infrastructure/Identity/ApplicationUser.cs create mode 100644 Club12-Backend/Infrastructure/Identity/IdentityAppDbContext.cs create mode 100644 Club12-Backend/Infrastructure/Identity/IdentityAppDbContextFactory.cs create mode 100644 Club12-Backend/Infrastructure/Identity/IdentityAuthenticationService.cs create mode 100644 Club12-Backend/Infrastructure/Identity/IdentitySeeder.cs create mode 100644 Club12-Backend/Infrastructure/Identity/IdentityUserManagementService.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/20260315043537_InitialIdentityMigration.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/20260315043537_InitialIdentityMigration.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/20260316030250_changesPropsForUsers.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/20260316030250_changesPropsForUsers.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/20260316031655_addDateCreatedForUsers.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/20260316031655_addDateCreatedForUsers.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/20260316042032_addMustChangePassword.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/20260316042032_addMustChangePassword.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/20260326215327_AddNewPropsForIdentity.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/20260326215327_AddNewPropsForIdentity.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/20260828021949_RemoveTournamentAndTeamManagerRoles.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/20260828021949_RemoveTournamentAndTeamManagerRoles.cs create mode 100644 Club12-Backend/Infrastructure/Identity/Migrations/IdentityAppDbContextModelSnapshot.cs create mode 100644 Club12-Backend/Infrastructure/Infrastructure.csproj create mode 100644 Club12-Backend/Infrastructure/Migrations/20250316201442_InitialMigration.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250316201442_InitialMigration.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250317005231_LatestChanges.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250317005231_LatestChanges.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250724231610_AddStageEntity.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250724231610_AddStageEntity.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250726012040_AddUniqueNameAndDivisionIdInsideStageTable.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250726012040_AddUniqueNameAndDivisionIdInsideStageTable.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250729230926_removeFK_DivisionIdToTeam.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250729230926_removeFK_DivisionIdToTeam.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250731014937_AddContraintToUniqueDocumentNumberInsidePlayerTable.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250731014937_AddContraintToUniqueDocumentNumberInsidePlayerTable.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250801010149_addVenueIntoMatch.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250801010149_addVenueIntoMatch.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250804000142_ChangeDateTimeWithTimeZoneToDateTimeWithoutTimeZone.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250804000142_ChangeDateTimeWithTimeZoneToDateTimeWithoutTimeZone.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250809010120_addMatchIdIntoPlayerSanctionEntity.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250809010120_addMatchIdIntoPlayerSanctionEntity.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250816164236_AddChangesInTournamentEntity.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20250816164236_AddChangesInTournamentEntity.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260201150715_ChangesEntities.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260201150715_ChangesEntities.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260204033209_addOrderColumnIntoStage.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260204033209_addOrderColumnIntoStage.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260205004338_removeUnneccessaryProp.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260205004338_removeUnneccessaryProp.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260218023223_AddStageTeamMatchTable.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260218023223_AddStageTeamMatchTable.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260315232746_cleanDB.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260315232746_cleanDB.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260326234428_changeTournamentEntity.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260326234428_changeTournamentEntity.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260403194406_AddScorerTable.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260403194406_AddScorerTable.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260629150628_AddPlayerStatisticType.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260629150628_AddPlayerStatisticType.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260629151546_AddSanctionAppeal.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260629151546_AddSanctionAppeal.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260816030250_AddPlayoffSeriesAndBracketName.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260816030250_AddPlayoffSeriesAndBracketName.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260817044123_AddSlugToTournamentAndTeam.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260817044123_AddSlugToTournamentAndTeam.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260817064644_AddSlugToMatch.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260817064644_AddSlugToMatch.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260817082125_AddPlayerTeamRegistrationTable.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260817082125_AddPlayerTeamRegistrationTable.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260817115955_AddSlugToBlogPost.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260817115955_AddSlugToBlogPost.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260817120017_AddSlugToPlayerSanction.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260817120017_AddSlugToPlayerSanction.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260820005357_AddBackupRecordTable.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260820005357_AddBackupRecordTable.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260827170738_AddTeamTournamentRegistrationTable.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260827170738_AddTeamTournamentRegistrationTable.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828003816_AddSlugToDivisionStageVenuePlayer.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828003816_AddSlugToDivisionStageVenuePlayer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828010238_DropTournamentTeamCountLimits.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828010238_DropTournamentTeamCountLimits.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828011954_AddMatchStatus.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828011954_AddMatchStatus.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828013414_AddDivisionPointsAndPlayoffMapping.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828013414_AddDivisionPointsAndPlayoffMapping.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828020000_AddMedicalRecordToPlayerTeamRegistration.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828020000_AddMedicalRecordToPlayerTeamRegistration.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828030000_AddRoundToMatch.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828030000_AddRoundToMatch.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828031000_AddTournamentAndDivisionCategory.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828031000_AddTournamentAndDivisionCategory.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828040000_AddSubjectToPlayerSanction.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828040000_AddSubjectToPlayerSanction.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828053250_AddAuditLogBlogPublishAndJerseyNumber.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828053250_AddAuditLogBlogPublishAndJerseyNumber.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828054810_AddClubEntity.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828054810_AddClubEntity.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828163513_AddQualifiersPerGroupToDivision.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260828163513_AddQualifiersPerGroupToDivision.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260829020604_AddTeamJerseyKit.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260829020604_AddTeamJerseyKit.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260829033721_RebackfillDivisionStageSlugs.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260829033721_RebackfillDivisionStageSlugs.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260829044303_AddSeason.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260829044303_AddSeason.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260829101157_AddTeamPointDeduction.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260829101157_AddTeamPointDeduction.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260829164705_RebackfillPlayerSlugsWithoutDocumentNumber.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260829164705_RebackfillPlayerSlugsWithoutDocumentNumber.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260829225314_AddVenueImageAndLocation.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260829225314_AddVenueImageAndLocation.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260901070931_HardenMatchSeriesTeamOnDelete.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260901070931_HardenMatchSeriesTeamOnDelete.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260901193848_AddTeamStaffTable.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260901193848_AddTeamStaffTable.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260902050833_CleanupOrphanedTournaments.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260902050833_CleanupOrphanedTournaments.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260902054533_AddTargetNameToAuditLog.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260902054533_AddTargetNameToAuditLog.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260902055555_AddWentToOvertimeToMatch.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260902055555_AddWentToOvertimeToMatch.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260902173728_AddTeamShirtTertiaryColor.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260902173728_AddTeamShirtTertiaryColor.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260906005632_AddDivisionTeamRegistrationAndStageDrawnAt.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260906005632_AddDivisionTeamRegistrationAndStageDrawnAt.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260907015056_AddClubParentClubId.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260907015056_AddClubParentClubId.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260907042600_WidenDivisionNameMaxLength.Designer.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/20260907042600_WidenDivisionNameMaxLength.cs create mode 100644 Club12-Backend/Infrastructure/Migrations/ApplicationDBContextModelSnapshot.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/ApplicationDBContext.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/ApplicationDBContextFactory.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/AuditLogEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/BackupRecordEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/BaseEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/BlogPostEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/ClubEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/DivisionEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/DivisionPlayoffMappingEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/DivisionTeamRegistrationEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/MatchEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/MatchSeriesEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/PlayerEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/PlayerSanctionEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/PlayerStatisticEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/PlayerTeamRegistrationEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/ScorerEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/SeasonEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/StageEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/StageTeamMatchEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/TeamEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/TeamPointDeductionEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/TeamStaffEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/TeamTournamentRegistrationEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/TournamentEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Configurations/VenueEntityConfiguration.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Converters/NullableUtcDateTimeConverter.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Converters/UtcDateTimeConverter.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/EfBackupCatalog.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/EntityConstants.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/IClub12DBContext.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Seeding/Assets/ficha-medica-generica.pdf create mode 100644 Club12-Backend/Infrastructure/Persistance/Seeding/DataMaintenanceService.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Seeding/DataSeeder.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Seeding/MedicalRecordSeedBackfiller.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Seeding/SampleArtwork.cs create mode 100644 Club12-Backend/Infrastructure/Persistance/Seeding/SampleTournamentBuilder.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/AuditLogRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/BlogPostRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/ClubRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/DivisionRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/DivisionTeamRegistrationRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/GenericRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/MatchRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/MatchSeriesRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/PlayerRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/PlayerSanctionRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/PlayerStatisticRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/PlayerTeamRegistrationRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/ScorerRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/SeasonRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/StageRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/StageTeamMatchRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/StatisticsRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/TeamPointDeductionRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/TeamRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/TeamStaffRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/TeamTournamentRegistrationRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/TournamentRepository.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/UnitOfWork.cs create mode 100644 Club12-Backend/Infrastructure/Repositories/VenueRepository.cs create mode 100644 Club12-Backend/Infrastructure/Storage/SupabaseHelper.cs create mode 100644 Club12-Backend/Infrastructure/Storage/SupabaseMedicalRecordStorage.cs create mode 100644 Club12-Backend/Solution/Club12.sln diff --git a/Club12-Backend/.dockerignore b/Club12-Backend/.dockerignore new file mode 100644 index 0000000..6eb6361 --- /dev/null +++ b/Club12-Backend/.dockerignore @@ -0,0 +1,16 @@ +**/bin/ +**/obj/ +**/.vs/ +**/*.user +# Per-developer secrets (e.g. appsettings.Franco.json) are excluded; the +# glob also matches appsettings.Development.json, which the runtime image +# MUST still contain per the container-deployment spec, so it is +# explicitly re-included below. appsettings.json is never matched by this +# glob (it lacks the second ".") and always stays. +**/appsettings.*.json +!API/appsettings.Development.json +API.Tests/ +Solution/ +.git +Dockerfile +.dockerignore diff --git a/Club12-Backend/.editorconfig b/Club12-Backend/.editorconfig new file mode 100644 index 0000000..8f6281f --- /dev/null +++ b/Club12-Backend/.editorconfig @@ -0,0 +1,70 @@ +# Club12 Backend — .editorconfig +# +# Codifies conventions the codebase already follows consistently (verified 2026-09-05 +# by sampling across Domain/Application/Infrastructure/API), not aspirational new rules. +# One exception was found and deliberately left out below: const-field casing is mixed +# in practice (PascalCase almost everywhere, but Application/Utils/Constants/Stage/MaxTeams.cs +# uses SCREAMING_SNAKE_CASE) — picking a side wasn't part of this task, so no naming rule +# is enforced for constants here. + +root = true + +[*] +charset = utf-8 +end_of_line = crlf +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.cs] +charset = utf-8-bom + +# Allman braces (opening brace on its own line) — the only style seen in this codebase. +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true + +# File-scoped namespaces are used everywhere. +csharp_style_namespace_declarations = file_scoped:warning + +# Explicit types are used almost everywhere (`var` appears once in the whole backend) — +# this is a deliberate house style, not an oversight. +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = false:suggestion +csharp_style_var_elsewhere = false:suggestion + +# Plain alphabetical using order — System is not special-cased to the top. +dotnet_sort_system_directives_first = false +dotnet_separate_import_directive_groups = false + +# Primary constructors are the established pattern for services/repositories with DI. +csharp_style_prefer_primary_constructors = true:suggestion + +[*.{json,yml,yaml}] +indent_size = 2 + +[*.{csproj,props,targets}] +indent_size = 2 + +# Naming: interfaces and private fields — both 100% consistent across the sampled codebase. +dotnet_naming_rule.interface_should_be_prefixed_with_i.severity = warning +dotnet_naming_rule.interface_should_be_prefixed_with_i.symbols = interface_type +dotnet_naming_rule.interface_should_be_prefixed_with_i.style = prefix_i + +dotnet_naming_symbols.interface_type.applicable_kinds = interface +dotnet_naming_symbols.interface_type.applicable_accessibilities = * + +dotnet_naming_style.prefix_i.required_prefix = I +dotnet_naming_style.prefix_i.capitalization = pascal_case + +dotnet_naming_rule.private_field_should_be_camel_case_with_underscore.severity = warning +dotnet_naming_rule.private_field_should_be_camel_case_with_underscore.symbols = private_field +dotnet_naming_rule.private_field_should_be_camel_case_with_underscore.style = underscore_camel_case + +dotnet_naming_symbols.private_field.applicable_kinds = field +dotnet_naming_symbols.private_field.applicable_accessibilities = private + +dotnet_naming_style.underscore_camel_case.required_prefix = _ +dotnet_naming_style.underscore_camel_case.capitalization = camel_case diff --git a/Club12-Backend/Directory.Build.props b/Club12-Backend/Directory.Build.props new file mode 100644 index 0000000..f68bb71 --- /dev/null +++ b/Club12-Backend/Directory.Build.props @@ -0,0 +1,10 @@ + + + 1.0.0 + $(NoWarn);CS1591 + + + + + + diff --git a/Club12-Backend/Dockerfile b/Club12-Backend/Dockerfile new file mode 100644 index 0000000..716d2c3 --- /dev/null +++ b/Club12-Backend/Dockerfile @@ -0,0 +1,32 @@ +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /src +COPY API/API.csproj API/ +COPY Application/Application.csproj Application/ +COPY Domain/Domain.csproj Domain/ +COPY Infrastructure/Infrastructure.csproj Infrastructure/ +RUN dotnet restore API/API.csproj +COPY API/ API/ +COPY Application/ Application/ +COPY Domain/ Domain/ +COPY Infrastructure/ Infrastructure/ +RUN dotnet publish API/API.csproj -c Release -o /app/publish --no-restore /p:UseAppHost=false + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime +RUN apt-get update \ + && apt-get install -y --no-install-recommends curl ca-certificates gnupg \ + && install -d /usr/share/postgresql-common/pgdg \ + && curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc \ + && echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(. /etc/os-release && echo $VERSION_CODENAME)-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends postgresql-client-17 \ + && rm -rf /var/lib/apt/lists/* +WORKDIR /app +COPY --from=build /app/publish . +ENV ASPNETCORE_ENVIRONMENT=Production \ + ASPNETCORE_HTTP_PORTS=8080 +EXPOSE 8080 +RUN mkdir -p /app/backups && chown $APP_UID:$APP_UID /app/backups +USER $APP_UID +HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \ + CMD curl -fsS http://localhost:8080/health || exit 1 +ENTRYPOINT ["dotnet", "API.dll"] diff --git a/Club12-Backend/Infrastructure/Backup/LocalDirectoryBackupStorage.cs b/Club12-Backend/Infrastructure/Backup/LocalDirectoryBackupStorage.cs new file mode 100644 index 0000000..2a70cec --- /dev/null +++ b/Club12-Backend/Infrastructure/Backup/LocalDirectoryBackupStorage.cs @@ -0,0 +1,90 @@ +using Application.Interfaces.Backup; + +using Microsoft.Extensions.Logging; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Infrastructure.Backup; + +/// +/// IBackupStorage implementation backed by a local or mounted directory, used as the fallback storage adapter when Supabase storage is not configured. +/// +public sealed class LocalDirectoryBackupStorage : IBackupStorage +{ + private readonly string _directoryPath; + private readonly ILogger _logger; + + public LocalDirectoryBackupStorage(string directoryPath, ILogger logger) + { + ArgumentException.ThrowIfNullOrWhiteSpace(directoryPath); + _logger = logger; + _directoryPath = Path.GetFullPath(directoryPath); + Directory.CreateDirectory(_directoryPath); + } + + public async Task StoreAsync(string name, Stream content, CancellationToken ct = default) + { + string path = ResolveSafePath(name); + await using FileStream file = File.Create(path); + await content.CopyToAsync(file, ct); + } + + public Task> ListAsync(CancellationToken ct = default) + { + IReadOnlyList files = Directory.EnumerateFiles(_directoryPath) + .Select(path => new BackupFile( + Path.GetFileName(path), + new DateTimeOffset(File.GetLastWriteTimeUtc(path), TimeSpan.Zero))) + .ToList(); + + return Task.FromResult(files); + } + + public Task OpenReadAsync(string name, CancellationToken ct = default) + { + string path = ResolveSafePath(name); + Stream stream = File.OpenRead(path); + return Task.FromResult(stream); + } + + public Task DeleteAsync(string name, CancellationToken ct = default) + { + string path = ResolveSafePath(name); + if (File.Exists(path)) + { + File.Delete(path); + } + else + { + _logger.LogWarning("DeleteAsync called for a backup that no longer exists: {Name}", name); + } + + return Task.CompletedTask; + } + + /// + /// Resolves name against the configured directory and throws ArgumentException unless the resolved path stays strictly inside it. + /// + private string ResolveSafePath(string name) + { + if (string.IsNullOrWhiteSpace(name) || Path.IsPathRooted(name)) + { + throw new ArgumentException("Backup file name must be a non-empty, relative name.", nameof(name)); + } + + string fullPath = Path.GetFullPath(Path.Combine(_directoryPath, name)); + string prefix = _directoryPath.EndsWith(Path.DirectorySeparatorChar) + ? _directoryPath + : _directoryPath + Path.DirectorySeparatorChar; + + return !fullPath.StartsWith(prefix, StringComparison.Ordinal) + ? throw new ArgumentException( + $"Backup file name '{name}' resolves outside the configured backup directory.", nameof(name)) + : fullPath; + } +} diff --git a/Club12-Backend/Infrastructure/Backup/PgDumpBackupService.cs b/Club12-Backend/Infrastructure/Backup/PgDumpBackupService.cs new file mode 100644 index 0000000..5b21cac --- /dev/null +++ b/Club12-Backend/Infrastructure/Backup/PgDumpBackupService.cs @@ -0,0 +1,97 @@ +using Application.Interfaces.Backup; +using Application.Utils.Constants.Configuration; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +using Infrastructure.Persistance; + +using Npgsql; + +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +namespace Infrastructure.Backup; + +/// +/// IDatabaseBackupService implementation that shells out to pg_dump via IProcessRunner, passing connection details as a discrete argument vector instead of a shell command string. +/// +public sealed class PgDumpBackupService( + IProcessRunner processRunner, + IConfiguration configuration, + ILogger logger) : IDatabaseBackupService +{ + /// + /// Matches a CREATE, DROP, ALTER, or COMMENT ON EVENT TRIGGER statement so it can be stripped from the dump before restore. + /// + private static readonly Regex EventTriggerStatementPattern = new( + @"^\s*(CREATE|DROP|ALTER|COMMENT\s+ON)\s+EVENT\s+TRIGGER\b.*?;\s*$", + RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled); + + + public async Task CreateDumpAsync(CancellationToken ct = default) + { + string connectionString = configuration.GetConnectionString(ConfigurationKeys.DbConnection) + ?? throw new BackupExecutionException( + $"ConnectionStrings:{ConfigurationKeys.DbConnection} is not configured."); + + string? configuredPgDumpPath = configuration[ConfigurationKeys.Backup.PgDumpPath]; + string pgDumpPath = string.IsNullOrWhiteSpace(configuredPgDumpPath) ? "pg_dump" : configuredPgDumpPath; + + NpgsqlConnectionStringBuilder builder = new(connectionString); + + List args = + [ + "-h", builder.Host ?? "localhost", + "-p", builder.Port.ToString(), + "-U", builder.Username ?? string.Empty, + "-d", builder.Database ?? string.Empty, + // These flags shift ownership and role safety to the dump so restoring into a Supabase-managed database with different owners does not fail on CREATE or ALTER OWNER statements. + "--clean", "--if-exists", "--no-owner", "--no-privileges", + // Restricting to public and Club12 schemas keeps pg_dump from capturing Supabase-platform-owned objects the app's role doesn't own, which would make --clean's DROP fail on restore. + // Club12 must be double-quoted because pg_dump's -n pattern matching lowercases an unquoted argument before comparing it against the catalog, and the schema's real name is mixed-case, so an unquoted argument silently matches nothing. + "-n", "public", "-n", $"\"{EntityConstants.Schema}\"", + ]; + + Dictionary? environmentVariables = null; + if (!string.IsNullOrEmpty(builder.Password)) + { + environmentVariables = new Dictionary + { + ["PGPASSWORD"] = builder.Password, + }; + } + + ProcessResult result = await processRunner.RunAsync(pgDumpPath, args, environmentVariables, ct); + + if (result.ExitCode != 0) + { + logger.LogError( + "pg_dump failed with exit code {ExitCode}. Path: '{PgDumpPath}'. StdErr: {StdErr}", + result.ExitCode, pgDumpPath, result.StdErr); + + throw new BackupExecutionException( + $"pg_dump failed (exit code {result.ExitCode}) using '{pgDumpPath}'. " + + "Verify pg_dump is installed and on PATH, or set Backup:PgDumpPath to its full path. " + + $"Details: {result.StdErr}"); + } + + int strippedCount = EventTriggerStatementPattern.Matches(result.StdOut).Count; + string filteredDump = EventTriggerStatementPattern.Replace(result.StdOut, string.Empty); + + if (strippedCount > 0) + { + logger.LogInformation( + "Stripped {Count} EVENT TRIGGER statement(s) from the pg_dump output — these are " + + "Supabase/PostgREST-managed infrastructure objects the app does not own and cannot " + + "DROP/CREATE on restore.", + strippedCount); + } + + return new MemoryStream(Encoding.UTF8.GetBytes(filteredDump)); + } +} diff --git a/Club12-Backend/Infrastructure/Backup/ProcessRunner.cs b/Club12-Backend/Infrastructure/Backup/ProcessRunner.cs new file mode 100644 index 0000000..69acefd --- /dev/null +++ b/Club12-Backend/Infrastructure/Backup/ProcessRunner.cs @@ -0,0 +1,64 @@ +using Application.Interfaces.Backup; + +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace Infrastructure.Backup; + +/// +/// IProcessRunner adapter backed by Process that passes arguments via ArgumentList instead of a concatenated shell command string. +/// +public sealed class ProcessRunner : IProcessRunner +{ + public async Task RunAsync( + string fileName, + IReadOnlyList args, + IReadOnlyDictionary? environmentVariables = null, + CancellationToken ct = default) + { + ProcessStartInfo startInfo = new() + { + FileName = fileName, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + + foreach (string arg in args) + { + startInfo.ArgumentList.Add(arg); + } + + if (environmentVariables is not null) + { + foreach (KeyValuePair kvp in environmentVariables) + { + startInfo.Environment[kvp.Key] = kvp.Value; + } + } + + using Process process = new() { StartInfo = startInfo }; + + try + { + process.Start(); + } + catch (Win32Exception ex) + { + return new ProcessResult(-1, string.Empty, $"Failed to start process '{fileName}': {ex.Message}"); + } + + Task stdOutTask = process.StandardOutput.ReadToEndAsync(ct); + Task stdErrTask = process.StandardError.ReadToEndAsync(ct); + + await process.WaitForExitAsync(ct); + string stdOut = await stdOutTask; + string stdErr = await stdErrTask; + + return new ProcessResult(process.ExitCode, stdOut, stdErr); + } +} diff --git a/Club12-Backend/Infrastructure/Backup/PsqlDatabaseRestoreService.cs b/Club12-Backend/Infrastructure/Backup/PsqlDatabaseRestoreService.cs new file mode 100644 index 0000000..159a8e8 --- /dev/null +++ b/Club12-Backend/Infrastructure/Backup/PsqlDatabaseRestoreService.cs @@ -0,0 +1,68 @@ +using Application.Interfaces.Backup; +using Application.Utils.Constants.Configuration; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +using Npgsql; + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Infrastructure.Backup; + +/// +/// IDatabaseRestoreService implementation that shells out to psql via IProcessRunner because pg_dump produces plain SQL, which only psql can replay. +/// +public sealed class PsqlDatabaseRestoreService( + IProcessRunner processRunner, + IConfiguration configuration, + ILogger logger) : IDatabaseRestoreService +{ + public async Task RestoreAsync(string dumpFilePath, CancellationToken ct = default) + { + string connectionString = configuration.GetConnectionString(ConfigurationKeys.DbConnection) + ?? throw new BackupExecutionException( + $"ConnectionStrings:{ConfigurationKeys.DbConnection} is not configured."); + + string? configuredPsqlPath = configuration[ConfigurationKeys.Backup.PsqlPath]; + string psqlPath = string.IsNullOrWhiteSpace(configuredPsqlPath) ? "psql" : configuredPsqlPath; + + NpgsqlConnectionStringBuilder builder = new(connectionString); + + List args = + [ + "-h", builder.Host ?? "localhost", + "-p", builder.Port.ToString(), + "-U", builder.Username ?? string.Empty, + "-d", builder.Database ?? string.Empty, + // ON_ERROR_STOP=1 turns the first SQL error in the dump into a non-zero exit code instead of continuing silently, so a partial or failed restore is detected instead of reported as success. + "-v", "ON_ERROR_STOP=1", + "-f", dumpFilePath, + ]; + + Dictionary? environmentVariables = null; + if (!string.IsNullOrEmpty(builder.Password)) + { + environmentVariables = new Dictionary + { + ["PGPASSWORD"] = builder.Password, + }; + } + + ProcessResult result = await processRunner.RunAsync(psqlPath, args, environmentVariables, ct); + + if (result.ExitCode != 0) + { + logger.LogError( + "psql restore failed with exit code {ExitCode}. Path: '{PsqlPath}'. StdErr: {StdErr}", + result.ExitCode, psqlPath, result.StdErr); + + throw new BackupExecutionException( + $"psql restore failed (exit code {result.ExitCode}) using '{psqlPath}'. " + + "Verify psql is installed and on PATH, or set Backup:PsqlPath to its full path. " + + $"Details: {result.StdErr}"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Backup/SupabaseBackupStorage.cs b/Club12-Backend/Infrastructure/Backup/SupabaseBackupStorage.cs new file mode 100644 index 0000000..8c48a25 --- /dev/null +++ b/Club12-Backend/Infrastructure/Backup/SupabaseBackupStorage.cs @@ -0,0 +1,105 @@ +using Application.Interfaces.Backup; +using Application.Utils.Helper.SupabaseHelper; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Infrastructure.Backup; + +/// +/// IBackupStorage implementation that reuses the existing Supabase client and credentials through ISupabaseRawStorage instead of provisioning a second Supabase client. +/// +public sealed class SupabaseBackupStorage(ISupabaseRawStorage rawStorage) : IBackupStorage +{ + private const string Prefix = "backups/"; + + public async Task StoreAsync(string name, Stream content, CancellationToken ct = default) + { + string objectPath = ToObjectPath(name); + + try + { + await rawStorage.UploadRawAsync(objectPath, content); + } + catch (Exception ex) + { + throw new BackupExecutionException( + $"Failed to upload backup '{name}' to Supabase storage: {ex.Message}", ex); + } + } + + public async Task OpenReadAsync(string name, CancellationToken ct = default) + { + string objectPath = ToObjectPath(name); + + try + { + byte[] bytes = await rawStorage.DownloadRawAsync(objectPath); + return new MemoryStream(bytes); + } + catch (Exception ex) + { + throw new BackupExecutionException( + $"Failed to download backup '{name}' from Supabase storage: {ex.Message}", ex); + } + } + + public async Task> ListAsync(CancellationToken ct = default) + { + try + { + IReadOnlyList entries = await rawStorage.ListRawAsync(Prefix); + return entries + .Where(entry => !string.IsNullOrEmpty(entry.Name)) + .Select(entry => new BackupFile(entry.Name, entry.UpdatedAt ?? DateTimeOffset.MinValue)) + .ToList(); + } + catch (Exception ex) + { + throw new BackupExecutionException($"Failed to list Supabase backups: {ex.Message}", ex); + } + } + + public async Task DeleteAsync(string name, CancellationToken ct = default) + { + string objectPath = ToObjectPath(name); + + try + { + await rawStorage.RemoveRawAsync(objectPath); + } + catch (Exception ex) + { + throw new BackupExecutionException( + $"Failed to delete backup '{name}' from Supabase storage: {ex.Message}", ex); + } + } + + /// + /// Resolves name against the backups/ prefix and throws ArgumentException unless it is a simple, non-rooted, non-traversing relative name. + /// + private static string ToObjectPath(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException("Backup file name must be non-empty.", nameof(name)); + } + + if (Path.IsPathRooted(name)) + { + throw new ArgumentException( + $"Backup file name '{name}' must be a relative name, not a rooted path.", nameof(name)); + } + + string normalized = name.Replace('\\', '/'); + string[] segments = normalized.Split('/', StringSplitOptions.RemoveEmptyEntries); + return segments.Length == 0 || Array.Exists(segments, segment => segment is ".." or ".") + ? throw new ArgumentException( + $"Backup file name '{name}' resolves outside the configured '{Prefix}' prefix.", nameof(name)) + : Prefix + normalized; + } +} diff --git a/Club12-Backend/Infrastructure/Email/EmailTemplateLoader.cs b/Club12-Backend/Infrastructure/Email/EmailTemplateLoader.cs new file mode 100644 index 0000000..4970922 --- /dev/null +++ b/Club12-Backend/Infrastructure/Email/EmailTemplateLoader.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace Infrastructure.Email; + +/// +/// Loads HTML email templates from embedded resources and replaces named tokens with their corresponding values. +/// +internal static class EmailTemplateLoader +{ + private static readonly Assembly _assembly = typeof(EmailTemplateLoader).Assembly; + + /// + /// Reads the template file and replaces every key in tokens with its corresponding value. + /// + /// + /// File name without extension. + /// The file must be at Utils/Helper/Email/Templates/{templateName}.html + /// and marked as EmbeddedResource. + /// + /// Dictionary of {{Token}} → replacement value pairs. + public static string Render(string templateName, Dictionary tokens) + { + string resourceName = + $"Infrastructure.Email.Templates.{templateName}.html"; + + using Stream? stream = _assembly.GetManifestResourceStream(resourceName) ?? throw new InvalidOperationException( + $"Email template '{templateName}' not found as embedded resource. " + + $"Ensure the file exists at 'Email/Templates/{templateName}.html' " + + $"and is marked as in the Infrastructure.csproj."); + using StreamReader reader = new(stream); + string template = reader.ReadToEnd(); + + return tokens.Aggregate(template, + (current, token) => current.Replace(token.Key, token.Value)); + } +} diff --git a/Club12-Backend/Infrastructure/Email/FluentEmailHelper.cs b/Club12-Backend/Infrastructure/Email/FluentEmailHelper.cs new file mode 100644 index 0000000..7b32e3c --- /dev/null +++ b/Club12-Backend/Infrastructure/Email/FluentEmailHelper.cs @@ -0,0 +1,82 @@ +using Application.Interfaces.Services; + +using FluentEmail.Core; +using FluentEmail.Core.Models; + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Infrastructure.Email; + +/// +/// Transactional email sender backed by FluentEmail and SendGrid. +/// +public sealed class FluentEmailHelper(IFluentEmailFactory emailFactory) : IEmailService +{ + public async Task SendPasswordResetAsync( + string toEmail, string toUsername, string resetLink, + CancellationToken ct = default) + { + string body = EmailTemplateLoader.Render("PasswordResetTemplate", new() + { + ["{{Username}}"] = toUsername, + ["{{ResetLink}}"] = resetLink, + }); + + SendResponse result = await emailFactory.Create() + .To(toEmail) + .Subject("Restablecimiento de contraseña - Club12") + .Body(body, isHtml: true) + .SendAsync(ct); + + ThrowIfFailed(result, "password reset"); + } + + public async Task SendWelcomeSetPasswordAsync( + string toEmail, string toUsername, string setPasswordLink, + CancellationToken ct = default) + { + string body = EmailTemplateLoader.Render("WelcomeSetPasswordTemplate", new() + { + ["{{Username}}"] = toUsername, + ["{{SetPasswordLink}}"] = setPasswordLink, + }); + + SendResponse result = await emailFactory.Create() + .To(toEmail) + .Subject("Activá tu cuenta - Club12") + .Body(body, isHtml: true) + .SendAsync(ct); + + ThrowIfFailed(result, "welcome set-password"); + } + + public async Task SendMagicLinkAsync( + string toEmail, string toUsername, string magicLink, + CancellationToken ct = default) + { + string body = EmailTemplateLoader.Render("MagicLinkTemplate", new() + { + ["{{Username}}"] = toUsername, + ["{{MagicLink}}"] = magicLink, + }); + + SendResponse result = await emailFactory.Create() + .To(toEmail) + .Subject("Tu enlace de acceso - Club12") + .Body(body, isHtml: true) + .SendAsync(ct); + + ThrowIfFailed(result, "magic link"); + } + + private static void ThrowIfFailed(SendResponse result, string context) + { + if (!result.Successful) + { + throw new InvalidOperationException( + $"Failed to send {context} email: {string.Join(", ", result.ErrorMessages)}"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Email/Templates/MagicLinkTemplate.html b/Club12-Backend/Infrastructure/Email/Templates/MagicLinkTemplate.html new file mode 100644 index 0000000..e9936a2 --- /dev/null +++ b/Club12-Backend/Infrastructure/Email/Templates/MagicLinkTemplate.html @@ -0,0 +1,67 @@ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+

+ Club12 +

+
+

+ Tu enlace de acceso +

+

+ Hola, {{Username}}. +

+

+ Tu enlace de acceso a Club12 está listo. + Hacé clic en el botón para ingresar a tu cuenta. +

+

+ ⚠ Este enlace expira en 15 minutos y es de un solo uso. +

+ + Ingresar a Club12 + +
+

+ Si no solicitaste este enlace, podés ignorarlo de forma segura.
+ Nadie puede ingresar a tu cuenta sin hacer clic en él. +

+
+
+ + \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Email/Templates/PasswordResetTemplate.html b/Club12-Backend/Infrastructure/Email/Templates/PasswordResetTemplate.html new file mode 100644 index 0000000..ce96125 --- /dev/null +++ b/Club12-Backend/Infrastructure/Email/Templates/PasswordResetTemplate.html @@ -0,0 +1,68 @@ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+

+ Club12 +

+
+

+ Restablecimiento de contraseña +

+

+ Hola, {{Username}}. +

+

+ Un administrador ha solicitado el restablecimiento de tu contraseña + en Club12. Hacé clic en el botón para establecer + una nueva contraseña. +

+

+ ⚠ Este enlace expira en 24 horas. +

+ + Restablecer contraseña + +
+

+ Si no esperabas este correo, podés ignorarlo de forma segura.
+ No se realizarán cambios hasta que uses el enlace. +

+
+
+ + \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Email/Templates/WelcomeSetPasswordTemplate.html b/Club12-Backend/Infrastructure/Email/Templates/WelcomeSetPasswordTemplate.html new file mode 100644 index 0000000..2ba4229 --- /dev/null +++ b/Club12-Backend/Infrastructure/Email/Templates/WelcomeSetPasswordTemplate.html @@ -0,0 +1,56 @@ + + + + + + + + + + + +
+ + + + + + + + + + + + +
+

+ Club12 +

+
+

+ Tu cuenta fue creada +

+

+ Hola, {{Username}}. +

+

+ Tu cuenta en Club12 fue creada con una contraseña temporal. + Para activar el acceso, definí tu nueva contraseña desde el siguiente enlace. +

+ + Definir contraseña + +
+

+ Si no esperabas este correo, podés ignorarlo. +

+
+
+ + \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Identity/ApplicationUser.cs b/Club12-Backend/Infrastructure/Identity/ApplicationUser.cs new file mode 100644 index 0000000..7c5f3df --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/ApplicationUser.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Identity; + +using System; + +namespace Infrastructure.Identity; + +/// +/// Identity user for authentication, kept separate from domain entities. +/// +public class ApplicationUser : IdentityUser +{ + /// + /// Opaque refresh token stored server-side. + /// + public string? RefreshToken { get; set; } + + /// + /// UTC expiry for the stored refresh token. + /// + public DateTime? RefreshTokenExpiryTime { get; set; } + + /// + /// The Id of the OWNER who registered this account, when it was created by an owner rather than an admin. + /// + public Guid? CreatedByOwnerId { get; set; } + + public DateTime DateCreated { get; set; } = DateTime.UtcNow; + + public bool MustChangePassword { get; set; } +} diff --git a/Club12-Backend/Infrastructure/Identity/IdentityAppDbContext.cs b/Club12-Backend/Infrastructure/Identity/IdentityAppDbContext.cs new file mode 100644 index 0000000..da38f66 --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/IdentityAppDbContext.cs @@ -0,0 +1,49 @@ +using Domain.Enums; + +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +using System; + +namespace Infrastructure.Identity; + +/// +/// EF Core context for ASP.NET Core Identity tables, generating AspNetUsers, AspNetRoles, and related tables. +/// +public class IdentityAppDbContext(DbContextOptions options) + : IdentityDbContext, Guid>(options) +{ + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.Entity() + .HasIndex(u => u.NormalizedEmail) + .IsUnique(); + + SeedRoles(builder); + } + + private static void SeedRoles(ModelBuilder builder) + { + // Seed values 3 and 4 are intentionally skipped because the former TournamentManager and TeamManager roles were removed; Guest keeps its original seed value 5 so its role Id stays stable. + builder.Entity>().HasData( + CreateRole(1, UserRoleType.ADMIN), + CreateRole(2, UserRoleType.OWNER), + CreateRole(5, UserRoleType.GUEST) + ); + } + + private static IdentityRole CreateRole(int seed, UserRoleType roleType) + { + string name = roleType.ToRoleName(); + return new IdentityRole + { + Id = new Guid($"00000000-0000-0000-0000-{seed:D12}"), + Name = name, + NormalizedName = name.ToUpperInvariant(), + ConcurrencyStamp = seed.ToString() + }; + } +} \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Identity/IdentityAppDbContextFactory.cs b/Club12-Backend/Infrastructure/Identity/IdentityAppDbContextFactory.cs new file mode 100644 index 0000000..a8a19e9 --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/IdentityAppDbContextFactory.cs @@ -0,0 +1,78 @@ +using Application.Utils.Constants.Configuration; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +using System; +using System.IO; +using System.Linq; + +namespace Infrastructure.Identity; + +/// +/// Provides a design-time instance of IdentityAppDbContext for EF Core CLI tools. +/// +public sealed class IdentityAppDbContextFactory : IDesignTimeDbContextFactory +{ + /// + /// Builds configuration the same way WebApplication.CreateBuilder does at runtime, with later appsettings files overriding earlier ones. + /// + public IdentityAppDbContext CreateDbContext(string[] args) + { + string basePath = ResolveBasePath(); + + string aspNetEnv = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") + ?? "Development"; + + ConfigurationBuilder builder = new(); + builder.SetBasePath(basePath); + builder.AddJsonFile("appsettings.json", optional: true); + builder.AddJsonFile($"appsettings.{aspNetEnv}.json", optional: true); + + if (Directory.Exists(basePath)) + { + string?[] extras = Directory + .GetFiles(basePath, "appsettings.*.json") + .Select(Path.GetFileName) + .Where(f => !string.Equals( + f, $"appsettings.{aspNetEnv}.json", + StringComparison.OrdinalIgnoreCase)) + .ToArray(); + + foreach (string? file in extras) + { + builder.AddJsonFile(file!, optional: true); + } + } + + IConfigurationRoot config = builder.Build(); + + string connectionString = config.GetConnectionString(ConfigurationKeys.DbConnection) + ?? throw new InvalidOperationException( + $"Connection string '{ConfigurationKeys.DbConnection}' not found.\n" + + $"Searched in: {basePath}\n" + + $"Files loaded: appsettings.json, appsettings.{aspNetEnv}.json + all appsettings.*.json"); + + DbContextOptionsBuilder optionsBuilder = new(); + optionsBuilder.UseNpgsql(connectionString); + + return new IdentityAppDbContext(optionsBuilder.Options); + } + + private static string ResolveBasePath() + { + string current = Directory.GetCurrentDirectory(); + + string[] candidates = + [ + current, + Path.Combine(current, "API"), + Path.Combine(current, "..", "API"), + ]; + + string? match = Array.Find(candidates, path => File.Exists(Path.Combine(path, "appsettings.json"))); + + return match is null ? current : Path.GetFullPath(match); + } +} \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Identity/IdentityAuthenticationService.cs b/Club12-Backend/Infrastructure/Identity/IdentityAuthenticationService.cs new file mode 100644 index 0000000..6bd2eac --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/IdentityAuthenticationService.cs @@ -0,0 +1,401 @@ +using Application.DTOs.Auth.Request; +using Application.DTOs.Auth.Response; +using Application.Interfaces.Services; +using Application.Utils.Constants; +using Application.Utils.Constants.Auth; +using Application.Utils.Constants.Configuration; + +using Domain.Enums; + +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; + +namespace Infrastructure.Identity; + +/// +/// Infrastructure implementation of IAuthenticationService backed by ASP.NET Core Identity. +/// +public sealed class IdentityAuthenticationService( + UserManager userManager, + IAuthService authService, + IEmailService emailService, + IConfiguration configuration) : IAuthenticationService +{ + private const string MagicLinkPurpose = "MagicLink"; + private const int RefreshExpiryDays = 7; + + private static readonly Dictionary> _creationPolicy = + new(StringComparer.OrdinalIgnoreCase) + { + [UserRoleType.ADMIN.ToRoleName()] = new(StringComparer.OrdinalIgnoreCase) + { + UserRoleType.ADMIN.ToRoleName(), + UserRoleType.OWNER.ToRoleName() + }, + [UserRoleType.OWNER.ToRoleName()] = new(StringComparer.OrdinalIgnoreCase) + { + UserRoleType.ADMIN.ToRoleName(), + UserRoleType.OWNER.ToRoleName() + } + }; + + /// + public async Task RegisterAsync( + RegisterUserRequest request, string callerRole, Guid callerId, CancellationToken ct = default) + { + EnforceCreationPolicy(callerRole, request.Role); + + ApplicationUser? existing = await userManager.FindByEmailAsync(request.Email); + if (existing is not null) + { + throw new InvalidOperationException(ErrorMessages.Auth.EmailAlreadyExists); + } + + ApplicationUser user = new() + { + UserName = request.Username, + Email = request.Email, + EmailConfirmed = true, + PhoneNumber = request.Phone, + CreatedByOwnerId = IsOwner(callerRole) ? callerId : null, + MustChangePassword = true + }; + + string temporaryPassword = GenerateTemporaryPassword(); + + IdentityResult result = await userManager.CreateAsync(user, temporaryPassword); + + if (!result.Succeeded) + { + string errors = string.Join(" | ", result.Errors.Select(e => e.Description)); + throw new InvalidOperationException(ErrorMessages.Auth.UserCreationFailed(errors)); + } + + await userManager.AddToRoleAsync(user, request.Role.ToUpperInvariant()); + + string token = await userManager.GeneratePasswordResetTokenAsync(user); + + string frontendUrl = configuration[ConfigurationKeys.Frontend.PasswordResetUrl] + ?? throw new InvalidOperationException( + ErrorMessages.Configuration.KeyNotConfigured(ConfigurationKeys.Frontend.PasswordResetUrl)); + + string setPasswordLink = + $"{frontendUrl}" + + $"?email={Uri.EscapeDataString(user.Email!)}" + + $"&token={Uri.EscapeDataString(token)}"; + + await emailService.SendWelcomeSetPasswordAsync(user.Email!, user.UserName!, setPasswordLink, ct); + + return new RegisterUserResponse( + user.Id, user.Email!, user.UserName!, request.Role.ToUpperInvariant(), user.PhoneNumber); + } + + /// + public async Task InviteUserAsync( + InviteUserRequest request, string callerRole, Guid callerId, CancellationToken ct = default) + { + EnforceCreationPolicy(callerRole, request.Role); + + ApplicationUser? existing = await userManager.FindByEmailAsync(request.Email); + if (existing is not null) + { + throw new InvalidOperationException(ErrorMessages.Auth.EmailAlreadyExists); + } + + // No password is set for an invited account; UserName mirrors the email since Identity requires a unique UserName, and MustChangePassword keeps the account gated until activation completes. + ApplicationUser user = new() + { + UserName = request.Email, + Email = request.Email, + EmailConfirmed = true, + PhoneNumber = request.Phone, + CreatedByOwnerId = IsOwner(callerRole) ? callerId : null, + MustChangePassword = true + }; + + IdentityResult result = await userManager.CreateAsync(user); + + if (!result.Succeeded) + { + string errors = string.Join(" | ", result.Errors.Select(e => e.Description)); + throw new InvalidOperationException(ErrorMessages.Auth.UserCreationFailed(errors)); + } + + await userManager.AddToRoleAsync(user, request.Role.ToUpperInvariant()); + + // The activation token is a standard Identity password-reset token, which works on a passwordless account because it derives from the SecurityStamp rather than an existing password, and it is later verified by ActivateAccountAsync's ResetPasswordAsync call. + string token = await userManager.GeneratePasswordResetTokenAsync(user); + string activationLink = BuildTokenLink(user.Email!, token, activation: true); + + await emailService.SendWelcomeSetPasswordAsync(user.Email!, user.UserName!, activationLink, ct); + + return new InviteUserResponse(user.Id, user.Email!, request.Role.ToUpperInvariant()); + } + + /// + public async Task ActivateAccountAsync( + ActivateAccountRequest request, CancellationToken ct = default) + { + ApplicationUser user = await userManager.FindByEmailAsync(request.Email) + ?? throw new UnauthorizedAccessException(ErrorMessages.Auth.InvalidPasswordResetRequest); + + // ResetPasswordAsync sets the password hash whether or not one already existed, so it doubles as the first password set for an invited, passwordless account. + IdentityResult result = await userManager.ResetPasswordAsync( + user, request.Token, request.NewPassword); + + if (!result.Succeeded) + { + throw new InvalidOperationException( + string.Join("; ", result.Errors.Select(e => e.Description))); + } + + user.MustChangePassword = false; + await userManager.UpdateAsync(user); + + IList roles = await userManager.GetRolesAsync(user); + return await BuildTokenResponseAsync(user, roles, ct); + } + + /// + public async Task RequestPasswordResetAsync( + RequestPasswordResetRequest request, CancellationToken ct = default) + { + ApplicationUser? user = await userManager.FindByEmailAsync(request.Email); + + // Silently no-op for an unknown email so the endpoint never reveals which addresses have accounts. + if (user is null) + { + return; + } + + string token = await userManager.GeneratePasswordResetTokenAsync(user); + string resetLink = BuildTokenLink(user.Email!, token, activation: false); + + await emailService.SendPasswordResetAsync(user.Email!, user.UserName!, resetLink, ct); + } + + /// + public async Task LoginAsync(LogInUserRequest request, CancellationToken ct = default) + { + ApplicationUser user = await userManager.FindByEmailAsync(request.Email) + ?? throw new UnauthorizedAccessException(ErrorMessages.Auth.InvalidCredentials); + + if (!await userManager.CheckPasswordAsync(user, request.Password)) + { + throw new UnauthorizedAccessException(ErrorMessages.Auth.InvalidCredentials); + } + + if (await userManager.IsLockedOutAsync(user)) + { + throw new UnauthorizedAccessException(ErrorMessages.Auth.AccountDeactivated); + } + + // The role model is now just the two password-based operator accounts; the former TeamManager-only magic-link gate was removed along with that role. + IList roles = await userManager.GetRolesAsync(user); + return await BuildTokenResponseAsync(user, roles, ct); + } + + /// + public async Task RequestMagicLinkAsync( + MagicLinkRequest request, CancellationToken ct = default) + { + ApplicationUser user = await userManager.FindByEmailAsync(request.Email) + ?? throw new KeyNotFoundException(ErrorMessages.Auth.NoAccountForEmail); + + // Magic-link was the TeamManager-only auth path; that role no longer exists and the flow is deferred, so the role gate was dropped but the token flow itself is left in place for future use. + string token = await userManager.GenerateUserTokenAsync( + user, TokenOptions.DefaultEmailProvider, MagicLinkPurpose); + + string magicLink = + $"/api/auth/magic-link/login" + + $"?email={Uri.EscapeDataString(user.Email!)}" + + $"&token={Uri.EscapeDataString(token)}"; + + return new MagicLinkResponse("Magic link generated. Check your email.", magicLink); + } + + /// + public async Task MagicLinkLoginAsync( + MagicLinkLoginRequest request, CancellationToken ct = default) + { + ApplicationUser user = await userManager.FindByEmailAsync(request.Email) + ?? throw new UnauthorizedAccessException(ErrorMessages.Auth.InvalidMagicLink); + + bool valid = await userManager.VerifyUserTokenAsync( + user, TokenOptions.DefaultEmailProvider, MagicLinkPurpose, request.Token); + + if (!valid) + { + throw new UnauthorizedAccessException(ErrorMessages.Auth.MagicLinkAlreadyUsed); + } + + if (await userManager.IsLockedOutAsync(user)) + { + throw new UnauthorizedAccessException(ErrorMessages.Auth.AccountDeactivated); + } + + IList roles = await userManager.GetRolesAsync(user); + return await BuildTokenResponseAsync(user, roles, ct); + } + + /// + public async Task ConfirmPasswordResetAsync( + PasswordResetConfirmRequest request, CancellationToken ct = default) + { + ApplicationUser user = await userManager.FindByEmailAsync(request.Email) + ?? throw new UnauthorizedAccessException(ErrorMessages.Auth.InvalidPasswordResetRequest); + + IdentityResult result = await userManager.ResetPasswordAsync( + user, request.Token, request.NewPassword); + + if (!result.Succeeded) + { + throw new InvalidOperationException( + string.Join("; ", result.Errors.Select(e => e.Description))); + } + + user.MustChangePassword = false; + await userManager.UpdateAsync(user); + + IList roles = await userManager.GetRolesAsync(user); + return await BuildTokenResponseAsync(user, roles, ct); + } + + /// + public async Task GuestAsync(CancellationToken ct = default) + { + Claim[] claims = [new(ClaimTypes.Role, UserRoleType.GUEST.ToRoleName())]; + TokenResponse response = await authService.GenerateJwtTokenAsync(claims, ct); + return new TokenResponse(response.AccessToken, response.ExpiresIn, refreshToken: null); + } + + /// + public async Task RefreshAsync( + RefreshTokenRequest request, CancellationToken ct = default) + { + ApplicationUser user = await userManager.Users + .FirstOrDefaultAsync(u => u.RefreshToken == request.RefreshToken, ct) + ?? throw new UnauthorizedAccessException(ErrorMessages.Auth.InvalidRefreshToken); + + if (user.RefreshTokenExpiryTime <= DateTime.UtcNow) + { + throw new UnauthorizedAccessException(ErrorMessages.Auth.RefreshTokenExpired); + } + + IList roles = await userManager.GetRolesAsync(user); + return await BuildTokenResponseAsync(user, roles, ct); + } + + /// + public async Task LogoutAsync(Guid userId, CancellationToken ct = default) + { + ApplicationUser? user = await userManager.FindByIdAsync(userId.ToString()); + if (user is not null) + { + user.RefreshToken = null; + user.RefreshTokenExpiryTime = null; + await userManager.UpdateAsync(user); + } + } + + private async Task BuildTokenResponseAsync( + ApplicationUser user, IList roles, CancellationToken ct) + { + List claims = + [ + new(ClaimTypes.NameIdentifier, user.Id.ToString()), + new(ClaimTypes.Email, user.Email!), + .. roles.Select(r => new Claim(ClaimTypes.Role, r)) + ]; + + if (user.MustChangePassword) + { + claims.Add(new Claim(CustomClaimTypes.MustChangePassword, "true")); + } + + TokenResponse response = await authService.GenerateJwtTokenAsync(claims, ct); + + user.RefreshToken = response.RefreshToken; + user.RefreshTokenExpiryTime = DateTime.UtcNow.AddDays(RefreshExpiryDays); + await userManager.UpdateAsync(user); + + return response; + } + + /// + /// Builds an email link that carries an Identity token, using ActivationUrl for activation links and PasswordResetUrl otherwise. + /// + private string BuildTokenLink(string email, string token, bool activation) + { + string baseUrl = activation + ? configuration[ConfigurationKeys.Frontend.ActivationUrl] + ?? configuration[ConfigurationKeys.Frontend.PasswordResetUrl] + ?? throw new InvalidOperationException( + ErrorMessages.Configuration.KeyNotConfigured(ConfigurationKeys.Frontend.PasswordResetUrl)) + : configuration[ConfigurationKeys.Frontend.PasswordResetUrl] + ?? throw new InvalidOperationException( + ErrorMessages.Configuration.KeyNotConfigured(ConfigurationKeys.Frontend.PasswordResetUrl)); + + return $"{baseUrl}" + + $"?email={Uri.EscapeDataString(email)}" + + $"&token={Uri.EscapeDataString(token)}"; + } + + private static string GenerateTemporaryPassword(int length = 16) + { + if (length < 8) + { + length = 8; + } + + const string upper = "ABCDEFGHJKLMNPQRSTUVWXYZ"; + const string lower = "abcdefghijkmnopqrstuvwxyz"; + const string digits = "23456789"; + const string special = "!@#$%^&*_-+=?"; + string all = upper + lower + digits + special; + + char[] chars = new char[length]; + chars[0] = upper[RandomNumberGenerator.GetInt32(upper.Length)]; + chars[1] = lower[RandomNumberGenerator.GetInt32(lower.Length)]; + chars[2] = digits[RandomNumberGenerator.GetInt32(digits.Length)]; + chars[3] = special[RandomNumberGenerator.GetInt32(special.Length)]; + + for (int i = 4; i < chars.Length; i++) + { + chars[i] = all[RandomNumberGenerator.GetInt32(all.Length)]; + } + + for (int i = chars.Length - 1; i > 0; i--) + { + int j = RandomNumberGenerator.GetInt32(i + 1); + (chars[i], chars[j]) = (chars[j], chars[i]); + } + + return new string(chars); + } + + private static void EnforceCreationPolicy(string callerRole, string targetRole) + { + bool allowed = _creationPolicy.TryGetValue(callerRole, out HashSet? permitted) + && permitted.Contains(targetRole); + + if (!allowed) + { + throw new UnauthorizedAccessException(ErrorMessages.Auth.RoleNotAllowedToCreate(callerRole, targetRole)); + } + } + + private static bool IsOwner(string role) + { + return role.Equals(UserRoleType.OWNER.ToRoleName(), StringComparison.OrdinalIgnoreCase); + } +} diff --git a/Club12-Backend/Infrastructure/Identity/IdentitySeeder.cs b/Club12-Backend/Infrastructure/Identity/IdentitySeeder.cs new file mode 100644 index 0000000..370acaa --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/IdentitySeeder.cs @@ -0,0 +1,65 @@ +using Application.Utils.Constants.Configuration; + +using Domain.Enums; + +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +using System.Linq; +using System.Threading.Tasks; + +namespace Infrastructure.Identity; + +/// +/// Seeds the initial admin user into the Identity database at startup. +/// +public sealed class IdentitySeeder( + UserManager userManager, + IConfiguration configuration, + ILogger logger) +{ + public async Task SeedAsync() + { + await SeedAdminAsync(); + } + + private async Task SeedAdminAsync() + { + string? email = configuration[ConfigurationKeys.AdminUser.Email]; + string? password = configuration[ConfigurationKeys.AdminUser.Password]; + + if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(password)) + { + logger.LogWarning( + "AdminUser:Email or AdminUser:Password not configured — skipping admin seeding."); + return; + } + + if (await userManager.FindByEmailAsync(email) is not null) + { + logger.LogInformation("Admin user '{Email}' already exists — skipping.", email); + return; + } + + ApplicationUser admin = new() + { + UserName = email, + Email = email, + EmailConfirmed = true, + }; + + IdentityResult result = await userManager.CreateAsync(admin, password); + + if (!result.Succeeded) + { + string errors = string.Join(", ", result.Errors.Select(e => e.Description)); + logger.LogError("Failed to create admin user '{Email}': {Errors}", email, errors); + return; + } + + await userManager.AddToRoleAsync(admin, UserRoleType.ADMIN.ToRoleName()); + + logger.LogInformation("Admin user '{Email}' created and assigned role ADMIN.", email); + } +} \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Identity/IdentityUserManagementService.cs b/Club12-Backend/Infrastructure/Identity/IdentityUserManagementService.cs new file mode 100644 index 0000000..9cfeae2 --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/IdentityUserManagementService.cs @@ -0,0 +1,397 @@ +using Application.DTOs.Abstract.Request; +using Application.DTOs.Abstract.Response; +using Application.DTOs.User.Request; +using Application.DTOs.User.Response; +using Application.Interfaces.Services; +using Application.Utils.Constants; +using Application.Utils.Constants.Configuration; + +using Domain.Enums; + +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; + +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Infrastructure.Identity; + +/// +/// Identity-backed implementation of IUserManagementService that enforces all access-control rules, keeping controllers thin. +/// +public sealed class IdentityUserManagementService( + UserManager userManager, + IdentityAppDbContext identityDbContext, + IEmailService emailService, + IConfiguration configuration) : IUserManagementService +{ + /// + public async Task> GetAllAsync( + string callerRole, Guid callerId, + UserFilteredRequest filter, + CancellationToken ct = default) + { + IQueryable query = ResolveVisibleUsersQuery(callerRole); + + if (!string.IsNullOrWhiteSpace(filter.UserName)) + { + query = query.Where(u => u.UserName != null && + u.UserName.ToLower().Contains(filter.UserName.ToLower())); + } + + if (!string.IsNullOrWhiteSpace(filter.Email)) + { + query = query.Where(u => u.Email != null && + u.Email.ToLower().Contains(filter.Email.ToLower())); + } + + if (!string.IsNullOrWhiteSpace(filter.PhoneNumber)) + { + query = query.Where(u => u.PhoneNumber != null && + u.PhoneNumber.Contains(filter.PhoneNumber)); + } + + if (filter.Role.HasValue) + { + string roleName = filter.Role.Value.ToRoleName(); + + IQueryable userIdsWithRole = + from ur in identityDbContext.UserRoles + join r in identityDbContext.Roles on ur.RoleId equals r.Id + where r.Name == roleName + select ur.UserId; + + query = query.Where(u => userIdsWithRole.Contains(u.Id)); + } + + int totalCount = await query.CountAsync(ct); + + query = filter.OrderBy?.ToLower() switch + { + "username" => filter.Order == SortOrder.Descending + ? query.OrderByDescending(u => u.UserName) + : query.OrderBy(u => u.UserName), + + "email" => filter.Order == SortOrder.Descending + ? query.OrderByDescending(u => u.Email) + : query.OrderBy(u => u.Email), + + _ => filter.Order == SortOrder.Descending + ? query.OrderByDescending(u => u.DateCreated) + : query.OrderBy(u => u.DateCreated), + }; + + int skip = (filter.PageNumber - 1) * filter.PageSize; + List users = await query.Skip(skip).Take(filter.PageSize).ToListAsync(ct); + + IReadOnlyList items = await MapManyAsync(users); + + return new PaginatedResponse + { + Items = items, + Page = filter.PageNumber, + PageSize = filter.PageSize, + TotalCount = totalCount, + }; + } + + /// + public async Task GetByIdAsync( + string callerRole, Guid callerId, Guid userId, CancellationToken ct = default) + { + ApplicationUser user = await FindOrThrowAsync(userId); + EnforceReadAccess(user, callerRole, callerId); + return await MapOneAsync(user); + } + + /// + public async Task UpdateAsync( + string callerRole, Guid callerId, Guid userId, + UpdateUserRequest request, CancellationToken ct = default) + { + ApplicationUser user = await FindOrThrowAsync(userId); + EnforceWriteAccess(user, callerRole, callerId); + + if (!string.IsNullOrWhiteSpace(request.Username)) + { + ThrowIfFailed(await userManager.SetUserNameAsync(user, request.Username)); + } + + if (!string.IsNullOrWhiteSpace(request.Email)) + { + ThrowIfFailed(await userManager.SetEmailAsync(user, request.Email)); + } + + if (request.Phone is not null) + { + ThrowIfFailed(await userManager.SetPhoneNumberAsync(user, request.Phone)); + } + + if (request.Role.HasValue) + { + await ChangeRoleAsync(user, request.Role.Value, callerRole, callerId); + } + + return await MapOneAsync(user); + } + + /// + public async Task ChangePasswordAsync( + string callerRole, Guid callerId, Guid userId, + ChangePasswordRequest request, CancellationToken ct = default) + { + ApplicationUser user = await FindOrThrowAsync(userId); + EnforceWriteAccess(user, callerRole, callerId); + + bool isSelfUpdate = user.Id == callerId; + + if (isSelfUpdate) + { + if (string.IsNullOrWhiteSpace(request.CurrentPassword)) + { + throw new ArgumentException(ErrorMessages.User.CurrentPasswordRequired); + } + + ThrowIfFailed(await userManager.ChangePasswordAsync( + user, request.CurrentPassword, request.NewPassword)); + } + else + { + ThrowIfFailed(await userManager.RemovePasswordAsync(user)); + ThrowIfFailed(await userManager.AddPasswordAsync(user, request.NewPassword)); + } + + user.MustChangePassword = false; + ThrowIfFailed(await userManager.UpdateAsync(user)); + } + + /// + /// Generates a password reset token and flags the account so MustChangePasswordMiddleware blocks access until the reset completes. + /// + public async Task ResetPasswordAsync( + string callerRole, Guid callerId, Guid userId, CancellationToken ct = default) + { + ApplicationUser user = await FindOrThrowAsync(userId); + EnforceResetPasswordAccess(callerRole); + + string token = await userManager.GeneratePasswordResetTokenAsync(user); + + user.MustChangePassword = true; + ThrowIfFailed(await userManager.UpdateAsync(user)); + + string frontendUrl = configuration[ConfigurationKeys.Frontend.PasswordResetUrl] + ?? throw new InvalidOperationException( + ErrorMessages.Configuration.KeyNotConfigured(ConfigurationKeys.Frontend.PasswordResetUrl)); + + string resetLink = + $"{frontendUrl}" + + $"?email={Uri.EscapeDataString(user.Email!)}" + + $"&token={Uri.EscapeDataString(token)}"; + + await emailService.SendPasswordResetAsync(user.Email!, user.UserName!, resetLink, ct); + + return new ResetPasswordResponse(user.Id); + } + + /// + public async Task DeleteAsync( + string callerRole, Guid callerId, Guid userId, CancellationToken ct = default) + { + ApplicationUser user = await FindOrThrowAsync(userId); + EnforceDeleteAccess(callerRole); + ThrowIfFailed(await userManager.DeleteAsync(user)); + } + + /// + public async Task SetActiveAsync( + string callerRole, Guid callerId, Guid userId, bool isActive, CancellationToken ct = default) + { + ApplicationUser user = await FindOrThrowAsync(userId); + EnforceDeleteAccess(callerRole); + + if (user.Id == callerId) + { + throw new InvalidOperationException(ErrorMessages.User.CannotChangeOwnActiveState); + } + + if (isActive) + { + ThrowIfFailed(await userManager.SetLockoutEndDateAsync(user, null)); + } + else + { + ThrowIfFailed(await userManager.SetLockoutEnabledAsync(user, true)); + ThrowIfFailed(await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue)); + } + + return await MapOneAsync(user); + } + + private static void EnforceReadAccess(ApplicationUser target, string callerRole, Guid callerId) + { + if (IsAdmin(callerRole) || IsOwner(callerRole)) + { + return; + } + + if (target.Id == callerId) + { + return; + } + + throw new UnauthorizedAccessException(ErrorMessages.Auth.AccessDenied); + } + + /// + /// Write access currently follows the exact same rule as read access, kept as its own method for a future divergence. + /// + private static void EnforceWriteAccess(ApplicationUser target, string callerRole, Guid callerId) + { + EnforceReadAccess(target, callerRole, callerId); + } + + private static void EnforceResetPasswordAccess(string callerRole) + { + if (IsAdmin(callerRole) || IsOwner(callerRole)) + { + return; + } + + throw new UnauthorizedAccessException(ErrorMessages.User.PasswordResetRestricted); + } + + private static void EnforceDeleteAccess(string callerRole) + { + if (IsAdmin(callerRole) || IsOwner(callerRole)) + { + return; + } + + throw new UnauthorizedAccessException(ErrorMessages.User.InsufficientPermissionsToDelete); + } + + /// + /// Which roles each caller role may assign to another user, kept as separate dictionary entries per role for a future divergence. + /// + private static readonly Dictionary> _roleChangePolicy = + new(StringComparer.OrdinalIgnoreCase) + { + [Roles.Admin] = new(StringComparer.OrdinalIgnoreCase) + { + Roles.Admin, Roles.Owner + }, + [Roles.Owner] = new(StringComparer.OrdinalIgnoreCase) + { + Roles.Admin, Roles.Owner + } + }; + + /// + /// Replaces the target user's role with newRole so they end up in exactly one role, never accumulating multiple. + /// + private async Task ChangeRoleAsync( + ApplicationUser user, UserRoleType newRole, string callerRole, Guid callerId) + { + if (!Enum.IsDefined(newRole)) + { + throw new ArgumentException(ErrorMessages.User.InvalidRole(newRole)); + } + + string targetRoleName = newRole.ToRoleName(); + EnforceRoleChangeAccess(user, callerRole, callerId, targetRoleName); + + IList currentRoles = await userManager.GetRolesAsync(user); + if (currentRoles.Count > 0) + { + ThrowIfFailed(await userManager.RemoveFromRolesAsync(user, currentRoles)); + } + + ThrowIfFailed(await userManager.AddToRoleAsync(user, targetRoleName)); + } + + /// + /// A user may never change their own role, since that is the one path that could let someone grant themselves more privilege. + /// + private static void EnforceRoleChangeAccess( + ApplicationUser target, string callerRole, Guid callerId, string targetRoleName) + { + if (target.Id == callerId) + { + throw new InvalidOperationException(ErrorMessages.User.CannotChangeOwnRole); + } + + bool hasJurisdictionOverTarget = IsAdmin(callerRole) || IsOwner(callerRole); + + if (!hasJurisdictionOverTarget) + { + throw new UnauthorizedAccessException(ErrorMessages.User.InsufficientPermissionsToChangeRole); + } + + bool allowedToAssignTargetRole = + _roleChangePolicy.TryGetValue(callerRole, out HashSet? permittedRoles) + && permittedRoles.Contains(targetRoleName); + + if (!allowedToAssignTargetRole) + { + throw new UnauthorizedAccessException( + ErrorMessages.User.RoleNotAllowedToAssign(callerRole, targetRoleName)); + } + } + + private async Task MapOneAsync(ApplicationUser user) + { + IList roles = await userManager.GetRolesAsync(user); + string role = roles.FirstOrDefault() ?? string.Empty; + bool isActive = !(user.LockoutEnd.HasValue && user.LockoutEnd.Value > DateTimeOffset.UtcNow); + return new UserResponse(user.Id, user.Email!, user.UserName!, role, + user.PhoneNumber, user.CreatedByOwnerId, isActive); + } + + private async Task> MapManyAsync(IEnumerable users) + { + List results = []; + foreach (ApplicationUser user in users) + { + results.Add(await MapOneAsync(user)); + } + + return results; + } + + private async Task FindOrThrowAsync(Guid userId) + { + ApplicationUser? user = await userManager.FindByIdAsync(userId.ToString()); + return user ?? throw new KeyNotFoundException(ErrorMessages.User.NotFound(userId.ToString())); + } + + private static void ThrowIfFailed(IdentityResult result) + { + if (!result.Succeeded) + { + throw new InvalidOperationException( + string.Join("; ", result.Errors.Select(e => e.Description))); + } + } + + private IQueryable ResolveVisibleUsersQuery(string callerRole) + { + return IsAdmin(callerRole) || IsOwner(callerRole) + ? userManager.Users + : throw new UnauthorizedAccessException(ErrorMessages.User.InsufficientPermissionsToListUsers); + } + + private static bool IsAdmin(string role) + { + return role == Roles.Admin; + } + + private static bool IsOwner(string role) + { + return role == Roles.Owner; + } +} \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/20260315043537_InitialIdentityMigration.Designer.cs b/Club12-Backend/Infrastructure/Identity/Migrations/20260315043537_InitialIdentityMigration.Designer.cs new file mode 100644 index 0000000..f7593c0 --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/20260315043537_InitialIdentityMigration.Designer.cs @@ -0,0 +1,319 @@ +// +using System; +using Infrastructure.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Identity.Migrations +{ + [DbContext(typeof(IdentityAppDbContext))] + [Migration("20260315043537_InitialIdentityMigration")] + partial class InitialIdentityMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("RefreshToken") + .HasColumnType("text"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = new Guid("00000000-0000-0000-0000-000000000001"), + ConcurrencyStamp = "1", + Name = "ADMIN", + NormalizedName = "ADMIN" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000002"), + ConcurrencyStamp = "2", + Name = "OWNER", + NormalizedName = "OWNER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000003"), + ConcurrencyStamp = "3", + Name = "TOURNAMENT_MANAGER", + NormalizedName = "TOURNAMENT_MANAGER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000004"), + ConcurrencyStamp = "4", + Name = "TEAM_MANAGER", + NormalizedName = "TEAM_MANAGER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000005"), + ConcurrencyStamp = "5", + Name = "GUEST", + NormalizedName = "GUEST" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/20260315043537_InitialIdentityMigration.cs b/Club12-Backend/Infrastructure/Identity/Migrations/20260315043537_InitialIdentityMigration.cs new file mode 100644 index 0000000..da24deb --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/20260315043537_InitialIdentityMigration.cs @@ -0,0 +1,240 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +using System; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Infrastructure.Identity.Migrations; + +/// +public partial class InitialIdentityMigration : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + RefreshToken = table.Column(type: "text", nullable: true), + RefreshTokenExpiryTime = table.Column(type: "timestamp with time zone", nullable: true), + UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "boolean", nullable: false), + PasswordHash = table.Column(type: "text", nullable: true), + SecurityStamp = table.Column(type: "text", nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true), + PhoneNumber = table.Column(type: "text", nullable: true), + PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), + TwoFactorEnabled = table.Column(type: "boolean", nullable: false), + LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), + LockoutEnabled = table.Column(type: "boolean", nullable: false), + AccessFailedCount = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + RoleId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + UserId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "text", nullable: false), + ProviderKey = table.Column(type: "text", nullable: false), + ProviderDisplayName = table.Column(type: "text", nullable: true), + UserId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "uuid", nullable: false), + RoleId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "uuid", nullable: false), + LoginProvider = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { new Guid("00000000-0000-0000-0000-000000000001"), "1", "ADMIN", "ADMIN" }, + { new Guid("00000000-0000-0000-0000-000000000002"), "2", "OWNER", "OWNER" }, + { new Guid("00000000-0000-0000-0000-000000000003"), "3", "TOURNAMENT_MANAGER", "TOURNAMENT_MANAGER" }, + { new Guid("00000000-0000-0000-0000-000000000004"), "4", "TEAM_MANAGER", "TEAM_MANAGER" }, + { new Guid("00000000-0000-0000-0000-000000000005"), "5", "GUEST", "GUEST" } + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } +} diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/20260316030250_changesPropsForUsers.Designer.cs b/Club12-Backend/Infrastructure/Identity/Migrations/20260316030250_changesPropsForUsers.Designer.cs new file mode 100644 index 0000000..ae6f07e --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/20260316030250_changesPropsForUsers.Designer.cs @@ -0,0 +1,322 @@ +// +using System; +using Infrastructure.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Identity.Migrations +{ + [DbContext(typeof(IdentityAppDbContext))] + [Migration("20260316030250_changesPropsForUsers")] + partial class changesPropsForUsers + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedByOwnerId") + .HasColumnType("uuid"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("RefreshToken") + .HasColumnType("text"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = new Guid("00000000-0000-0000-0000-000000000001"), + ConcurrencyStamp = "1", + Name = "ADMIN", + NormalizedName = "ADMIN" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000002"), + ConcurrencyStamp = "2", + Name = "OWNER", + NormalizedName = "OWNER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000003"), + ConcurrencyStamp = "3", + Name = "TOURNAMENT_MANAGER", + NormalizedName = "TOURNAMENT_MANAGER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000004"), + ConcurrencyStamp = "4", + Name = "TEAM_MANAGER", + NormalizedName = "TEAM_MANAGER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000005"), + ConcurrencyStamp = "5", + Name = "GUEST", + NormalizedName = "GUEST" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/20260316030250_changesPropsForUsers.cs b/Club12-Backend/Infrastructure/Identity/Migrations/20260316030250_changesPropsForUsers.cs new file mode 100644 index 0000000..6636786 --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/20260316030250_changesPropsForUsers.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Infrastructure.Identity.Migrations; + +/// +public partial class changesPropsForUsers : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CreatedByOwnerId", + table: "AspNetUsers", + type: "uuid", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CreatedByOwnerId", + table: "AspNetUsers"); + } +} diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/20260316031655_addDateCreatedForUsers.Designer.cs b/Club12-Backend/Infrastructure/Identity/Migrations/20260316031655_addDateCreatedForUsers.Designer.cs new file mode 100644 index 0000000..189ba90 --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/20260316031655_addDateCreatedForUsers.Designer.cs @@ -0,0 +1,325 @@ +// +using System; +using Infrastructure.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Identity.Migrations +{ + [DbContext(typeof(IdentityAppDbContext))] + [Migration("20260316031655_addDateCreatedForUsers")] + partial class addDateCreatedForUsers + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedByOwnerId") + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("RefreshToken") + .HasColumnType("text"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = new Guid("00000000-0000-0000-0000-000000000001"), + ConcurrencyStamp = "1", + Name = "ADMIN", + NormalizedName = "ADMIN" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000002"), + ConcurrencyStamp = "2", + Name = "OWNER", + NormalizedName = "OWNER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000003"), + ConcurrencyStamp = "3", + Name = "TOURNAMENT_MANAGER", + NormalizedName = "TOURNAMENT_MANAGER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000004"), + ConcurrencyStamp = "4", + Name = "TEAM_MANAGER", + NormalizedName = "TEAM_MANAGER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000005"), + ConcurrencyStamp = "5", + Name = "GUEST", + NormalizedName = "GUEST" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/20260316031655_addDateCreatedForUsers.cs b/Club12-Backend/Infrastructure/Identity/Migrations/20260316031655_addDateCreatedForUsers.cs new file mode 100644 index 0000000..eb33a4a --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/20260316031655_addDateCreatedForUsers.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Infrastructure.Identity.Migrations; + +/// +public partial class addDateCreatedForUsers : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DateCreated", + table: "AspNetUsers", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DateCreated", + table: "AspNetUsers"); + } +} diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/20260316042032_addMustChangePassword.Designer.cs b/Club12-Backend/Infrastructure/Identity/Migrations/20260316042032_addMustChangePassword.Designer.cs new file mode 100644 index 0000000..b072663 --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/20260316042032_addMustChangePassword.Designer.cs @@ -0,0 +1,328 @@ +// +using System; +using Infrastructure.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Identity.Migrations +{ + [DbContext(typeof(IdentityAppDbContext))] + [Migration("20260316042032_addMustChangePassword")] + partial class addMustChangePassword + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedByOwnerId") + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("MustChangePassword") + .HasColumnType("boolean"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("RefreshToken") + .HasColumnType("text"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = new Guid("00000000-0000-0000-0000-000000000001"), + ConcurrencyStamp = "1", + Name = "ADMIN", + NormalizedName = "ADMIN" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000002"), + ConcurrencyStamp = "2", + Name = "OWNER", + NormalizedName = "OWNER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000003"), + ConcurrencyStamp = "3", + Name = "TOURNAMENT_MANAGER", + NormalizedName = "TOURNAMENT_MANAGER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000004"), + ConcurrencyStamp = "4", + Name = "TEAM_MANAGER", + NormalizedName = "TEAM_MANAGER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000005"), + ConcurrencyStamp = "5", + Name = "GUEST", + NormalizedName = "GUEST" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/20260316042032_addMustChangePassword.cs b/Club12-Backend/Infrastructure/Identity/Migrations/20260316042032_addMustChangePassword.cs new file mode 100644 index 0000000..b2d48a8 --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/20260316042032_addMustChangePassword.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Identity.Migrations; + +/// +public partial class addMustChangePassword : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "MustChangePassword", + table: "AspNetUsers", + type: "boolean", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "MustChangePassword", + table: "AspNetUsers"); + } +} diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/20260326215327_AddNewPropsForIdentity.Designer.cs b/Club12-Backend/Infrastructure/Identity/Migrations/20260326215327_AddNewPropsForIdentity.Designer.cs new file mode 100644 index 0000000..4095ea5 --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/20260326215327_AddNewPropsForIdentity.Designer.cs @@ -0,0 +1,329 @@ +// +using System; +using Infrastructure.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Identity.Migrations +{ + [DbContext(typeof(IdentityAppDbContext))] + [Migration("20260326215327_AddNewPropsForIdentity")] + partial class AddNewPropsForIdentity + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedByOwnerId") + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("MustChangePassword") + .HasColumnType("boolean"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("RefreshToken") + .HasColumnType("text"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .IsUnique() + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = new Guid("00000000-0000-0000-0000-000000000001"), + ConcurrencyStamp = "1", + Name = "ADMIN", + NormalizedName = "ADMIN" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000002"), + ConcurrencyStamp = "2", + Name = "OWNER", + NormalizedName = "OWNER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000003"), + ConcurrencyStamp = "3", + Name = "TOURNAMENT_MANAGER", + NormalizedName = "TOURNAMENT_MANAGER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000004"), + ConcurrencyStamp = "4", + Name = "TEAM_MANAGER", + NormalizedName = "TEAM_MANAGER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000005"), + ConcurrencyStamp = "5", + Name = "GUEST", + NormalizedName = "GUEST" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/20260326215327_AddNewPropsForIdentity.cs b/Club12-Backend/Infrastructure/Identity/Migrations/20260326215327_AddNewPropsForIdentity.cs new file mode 100644 index 0000000..4a08bd8 --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/20260326215327_AddNewPropsForIdentity.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Identity.Migrations; + +/// +public partial class AddNewPropsForIdentity : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "EmailIndex", + table: "AspNetUsers"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "EmailIndex", + table: "AspNetUsers"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + } +} diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/20260828021949_RemoveTournamentAndTeamManagerRoles.Designer.cs b/Club12-Backend/Infrastructure/Identity/Migrations/20260828021949_RemoveTournamentAndTeamManagerRoles.Designer.cs new file mode 100644 index 0000000..85b0151 --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/20260828021949_RemoveTournamentAndTeamManagerRoles.Designer.cs @@ -0,0 +1,315 @@ +// +using System; +using Infrastructure.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Identity.Migrations +{ + [DbContext(typeof(IdentityAppDbContext))] + [Migration("20260828021949_RemoveTournamentAndTeamManagerRoles")] + partial class RemoveTournamentAndTeamManagerRoles + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedByOwnerId") + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("MustChangePassword") + .HasColumnType("boolean"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("RefreshToken") + .HasColumnType("text"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .IsUnique() + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = new Guid("00000000-0000-0000-0000-000000000001"), + ConcurrencyStamp = "1", + Name = "ADMIN", + NormalizedName = "ADMIN" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000002"), + ConcurrencyStamp = "2", + Name = "OWNER", + NormalizedName = "OWNER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000005"), + ConcurrencyStamp = "5", + Name = "GUEST", + NormalizedName = "GUEST" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/20260828021949_RemoveTournamentAndTeamManagerRoles.cs b/Club12-Backend/Infrastructure/Identity/Migrations/20260828021949_RemoveTournamentAndTeamManagerRoles.cs new file mode 100644 index 0000000..02db452 --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/20260828021949_RemoveTournamentAndTeamManagerRoles.cs @@ -0,0 +1,40 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Infrastructure.Identity.Migrations +{ + /// + public partial class RemoveTournamentAndTeamManagerRoles : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("00000000-0000-0000-0000-000000000003")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("00000000-0000-0000-0000-000000000004")); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { new Guid("00000000-0000-0000-0000-000000000003"), "3", "TOURNAMENT_MANAGER", "TOURNAMENT_MANAGER" }, + { new Guid("00000000-0000-0000-0000-000000000004"), "4", "TEAM_MANAGER", "TEAM_MANAGER" } + }); + } + } +} diff --git a/Club12-Backend/Infrastructure/Identity/Migrations/IdentityAppDbContextModelSnapshot.cs b/Club12-Backend/Infrastructure/Identity/Migrations/IdentityAppDbContextModelSnapshot.cs new file mode 100644 index 0000000..e5e7dba --- /dev/null +++ b/Club12-Backend/Infrastructure/Identity/Migrations/IdentityAppDbContextModelSnapshot.cs @@ -0,0 +1,312 @@ +// +using System; +using Infrastructure.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Identity.Migrations +{ + [DbContext(typeof(IdentityAppDbContext))] + partial class IdentityAppDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedByOwnerId") + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("MustChangePassword") + .HasColumnType("boolean"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("RefreshToken") + .HasColumnType("text"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .IsUnique() + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = new Guid("00000000-0000-0000-0000-000000000001"), + ConcurrencyStamp = "1", + Name = "ADMIN", + NormalizedName = "ADMIN" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000002"), + ConcurrencyStamp = "2", + Name = "OWNER", + NormalizedName = "OWNER" + }, + new + { + Id = new Guid("00000000-0000-0000-0000-000000000005"), + ConcurrencyStamp = "5", + Name = "GUEST", + NormalizedName = "GUEST" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Infrastructure.csproj b/Club12-Backend/Infrastructure/Infrastructure.csproj new file mode 100644 index 0000000..16cce2c --- /dev/null +++ b/Club12-Backend/Infrastructure/Infrastructure.csproj @@ -0,0 +1,37 @@ + + + + NU1903 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + net8.0 + enable + true + + \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Migrations/20250316201442_InitialMigration.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20250316201442_InitialMigration.Designer.cs new file mode 100644 index 0000000..e08bc3f --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250316201442_InitialMigration.Designer.cs @@ -0,0 +1,625 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20250316201442_InitialMigration")] + partial class InitialMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPostEntity.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.DivisionEntity.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PlayoffsGenerated") + .HasColumnType("boolean"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.MatchEntity.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp with time zone"); + + b.Property("MatchWeek") + .HasColumnType("integer"); + + b.Property("RoundName") + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerEntity.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Category") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Club") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("IsFederated") + .HasColumnType("boolean"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctionEntity.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatisticEntity.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.StaffEntity.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.TeamEntity.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.TournamentEntity.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.UserEntity.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.VenueEntity.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.DivisionEntity.Division", b => + { + b.HasOne("Entities.Models.TournamentEntity.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.MatchEntity.Match", b => + { + b.HasOne("Entities.Models.DivisionEntity.Division", "Division") + .WithMany("Matches") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.TeamEntity.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.VenueEntity.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Entities.Models.TeamEntity.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.TeamEntity.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("Division"); + + b.Navigation("HomeTeam"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerEntity.Player", b => + { + b.HasOne("Entities.Models.TeamEntity.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctionEntity.PlayerSanction", b => + { + b.HasOne("Entities.Models.PlayerEntity.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatisticEntity.PlayerStatistic", b => + { + b.HasOne("Entities.Models.MatchEntity.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.PlayerEntity.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.StaffEntity.Staff", b => + { + b.HasOne("Entities.Models.TeamEntity.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.TeamEntity.Team", b => + { + b.HasOne("Entities.Models.DivisionEntity.Division", "Division") + .WithMany("Teams") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.DivisionEntity.Division", b => + { + b.Navigation("Matches"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Entities.Models.MatchEntity.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.TeamEntity.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.TournamentEntity.Tournament", b => + { + b.Navigation("Divisions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250316201442_InitialMigration.cs b/Club12-Backend/Infrastructure/Migrations/20250316201442_InitialMigration.cs new file mode 100644 index 0000000..90a98c2 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250316201442_InitialMigration.cs @@ -0,0 +1,434 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class InitialMigration : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "Club12"); + + migrationBuilder.CreateTable( + name: "BlogPosts", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Author = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + Title = table.Column(type: "text", nullable: false), + Views = table.Column(type: "integer", nullable: false), + PhotoUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + MarkdownText = table.Column(type: "text", nullable: false), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_BlogPosts", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Tournaments", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Description = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Tournaments", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Users", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Username = table.Column(type: "character varying(25)", maxLength: 25, nullable: false), + Password = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + Role = table.Column(type: "character varying(10)", maxLength: 10, nullable: false), + RefreshToken = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + RefreshTokenExpiryTime = table.Column(type: "timestamp with time zone", nullable: true), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Venues", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + Address = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + PhotoUrl = table.Column(type: "text", nullable: true), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Venues", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Divisions", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(30)", maxLength: 30, nullable: false), + IsFinished = table.Column(type: "boolean", nullable: false), + TournamentId = table.Column(type: "uuid", nullable: false), + PlayoffsGenerated = table.Column(type: "boolean", nullable: false), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Divisions", x => x.Id); + table.ForeignKey( + name: "FK_Divisions_Tournaments_TournamentId", + column: x => x.TournamentId, + principalSchema: "Club12", + principalTable: "Tournaments", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Teams", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + ThreeLetterCode = table.Column(type: "text", nullable: false), + LogoUrl = table.Column(type: "text", nullable: false), + ShirtColor = table.Column(type: "text", nullable: false), + DivisionId = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Teams", x => x.Id); + table.ForeignKey( + name: "FK_Teams_Divisions_DivisionId", + column: x => x.DivisionId, + principalSchema: "Club12", + principalTable: "Divisions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Matches", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + MatchDate = table.Column(type: "timestamp with time zone", nullable: false), + Type = table.Column(type: "text", nullable: false), + MatchWeek = table.Column(type: "integer", nullable: true), + RoundName = table.Column(type: "text", nullable: true), + GameNumber = table.Column(type: "integer", nullable: true), + HomeTeamId = table.Column(type: "uuid", nullable: false), + VisitorTeamId = table.Column(type: "uuid", nullable: false), + HomeScore = table.Column(type: "integer", nullable: true), + VisitorScore = table.Column(type: "integer", nullable: true), + IsFinished = table.Column(type: "boolean", nullable: false), + WinningTeamId = table.Column(type: "uuid", nullable: true), + DivisionId = table.Column(type: "uuid", nullable: false), + VenueId = table.Column(type: "uuid", nullable: true), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Matches", x => x.Id); + table.ForeignKey( + name: "FK_Matches_Divisions_DivisionId", + column: x => x.DivisionId, + principalSchema: "Club12", + principalTable: "Divisions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Matches_Teams_HomeTeamId", + column: x => x.HomeTeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Matches_Teams_VisitorTeamId", + column: x => x.VisitorTeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Matches_Teams_WinningTeamId", + column: x => x.WinningTeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Matches_Venues_VenueId", + column: x => x.VenueId, + principalSchema: "Club12", + principalTable: "Venues", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "Players", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Names = table.Column(type: "character varying(70)", maxLength: 70, nullable: false), + LastName = table.Column(type: "character varying(70)", maxLength: 70, nullable: false), + DocumentNumber = table.Column(type: "character varying(15)", maxLength: 15, nullable: false), + IsSanctioned = table.Column(type: "boolean", nullable: false), + IsFederated = table.Column(type: "boolean", nullable: false), + PhoneNumber = table.Column(type: "text", nullable: true), + Club = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + Category = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + BirthDate = table.Column(type: "timestamp with time zone", nullable: false), + SocialSecurity = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + TeamId = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Players", x => x.Id); + table.ForeignKey( + name: "FK_Players_Teams_TeamId", + column: x => x.TeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Staffs", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Names = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + LastName = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + PhoneNumber = table.Column(type: "character varying(15)", maxLength: 15, nullable: false), + Type = table.Column(type: "text", nullable: false), + TeamId = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Staffs", x => x.Id); + table.ForeignKey( + name: "FK_Staffs_Teams_TeamId", + column: x => x.TeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "PlayerSanctions", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Duration = table.Column(type: "integer", nullable: false), + IssuedDate = table.Column(type: "timestamp with time zone", nullable: false), + Description = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + PlayerId = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PlayerSanctions", x => x.Id); + table.ForeignKey( + name: "FK_PlayerSanctions_Players_PlayerId", + column: x => x.PlayerId, + principalSchema: "Club12", + principalTable: "Players", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "PlayersStatistics", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Value = table.Column(type: "integer", nullable: false), + MatchId = table.Column(type: "uuid", nullable: false), + PlayerId = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PlayersStatistics", x => x.Id); + table.ForeignKey( + name: "FK_PlayersStatistics_Matches_MatchId", + column: x => x.MatchId, + principalSchema: "Club12", + principalTable: "Matches", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_PlayersStatistics_Players_PlayerId", + column: x => x.PlayerId, + principalSchema: "Club12", + principalTable: "Players", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Divisions_TournamentId", + schema: "Club12", + table: "Divisions", + column: "TournamentId"); + + migrationBuilder.CreateIndex( + name: "IX_Matches_DivisionId", + schema: "Club12", + table: "Matches", + column: "DivisionId"); + + migrationBuilder.CreateIndex( + name: "IX_Matches_HomeTeamId", + schema: "Club12", + table: "Matches", + column: "HomeTeamId"); + + migrationBuilder.CreateIndex( + name: "IX_Matches_VenueId", + schema: "Club12", + table: "Matches", + column: "VenueId"); + + migrationBuilder.CreateIndex( + name: "IX_Matches_VisitorTeamId", + schema: "Club12", + table: "Matches", + column: "VisitorTeamId"); + + migrationBuilder.CreateIndex( + name: "IX_Matches_WinningTeamId", + schema: "Club12", + table: "Matches", + column: "WinningTeamId"); + + migrationBuilder.CreateIndex( + name: "IX_Players_TeamId", + schema: "Club12", + table: "Players", + column: "TeamId"); + + migrationBuilder.CreateIndex( + name: "IX_PlayerSanctions_PlayerId", + schema: "Club12", + table: "PlayerSanctions", + column: "PlayerId"); + + migrationBuilder.CreateIndex( + name: "IX_PlayersStatistics_MatchId", + schema: "Club12", + table: "PlayersStatistics", + column: "MatchId"); + + migrationBuilder.CreateIndex( + name: "IX_PlayersStatistics_PlayerId", + schema: "Club12", + table: "PlayersStatistics", + column: "PlayerId"); + + migrationBuilder.CreateIndex( + name: "IX_Staffs_TeamId", + schema: "Club12", + table: "Staffs", + column: "TeamId"); + + migrationBuilder.CreateIndex( + name: "IX_Teams_DivisionId", + schema: "Club12", + table: "Teams", + column: "DivisionId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "BlogPosts", + schema: "Club12"); + + migrationBuilder.DropTable( + name: "PlayerSanctions", + schema: "Club12"); + + migrationBuilder.DropTable( + name: "PlayersStatistics", + schema: "Club12"); + + migrationBuilder.DropTable( + name: "Staffs", + schema: "Club12"); + + migrationBuilder.DropTable( + name: "Users", + schema: "Club12"); + + migrationBuilder.DropTable( + name: "Matches", + schema: "Club12"); + + migrationBuilder.DropTable( + name: "Players", + schema: "Club12"); + + migrationBuilder.DropTable( + name: "Venues", + schema: "Club12"); + + migrationBuilder.DropTable( + name: "Teams", + schema: "Club12"); + + migrationBuilder.DropTable( + name: "Divisions", + schema: "Club12"); + + migrationBuilder.DropTable( + name: "Tournaments", + schema: "Club12"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250317005231_LatestChanges.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20250317005231_LatestChanges.Designer.cs new file mode 100644 index 0000000..1192b51 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250317005231_LatestChanges.Designer.cs @@ -0,0 +1,697 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20250317005231_LatestChanges")] + partial class LatestChanges + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPostEntity.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.DivisionEntity.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PlayoffsGenerated") + .HasColumnType("boolean"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.MatchEntity.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp with time zone"); + + b.Property("MatchWeek") + .HasColumnType("integer"); + + b.Property("PlayoffSeriesId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("PlayoffSeriesId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerEntity.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Category") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Club") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("IsFederated") + .HasColumnType("boolean"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctionEntity.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatisticEntity.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayoffSeriesEntity.PlayoffSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("GamesRequiredToWin") + .HasColumnType("integer"); + + b.Property("HomeTeamWins") + .HasColumnType("integer"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("NextSeriesId") + .HasColumnType("uuid"); + + b.Property("RoundName") + .HasColumnType("integer"); + + b.Property("VisitorTeamWins") + .HasColumnType("integer"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("NextSeriesId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("PlayoffSeries", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.StaffEntity.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.TeamEntity.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Seed") + .HasColumnType("integer"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.TournamentEntity.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.UserEntity.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.VenueEntity.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.DivisionEntity.Division", b => + { + b.HasOne("Entities.Models.TournamentEntity.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.MatchEntity.Match", b => + { + b.HasOne("Entities.Models.DivisionEntity.Division", "Division") + .WithMany("Matches") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.TeamEntity.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.PlayoffSeriesEntity.PlayoffSeries", "PlayoffSeries") + .WithMany("Matches") + .HasForeignKey("PlayoffSeriesId"); + + b.HasOne("Entities.Models.VenueEntity.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Entities.Models.TeamEntity.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.TeamEntity.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("Division"); + + b.Navigation("HomeTeam"); + + b.Navigation("PlayoffSeries"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerEntity.Player", b => + { + b.HasOne("Entities.Models.TeamEntity.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctionEntity.PlayerSanction", b => + { + b.HasOne("Entities.Models.PlayerEntity.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatisticEntity.PlayerStatistic", b => + { + b.HasOne("Entities.Models.MatchEntity.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.PlayerEntity.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayoffSeriesEntity.PlayoffSeries", b => + { + b.HasOne("Entities.Models.PlayoffSeriesEntity.PlayoffSeries", "NextSeries") + .WithMany() + .HasForeignKey("NextSeriesId"); + + b.HasOne("Entities.Models.TeamEntity.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("NextSeries"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.StaffEntity.Staff", b => + { + b.HasOne("Entities.Models.TeamEntity.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.TeamEntity.Team", b => + { + b.HasOne("Entities.Models.DivisionEntity.Division", "Division") + .WithMany("Teams") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.DivisionEntity.Division", b => + { + b.Navigation("Matches"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Entities.Models.MatchEntity.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.PlayoffSeriesEntity.PlayoffSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Entities.Models.TeamEntity.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.TournamentEntity.Tournament", b => + { + b.Navigation("Divisions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250317005231_LatestChanges.cs b/Club12-Backend/Infrastructure/Migrations/20250317005231_LatestChanges.cs new file mode 100644 index 0000000..fc157c8 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250317005231_LatestChanges.cs @@ -0,0 +1,179 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class LatestChanges : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Matches_Teams_HomeTeamId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "RoundName", + schema: "Club12", + table: "Matches"); + + migrationBuilder.AddColumn( + name: "Seed", + schema: "Club12", + table: "Teams", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AlterColumn( + name: "HomeTeamId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AddColumn( + name: "PlayoffSeriesId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: true); + + migrationBuilder.CreateTable( + name: "PlayoffSeries", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + RoundName = table.Column(type: "integer", nullable: false), + WinningTeamId = table.Column(type: "uuid", nullable: true), + IsFinished = table.Column(type: "boolean", nullable: false), + GamesRequiredToWin = table.Column(type: "integer", nullable: false), + HomeTeamWins = table.Column(type: "integer", nullable: false), + VisitorTeamWins = table.Column(type: "integer", nullable: false), + NextSeriesId = table.Column(type: "uuid", nullable: true), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PlayoffSeries", x => x.Id); + table.ForeignKey( + name: "FK_PlayoffSeries_PlayoffSeries_NextSeriesId", + column: x => x.NextSeriesId, + principalSchema: "Club12", + principalTable: "PlayoffSeries", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_PlayoffSeries_Teams_WinningTeamId", + column: x => x.WinningTeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Matches_PlayoffSeriesId", + schema: "Club12", + table: "Matches", + column: "PlayoffSeriesId"); + + migrationBuilder.CreateIndex( + name: "IX_PlayoffSeries_NextSeriesId", + schema: "Club12", + table: "PlayoffSeries", + column: "NextSeriesId"); + + migrationBuilder.CreateIndex( + name: "IX_PlayoffSeries_WinningTeamId", + schema: "Club12", + table: "PlayoffSeries", + column: "WinningTeamId"); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_PlayoffSeries_PlayoffSeriesId", + schema: "Club12", + table: "Matches", + column: "PlayoffSeriesId", + principalSchema: "Club12", + principalTable: "PlayoffSeries", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_Teams_HomeTeamId", + schema: "Club12", + table: "Matches", + column: "HomeTeamId", + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Matches_PlayoffSeries_PlayoffSeriesId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropForeignKey( + name: "FK_Matches_Teams_HomeTeamId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropTable( + name: "PlayoffSeries", + schema: "Club12"); + + migrationBuilder.DropIndex( + name: "IX_Matches_PlayoffSeriesId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "Seed", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropColumn( + name: "PlayoffSeriesId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.AlterColumn( + name: "HomeTeamId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: false, + defaultValue: Guid.Empty, + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AddColumn( + name: "RoundName", + schema: "Club12", + table: "Matches", + type: "text", + nullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_Teams_HomeTeamId", + schema: "Club12", + table: "Matches", + column: "HomeTeamId", + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250724231610_AddStageEntity.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20250724231610_AddStageEntity.Designer.cs new file mode 100644 index 0000000..48ab7bb --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250724231610_AddStageEntity.Designer.cs @@ -0,0 +1,679 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20250724231610_AddStageEntity")] + partial class AddStageEntity + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPosts.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PlayoffsGenerated") + .HasColumnType("boolean"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp with time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Category") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Club") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("IsFederated") + .HasColumnType("boolean"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Seed") + .HasColumnType("integer"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Venues.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Matches") + .HasForeignKey("DivisionId"); + + b.HasOne("Entities.Models.Teams.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Teams") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Navigation("Matches"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Navigation("Divisions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250724231610_AddStageEntity.cs b/Club12-Backend/Infrastructure/Migrations/20250724231610_AddStageEntity.cs new file mode 100644 index 0000000..e4ae3cd --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250724231610_AddStageEntity.cs @@ -0,0 +1,293 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class AddStageEntity : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Matches_Divisions_DivisionId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropForeignKey( + name: "FK_Matches_PlayoffSeries_PlayoffSeriesId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropForeignKey( + name: "FK_Matches_Venues_VenueId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropTable( + name: "PlayoffSeries", + schema: "Club12"); + + migrationBuilder.DropIndex( + name: "IX_Matches_PlayoffSeriesId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropIndex( + name: "IX_Matches_VenueId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "GameNumber", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "MatchWeek", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "PlayoffSeriesId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "VenueId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.AlterColumn( + name: "DivisionId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AddColumn( + name: "StageId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: false, + defaultValue: Guid.Empty); + + migrationBuilder.CreateTable( + name: "Stages", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), + StageType = table.Column(type: "text", nullable: false), + IsActive = table.Column(type: "boolean", nullable: false), + IsElimination = table.Column(type: "boolean", nullable: false), + StartDate = table.Column(type: "timestamp with time zone", nullable: false), + EndDate = table.Column(type: "timestamp with time zone", nullable: false), + DivisionId = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Stages", x => x.Id); + table.ForeignKey( + name: "FK_Stages_Divisions_DivisionId", + column: x => x.DivisionId, + principalSchema: "Club12", + principalTable: "Divisions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Matches_StageId", + schema: "Club12", + table: "Matches", + column: "StageId"); + + migrationBuilder.CreateIndex( + name: "IX_Stages_DivisionId", + schema: "Club12", + table: "Stages", + column: "DivisionId"); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_Divisions_DivisionId", + schema: "Club12", + table: "Matches", + column: "DivisionId", + principalSchema: "Club12", + principalTable: "Divisions", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_Stages_StageId", + schema: "Club12", + table: "Matches", + column: "StageId", + principalSchema: "Club12", + principalTable: "Stages", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Matches_Divisions_DivisionId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropForeignKey( + name: "FK_Matches_Stages_StageId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropTable( + name: "Stages", + schema: "Club12"); + + migrationBuilder.DropIndex( + name: "IX_Matches_StageId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "StageId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.AlterColumn( + name: "DivisionId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: false, + defaultValue: Guid.Empty, + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AddColumn( + name: "GameNumber", + schema: "Club12", + table: "Matches", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "MatchWeek", + schema: "Club12", + table: "Matches", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "PlayoffSeriesId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "VenueId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: true); + + migrationBuilder.CreateTable( + name: "PlayoffSeries", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + NextSeriesId = table.Column(type: "uuid", nullable: true), + WinningTeamId = table.Column(type: "uuid", nullable: true), + DateCreated = table.Column(type: "timestamp with time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp with time zone", nullable: true), + GamesRequiredToWin = table.Column(type: "integer", nullable: false), + HomeTeamWins = table.Column(type: "integer", nullable: false), + IsFinished = table.Column(type: "boolean", nullable: false), + RoundName = table.Column(type: "integer", nullable: false), + VisitorTeamWins = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PlayoffSeries", x => x.Id); + table.ForeignKey( + name: "FK_PlayoffSeries_PlayoffSeries_NextSeriesId", + column: x => x.NextSeriesId, + principalSchema: "Club12", + principalTable: "PlayoffSeries", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_PlayoffSeries_Teams_WinningTeamId", + column: x => x.WinningTeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Matches_PlayoffSeriesId", + schema: "Club12", + table: "Matches", + column: "PlayoffSeriesId"); + + migrationBuilder.CreateIndex( + name: "IX_Matches_VenueId", + schema: "Club12", + table: "Matches", + column: "VenueId"); + + migrationBuilder.CreateIndex( + name: "IX_PlayoffSeries_NextSeriesId", + schema: "Club12", + table: "PlayoffSeries", + column: "NextSeriesId"); + + migrationBuilder.CreateIndex( + name: "IX_PlayoffSeries_WinningTeamId", + schema: "Club12", + table: "PlayoffSeries", + column: "WinningTeamId"); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_Divisions_DivisionId", + schema: "Club12", + table: "Matches", + column: "DivisionId", + principalSchema: "Club12", + principalTable: "Divisions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_PlayoffSeries_PlayoffSeriesId", + schema: "Club12", + table: "Matches", + column: "PlayoffSeriesId", + principalSchema: "Club12", + principalTable: "PlayoffSeries", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_Venues_VenueId", + schema: "Club12", + table: "Matches", + column: "VenueId", + principalSchema: "Club12", + principalTable: "Venues", + principalColumn: "Id"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250726012040_AddUniqueNameAndDivisionIdInsideStageTable.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20250726012040_AddUniqueNameAndDivisionIdInsideStageTable.Designer.cs new file mode 100644 index 0000000..bfb54e6 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250726012040_AddUniqueNameAndDivisionIdInsideStageTable.Designer.cs @@ -0,0 +1,685 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20250726012040_AddUniqueNameAndDivisionIdInsideStageTable")] + partial class AddUniqueNameAndDivisionIdInsideStageTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPosts.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PlayoffsGenerated") + .HasColumnType("boolean"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp with time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Category") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Club") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("IsFederated") + .HasColumnType("boolean"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Seed") + .HasColumnType("integer"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Venues.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Matches") + .HasForeignKey("DivisionId"); + + b.HasOne("Entities.Models.Teams.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Teams") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Navigation("Matches"); + + b.Navigation("Stages"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Navigation("Divisions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250726012040_AddUniqueNameAndDivisionIdInsideStageTable.cs b/Club12-Backend/Infrastructure/Migrations/20250726012040_AddUniqueNameAndDivisionIdInsideStageTable.cs new file mode 100644 index 0000000..701f2ce --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250726012040_AddUniqueNameAndDivisionIdInsideStageTable.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class AddUniqueNameAndDivisionIdInsideStageTable : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID", + schema: "Club12", + table: "Stages", + columns: new[] { "Name", "DivisionId" }, + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID", + schema: "Club12", + table: "Stages"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250729230926_removeFK_DivisionIdToTeam.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20250729230926_removeFK_DivisionIdToTeam.Designer.cs new file mode 100644 index 0000000..02e6294 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250729230926_removeFK_DivisionIdToTeam.Designer.cs @@ -0,0 +1,678 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20250729230926_removeFK_DivisionIdToTeam")] + partial class removeFK_DivisionIdToTeam + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPosts.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PlayoffsGenerated") + .HasColumnType("boolean"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp with time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Category") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Club") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("IsFederated") + .HasColumnType("boolean"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Venues.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Matches") + .HasForeignKey("DivisionId"); + + b.HasOne("Entities.Models.Teams.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Teams") + .HasForeignKey("DivisionId"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Navigation("Matches"); + + b.Navigation("Stages"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Navigation("Divisions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250729230926_removeFK_DivisionIdToTeam.cs b/Club12-Backend/Infrastructure/Migrations/20250729230926_removeFK_DivisionIdToTeam.cs new file mode 100644 index 0000000..77a37ea --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250729230926_removeFK_DivisionIdToTeam.cs @@ -0,0 +1,81 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class removeFK_DivisionIdToTeam : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Teams_Divisions_DivisionId", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropColumn( + name: "Seed", + schema: "Club12", + table: "Teams"); + + migrationBuilder.AlterColumn( + name: "DivisionId", + schema: "Club12", + table: "Teams", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AddForeignKey( + name: "FK_Teams_Divisions_DivisionId", + schema: "Club12", + table: "Teams", + column: "DivisionId", + principalSchema: "Club12", + principalTable: "Divisions", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Teams_Divisions_DivisionId", + schema: "Club12", + table: "Teams"); + + migrationBuilder.AlterColumn( + name: "DivisionId", + schema: "Club12", + table: "Teams", + type: "uuid", + nullable: false, + defaultValue: Guid.Empty, + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AddColumn( + name: "Seed", + schema: "Club12", + table: "Teams", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddForeignKey( + name: "FK_Teams_Divisions_DivisionId", + schema: "Club12", + table: "Teams", + column: "DivisionId", + principalSchema: "Club12", + principalTable: "Divisions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250731014937_AddContraintToUniqueDocumentNumberInsidePlayerTable.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20250731014937_AddContraintToUniqueDocumentNumberInsidePlayerTable.Designer.cs new file mode 100644 index 0000000..f697a13 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250731014937_AddContraintToUniqueDocumentNumberInsidePlayerTable.Designer.cs @@ -0,0 +1,674 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20250731014937_AddContraintToUniqueDocumentNumberInsidePlayerTable")] + partial class AddContraintToUniqueDocumentNumberInsidePlayerTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPosts.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PlayoffsGenerated") + .HasColumnType("boolean"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp with time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Venues.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Matches") + .HasForeignKey("DivisionId"); + + b.HasOne("Entities.Models.Teams.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Teams") + .HasForeignKey("DivisionId"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Navigation("Matches"); + + b.Navigation("Stages"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Navigation("Divisions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250731014937_AddContraintToUniqueDocumentNumberInsidePlayerTable.cs b/Club12-Backend/Infrastructure/Migrations/20250731014937_AddContraintToUniqueDocumentNumberInsidePlayerTable.cs new file mode 100644 index 0000000..6299774 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250731014937_AddContraintToUniqueDocumentNumberInsidePlayerTable.cs @@ -0,0 +1,93 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class AddContraintToUniqueDocumentNumberInsidePlayerTable : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Category", + schema: "Club12", + table: "Players"); + + migrationBuilder.DropColumn( + name: "Club", + schema: "Club12", + table: "Players"); + + migrationBuilder.DropColumn( + name: "IsFederated", + schema: "Club12", + table: "Players"); + + migrationBuilder.RenameColumn( + name: "Names", + schema: "Club12", + table: "Players", + newName: "FirstName"); + + migrationBuilder.AddColumn( + name: "SecondName", + schema: "Club12", + table: "Players", + type: "character varying(70)", + maxLength: 70, + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Players_DocumentNumber", + schema: "Club12", + table: "Players", + column: "DocumentNumber", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Players_DocumentNumber", + schema: "Club12", + table: "Players"); + + migrationBuilder.DropColumn( + name: "SecondName", + schema: "Club12", + table: "Players"); + + migrationBuilder.RenameColumn( + name: "FirstName", + schema: "Club12", + table: "Players", + newName: "Names"); + + migrationBuilder.AddColumn( + name: "Category", + schema: "Club12", + table: "Players", + type: "character varying(50)", + maxLength: 50, + nullable: true); + + migrationBuilder.AddColumn( + name: "Club", + schema: "Club12", + table: "Players", + type: "character varying(100)", + maxLength: 100, + nullable: true); + + migrationBuilder.AddColumn( + name: "IsFederated", + schema: "Club12", + table: "Players", + type: "boolean", + nullable: false, + defaultValue: false); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250801010149_addVenueIntoMatch.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20250801010149_addVenueIntoMatch.Designer.cs new file mode 100644 index 0000000..a7ed1c0 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250801010149_addVenueIntoMatch.Designer.cs @@ -0,0 +1,685 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20250801010149_addVenueIntoMatch")] + partial class addVenueIntoMatch + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPosts.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PlayoffsGenerated") + .HasColumnType("boolean"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp with time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Venues.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Matches") + .HasForeignKey("DivisionId"); + + b.HasOne("Entities.Models.Teams.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Venues.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Entities.Models.Teams.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Teams") + .HasForeignKey("DivisionId"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Navigation("Matches"); + + b.Navigation("Stages"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Navigation("Divisions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250801010149_addVenueIntoMatch.cs b/Club12-Backend/Infrastructure/Migrations/20250801010149_addVenueIntoMatch.cs new file mode 100644 index 0000000..9a4d313 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250801010149_addVenueIntoMatch.cs @@ -0,0 +1,56 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class addVenueIntoMatch : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "VenueId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Matches_VenueId", + schema: "Club12", + table: "Matches", + column: "VenueId"); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_Venues_VenueId", + schema: "Club12", + table: "Matches", + column: "VenueId", + principalSchema: "Club12", + principalTable: "Venues", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Matches_Venues_VenueId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropIndex( + name: "IX_Matches_VenueId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "VenueId", + schema: "Club12", + table: "Matches"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250804000142_ChangeDateTimeWithTimeZoneToDateTimeWithoutTimeZone.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20250804000142_ChangeDateTimeWithTimeZoneToDateTimeWithoutTimeZone.Designer.cs new file mode 100644 index 0000000..d0c8633 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250804000142_ChangeDateTimeWithTimeZoneToDateTimeWithoutTimeZone.Designer.cs @@ -0,0 +1,685 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20250804000142_ChangeDateTimeWithTimeZoneToDateTimeWithoutTimeZone")] + partial class ChangeDateTimeWithTimeZoneToDateTimeWithoutTimeZone + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPosts.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PlayoffsGenerated") + .HasColumnType("boolean"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Venues.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Matches") + .HasForeignKey("DivisionId"); + + b.HasOne("Entities.Models.Teams.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Venues.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Entities.Models.Teams.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Teams") + .HasForeignKey("DivisionId"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Navigation("Matches"); + + b.Navigation("Stages"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Navigation("Divisions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250804000142_ChangeDateTimeWithTimeZoneToDateTimeWithoutTimeZone.cs b/Club12-Backend/Infrastructure/Migrations/20250804000142_ChangeDateTimeWithTimeZoneToDateTimeWithoutTimeZone.cs new file mode 100644 index 0000000..1a57483 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250804000142_ChangeDateTimeWithTimeZoneToDateTimeWithoutTimeZone.cs @@ -0,0 +1,585 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class ChangeDateTimeWithTimeZoneToDateTimeWithoutTimeZone : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Venues", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Venues", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RefreshTokenExpiryTime", + schema: "Club12", + table: "Users", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Users", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Users", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Tournaments", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Tournaments", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Teams", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Teams", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "StartDate", + schema: "Club12", + table: "Stages", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "EndDate", + schema: "Club12", + table: "Stages", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Stages", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Stages", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Staffs", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Staffs", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "PlayersStatistics", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "PlayersStatistics", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "IssuedDate", + schema: "Club12", + table: "PlayerSanctions", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "PlayerSanctions", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "PlayerSanctions", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Players", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Players", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "BirthDate", + schema: "Club12", + table: "Players", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "MatchDate", + schema: "Club12", + table: "Matches", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Matches", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Matches", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Divisions", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Divisions", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "BlogPosts", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "BlogPosts", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Venues", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Venues", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RefreshTokenExpiryTime", + schema: "Club12", + table: "Users", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Users", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Users", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Tournaments", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Tournaments", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Teams", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Teams", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "StartDate", + schema: "Club12", + table: "Stages", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "EndDate", + schema: "Club12", + table: "Stages", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Stages", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Stages", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Staffs", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Staffs", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "PlayersStatistics", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "PlayersStatistics", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "IssuedDate", + schema: "Club12", + table: "PlayerSanctions", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "PlayerSanctions", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "PlayerSanctions", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Players", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Players", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "BirthDate", + schema: "Club12", + table: "Players", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "MatchDate", + schema: "Club12", + table: "Matches", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Matches", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Matches", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "Divisions", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "Divisions", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "DateUpdated", + schema: "Club12", + table: "BlogPosts", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DateCreated", + schema: "Club12", + table: "BlogPosts", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250809010120_addMatchIdIntoPlayerSanctionEntity.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20250809010120_addMatchIdIntoPlayerSanctionEntity.Designer.cs new file mode 100644 index 0000000..777a1f5 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250809010120_addMatchIdIntoPlayerSanctionEntity.Designer.cs @@ -0,0 +1,698 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20250809010120_addMatchIdIntoPlayerSanctionEntity")] + partial class addMatchIdIntoPlayerSanctionEntity + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPosts.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PlayoffsGenerated") + .HasColumnType("boolean"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Venues.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Matches") + .HasForeignKey("DivisionId"); + + b.HasOne("Entities.Models.Teams.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Venues.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Entities.Models.Teams.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Teams") + .HasForeignKey("DivisionId"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Navigation("Matches"); + + b.Navigation("Stages"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Navigation("Divisions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250809010120_addMatchIdIntoPlayerSanctionEntity.cs b/Club12-Backend/Infrastructure/Migrations/20250809010120_addMatchIdIntoPlayerSanctionEntity.cs new file mode 100644 index 0000000..edb1297 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250809010120_addMatchIdIntoPlayerSanctionEntity.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class addMatchIdIntoPlayerSanctionEntity : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "MatchId", + schema: "Club12", + table: "PlayerSanctions", + type: "uuid", + nullable: false, + defaultValue: Guid.Empty); + + migrationBuilder.CreateIndex( + name: "IX_PlayerSanctions_MatchId", + schema: "Club12", + table: "PlayerSanctions", + column: "MatchId"); + + migrationBuilder.AddForeignKey( + name: "FK_PlayerSanctions_Matches_MatchId", + schema: "Club12", + table: "PlayerSanctions", + column: "MatchId", + principalSchema: "Club12", + principalTable: "Matches", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_PlayerSanctions_Matches_MatchId", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropIndex( + name: "IX_PlayerSanctions_MatchId", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropColumn( + name: "MatchId", + schema: "Club12", + table: "PlayerSanctions"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250816164236_AddChangesInTournamentEntity.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20250816164236_AddChangesInTournamentEntity.Designer.cs new file mode 100644 index 0000000..b959bb7 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250816164236_AddChangesInTournamentEntity.Designer.cs @@ -0,0 +1,729 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20250816164236_AddChangesInTournamentEntity")] + partial class AddChangesInTournamentEntity + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPosts.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PlayoffsGenerated") + .HasColumnType("boolean"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "[TeamRegistrationDeadline] < [StartDate]"); + }); + }); + + modelBuilder.Entity("Entities.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Venues.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Matches") + .HasForeignKey("DivisionId"); + + b.HasOne("Entities.Models.Teams.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Venues.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Entities.Models.Teams.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.HasOne("Entities.Models.Divisions.Division", null) + .WithMany("Teams") + .HasForeignKey("DivisionId"); + + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Navigation("Matches"); + + b.Navigation("Stages"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20250816164236_AddChangesInTournamentEntity.cs b/Club12-Backend/Infrastructure/Migrations/20250816164236_AddChangesInTournamentEntity.cs new file mode 100644 index 0000000..e8a4e70 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20250816164236_AddChangesInTournamentEntity.cs @@ -0,0 +1,132 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class AddChangesInTournamentEntity : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsFinished", + schema: "Club12", + table: "Tournaments", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "MaxTeams", + schema: "Club12", + table: "Tournaments", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "MinTeams", + schema: "Club12", + table: "Tournaments", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "StartDate", + schema: "Club12", + table: "Tournaments", + type: "timestamp without time zone", + nullable: false, + defaultValue: new DateTime(2, 2, 2, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "TeamRegistrationDeadline", + schema: "Club12", + table: "Tournaments", + type: "timestamp without time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "TournamentId", + schema: "Club12", + table: "Teams", + type: "uuid", + nullable: true); + + migrationBuilder.AddCheckConstraint( + name: "CK_Tournament_DeadlineBeforeStart", + schema: "Club12", + table: "Tournaments", + sql: "\"TeamRegistrationDeadline\" < \"StartDate\""); + + migrationBuilder.CreateIndex( + name: "IX_Teams_TournamentId", + schema: "Club12", + table: "Teams", + column: "TournamentId"); + + migrationBuilder.AddForeignKey( + name: "FK_Teams_Tournaments_TournamentId", + schema: "Club12", + table: "Teams", + column: "TournamentId", + principalSchema: "Club12", + principalTable: "Tournaments", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Teams_Tournaments_TournamentId", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropCheckConstraint( + name: "CK_Tournament_DeadlineBeforeStart", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropIndex( + name: "IX_Teams_TournamentId", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropColumn( + name: "IsFinished", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropColumn( + name: "MaxTeams", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropColumn( + name: "MinTeams", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropColumn( + name: "StartDate", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropColumn( + name: "TeamRegistrationDeadline", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropColumn( + name: "TournamentId", + schema: "Club12", + table: "Teams"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260201150715_ChangesEntities.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260201150715_ChangesEntities.Designer.cs new file mode 100644 index 0000000..47a858b --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260201150715_ChangesEntities.Designer.cs @@ -0,0 +1,707 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260201150715_ChangesEntities")] + partial class ChangesEntities + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPosts.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CanGenerateStageAutomated") + .HasColumnType("boolean"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Entities.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Venues.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.HasOne("Entities.Models.Teams.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Venues.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Entities.Models.Teams.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260201150715_ChangesEntities.cs b/Club12-Backend/Infrastructure/Migrations/20260201150715_ChangesEntities.cs new file mode 100644 index 0000000..0008299 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260201150715_ChangesEntities.cs @@ -0,0 +1,139 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class ChangesEntities : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Matches_Divisions_DivisionId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropForeignKey( + name: "FK_Teams_Divisions_DivisionId", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropCheckConstraint( + name: "CK_Tournament_DeadlineBeforeStart", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropIndex( + name: "IX_Teams_DivisionId", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropIndex( + name: "IX_Matches_DivisionId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "DivisionId", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropColumn( + name: "DivisionId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.RenameColumn( + name: "PlayoffsGenerated", + schema: "Club12", + table: "Divisions", + newName: "CanGenerateStageAutomated"); + + migrationBuilder.AddCheckConstraint( + name: "CK_Tournament_DeadlineBeforeStart", + schema: "Club12", + table: "Tournaments", + sql: "\"TeamRegistrationDeadline\" < \"StartDate\""); + + migrationBuilder.Sql( + @" + UPDATE + ""Club12"".""Divisions"" + SET + ""CanGenerateStageAutomated"" = CASE + WHEN (SELECT COUNT(*) FROM ""Club12"".""Stages"" WHERE ""Stages"".""DivisionId"" = ""Divisions"".""Id"") = 0 THEN TRUE + ELSE FALSE + END + " + ); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropCheckConstraint( + name: "CK_Tournament_DeadlineBeforeStart", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.RenameColumn( + name: "CanGenerateStageAutomated", + schema: "Club12", + table: "Divisions", + newName: "PlayoffsGenerated"); + + migrationBuilder.AddColumn( + name: "DivisionId", + schema: "Club12", + table: "Teams", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "DivisionId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: true); + + migrationBuilder.AddCheckConstraint( + name: "CK_Tournament_DeadlineBeforeStart", + schema: "Club12", + table: "Tournaments", + sql: "\"TeamRegistrationDeadline\" < \"StartDate\""); + + migrationBuilder.CreateIndex( + name: "IX_Teams_DivisionId", + schema: "Club12", + table: "Teams", + column: "DivisionId"); + + migrationBuilder.CreateIndex( + name: "IX_Matches_DivisionId", + schema: "Club12", + table: "Matches", + column: "DivisionId"); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_Divisions_DivisionId", + schema: "Club12", + table: "Matches", + column: "DivisionId", + principalSchema: "Club12", + principalTable: "Divisions", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_Teams_Divisions_DivisionId", + schema: "Club12", + table: "Teams", + column: "DivisionId", + principalSchema: "Club12", + principalTable: "Divisions", + principalColumn: "Id"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260204033209_addOrderColumnIntoStage.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260204033209_addOrderColumnIntoStage.Designer.cs new file mode 100644 index 0000000..09bf366 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260204033209_addOrderColumnIntoStage.Designer.cs @@ -0,0 +1,710 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260204033209_addOrderColumnIntoStage")] + partial class addOrderColumnIntoStage + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPosts.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CanGenerateStageAutomated") + .HasColumnType("boolean"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Entities.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Venues.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.HasOne("Entities.Models.Teams.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Venues.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Entities.Models.Teams.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260204033209_addOrderColumnIntoStage.cs b/Club12-Backend/Infrastructure/Migrations/20260204033209_addOrderColumnIntoStage.cs new file mode 100644 index 0000000..6f3780a --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260204033209_addOrderColumnIntoStage.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class addOrderColumnIntoStage : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Order", + schema: "Club12", + table: "Stages", + type: "integer", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Order", + schema: "Club12", + table: "Stages"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260205004338_removeUnneccessaryProp.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260205004338_removeUnneccessaryProp.Designer.cs new file mode 100644 index 0000000..0d90046 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260205004338_removeUnneccessaryProp.Designer.cs @@ -0,0 +1,705 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260205004338_removeUnneccessaryProp")] + partial class removeUnneccessaryProp + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPosts.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Entities.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Venues.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.HasOne("Entities.Models.Teams.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Venues.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Entities.Models.Teams.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Entities.Models.Teams.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260205004338_removeUnneccessaryProp.cs b/Club12-Backend/Infrastructure/Migrations/20260205004338_removeUnneccessaryProp.cs new file mode 100644 index 0000000..dd9141a --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260205004338_removeUnneccessaryProp.cs @@ -0,0 +1,81 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class removeUnneccessaryProp : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Matches_Teams_VisitorTeamId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "CanGenerateStageAutomated", + schema: "Club12", + table: "Divisions"); + + migrationBuilder.AlterColumn( + name: "VisitorTeamId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_Teams_VisitorTeamId", + schema: "Club12", + table: "Matches", + column: "VisitorTeamId", + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Matches_Teams_VisitorTeamId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.AlterColumn( + name: "VisitorTeamId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: false, + defaultValue: Guid.Empty, + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AddColumn( + name: "CanGenerateStageAutomated", + schema: "Club12", + table: "Divisions", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_Teams_VisitorTeamId", + schema: "Club12", + table: "Matches", + column: "VisitorTeamId", + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260218023223_AddStageTeamMatchTable.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260218023223_AddStageTeamMatchTable.Designer.cs new file mode 100644 index 0000000..2d8e767 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260218023223_AddStageTeamMatchTable.Designer.cs @@ -0,0 +1,757 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260218023223_AddStageTeamMatchTable")] + partial class AddStageTeamMatchTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Entities.Models.BlogPosts.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Names") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Staffs", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.StageTeamMatches.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Entities.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RefreshToken") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Users", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Venues.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateCreated"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone") + .HasColumnName("DateUpdated"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.HasOne("Entities.Models.Teams.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Venues.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Entities.Models.Teams.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Entities.Models.Teams.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Entities.Models.PlayerSanctions.PlayerSanction", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.PlayerStatistics.PlayerStatistic", b => + { + b.HasOne("Entities.Models.Matches.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Players.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Entities.Models.Players.Player", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Staffs.Staff", b => + { + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("Staff") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.StageTeamMatches.StageTeamMatch", b => + { + b.HasOne("Entities.Models.Stages.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Models.Teams.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.HasOne("Entities.Models.Divisions.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.HasOne("Entities.Models.Tournaments.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Entities.Models.Divisions.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Entities.Models.Matches.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Entities.Models.Stages.Stage", b => + { + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Entities.Models.Teams.Team", b => + { + b.Navigation("Players"); + + b.Navigation("Staff"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Entities.Models.Tournaments.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260218023223_AddStageTeamMatchTable.cs b/Club12-Backend/Infrastructure/Migrations/20260218023223_AddStageTeamMatchTable.cs new file mode 100644 index 0000000..2b76584 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260218023223_AddStageTeamMatchTable.cs @@ -0,0 +1,65 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class AddStageTeamMatchTable : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "StageTeamMatches", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + StageId = table.Column(type: "uuid", nullable: false), + TeamId = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_StageTeamMatches", x => x.Id); + table.ForeignKey( + name: "FK_StageTeamMatches_Stages_StageId", + column: x => x.StageId, + principalSchema: "Club12", + principalTable: "Stages", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_StageTeamMatches_Teams_TeamId", + column: x => x.TeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_StageTeamMatches_StageId", + schema: "Club12", + table: "StageTeamMatches", + column: "StageId"); + + migrationBuilder.CreateIndex( + name: "IX_StageTeamMatches_TeamId", + schema: "Club12", + table: "StageTeamMatches", + column: "TeamId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "StageTeamMatches", + schema: "Club12"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260315232746_cleanDB.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260315232746_cleanDB.Designer.cs new file mode 100644 index 0000000..5c75926 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260315232746_cleanDB.Designer.cs @@ -0,0 +1,748 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260315232746_cleanDB")] + partial class cleanDB + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260315232746_cleanDB.cs b/Club12-Backend/Infrastructure/Migrations/20260315232746_cleanDB.cs new file mode 100644 index 0000000..46d52a4 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260315232746_cleanDB.cs @@ -0,0 +1,474 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class cleanDB : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Staffs", + schema: "Club12"); + + migrationBuilder.DropTable( + name: "Users", + schema: "Club12"); + + migrationBuilder.AddColumn( + name: "CreatedBy", + schema: "Club12", + table: "Venues", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Venues", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + schema: "Club12", + table: "Tournaments", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Tournaments", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + schema: "Club12", + table: "Teams", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Teams", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + schema: "Club12", + table: "StageTeamMatches", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + schema: "Club12", + table: "StageTeamMatches", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + schema: "Club12", + table: "Stages", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Stages", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + schema: "Club12", + table: "PlayersStatistics", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + schema: "Club12", + table: "PlayersStatistics", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + schema: "Club12", + table: "PlayerSanctions", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + schema: "Club12", + table: "PlayerSanctions", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + schema: "Club12", + table: "Players", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Players", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + schema: "Club12", + table: "Matches", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Matches", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + schema: "Club12", + table: "Divisions", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Divisions", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + schema: "Club12", + table: "BlogPosts", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + schema: "Club12", + table: "BlogPosts", + type: "text", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Venue_CreatedAt", + schema: "Club12", + table: "Venues", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_Tournament_CreatedAt", + schema: "Club12", + table: "Tournaments", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_Team_CreatedAt", + schema: "Club12", + table: "Teams", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_StageTeamMatch_CreatedAt", + schema: "Club12", + table: "StageTeamMatches", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_Stage_CreatedAt", + schema: "Club12", + table: "Stages", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_PlayerStatistic_CreatedAt", + schema: "Club12", + table: "PlayersStatistics", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_PlayerSanction_CreatedAt", + schema: "Club12", + table: "PlayerSanctions", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_Player_CreatedAt", + schema: "Club12", + table: "Players", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_Match_CreatedAt", + schema: "Club12", + table: "Matches", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_Division_CreatedAt", + schema: "Club12", + table: "Divisions", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_BlogPost_CreatedAt", + schema: "Club12", + table: "BlogPosts", + column: "DateCreated"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Venue_CreatedAt", + schema: "Club12", + table: "Venues"); + + migrationBuilder.DropIndex( + name: "IX_Tournament_CreatedAt", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropIndex( + name: "IX_Team_CreatedAt", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropIndex( + name: "IX_StageTeamMatch_CreatedAt", + schema: "Club12", + table: "StageTeamMatches"); + + migrationBuilder.DropIndex( + name: "IX_Stage_CreatedAt", + schema: "Club12", + table: "Stages"); + + migrationBuilder.DropIndex( + name: "IX_PlayerStatistic_CreatedAt", + schema: "Club12", + table: "PlayersStatistics"); + + migrationBuilder.DropIndex( + name: "IX_PlayerSanction_CreatedAt", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropIndex( + name: "IX_Player_CreatedAt", + schema: "Club12", + table: "Players"); + + migrationBuilder.DropIndex( + name: "IX_Match_CreatedAt", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropIndex( + name: "IX_Division_CreatedAt", + schema: "Club12", + table: "Divisions"); + + migrationBuilder.DropIndex( + name: "IX_BlogPost_CreatedAt", + schema: "Club12", + table: "BlogPosts"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + schema: "Club12", + table: "Venues"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Venues"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + schema: "Club12", + table: "StageTeamMatches"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + schema: "Club12", + table: "StageTeamMatches"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + schema: "Club12", + table: "Stages"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Stages"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + schema: "Club12", + table: "PlayersStatistics"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + schema: "Club12", + table: "PlayersStatistics"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + schema: "Club12", + table: "Players"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Players"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + schema: "Club12", + table: "Divisions"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + schema: "Club12", + table: "Divisions"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + schema: "Club12", + table: "BlogPosts"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + schema: "Club12", + table: "BlogPosts"); + + migrationBuilder.CreateTable( + name: "Staffs", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + TeamId = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + LastName = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + Names = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + PhoneNumber = table.Column(type: "character varying(15)", maxLength: 15, nullable: false), + Type = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Staffs", x => x.Id); + table.ForeignKey( + name: "FK_Staffs_Teams_TeamId", + column: x => x.TeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Users", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + Password = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + RefreshToken = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + RefreshTokenExpiryTime = table.Column(type: "timestamp without time zone", nullable: true), + Role = table.Column(type: "character varying(10)", maxLength: 10, nullable: false), + Username = table.Column(type: "character varying(25)", maxLength: 25, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Staffs_TeamId", + schema: "Club12", + table: "Staffs", + column: "TeamId"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260326234428_changeTournamentEntity.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260326234428_changeTournamentEntity.Designer.cs new file mode 100644 index 0000000..095a3e8 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260326234428_changeTournamentEntity.Designer.cs @@ -0,0 +1,750 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260326234428_changeTournamentEntity")] + partial class changeTournamentEntity + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260326234428_changeTournamentEntity.cs b/Club12-Backend/Infrastructure/Migrations/20260326234428_changeTournamentEntity.cs new file mode 100644 index 0000000..a4bba14 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260326234428_changeTournamentEntity.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class changeTournamentEntity : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsFinished", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.AddColumn( + name: "Status", + schema: "Club12", + table: "Tournaments", + type: "integer", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Status", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.AddColumn( + name: "IsFinished", + schema: "Club12", + table: "Tournaments", + type: "boolean", + nullable: false, + defaultValue: false); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260403194406_AddScorerTable.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260403194406_AddScorerTable.Designer.cs new file mode 100644 index 0000000..caa551c --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260403194406_AddScorerTable.Designer.cs @@ -0,0 +1,816 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260403194406_AddScorerTable")] + partial class AddScorerTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260403194406_AddScorerTable.cs b/Club12-Backend/Infrastructure/Migrations/20260403194406_AddScorerTable.cs new file mode 100644 index 0000000..fd3bd01 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260403194406_AddScorerTable.cs @@ -0,0 +1,74 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Persistance.Migrations; + +/// +public partial class AddScorerTable : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Scorers", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + PlayerId = table.Column(type: "uuid", nullable: false), + Points = table.Column(type: "integer", nullable: false), + MatchId = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + CreatedBy = table.Column(type: "text", nullable: false), + UpdatedBy = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Scorers", x => x.Id); + table.ForeignKey( + name: "FK_Scorers_Matches_MatchId", + column: x => x.MatchId, + principalSchema: "Club12", + principalTable: "Matches", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Scorers_Players_PlayerId", + column: x => x.PlayerId, + principalSchema: "Club12", + principalTable: "Players", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Scorer_CreatedAt", + schema: "Club12", + table: "Scorers", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_Scorers_MatchId", + schema: "Club12", + table: "Scorers", + column: "MatchId"); + + migrationBuilder.CreateIndex( + name: "IX_Scorers_PlayerId", + schema: "Club12", + table: "Scorers", + column: "PlayerId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Scorers", + schema: "Club12"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260629150628_AddPlayerStatisticType.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260629150628_AddPlayerStatisticType.Designer.cs new file mode 100644 index 0000000..6becc45 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260629150628_AddPlayerStatisticType.Designer.cs @@ -0,0 +1,820 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260629150628_AddPlayerStatisticType")] + partial class AddPlayerStatisticType + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.25") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260629150628_AddPlayerStatisticType.cs b/Club12-Backend/Infrastructure/Migrations/20260629150628_AddPlayerStatisticType.cs new file mode 100644 index 0000000..e4792c9 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260629150628_AddPlayerStatisticType.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations; + +/// +public partial class AddPlayerStatisticType : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Type", + schema: "Club12", + table: "PlayersStatistics", + type: "text", + nullable: false, + defaultValue: "Points"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Type", + schema: "Club12", + table: "PlayersStatistics"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260629151546_AddSanctionAppeal.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260629151546_AddSanctionAppeal.Designer.cs new file mode 100644 index 0000000..6ef71bf --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260629151546_AddSanctionAppeal.Designer.cs @@ -0,0 +1,838 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260629151546_AddSanctionAppeal")] + partial class AddSanctionAppeal + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.25") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260629151546_AddSanctionAppeal.cs b/Club12-Backend/Infrastructure/Migrations/20260629151546_AddSanctionAppeal.cs new file mode 100644 index 0000000..2a67918 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260629151546_AddSanctionAppeal.cs @@ -0,0 +1,82 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Infrastructure.Migrations; + +/// +public partial class AddSanctionAppeal : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "AppealDate", + schema: "Club12", + table: "PlayerSanctions", + type: "timestamp without time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "AppealReason", + schema: "Club12", + table: "PlayerSanctions", + type: "character varying(1000)", + maxLength: 1000, + nullable: true); + + migrationBuilder.AddColumn( + name: "AppealResolution", + schema: "Club12", + table: "PlayerSanctions", + type: "character varying(1000)", + maxLength: 1000, + nullable: true); + + migrationBuilder.AddColumn( + name: "AppealResolvedDate", + schema: "Club12", + table: "PlayerSanctions", + type: "timestamp without time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "AppealStatus", + schema: "Club12", + table: "PlayerSanctions", + type: "text", + nullable: false, + defaultValue: "None"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "AppealDate", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropColumn( + name: "AppealReason", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropColumn( + name: "AppealResolution", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropColumn( + name: "AppealResolvedDate", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropColumn( + name: "AppealStatus", + schema: "Club12", + table: "PlayerSanctions"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260816030250_AddPlayoffSeriesAndBracketName.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260816030250_AddPlayoffSeriesAndBracketName.Designer.cs new file mode 100644 index 0000000..bef4902 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260816030250_AddPlayoffSeriesAndBracketName.Designer.cs @@ -0,0 +1,959 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260816030250_AddPlayoffSeriesAndBracketName")] + partial class AddPlayoffSeriesAndBracketName + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260816030250_AddPlayoffSeriesAndBracketName.cs b/Club12-Backend/Infrastructure/Migrations/20260816030250_AddPlayoffSeriesAndBracketName.cs new file mode 100644 index 0000000..5df628f --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260816030250_AddPlayoffSeriesAndBracketName.cs @@ -0,0 +1,202 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Infrastructure.Migrations; + +/// +public partial class AddPlayoffSeriesAndBracketName : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "BestOf", + schema: "Club12", + table: "Stages", + type: "integer", + nullable: false, + defaultValue: 1); + + migrationBuilder.AddColumn( + name: "BracketName", + schema: "Club12", + table: "Stages", + type: "character varying(100)", + maxLength: 100, + nullable: true); + + migrationBuilder.AddColumn( + name: "RoundRobinLegs", + schema: "Club12", + table: "Stages", + type: "integer", + nullable: false, + defaultValue: 1); + + migrationBuilder.AddColumn( + name: "GameNumber", + schema: "Club12", + table: "Matches", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "SeriesId", + schema: "Club12", + table: "Matches", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "IsCrossDivisionCup", + schema: "Club12", + table: "Divisions", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateTable( + name: "MatchSeries", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + StageId = table.Column(type: "uuid", nullable: false), + HomeTeamId = table.Column(type: "uuid", nullable: false), + VisitorTeamId = table.Column(type: "uuid", nullable: false), + BestOf = table.Column(type: "integer", nullable: false), + WinningTeamId = table.Column(type: "uuid", nullable: true), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + CreatedBy = table.Column(type: "text", nullable: false), + UpdatedBy = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MatchSeries", x => x.Id); + table.ForeignKey( + name: "FK_MatchSeries_Stages_StageId", + column: x => x.StageId, + principalSchema: "Club12", + principalTable: "Stages", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MatchSeries_Teams_HomeTeamId", + column: x => x.HomeTeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MatchSeries_Teams_VisitorTeamId", + column: x => x.VisitorTeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MatchSeries_Teams_WinningTeamId", + column: x => x.WinningTeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Matches_SeriesId", + schema: "Club12", + table: "Matches", + column: "SeriesId"); + + migrationBuilder.CreateIndex( + name: "IX_MatchSeries_CreatedAt", + schema: "Club12", + table: "MatchSeries", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_MatchSeries_HomeTeamId", + schema: "Club12", + table: "MatchSeries", + column: "HomeTeamId"); + + migrationBuilder.CreateIndex( + name: "IX_MatchSeries_StageId", + schema: "Club12", + table: "MatchSeries", + column: "StageId"); + + migrationBuilder.CreateIndex( + name: "IX_MatchSeries_VisitorTeamId", + schema: "Club12", + table: "MatchSeries", + column: "VisitorTeamId"); + + migrationBuilder.CreateIndex( + name: "IX_MatchSeries_WinningTeamId", + schema: "Club12", + table: "MatchSeries", + column: "WinningTeamId"); + + migrationBuilder.AddForeignKey( + name: "FK_Matches_MatchSeries_SeriesId", + schema: "Club12", + table: "Matches", + column: "SeriesId", + principalSchema: "Club12", + principalTable: "MatchSeries", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Matches_MatchSeries_SeriesId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropTable( + name: "MatchSeries", + schema: "Club12"); + + migrationBuilder.DropIndex( + name: "IX_Matches_SeriesId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "BestOf", + schema: "Club12", + table: "Stages"); + + migrationBuilder.DropColumn( + name: "BracketName", + schema: "Club12", + table: "Stages"); + + migrationBuilder.DropColumn( + name: "RoundRobinLegs", + schema: "Club12", + table: "Stages"); + + migrationBuilder.DropColumn( + name: "GameNumber", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "SeriesId", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "IsCrossDivisionCup", + schema: "Club12", + table: "Divisions"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260817044123_AddSlugToTournamentAndTeam.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260817044123_AddSlugToTournamentAndTeam.Designer.cs new file mode 100644 index 0000000..29e3679 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260817044123_AddSlugToTournamentAndTeam.Designer.cs @@ -0,0 +1,975 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260817044123_AddSlugToTournamentAndTeam")] + partial class AddSlugToTournamentAndTeam + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260817044123_AddSlugToTournamentAndTeam.cs b/Club12-Backend/Infrastructure/Migrations/20260817044123_AddSlugToTournamentAndTeam.cs new file mode 100644 index 0000000..65b0406 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260817044123_AddSlugToTournamentAndTeam.cs @@ -0,0 +1,146 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations; + +/// +public partial class AddSlugToTournamentAndTeam : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Slug", + schema: "Club12", + table: "Tournaments", + type: "character varying(220)", + maxLength: 220, + nullable: true); + + migrationBuilder.AddColumn( + name: "Slug", + schema: "Club12", + table: "Teams", + type: "character varying(220)", + maxLength: 220, + nullable: true); + + // Backfills every existing row's Slug from its current Name, mirroring + // SlugGenerator.GenerateSlug (lowercase, accented characters + // transliterated to their base letter, non-alphanumeric runs collapsed + // to a single hyphen, leading/trailing hyphens trimmed). Rows whose + // computed base slug collides get a numeric suffix (-2, -3, ...) + // ordered by Id, the same disambiguation strategy SlugGenerator uses + // for newly created rows. + migrationBuilder.Sql( + @" + WITH base AS ( + SELECT ""Id"", + trim(both '-' from + regexp_replace( + translate(lower(""Name""), 'áéíóúüñ', 'aeiouun'), + '[^a-z0-9]+', '-', 'g' + ) + ) AS slug_base + FROM ""Club12"".""Tournaments"" + ), + numbered AS ( + SELECT ""Id"", slug_base, + ROW_NUMBER() OVER (PARTITION BY slug_base ORDER BY ""Id"") AS rn + FROM base + ) + UPDATE ""Club12"".""Tournaments"" t + SET ""Slug"" = CASE WHEN n.rn = 1 THEN n.slug_base ELSE n.slug_base || '-' || n.rn::text END + FROM numbered n + WHERE t.""Id"" = n.""Id""; + " + ); + + migrationBuilder.Sql( + @" + WITH base AS ( + SELECT ""Id"", + trim(both '-' from + regexp_replace( + translate(lower(""Name""), 'áéíóúüñ', 'aeiouun'), + '[^a-z0-9]+', '-', 'g' + ) + ) AS slug_base + FROM ""Club12"".""Teams"" + ), + numbered AS ( + SELECT ""Id"", slug_base, + ROW_NUMBER() OVER (PARTITION BY slug_base ORDER BY ""Id"") AS rn + FROM base + ) + UPDATE ""Club12"".""Teams"" t + SET ""Slug"" = CASE WHEN n.rn = 1 THEN n.slug_base ELSE n.slug_base || '-' || n.rn::text END + FROM numbered n + WHERE t.""Id"" = n.""Id""; + " + ); + + migrationBuilder.AlterColumn( + name: "Slug", + schema: "Club12", + table: "Tournaments", + type: "character varying(220)", + maxLength: 220, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(220)", + oldMaxLength: 220, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Slug", + schema: "Club12", + table: "Teams", + type: "character varying(220)", + maxLength: 220, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(220)", + oldMaxLength: 220, + oldNullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Tournaments_Slug", + schema: "Club12", + table: "Tournaments", + column: "Slug", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Teams_Slug", + schema: "Club12", + table: "Teams", + column: "Slug", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Tournaments_Slug", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropIndex( + name: "IX_Teams_Slug", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropColumn( + name: "Slug", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropColumn( + name: "Slug", + schema: "Club12", + table: "Teams"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260817064644_AddSlugToMatch.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260817064644_AddSlugToMatch.Designer.cs new file mode 100644 index 0000000..60af11c --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260817064644_AddSlugToMatch.Designer.cs @@ -0,0 +1,983 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260817064644_AddSlugToMatch")] + partial class AddSlugToMatch + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260817064644_AddSlugToMatch.cs b/Club12-Backend/Infrastructure/Migrations/20260817064644_AddSlugToMatch.cs new file mode 100644 index 0000000..3d4a16c --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260817064644_AddSlugToMatch.cs @@ -0,0 +1,98 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations; + +/// +public partial class AddSlugToMatch : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Slug", + schema: "Club12", + table: "Matches", + type: "character varying(220)", + maxLength: 220, + nullable: true); + + // Backfills every existing match's Slug from its home/visitor team names + // and match date, mirroring MatchSlugSourceBuilder.Build + SlugGenerator.GenerateSlug + // ("{homeTeamName} vs {visitorTeamName} {yyyy-MM-dd}", lowercased, accented + // characters transliterated to their base letter, non-alphanumeric runs + // collapsed to a single hyphen, leading/trailing hyphens trimmed). A team + // missing from either side (unseeded match) falls back to the same "TBD" + // placeholder the application uses. Rows whose computed base slug collides + // (e.g. two legs between the same pairing on the same day, or several + // unseeded matches) get a numeric suffix (-2, -3, ...) ordered by Id, the + // same disambiguation strategy SlugGenerator uses for newly created rows. + migrationBuilder.Sql( + @" + WITH base AS ( + SELECT m.""Id"", + trim(both '-' from + regexp_replace( + translate( + lower( + concat( + COALESCE(ht.""Name"", 'TBD'), ' vs ', COALESCE(vt.""Name"", 'TBD'), + ' ', to_char(m.""MatchDate"", 'YYYY-MM-DD') + ) + ), + 'áéíóúüñ', 'aeiouun' + ), + '[^a-z0-9]+', '-', 'g' + ) + ) AS slug_base + FROM ""Club12"".""Matches"" m + LEFT JOIN ""Club12"".""Teams"" ht ON ht.""Id"" = m.""HomeTeamId"" + LEFT JOIN ""Club12"".""Teams"" vt ON vt.""Id"" = m.""VisitorTeamId"" + ), + numbered AS ( + SELECT ""Id"", slug_base, + ROW_NUMBER() OVER (PARTITION BY slug_base ORDER BY ""Id"") AS rn + FROM base + ) + UPDATE ""Club12"".""Matches"" m + SET ""Slug"" = CASE WHEN n.rn = 1 THEN n.slug_base ELSE n.slug_base || '-' || n.rn::text END + FROM numbered n + WHERE m.""Id"" = n.""Id""; + " + ); + + migrationBuilder.AlterColumn( + name: "Slug", + schema: "Club12", + table: "Matches", + type: "character varying(220)", + maxLength: 220, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(220)", + oldMaxLength: 220, + oldNullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Matches_Slug", + schema: "Club12", + table: "Matches", + column: "Slug", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Matches_Slug", + schema: "Club12", + table: "Matches"); + + migrationBuilder.DropColumn( + name: "Slug", + schema: "Club12", + table: "Matches"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260817082125_AddPlayerTeamRegistrationTable.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260817082125_AddPlayerTeamRegistrationTable.Designer.cs new file mode 100644 index 0000000..d1627f8 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260817082125_AddPlayerTeamRegistrationTable.Designer.cs @@ -0,0 +1,1057 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260817082125_AddPlayerTeamRegistrationTable")] + partial class AddPlayerTeamRegistrationTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260817082125_AddPlayerTeamRegistrationTable.cs b/Club12-Backend/Infrastructure/Migrations/20260817082125_AddPlayerTeamRegistrationTable.cs new file mode 100644 index 0000000..c566ee9 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260817082125_AddPlayerTeamRegistrationTable.cs @@ -0,0 +1,115 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Infrastructure.Migrations; + +/// +public partial class AddPlayerTeamRegistrationTable : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "PlayerTeamRegistrations", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + PlayerId = table.Column(type: "uuid", nullable: false), + TeamId = table.Column(type: "uuid", nullable: false), + TournamentId = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + CreatedBy = table.Column(type: "text", nullable: false), + UpdatedBy = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PlayerTeamRegistrations", x => x.Id); + table.ForeignKey( + name: "FK_PlayerTeamRegistrations_Players_PlayerId", + column: x => x.PlayerId, + principalSchema: "Club12", + principalTable: "Players", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_PlayerTeamRegistrations_Teams_TeamId", + column: x => x.TeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_PlayerTeamRegistrations_Tournaments_TournamentId", + column: x => x.TournamentId, + principalSchema: "Club12", + principalTable: "Tournaments", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_PlayerTeamRegistration_CreatedAt", + schema: "Club12", + table: "PlayerTeamRegistrations", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_PlayerTeamRegistrations_PlayerId_TournamentId", + schema: "Club12", + table: "PlayerTeamRegistrations", + columns: new[] { "PlayerId", "TournamentId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_PlayerTeamRegistrations_TeamId", + schema: "Club12", + table: "PlayerTeamRegistrations", + column: "TeamId"); + + migrationBuilder.CreateIndex( + name: "IX_PlayerTeamRegistrations_TournamentId", + schema: "Club12", + table: "PlayerTeamRegistrations", + column: "TournamentId"); + + // Data backfill: every existing player's CURRENT team assignment + // (Players.TeamId) becomes a season-scoped registration for + // whatever tournament that team is CURRENTLY assigned to + // (Teams.TournamentId at the time this migration runs). This + // preserves today's roster attribution exactly as-is — e.g. a + // player on a team currently pointed at "Apertura Club 12 2026" + // gets a registration for that same tournament, a player on a + // team currently pointed at "Femenino Club12 La Vuelta" gets a + // registration for that one. Players whose team has no current + // tournament (Teams.TournamentId IS NULL) are skipped: there is + // no season to attribute them to. + migrationBuilder.Sql( + """ + INSERT INTO "Club12"."PlayerTeamRegistrations" + ("Id", "PlayerId", "TeamId", "TournamentId", "DateCreated", "CreatedBy") + SELECT + gen_random_uuid(), + p."Id", + p."TeamId", + t."TournamentId", + p."DateCreated", + 'System' + FROM "Club12"."Players" p + JOIN "Club12"."Teams" t ON t."Id" = p."TeamId" + WHERE t."TournamentId" IS NOT NULL; + """); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "PlayerTeamRegistrations", + schema: "Club12"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260817115955_AddSlugToBlogPost.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260817115955_AddSlugToBlogPost.Designer.cs new file mode 100644 index 0000000..b95a19d --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260817115955_AddSlugToBlogPost.Designer.cs @@ -0,0 +1,1065 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260817115955_AddSlugToBlogPost")] + partial class AddSlugToBlogPost + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260817115955_AddSlugToBlogPost.cs b/Club12-Backend/Infrastructure/Migrations/20260817115955_AddSlugToBlogPost.cs new file mode 100644 index 0000000..189ecc8 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260817115955_AddSlugToBlogPost.cs @@ -0,0 +1,85 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations; + +/// +public partial class AddSlugToBlogPost : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Slug", + schema: "Club12", + table: "BlogPosts", + type: "character varying(220)", + maxLength: 220, + nullable: true); + + // Backfills every existing post's Slug from its current Title, mirroring + // SlugGenerator.GenerateSlug (lowercase, accented characters + // transliterated to their base letter, non-alphanumeric runs collapsed + // to a single hyphen, leading/trailing hyphens trimmed). Rows whose + // computed base slug collides get a numeric suffix (-2, -3, ...) + // ordered by Id, the same disambiguation strategy SlugGenerator uses + // for newly created rows. + migrationBuilder.Sql( + @" + WITH base AS ( + SELECT ""Id"", + trim(both '-' from + regexp_replace( + translate(lower(""Title""), 'áéíóúüñ', 'aeiouun'), + '[^a-z0-9]+', '-', 'g' + ) + ) AS slug_base + FROM ""Club12"".""BlogPosts"" + ), + numbered AS ( + SELECT ""Id"", slug_base, + ROW_NUMBER() OVER (PARTITION BY slug_base ORDER BY ""Id"") AS rn + FROM base + ) + UPDATE ""Club12"".""BlogPosts"" bp + SET ""Slug"" = CASE WHEN n.rn = 1 THEN n.slug_base ELSE n.slug_base || '-' || n.rn::text END + FROM numbered n + WHERE bp.""Id"" = n.""Id""; + " + ); + + migrationBuilder.AlterColumn( + name: "Slug", + schema: "Club12", + table: "BlogPosts", + type: "character varying(220)", + maxLength: 220, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(220)", + oldMaxLength: 220, + oldNullable: true); + + migrationBuilder.CreateIndex( + name: "IX_BlogPosts_Slug", + schema: "Club12", + table: "BlogPosts", + column: "Slug", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_BlogPosts_Slug", + schema: "Club12", + table: "BlogPosts"); + + migrationBuilder.DropColumn( + name: "Slug", + schema: "Club12", + table: "BlogPosts"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260817120017_AddSlugToPlayerSanction.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260817120017_AddSlugToPlayerSanction.Designer.cs new file mode 100644 index 0000000..37ac505 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260817120017_AddSlugToPlayerSanction.Designer.cs @@ -0,0 +1,1073 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260817120017_AddSlugToPlayerSanction")] + partial class AddSlugToPlayerSanction + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260817120017_AddSlugToPlayerSanction.cs b/Club12-Backend/Infrastructure/Migrations/20260817120017_AddSlugToPlayerSanction.cs new file mode 100644 index 0000000..247b1cc --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260817120017_AddSlugToPlayerSanction.cs @@ -0,0 +1,101 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations; + +/// +public partial class AddSlugToPlayerSanction : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Slug", + schema: "Club12", + table: "PlayerSanctions", + type: "character varying(220)", + maxLength: 220, + nullable: true); + + // Backfills every existing sanction's Slug from its sanctioned player's + // name and the sanction's issued date, mirroring PlayerSanctionService's + // "{playerFullName} {issuedDate:yyyy-MM-dd}" source string fed through + // SlugGenerator.GenerateSlug (lowercase, accented characters + // transliterated to their base letter, non-alphanumeric runs collapsed + // to a single hyphen, leading/trailing hyphens trimmed). The player's + // last/first/second name are concatenated the same way as the + // application's Player.FullName computed property (case does not + // matter here since the whole string is lowercased regardless). Rows + // whose computed base slug collides (e.g. the same player sanctioned + // twice on the same day) get a numeric suffix (-2, -3, ...) ordered by + // Id, the same disambiguation strategy SlugGenerator uses for newly + // created rows. + migrationBuilder.Sql( + @" + WITH base AS ( + SELECT ps.""Id"", + trim(both '-' from + regexp_replace( + translate( + lower( + concat( + p.""LastName"", ' ', p.""FirstName"", + CASE WHEN p.""SecondName"" IS NULL OR trim(p.""SecondName"") = '' THEN '' ELSE ' ' || p.""SecondName"" END, + ' ', to_char(ps.""IssuedDate"", 'YYYY-MM-DD') + ) + ), + 'áéíóúüñ', 'aeiouun' + ), + '[^a-z0-9]+', '-', 'g' + ) + ) AS slug_base + FROM ""Club12"".""PlayerSanctions"" ps + JOIN ""Club12"".""Players"" p ON p.""Id"" = ps.""PlayerId"" + ), + numbered AS ( + SELECT ""Id"", slug_base, + ROW_NUMBER() OVER (PARTITION BY slug_base ORDER BY ""Id"") AS rn + FROM base + ) + UPDATE ""Club12"".""PlayerSanctions"" ps + SET ""Slug"" = CASE WHEN n.rn = 1 THEN n.slug_base ELSE n.slug_base || '-' || n.rn::text END + FROM numbered n + WHERE ps.""Id"" = n.""Id""; + " + ); + + migrationBuilder.AlterColumn( + name: "Slug", + schema: "Club12", + table: "PlayerSanctions", + type: "character varying(220)", + maxLength: 220, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(220)", + oldMaxLength: 220, + oldNullable: true); + + migrationBuilder.CreateIndex( + name: "IX_PlayerSanctions_Slug", + schema: "Club12", + table: "PlayerSanctions", + column: "Slug", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_PlayerSanctions_Slug", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropColumn( + name: "Slug", + schema: "Club12", + table: "PlayerSanctions"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260820005357_AddBackupRecordTable.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260820005357_AddBackupRecordTable.Designer.cs new file mode 100644 index 0000000..8f73d6d --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260820005357_AddBackupRecordTable.Designer.cs @@ -0,0 +1,1112 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260820005357_AddBackupRecordTable")] + partial class AddBackupRecordTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260820005357_AddBackupRecordTable.cs b/Club12-Backend/Infrastructure/Migrations/20260820005357_AddBackupRecordTable.cs new file mode 100644 index 0000000..d576619 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260820005357_AddBackupRecordTable.cs @@ -0,0 +1,47 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations; + +/// +public partial class AddBackupRecordTable : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "BackupRecords", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + StoragePath = table.Column(type: "character varying(500)", maxLength: 500, nullable: false), + SizeBytes = table.Column(type: "bigint", nullable: false), + Origin = table.Column(type: "text", nullable: false), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + CreatedBy = table.Column(type: "text", nullable: false), + UpdatedBy = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_BackupRecords", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_BackupRecord_CreatedAt", + schema: "Club12", + table: "BackupRecords", + column: "DateCreated"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "BackupRecords", + schema: "Club12"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260827170738_AddTeamTournamentRegistrationTable.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260827170738_AddTeamTournamentRegistrationTable.Designer.cs new file mode 100644 index 0000000..d36bc11 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260827170738_AddTeamTournamentRegistrationTable.Designer.cs @@ -0,0 +1,1132 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260827170738_AddTeamTournamentRegistrationTable")] + partial class AddTeamTournamentRegistrationTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260827170738_AddTeamTournamentRegistrationTable.cs b/Club12-Backend/Infrastructure/Migrations/20260827170738_AddTeamTournamentRegistrationTable.cs new file mode 100644 index 0000000..122ca6a --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260827170738_AddTeamTournamentRegistrationTable.cs @@ -0,0 +1,74 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddTeamTournamentRegistrationTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "TeamTournamentRegistrations", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + TeamId = table.Column(type: "uuid", nullable: false), + TournamentId = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + CreatedBy = table.Column(type: "text", nullable: false), + UpdatedBy = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TeamTournamentRegistrations", x => x.Id); + table.ForeignKey( + name: "FK_TeamTournamentRegistrations_Teams_TeamId", + column: x => x.TeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_TeamTournamentRegistrations_Tournaments_TournamentId", + column: x => x.TournamentId, + principalSchema: "Club12", + principalTable: "Tournaments", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_TeamTournamentRegistration_CreatedAt", + schema: "Club12", + table: "TeamTournamentRegistrations", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_TeamTournamentRegistrations_TeamId_TournamentId", + schema: "Club12", + table: "TeamTournamentRegistrations", + columns: new[] { "TeamId", "TournamentId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_TeamTournamentRegistrations_TournamentId", + schema: "Club12", + table: "TeamTournamentRegistrations", + column: "TournamentId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TeamTournamentRegistrations", + schema: "Club12"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828003816_AddSlugToDivisionStageVenuePlayer.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260828003816_AddSlugToDivisionStageVenuePlayer.Designer.cs new file mode 100644 index 0000000..1176d43 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828003816_AddSlugToDivisionStageVenuePlayer.Designer.cs @@ -0,0 +1,1164 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260828003816_AddSlugToDivisionStageVenuePlayer")] + partial class AddSlugToDivisionStageVenuePlayer + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxTeams") + .HasColumnType("integer"); + + b.Property("MinTeams") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828003816_AddSlugToDivisionStageVenuePlayer.cs b/Club12-Backend/Infrastructure/Migrations/20260828003816_AddSlugToDivisionStageVenuePlayer.cs new file mode 100644 index 0000000..5f4df74 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828003816_AddSlugToDivisionStageVenuePlayer.cs @@ -0,0 +1,286 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddSlugToDivisionStageVenuePlayer : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Slug", + schema: "Club12", + table: "Divisions", + type: "character varying(220)", + maxLength: 220, + nullable: true); + + migrationBuilder.AddColumn( + name: "Slug", + schema: "Club12", + table: "Stages", + type: "character varying(220)", + maxLength: 220, + nullable: true); + + migrationBuilder.AddColumn( + name: "Slug", + schema: "Club12", + table: "Venues", + type: "character varying(220)", + maxLength: 220, + nullable: true); + + migrationBuilder.AddColumn( + name: "Slug", + schema: "Club12", + table: "Players", + type: "character varying(220)", + maxLength: 220, + nullable: true); + + // Backfills every existing Division/Stage/Venue row's Slug from its + // current Name, mirroring SlugGenerator.GenerateSlug (lowercase, + // accented characters transliterated to their base letter, + // non-alphanumeric runs collapsed to a single hyphen, + // leading/trailing hyphens trimmed). Rows whose computed base slug + // collides get a numeric suffix (-2, -3, ...) ordered by Id, the + // same disambiguation strategy SlugGenerator uses for newly created + // rows. + migrationBuilder.Sql( + @" + WITH base AS ( + SELECT ""Id"", + trim(both '-' from + regexp_replace( + translate(lower(""Name""), 'áéíóúüñ', 'aeiouun'), + '[^a-z0-9]+', '-', 'g' + ) + ) AS slug_base + FROM ""Club12"".""Divisions"" + ), + numbered AS ( + SELECT ""Id"", slug_base, + ROW_NUMBER() OVER (PARTITION BY slug_base ORDER BY ""Id"") AS rn + FROM base + ) + UPDATE ""Club12"".""Divisions"" t + SET ""Slug"" = CASE WHEN n.rn = 1 THEN n.slug_base ELSE n.slug_base || '-' || n.rn::text END + FROM numbered n + WHERE t.""Id"" = n.""Id""; + " + ); + + migrationBuilder.Sql( + @" + WITH base AS ( + SELECT ""Id"", + trim(both '-' from + regexp_replace( + translate(lower(""Name""), 'áéíóúüñ', 'aeiouun'), + '[^a-z0-9]+', '-', 'g' + ) + ) AS slug_base + FROM ""Club12"".""Stages"" + ), + numbered AS ( + SELECT ""Id"", slug_base, + ROW_NUMBER() OVER (PARTITION BY slug_base ORDER BY ""Id"") AS rn + FROM base + ) + UPDATE ""Club12"".""Stages"" t + SET ""Slug"" = CASE WHEN n.rn = 1 THEN n.slug_base ELSE n.slug_base || '-' || n.rn::text END + FROM numbered n + WHERE t.""Id"" = n.""Id""; + " + ); + + migrationBuilder.Sql( + @" + WITH base AS ( + SELECT ""Id"", + trim(both '-' from + regexp_replace( + translate(lower(""Name""), 'áéíóúüñ', 'aeiouun'), + '[^a-z0-9]+', '-', 'g' + ) + ) AS slug_base + FROM ""Club12"".""Venues"" + ), + numbered AS ( + SELECT ""Id"", slug_base, + ROW_NUMBER() OVER (PARTITION BY slug_base ORDER BY ""Id"") AS rn + FROM base + ) + UPDATE ""Club12"".""Venues"" t + SET ""Slug"" = CASE WHEN n.rn = 1 THEN n.slug_base ELSE n.slug_base || '-' || n.rn::text END + FROM numbered n + WHERE t.""Id"" = n.""Id""; + " + ); + + // Backfills every existing player's Slug from their full name, + // mirroring PlayerService's use of Player.FullName fed through + // SlugGenerator.GenerateSlug. The last/first/second name are + // concatenated the same way as the application's Player.FullName + // computed property (case does not matter here since the whole + // string is lowercased regardless). Duplicate names get a numeric + // suffix (-2, -3, ...) ordered by Id, matching SlugGenerator's + // disambiguation for newly created rows. + migrationBuilder.Sql( + @" + WITH base AS ( + SELECT ""Id"", + trim(both '-' from + regexp_replace( + translate( + lower( + concat( + ""LastName"", ' ', ""FirstName"", + CASE WHEN ""SecondName"" IS NULL OR trim(""SecondName"") = '' THEN '' ELSE ' ' || ""SecondName"" END + ) + ), + 'áéíóúüñ', 'aeiouun' + ), + '[^a-z0-9]+', '-', 'g' + ) + ) AS slug_base + FROM ""Club12"".""Players"" + ), + numbered AS ( + SELECT ""Id"", slug_base, + ROW_NUMBER() OVER (PARTITION BY slug_base ORDER BY ""Id"") AS rn + FROM base + ) + UPDATE ""Club12"".""Players"" t + SET ""Slug"" = CASE WHEN n.rn = 1 THEN n.slug_base ELSE n.slug_base || '-' || n.rn::text END + FROM numbered n + WHERE t.""Id"" = n.""Id""; + " + ); + + migrationBuilder.AlterColumn( + name: "Slug", + schema: "Club12", + table: "Divisions", + type: "character varying(220)", + maxLength: 220, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(220)", + oldMaxLength: 220, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Slug", + schema: "Club12", + table: "Stages", + type: "character varying(220)", + maxLength: 220, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(220)", + oldMaxLength: 220, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Slug", + schema: "Club12", + table: "Venues", + type: "character varying(220)", + maxLength: 220, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(220)", + oldMaxLength: 220, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Slug", + schema: "Club12", + table: "Players", + type: "character varying(220)", + maxLength: 220, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(220)", + oldMaxLength: 220, + oldNullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Divisions_Slug", + schema: "Club12", + table: "Divisions", + column: "Slug", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Stages_Slug", + schema: "Club12", + table: "Stages", + column: "Slug", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Venues_Slug", + schema: "Club12", + table: "Venues", + column: "Slug", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Players_Slug", + schema: "Club12", + table: "Players", + column: "Slug", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Venues_Slug", + schema: "Club12", + table: "Venues"); + + migrationBuilder.DropIndex( + name: "IX_Stages_Slug", + schema: "Club12", + table: "Stages"); + + migrationBuilder.DropIndex( + name: "IX_Players_Slug", + schema: "Club12", + table: "Players"); + + migrationBuilder.DropIndex( + name: "IX_Divisions_Slug", + schema: "Club12", + table: "Divisions"); + + migrationBuilder.DropColumn( + name: "Slug", + schema: "Club12", + table: "Venues"); + + migrationBuilder.DropColumn( + name: "Slug", + schema: "Club12", + table: "Stages"); + + migrationBuilder.DropColumn( + name: "Slug", + schema: "Club12", + table: "Players"); + + migrationBuilder.DropColumn( + name: "Slug", + schema: "Club12", + table: "Divisions"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828010238_DropTournamentTeamCountLimits.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260828010238_DropTournamentTeamCountLimits.Designer.cs new file mode 100644 index 0000000..87a2570 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828010238_DropTournamentTeamCountLimits.Designer.cs @@ -0,0 +1,1158 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260828010238_DropTournamentTeamCountLimits")] + partial class DropTournamentTeamCountLimits + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828010238_DropTournamentTeamCountLimits.cs b/Club12-Backend/Infrastructure/Migrations/20260828010238_DropTournamentTeamCountLimits.cs new file mode 100644 index 0000000..205a707 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828010238_DropTournamentTeamCountLimits.cs @@ -0,0 +1,66 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class DropTournamentTeamCountLimits : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // HU-34: MinTeams / MaxTeams are no longer part of the tournament model. + migrationBuilder.DropColumn( + name: "MaxTeams", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropColumn( + name: "MinTeams", + schema: "Club12", + table: "Tournaments"); + + // HU-35: the new RegistrationClosed status is inserted BETWEEN + // OpenForRegistration (1) and Ongoing in the TournamentStatus enum, + // which is persisted as its integer ordinal. Inserting a value in + // the middle shifts every following member up by one, so existing + // rows must be remapped: Ongoing 2->3, Finished 3->4, Canceled 4->5. + // Updated in descending order so an already-shifted value is never + // re-matched by a later statement. Scheduled (0) and + // OpenForRegistration (1) are unchanged; no existing row can hold + // the newly freed value 2 (RegistrationClosed). + migrationBuilder.Sql(@"UPDATE ""Club12"".""Tournaments"" SET ""Status"" = 5 WHERE ""Status"" = 4;"); + migrationBuilder.Sql(@"UPDATE ""Club12"".""Tournaments"" SET ""Status"" = 4 WHERE ""Status"" = 3;"); + migrationBuilder.Sql(@"UPDATE ""Club12"".""Tournaments"" SET ""Status"" = 3 WHERE ""Status"" = 2;"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + // Reverse the status remap (ascending, mirror of Up): Canceled + // 5->4, Finished 4->3, Ongoing 3->2. Any row left on 2 after this + // (a RegistrationClosed tournament) collapses back onto Ongoing (2), + // the closest pre-RegistrationClosed state. + migrationBuilder.Sql(@"UPDATE ""Club12"".""Tournaments"" SET ""Status"" = 2 WHERE ""Status"" = 3;"); + migrationBuilder.Sql(@"UPDATE ""Club12"".""Tournaments"" SET ""Status"" = 3 WHERE ""Status"" = 4;"); + migrationBuilder.Sql(@"UPDATE ""Club12"".""Tournaments"" SET ""Status"" = 4 WHERE ""Status"" = 5;"); + + migrationBuilder.AddColumn( + name: "MaxTeams", + schema: "Club12", + table: "Tournaments", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "MinTeams", + schema: "Club12", + table: "Tournaments", + type: "integer", + nullable: false, + defaultValue: 0); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828011954_AddMatchStatus.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260828011954_AddMatchStatus.Designer.cs new file mode 100644 index 0000000..89afac3 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828011954_AddMatchStatus.Designer.cs @@ -0,0 +1,1162 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260828011954_AddMatchStatus")] + partial class AddMatchStatus + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828011954_AddMatchStatus.cs b/Club12-Backend/Infrastructure/Migrations/20260828011954_AddMatchStatus.cs new file mode 100644 index 0000000..6fcb5a2 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828011954_AddMatchStatus.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddMatchStatus : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // HU-69: new result-lifecycle column. New rows default to + // "Scheduled"; the column is persisted as the enum name (string). + migrationBuilder.AddColumn( + name: "Status", + schema: "Club12", + table: "Matches", + type: "text", + nullable: false, + defaultValue: "Scheduled"); + + // Backfill existing rows: any already-finished match is a normally + // played result (walkovers did not exist before this column). + migrationBuilder.Sql( + @"UPDATE ""Club12"".""Matches"" SET ""Status"" = 'Played' WHERE ""IsFinished"" = true;"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Status", + schema: "Club12", + table: "Matches"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828013414_AddDivisionPointsAndPlayoffMapping.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260828013414_AddDivisionPointsAndPlayoffMapping.Designer.cs new file mode 100644 index 0000000..f0779ca --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828013414_AddDivisionPointsAndPlayoffMapping.Designer.cs @@ -0,0 +1,1228 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260828013414_AddDivisionPointsAndPlayoffMapping")] + partial class AddDivisionPointsAndPlayoffMapping + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828013414_AddDivisionPointsAndPlayoffMapping.cs b/Club12-Backend/Infrastructure/Migrations/20260828013414_AddDivisionPointsAndPlayoffMapping.cs new file mode 100644 index 0000000..8f37b96 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828013414_AddDivisionPointsAndPlayoffMapping.cs @@ -0,0 +1,88 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddDivisionPointsAndPlayoffMapping : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PointsForLoss", + schema: "Club12", + table: "Divisions", + type: "integer", + nullable: false, + defaultValue: 1); + + migrationBuilder.AddColumn( + name: "PointsForWin", + schema: "Club12", + table: "Divisions", + type: "integer", + nullable: false, + defaultValue: 2); + + migrationBuilder.CreateTable( + name: "DivisionPlayoffMappings", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + DivisionId = table.Column(type: "uuid", nullable: false), + FromPosition = table.Column(type: "integer", nullable: false), + ToPosition = table.Column(type: "integer", nullable: false), + Destination = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + CreatedBy = table.Column(type: "text", nullable: false), + UpdatedBy = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_DivisionPlayoffMappings", x => x.Id); + table.ForeignKey( + name: "FK_DivisionPlayoffMappings_Divisions_DivisionId", + column: x => x.DivisionId, + principalSchema: "Club12", + principalTable: "Divisions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_DivisionPlayoffMapping_CreatedAt", + schema: "Club12", + table: "DivisionPlayoffMappings", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_DivisionPlayoffMappings_DivisionId", + schema: "Club12", + table: "DivisionPlayoffMappings", + column: "DivisionId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "DivisionPlayoffMappings", + schema: "Club12"); + + migrationBuilder.DropColumn( + name: "PointsForLoss", + schema: "Club12", + table: "Divisions"); + + migrationBuilder.DropColumn( + name: "PointsForWin", + schema: "Club12", + table: "Divisions"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828020000_AddMedicalRecordToPlayerTeamRegistration.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260828020000_AddMedicalRecordToPlayerTeamRegistration.Designer.cs new file mode 100644 index 0000000..12246cd --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828020000_AddMedicalRecordToPlayerTeamRegistration.Designer.cs @@ -0,0 +1,1244 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260828020000_AddMedicalRecordToPlayerTeamRegistration")] + partial class AddMedicalRecordToPlayerTeamRegistration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828020000_AddMedicalRecordToPlayerTeamRegistration.cs b/Club12-Backend/Infrastructure/Migrations/20260828020000_AddMedicalRecordToPlayerTeamRegistration.cs new file mode 100644 index 0000000..7ef7d71 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828020000_AddMedicalRecordToPlayerTeamRegistration.cs @@ -0,0 +1,88 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +using System; + +#nullable disable + +namespace Infrastructure.Migrations; + +/// +/// Adds the medical-record / eligibility fields to PlayerTeamRegistration +/// (HU-55/57/58/59). Because the record lives on the season registration, it is +/// inherently per season: every existing row starts (and every new row +/// defaults to) 'Pending', so no prior approval is ever inherited (HU-59). +/// +public partial class AddMedicalRecordToPlayerTeamRegistration : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // Status persisted as the enum name (string), mirroring the other enum + // columns in this DB. Existing rows default to 'Pending' — no season + // ever inherits another season's approval (HU-59). + migrationBuilder.AddColumn( + name: "MedicalRecordStatus", + schema: "Club12", + table: "PlayerTeamRegistrations", + type: "text", + nullable: false, + defaultValue: "Pending"); + + migrationBuilder.AddColumn( + name: "MedicalRecordFileUrl", + schema: "Club12", + table: "PlayerTeamRegistrations", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "MedicalRecordFileName", + schema: "Club12", + table: "PlayerTeamRegistrations", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "MedicalRecordReviewReason", + schema: "Club12", + table: "PlayerTeamRegistrations", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "MedicalRecordReviewedAt", + schema: "Club12", + table: "PlayerTeamRegistrations", + type: "timestamp without time zone", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "MedicalRecordStatus", + schema: "Club12", + table: "PlayerTeamRegistrations"); + + migrationBuilder.DropColumn( + name: "MedicalRecordFileUrl", + schema: "Club12", + table: "PlayerTeamRegistrations"); + + migrationBuilder.DropColumn( + name: "MedicalRecordFileName", + schema: "Club12", + table: "PlayerTeamRegistrations"); + + migrationBuilder.DropColumn( + name: "MedicalRecordReviewReason", + schema: "Club12", + table: "PlayerTeamRegistrations"); + + migrationBuilder.DropColumn( + name: "MedicalRecordReviewedAt", + schema: "Club12", + table: "PlayerTeamRegistrations"); + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828030000_AddRoundToMatch.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260828030000_AddRoundToMatch.Designer.cs new file mode 100644 index 0000000..dceb808 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828030000_AddRoundToMatch.Designer.cs @@ -0,0 +1,1247 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260828030000_AddRoundToMatch")] + partial class AddRoundToMatch + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828030000_AddRoundToMatch.cs b/Club12-Backend/Infrastructure/Migrations/20260828030000_AddRoundToMatch.cs new file mode 100644 index 0000000..3c9b130 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828030000_AddRoundToMatch.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddRoundToMatch : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // HU-63/HU-65: the matchday (jornada) a match belongs to, 1-based. + // Nullable: existing rows and non-round-robin matches (e.g. knockout + // stages) have no round. New round-robin fixtures set it explicitly. + migrationBuilder.AddColumn( + name: "Round", + schema: "Club12", + table: "Matches", + type: "integer", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Round", + schema: "Club12", + table: "Matches"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828031000_AddTournamentAndDivisionCategory.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260828031000_AddTournamentAndDivisionCategory.Designer.cs new file mode 100644 index 0000000..e29ae41 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828031000_AddTournamentAndDivisionCategory.Designer.cs @@ -0,0 +1,1257 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260828031000_AddTournamentAndDivisionCategory")] + partial class AddTournamentAndDivisionCategory + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828031000_AddTournamentAndDivisionCategory.cs b/Club12-Backend/Infrastructure/Migrations/20260828031000_AddTournamentAndDivisionCategory.cs new file mode 100644 index 0000000..dd210f0 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828031000_AddTournamentAndDivisionCategory.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddTournamentAndDivisionCategory : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Category", + schema: "Club12", + table: "Tournaments", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Category", + schema: "Club12", + table: "Divisions", + type: "integer", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Category", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropColumn( + name: "Category", + schema: "Club12", + table: "Divisions"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828040000_AddSubjectToPlayerSanction.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260828040000_AddSubjectToPlayerSanction.Designer.cs new file mode 100644 index 0000000..1bba685 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828040000_AddSubjectToPlayerSanction.Designer.cs @@ -0,0 +1,1276 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260828040000_AddSubjectToPlayerSanction")] + partial class AddSubjectToPlayerSanction + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TeamId"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828040000_AddSubjectToPlayerSanction.cs b/Club12-Backend/Infrastructure/Migrations/20260828040000_AddSubjectToPlayerSanction.cs new file mode 100644 index 0000000..817f9b2 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828040000_AddSubjectToPlayerSanction.cs @@ -0,0 +1,111 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddSubjectToPlayerSanction : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // HU-77: a sanction subject can now be a player, a team or a staff + // member. Existing rows are all player sanctions, so SubjectType + // defaults to 'Player' and PlayerId keeps its value. + + // Drop the NOT NULL constraint on PlayerId: team/staff sanctions + // have no player. + migrationBuilder.AlterColumn( + name: "PlayerId", + schema: "Club12", + table: "PlayerSanctions", + type: "uuid", + nullable: true, + oldClrType: typeof(System.Guid), + oldType: "uuid"); + + migrationBuilder.AddColumn( + name: "SubjectType", + schema: "Club12", + table: "PlayerSanctions", + type: "text", + nullable: false, + defaultValue: "Player"); + + migrationBuilder.AddColumn( + name: "TeamId", + schema: "Club12", + table: "PlayerSanctions", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "StaffName", + schema: "Club12", + table: "PlayerSanctions", + type: "character varying(255)", + maxLength: 255, + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_PlayerSanctions_TeamId", + schema: "Club12", + table: "PlayerSanctions", + column: "TeamId"); + + migrationBuilder.AddForeignKey( + name: "FK_PlayerSanctions_Teams_TeamId", + schema: "Club12", + table: "PlayerSanctions", + column: "TeamId", + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_PlayerSanctions_Teams_TeamId", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropIndex( + name: "IX_PlayerSanctions_TeamId", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropColumn( + name: "SubjectType", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropColumn( + name: "TeamId", + schema: "Club12", + table: "PlayerSanctions"); + + migrationBuilder.DropColumn( + name: "StaffName", + schema: "Club12", + table: "PlayerSanctions"); + + // Restore NOT NULL on PlayerId. Any team/staff sanctions must be + // removed before rolling back, otherwise this will fail on their + // null PlayerId (intentional: they cannot be represented downlevel). + migrationBuilder.AlterColumn( + name: "PlayerId", + schema: "Club12", + table: "PlayerSanctions", + type: "uuid", + nullable: false, + defaultValue: System.Guid.Empty, + oldClrType: typeof(System.Guid), + oldType: "uuid", + oldNullable: true); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828053250_AddAuditLogBlogPublishAndJerseyNumber.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260828053250_AddAuditLogBlogPublishAndJerseyNumber.Designer.cs new file mode 100644 index 0000000..8f5bdd0 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828053250_AddAuditLogBlogPublishAndJerseyNumber.Designer.cs @@ -0,0 +1,1335 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260828053250_AddAuditLogBlogPublishAndJerseyNumber")] + partial class AddAuditLogBlogPublishAndJerseyNumber + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828053250_AddAuditLogBlogPublishAndJerseyNumber.cs b/Club12-Backend/Infrastructure/Migrations/20260828053250_AddAuditLogBlogPublishAndJerseyNumber.cs new file mode 100644 index 0000000..60b84d5 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828053250_AddAuditLogBlogPublishAndJerseyNumber.cs @@ -0,0 +1,122 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + /// Additive schema for three stories: + /// - HU-101: the AuditLogs table (sensitive-action trail). + /// - HU-16: BlogPosts.IsPublished (draft/published; defaults true so + /// existing posts stay publicly visible). + /// - HU-54: PlayerTeamRegistrations.JerseyNumber (dorsal) plus the unique + /// (TeamId, TournamentId, JerseyNumber) index enforcing unique dorsals + /// within a team+season. The pre-existing standalone TeamId index is + /// dropped because the new composite index covers it as a prefix. + /// + /// The HU-77 PlayerSanctions changes (SubjectType/StaffName/TeamId, nullable + /// PlayerId, the Team FK and its index) are intentionally NOT repeated here: + /// they were already applied to the database by the hand-authored + /// 20260828040000_AddSubjectToPlayerSanction migration. This migration only + /// carries the delta the model snapshot had not yet captured for the three + /// stories above, so the migration history and the model snapshot converge + /// without re-adding columns that already exist. + /// + /// + public partial class AddAuditLogBlogPublishAndJerseyNumber : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_PlayerTeamRegistrations_TeamId", + schema: "Club12", + table: "PlayerTeamRegistrations"); + + migrationBuilder.AddColumn( + name: "JerseyNumber", + schema: "Club12", + table: "PlayerTeamRegistrations", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "IsPublished", + schema: "Club12", + table: "BlogPosts", + type: "boolean", + nullable: false, + defaultValue: true); + + migrationBuilder.CreateTable( + name: "AuditLogs", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Action = table.Column(type: "text", nullable: false), + Actor = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + TargetType = table.Column(type: "character varying(128)", maxLength: 128, nullable: true), + TargetId = table.Column(type: "character varying(128)", maxLength: 128, nullable: true), + Detail = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: true), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + CreatedBy = table.Column(type: "text", nullable: false), + UpdatedBy = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AuditLogs", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_PlayerTeamRegistrations_TeamId_TournamentId_JerseyNumber", + schema: "Club12", + table: "PlayerTeamRegistrations", + columns: new[] { "TeamId", "TournamentId", "JerseyNumber" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AuditLog_CreatedAt", + schema: "Club12", + table: "AuditLogs", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_AuditLogs_Action", + schema: "Club12", + table: "AuditLogs", + column: "Action"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AuditLogs", + schema: "Club12"); + + migrationBuilder.DropIndex( + name: "IX_PlayerTeamRegistrations_TeamId_TournamentId_JerseyNumber", + schema: "Club12", + table: "PlayerTeamRegistrations"); + + migrationBuilder.DropColumn( + name: "JerseyNumber", + schema: "Club12", + table: "PlayerTeamRegistrations"); + + migrationBuilder.DropColumn( + name: "IsPublished", + schema: "Club12", + table: "BlogPosts"); + + migrationBuilder.CreateIndex( + name: "IX_PlayerTeamRegistrations_TeamId", + schema: "Club12", + table: "PlayerTeamRegistrations", + column: "TeamId"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828054810_AddClubEntity.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260828054810_AddClubEntity.Designer.cs new file mode 100644 index 0000000..7c9e68a --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828054810_AddClubEntity.Designer.cs @@ -0,0 +1,1394 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260828054810_AddClubEntity")] + partial class AddClubEntity + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828054810_AddClubEntity.cs b/Club12-Backend/Infrastructure/Migrations/20260828054810_AddClubEntity.cs new file mode 100644 index 0000000..b4aec24 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828054810_AddClubEntity.cs @@ -0,0 +1,93 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddClubEntity : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ClubId", + schema: "Club12", + table: "Teams", + type: "uuid", + nullable: true); + + migrationBuilder.CreateTable( + name: "Clubs", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + Slug = table.Column(type: "character varying(220)", maxLength: 220, nullable: false), + LogoUrl = table.Column(type: "text", nullable: true), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + CreatedBy = table.Column(type: "text", nullable: false), + UpdatedBy = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Clubs", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Teams_ClubId", + schema: "Club12", + table: "Teams", + column: "ClubId"); + + migrationBuilder.CreateIndex( + name: "IX_Club_CreatedAt", + schema: "Club12", + table: "Clubs", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_Clubs_Slug", + schema: "Club12", + table: "Clubs", + column: "Slug", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_Teams_Clubs_ClubId", + schema: "Club12", + table: "Teams", + column: "ClubId", + principalSchema: "Club12", + principalTable: "Clubs", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Teams_Clubs_ClubId", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropTable( + name: "Clubs", + schema: "Club12"); + + migrationBuilder.DropIndex( + name: "IX_Teams_ClubId", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropColumn( + name: "ClubId", + schema: "Club12", + table: "Teams"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828163513_AddQualifiersPerGroupToDivision.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260828163513_AddQualifiersPerGroupToDivision.Designer.cs new file mode 100644 index 0000000..ee96636 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828163513_AddQualifiersPerGroupToDivision.Designer.cs @@ -0,0 +1,1438 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260828163513_AddQualifiersPerGroupToDivision")] + partial class AddQualifiersPerGroupToDivision + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260828163513_AddQualifiersPerGroupToDivision.cs b/Club12-Backend/Infrastructure/Migrations/20260828163513_AddQualifiersPerGroupToDivision.cs new file mode 100644 index 0000000..24df4ef --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260828163513_AddQualifiersPerGroupToDivision.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddQualifiersPerGroupToDivision : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "QualifiersPerGroup", + schema: "Club12", + table: "Divisions", + type: "integer", + nullable: false, + defaultValue: 1); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "QualifiersPerGroup", + schema: "Club12", + table: "Divisions"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260829020604_AddTeamJerseyKit.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260829020604_AddTeamJerseyKit.Designer.cs new file mode 100644 index 0000000..9a6f447 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260829020604_AddTeamJerseyKit.Designer.cs @@ -0,0 +1,1449 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260829020604_AddTeamJerseyKit")] + partial class AddTeamJerseyKit + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260829020604_AddTeamJerseyKit.cs b/Club12-Backend/Infrastructure/Migrations/20260829020604_AddTeamJerseyKit.cs new file mode 100644 index 0000000..a3ff1a1 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260829020604_AddTeamJerseyKit.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddTeamJerseyKit : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "JerseyStyle", + schema: "Club12", + table: "Teams", + type: "character varying(20)", + maxLength: 20, + nullable: false, + defaultValue: "solid"); + + migrationBuilder.AddColumn( + name: "ShirtSecondaryColor", + schema: "Club12", + table: "Teams", + type: "character varying(9)", + maxLength: 9, + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "JerseyStyle", + schema: "Club12", + table: "Teams"); + + migrationBuilder.DropColumn( + name: "ShirtSecondaryColor", + schema: "Club12", + table: "Teams"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260829033721_RebackfillDivisionStageSlugs.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260829033721_RebackfillDivisionStageSlugs.Designer.cs new file mode 100644 index 0000000..9cd5413 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260829033721_RebackfillDivisionStageSlugs.Designer.cs @@ -0,0 +1,1449 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260829033721_RebackfillDivisionStageSlugs")] + partial class RebackfillDivisionStageSlugs + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260829033721_RebackfillDivisionStageSlugs.cs b/Club12-Backend/Infrastructure/Migrations/20260829033721_RebackfillDivisionStageSlugs.cs new file mode 100644 index 0000000..d2793a6 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260829033721_RebackfillDivisionStageSlugs.cs @@ -0,0 +1,81 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class RebackfillDivisionStageSlugs : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // Re-backfills every Division/Stage Slug from its current Name, + // overwriting the GUID-polluted slugs that the sample seeder used to + // produce (e.g. "primera-division-34ce6485-...") with a clean + // kebab-case slug (e.g. "primera-division"). Mirrors + // SlugGenerator.GenerateSlug exactly: lowercase, Spanish accents + // transliterated to their base letter, non-alphanumeric runs + // collapsed to a single hyphen, leading/trailing hyphens trimmed. + // Rows whose computed base slug collides get a numeric suffix + // (-2, -3, ...) ordered by Id — the same disambiguation strategy the + // application and the original 20260828003816 backfill use, so the + // Slug unique index is preserved. + migrationBuilder.Sql( + @" + WITH base AS ( + SELECT ""Id"", + trim(both '-' from + regexp_replace( + translate(lower(""Name""), 'áéíóúüñ', 'aeiouun'), + '[^a-z0-9]+', '-', 'g' + ) + ) AS slug_base + FROM ""Club12"".""Divisions"" + ), + numbered AS ( + SELECT ""Id"", slug_base, + ROW_NUMBER() OVER (PARTITION BY slug_base ORDER BY ""Id"") AS rn + FROM base + ) + UPDATE ""Club12"".""Divisions"" t + SET ""Slug"" = CASE WHEN n.rn = 1 THEN n.slug_base ELSE n.slug_base || '-' || n.rn::text END + FROM numbered n + WHERE t.""Id"" = n.""Id""; + " + ); + + migrationBuilder.Sql( + @" + WITH base AS ( + SELECT ""Id"", + trim(both '-' from + regexp_replace( + translate(lower(""Name""), 'áéíóúüñ', 'aeiouun'), + '[^a-z0-9]+', '-', 'g' + ) + ) AS slug_base + FROM ""Club12"".""Stages"" + ), + numbered AS ( + SELECT ""Id"", slug_base, + ROW_NUMBER() OVER (PARTITION BY slug_base ORDER BY ""Id"") AS rn + FROM base + ) + UPDATE ""Club12"".""Stages"" t + SET ""Slug"" = CASE WHEN n.rn = 1 THEN n.slug_base ELSE n.slug_base || '-' || n.rn::text END + FROM numbered n + WHERE t.""Id"" = n.""Id""; + " + ); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + // Data-only re-backfill: there is no meaningful inverse (the original + // GUID-suffixed slugs are not recoverable). Down is intentionally a + // no-op so the migration can still be rolled back structurally. + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260829044303_AddSeason.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260829044303_AddSeason.Designer.cs new file mode 100644 index 0000000..06f109b --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260829044303_AddSeason.Designer.cs @@ -0,0 +1,1512 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260829044303_AddSeason")] + partial class AddSeason + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260829044303_AddSeason.cs b/Club12-Backend/Infrastructure/Migrations/20260829044303_AddSeason.cs new file mode 100644 index 0000000..e0a5647 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260829044303_AddSeason.cs @@ -0,0 +1,93 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddSeason : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SeasonId", + schema: "Club12", + table: "Tournaments", + type: "uuid", + nullable: true); + + migrationBuilder.CreateTable( + name: "Seasons", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Slug = table.Column(type: "character varying(220)", maxLength: 220, nullable: false), + Year = table.Column(type: "integer", nullable: true), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + CreatedBy = table.Column(type: "text", nullable: false), + UpdatedBy = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Seasons", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Tournaments_SeasonId", + schema: "Club12", + table: "Tournaments", + column: "SeasonId"); + + migrationBuilder.CreateIndex( + name: "IX_Season_CreatedAt", + schema: "Club12", + table: "Seasons", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_Seasons_Slug", + schema: "Club12", + table: "Seasons", + column: "Slug", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_Tournaments_Seasons_SeasonId", + schema: "Club12", + table: "Tournaments", + column: "SeasonId", + principalSchema: "Club12", + principalTable: "Seasons", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Tournaments_Seasons_SeasonId", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropTable( + name: "Seasons", + schema: "Club12"); + + migrationBuilder.DropIndex( + name: "IX_Tournaments_SeasonId", + schema: "Club12", + table: "Tournaments"); + + migrationBuilder.DropColumn( + name: "SeasonId", + schema: "Club12", + table: "Tournaments"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260829101157_AddTeamPointDeduction.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260829101157_AddTeamPointDeduction.Designer.cs new file mode 100644 index 0000000..8124c8a --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260829101157_AddTeamPointDeduction.Designer.cs @@ -0,0 +1,1576 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260829101157_AddTeamPointDeduction")] + partial class AddTeamPointDeduction + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260829101157_AddTeamPointDeduction.cs b/Club12-Backend/Infrastructure/Migrations/20260829101157_AddTeamPointDeduction.cs new file mode 100644 index 0000000..5959691 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260829101157_AddTeamPointDeduction.cs @@ -0,0 +1,75 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddTeamPointDeduction : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "TeamPointDeductions", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + DivisionId = table.Column(type: "uuid", nullable: false), + TeamId = table.Column(type: "uuid", nullable: false), + Points = table.Column(type: "integer", nullable: false), + Reason = table.Column(type: "character varying(300)", maxLength: 300, nullable: false), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + CreatedBy = table.Column(type: "text", nullable: false), + UpdatedBy = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TeamPointDeductions", x => x.Id); + table.ForeignKey( + name: "FK_TeamPointDeductions_Divisions_DivisionId", + column: x => x.DivisionId, + principalSchema: "Club12", + principalTable: "Divisions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_TeamPointDeductions_Teams_TeamId", + column: x => x.TeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_TeamPointDeduction_CreatedAt", + schema: "Club12", + table: "TeamPointDeductions", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_TeamPointDeductions_DivisionId", + schema: "Club12", + table: "TeamPointDeductions", + column: "DivisionId"); + + migrationBuilder.CreateIndex( + name: "IX_TeamPointDeductions_TeamId", + schema: "Club12", + table: "TeamPointDeductions", + column: "TeamId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TeamPointDeductions", + schema: "Club12"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260829164705_RebackfillPlayerSlugsWithoutDocumentNumber.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260829164705_RebackfillPlayerSlugsWithoutDocumentNumber.Designer.cs new file mode 100644 index 0000000..0010834 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260829164705_RebackfillPlayerSlugsWithoutDocumentNumber.Designer.cs @@ -0,0 +1,1576 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260829164705_RebackfillPlayerSlugsWithoutDocumentNumber")] + partial class RebackfillPlayerSlugsWithoutDocumentNumber + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260829164705_RebackfillPlayerSlugsWithoutDocumentNumber.cs b/Club12-Backend/Infrastructure/Migrations/20260829164705_RebackfillPlayerSlugsWithoutDocumentNumber.cs new file mode 100644 index 0000000..1abb5c0 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260829164705_RebackfillPlayerSlugsWithoutDocumentNumber.cs @@ -0,0 +1,118 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class RebackfillPlayerSlugsWithoutDocumentNumber : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // Re-backfills every Player.Slug to the single canonical format + // "apellido-nombre[-segundo]" (Player.BuildSlugSource fed through + // SlugGenerator.GenerateSlug) — dropping the DocumentNumber segment + // that the old sample seeder used to append. Mirrors the shipped + // 20260828003816 player backfill verbatim (same concat, same + // translate/regexp_replace, same ROW_NUMBER() ... ORDER BY "Id" + // suffix rule) so create, seed and backfill share one rule. + // + // Reversible via a snapshot ledger: pre-migration slugs are an + // unrecoverable mixture of "apellido-nombre-dni" (seed) and + // "apellido-nombre[-N]" (create/backfill), so the only correct + // inverse is to store and restore them. The ledger table lives + // outside the EF model, so the model differ ignores it. + + // 1. Rollback ledger — drop+recreate (idempotent under a Down->Up + // cycle) and snapshot the current slugs. + migrationBuilder.Sql( + @" + DROP TABLE IF EXISTS ""Club12"".""PlayerSlugBackup_20260829""; + + CREATE TABLE ""Club12"".""PlayerSlugBackup_20260829"" ( + ""Id"" uuid PRIMARY KEY, + ""OldSlug"" character varying(220) NOT NULL + ); + + INSERT INTO ""Club12"".""PlayerSlugBackup_20260829"" (""Id"", ""OldSlug"") + SELECT ""Id"", ""Slug"" FROM ""Club12"".""Players""; + " + ); + + // 2. Park every slug on a value SlugGenerator's [^a-z0-9]+ rule can + // never emit ('_' is disjoint from every real and every final + // slug), so the two-phase reassignment below never trips the + // plain (non-deferrable) IX_Players_Slug unique index mid-update. + migrationBuilder.Sql( + @" + UPDATE ""Club12"".""Players"" SET ""Slug"" = '__tmp_' || ""Id""::text; + " + ); + + // 3. Assign canonical slugs. Targets are distinct by construction + // (ROW_NUMBER suffix) and disjoint from '__tmp_%'. + migrationBuilder.Sql( + @" + WITH base AS ( + SELECT p.""Id"", + trim(both '-' from + regexp_replace( + translate( + lower( + concat( + p.""LastName"", ' ', p.""FirstName"", + CASE WHEN p.""SecondName"" IS NULL OR trim(p.""SecondName"") = '' THEN '' ELSE ' ' || p.""SecondName"" END + ) + ), + 'áéíóúüñ', 'aeiouun' + ), + '[^a-z0-9]+', '-', 'g' + ) + ) AS slug_base + FROM ""Club12"".""Players"" p + ), + numbered AS ( + SELECT ""Id"", slug_base, + ROW_NUMBER() OVER (PARTITION BY slug_base ORDER BY ""Id"") AS rn + FROM base + ) + UPDATE ""Club12"".""Players"" t + SET ""Slug"" = CASE WHEN n.rn = 1 THEN n.slug_base ELSE n.slug_base || '-' || n.rn::text END + FROM numbered n + WHERE t.""Id"" = n.""Id""; + " + ); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + // True inverse, guarded so it never half-restores. Re-park first for + // the same index-permutation reason as Up. Players inserted AFTER Up + // have no ledger row and keep their canonical slug — correct, since + // no prior value exists to restore. Double-Down is a safe no-op. + migrationBuilder.Sql( + @" + UPDATE ""Club12"".""Players"" SET ""Slug"" = '__tmp_' || ""Id""::text + WHERE to_regclass('""Club12"".""PlayerSlugBackup_20260829""') IS NOT NULL; + " + ); + + migrationBuilder.Sql( + @" + UPDATE ""Club12"".""Players"" t + SET ""Slug"" = b.""OldSlug"" + FROM ""Club12"".""PlayerSlugBackup_20260829"" b + WHERE t.""Id"" = b.""Id""; + " + ); + + migrationBuilder.Sql( + @" + DROP TABLE IF EXISTS ""Club12"".""PlayerSlugBackup_20260829""; + " + ); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260829225314_AddVenueImageAndLocation.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260829225314_AddVenueImageAndLocation.Designer.cs new file mode 100644 index 0000000..5f2c157 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260829225314_AddVenueImageAndLocation.Designer.cs @@ -0,0 +1,1582 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260829225314_AddVenueImageAndLocation")] + partial class AddVenueImageAndLocation + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260829225314_AddVenueImageAndLocation.cs b/Club12-Backend/Infrastructure/Migrations/20260829225314_AddVenueImageAndLocation.cs new file mode 100644 index 0000000..e9bd22c --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260829225314_AddVenueImageAndLocation.cs @@ -0,0 +1,42 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddVenueImageAndLocation : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Latitude", + schema: "Club12", + table: "Venues", + type: "double precision", + nullable: true); + + migrationBuilder.AddColumn( + name: "Longitude", + schema: "Club12", + table: "Venues", + type: "double precision", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Latitude", + schema: "Club12", + table: "Venues"); + + migrationBuilder.DropColumn( + name: "Longitude", + schema: "Club12", + table: "Venues"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260901070931_HardenMatchSeriesTeamOnDelete.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260901070931_HardenMatchSeriesTeamOnDelete.Designer.cs new file mode 100644 index 0000000..56f30e7 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260901070931_HardenMatchSeriesTeamOnDelete.Designer.cs @@ -0,0 +1,1583 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260901070931_HardenMatchSeriesTeamOnDelete")] + partial class HardenMatchSeriesTeamOnDelete + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260901070931_HardenMatchSeriesTeamOnDelete.cs b/Club12-Backend/Infrastructure/Migrations/20260901070931_HardenMatchSeriesTeamOnDelete.cs new file mode 100644 index 0000000..22c3f09 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260901070931_HardenMatchSeriesTeamOnDelete.cs @@ -0,0 +1,107 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class HardenMatchSeriesTeamOnDelete : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_MatchSeries_Teams_HomeTeamId", + schema: "Club12", + table: "MatchSeries"); + + migrationBuilder.DropForeignKey( + name: "FK_MatchSeries_Teams_VisitorTeamId", + schema: "Club12", + table: "MatchSeries"); + + migrationBuilder.DropForeignKey( + name: "FK_MatchSeries_Teams_WinningTeamId", + schema: "Club12", + table: "MatchSeries"); + + migrationBuilder.AddForeignKey( + name: "FK_MatchSeries_Teams_HomeTeamId", + schema: "Club12", + table: "MatchSeries", + column: "HomeTeamId", + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_MatchSeries_Teams_VisitorTeamId", + schema: "Club12", + table: "MatchSeries", + column: "VisitorTeamId", + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_MatchSeries_Teams_WinningTeamId", + schema: "Club12", + table: "MatchSeries", + column: "WinningTeamId", + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_MatchSeries_Teams_HomeTeamId", + schema: "Club12", + table: "MatchSeries"); + + migrationBuilder.DropForeignKey( + name: "FK_MatchSeries_Teams_VisitorTeamId", + schema: "Club12", + table: "MatchSeries"); + + migrationBuilder.DropForeignKey( + name: "FK_MatchSeries_Teams_WinningTeamId", + schema: "Club12", + table: "MatchSeries"); + + migrationBuilder.AddForeignKey( + name: "FK_MatchSeries_Teams_HomeTeamId", + schema: "Club12", + table: "MatchSeries", + column: "HomeTeamId", + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_MatchSeries_Teams_VisitorTeamId", + schema: "Club12", + table: "MatchSeries", + column: "VisitorTeamId", + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_MatchSeries_Teams_WinningTeamId", + schema: "Club12", + table: "MatchSeries", + column: "WinningTeamId", + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260901193848_AddTeamStaffTable.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260901193848_AddTeamStaffTable.Designer.cs new file mode 100644 index 0000000..f0c5293 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260901193848_AddTeamStaffTable.Designer.cs @@ -0,0 +1,1647 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260901193848_AddTeamStaffTable")] + partial class AddTeamStaffTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamStaff_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId"); + + b.ToTable("TeamStaffs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260901193848_AddTeamStaffTable.cs b/Club12-Backend/Infrastructure/Migrations/20260901193848_AddTeamStaffTable.cs new file mode 100644 index 0000000..94c5a02 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260901193848_AddTeamStaffTable.cs @@ -0,0 +1,75 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddTeamStaffTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "TeamStaffs", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + TeamId = table.Column(type: "uuid", nullable: false), + TournamentId = table.Column(type: "uuid", nullable: false), + FullName = table.Column(type: "character varying(150)", maxLength: 150, nullable: false), + Role = table.Column(type: "integer", nullable: false), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + CreatedBy = table.Column(type: "text", nullable: false), + UpdatedBy = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TeamStaffs", x => x.Id); + table.ForeignKey( + name: "FK_TeamStaffs_Teams_TeamId", + column: x => x.TeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_TeamStaffs_Tournaments_TournamentId", + column: x => x.TournamentId, + principalSchema: "Club12", + principalTable: "Tournaments", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_TeamStaff_CreatedAt", + schema: "Club12", + table: "TeamStaffs", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_TeamStaffs_TeamId_TournamentId", + schema: "Club12", + table: "TeamStaffs", + columns: new[] { "TeamId", "TournamentId" }); + + migrationBuilder.CreateIndex( + name: "IX_TeamStaffs_TournamentId", + schema: "Club12", + table: "TeamStaffs", + column: "TournamentId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TeamStaffs", + schema: "Club12"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260902050833_CleanupOrphanedTournaments.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260902050833_CleanupOrphanedTournaments.Designer.cs new file mode 100644 index 0000000..2dba7f4 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260902050833_CleanupOrphanedTournaments.Designer.cs @@ -0,0 +1,1647 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260902050833_CleanupOrphanedTournaments")] + partial class CleanupOrphanedTournaments + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamStaff_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId"); + + b.ToTable("TeamStaffs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260902050833_CleanupOrphanedTournaments.cs b/Club12-Backend/Infrastructure/Migrations/20260902050833_CleanupOrphanedTournaments.cs new file mode 100644 index 0000000..56736a9 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260902050833_CleanupOrphanedTournaments.cs @@ -0,0 +1,71 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class CleanupOrphanedTournaments : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // Data-only cleanup: before this fix (see SeasonService.DeleteSeasonAsync), + // deleting a Season only detached its tournaments (SetNull, not + // Cascade) instead of deleting them, leaving "orphaned" tournaments + // behind — SeasonId is null, but the tournament (and its + // TeamTournamentRegistrations, blocking those teams' own deletion) + // is still alive and invisible from any season-scoped screen. + // + // This mirrors TournamentService.DeleteTournamentAsync exactly: the + // same "safe to delete" guard (not started — Status not in + // Ongoing/Finished — and no played match), AND the same pre-step of + // clearing every enrolled team's denormalized Team.TournamentId + // "current-season" pointer first. That FK (Team -> Tournament) is + // NoAction by design, so a still-set pointer aborts the delete with + // an opaque FK error (FK_Teams_Tournaments_TournamentId) — which is + // exactly what the first cut of this migration hit. Team identities + // survive; their TeamTournamentRegistration rows, plus the + // tournament's Divisions/Stages/Matches, cascade away via their + // existing FK configuration. An orphaned tournament that already has + // real history is never touched and is left for an admin to handle. + migrationBuilder.Sql( + """ + DO $$ + BEGIN + IF EXISTS ( + SELECT 1 FROM information_schema.tables + WHERE table_schema = 'Club12' AND table_name = 'Tournaments' + ) THEN + CREATE TEMPORARY TABLE _orphaned_tournaments ON COMMIT DROP AS + SELECT t."Id" + FROM "Club12"."Tournaments" t + WHERE t."SeasonId" IS NULL + AND t."Status" NOT IN (3, 4) -- Ongoing, Finished + AND NOT EXISTS ( + SELECT 1 + FROM "Club12"."Matches" m + JOIN "Club12"."Stages" s ON s."Id" = m."StageId" + JOIN "Club12"."Divisions" d ON d."Id" = s."DivisionId" + WHERE d."TournamentId" = t."Id" AND m."IsFinished" = TRUE + ); + + UPDATE "Club12"."Teams" + SET "TournamentId" = NULL + WHERE "TournamentId" IN (SELECT "Id" FROM _orphaned_tournaments); + + DELETE FROM "Club12"."Tournaments" + WHERE "Id" IN (SELECT "Id" FROM _orphaned_tournaments); + END IF; + END $$; + """); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + // Irreversible: the whole point is deleting rows that never should + // have survived their season's deletion. Nothing to restore. + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260902054533_AddTargetNameToAuditLog.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260902054533_AddTargetNameToAuditLog.Designer.cs new file mode 100644 index 0000000..849c982 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260902054533_AddTargetNameToAuditLog.Designer.cs @@ -0,0 +1,1651 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260902054533_AddTargetNameToAuditLog")] + partial class AddTargetNameToAuditLog + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamStaff_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId"); + + b.ToTable("TeamStaffs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260902054533_AddTargetNameToAuditLog.cs b/Club12-Backend/Infrastructure/Migrations/20260902054533_AddTargetNameToAuditLog.cs new file mode 100644 index 0000000..fec0060 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260902054533_AddTargetNameToAuditLog.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddTargetNameToAuditLog : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "TargetName", + schema: "Club12", + table: "AuditLogs", + type: "character varying(256)", + maxLength: 256, + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "TargetName", + schema: "Club12", + table: "AuditLogs"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260902055555_AddWentToOvertimeToMatch.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260902055555_AddWentToOvertimeToMatch.Designer.cs new file mode 100644 index 0000000..fff3a9c --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260902055555_AddWentToOvertimeToMatch.Designer.cs @@ -0,0 +1,1654 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260902055555_AddWentToOvertimeToMatch")] + partial class AddWentToOvertimeToMatch + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WentToOvertime") + .HasColumnType("boolean"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamStaff_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId"); + + b.ToTable("TeamStaffs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260902055555_AddWentToOvertimeToMatch.cs b/Club12-Backend/Infrastructure/Migrations/20260902055555_AddWentToOvertimeToMatch.cs new file mode 100644 index 0000000..0509528 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260902055555_AddWentToOvertimeToMatch.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddWentToOvertimeToMatch : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "WentToOvertime", + schema: "Club12", + table: "Matches", + type: "boolean", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "WentToOvertime", + schema: "Club12", + table: "Matches"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260902173728_AddTeamShirtTertiaryColor.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260902173728_AddTeamShirtTertiaryColor.Designer.cs new file mode 100644 index 0000000..0de4442 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260902173728_AddTeamShirtTertiaryColor.Designer.cs @@ -0,0 +1,1658 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260902173728_AddTeamShirtTertiaryColor")] + partial class AddTeamShirtTertiaryColor + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WentToOvertime") + .HasColumnType("boolean"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("ShirtTertiaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamStaff_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId"); + + b.ToTable("TeamStaffs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260902173728_AddTeamShirtTertiaryColor.cs b/Club12-Backend/Infrastructure/Migrations/20260902173728_AddTeamShirtTertiaryColor.cs new file mode 100644 index 0000000..46338bd --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260902173728_AddTeamShirtTertiaryColor.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddTeamShirtTertiaryColor : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ShirtTertiaryColor", + schema: "Club12", + table: "Teams", + type: "character varying(9)", + maxLength: 9, + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ShirtTertiaryColor", + schema: "Club12", + table: "Teams"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260906005632_AddDivisionTeamRegistrationAndStageDrawnAt.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260906005632_AddDivisionTeamRegistrationAndStageDrawnAt.Designer.cs new file mode 100644 index 0000000..eb3f3a9 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260906005632_AddDivisionTeamRegistrationAndStageDrawnAt.Designer.cs @@ -0,0 +1,1722 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260906005632_AddDivisionTeamRegistrationAndStageDrawnAt")] + partial class AddDivisionTeamRegistrationAndStageDrawnAt + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionTeamRegistration_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId", "DivisionId") + .IsUnique(); + + b.ToTable("DivisionTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WentToOvertime") + .HasColumnType("boolean"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("DrawnAt") + .HasColumnType("timestamp without time zone"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("ShirtTertiaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamStaff_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId"); + + b.ToTable("TeamStaffs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("DivisionTeamRegistrations") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("DivisionTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("DivisionTeamRegistrations"); + + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("DivisionTeamRegistrations"); + + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260906005632_AddDivisionTeamRegistrationAndStageDrawnAt.cs b/Club12-Backend/Infrastructure/Migrations/20260906005632_AddDivisionTeamRegistrationAndStageDrawnAt.cs new file mode 100644 index 0000000..0e93e56 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260906005632_AddDivisionTeamRegistrationAndStageDrawnAt.cs @@ -0,0 +1,117 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddDivisionTeamRegistrationAndStageDrawnAt : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DrawnAt", + schema: "Club12", + table: "Stages", + type: "timestamp without time zone", + nullable: true); + + migrationBuilder.CreateTable( + name: "DivisionTeamRegistrations", + schema: "Club12", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + TeamId = table.Column(type: "uuid", nullable: false), + DivisionId = table.Column(type: "uuid", nullable: false), + DateCreated = table.Column(type: "timestamp without time zone", nullable: false), + DateUpdated = table.Column(type: "timestamp without time zone", nullable: true), + CreatedBy = table.Column(type: "text", nullable: false), + UpdatedBy = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_DivisionTeamRegistrations", x => x.Id); + table.ForeignKey( + name: "FK_DivisionTeamRegistrations_Divisions_DivisionId", + column: x => x.DivisionId, + principalSchema: "Club12", + principalTable: "Divisions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_DivisionTeamRegistrations_Teams_TeamId", + column: x => x.TeamId, + principalSchema: "Club12", + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_DivisionTeamRegistration_CreatedAt", + schema: "Club12", + table: "DivisionTeamRegistrations", + column: "DateCreated"); + + migrationBuilder.CreateIndex( + name: "IX_DivisionTeamRegistrations_DivisionId", + schema: "Club12", + table: "DivisionTeamRegistrations", + column: "DivisionId"); + + migrationBuilder.CreateIndex( + name: "IX_DivisionTeamRegistrations_TeamId_DivisionId", + schema: "Club12", + table: "DivisionTeamRegistrations", + columns: new[] { "TeamId", "DivisionId" }, + unique: true); + + // Backfills one DivisionTeamRegistration row per distinct (TeamId, + // DivisionId) pair implied by existing StageTeamMatch rows, + // resolved through Stage.DivisionId. GROUP BY the pair, never + // TeamId alone, so a team placed in two sub-groups of one + // division or in a group stage plus a same-division bracket + // collapses to a single row, while a team placed in its regular + // division and separately in a cross-division-cup division keeps + // two distinct rows. The NOT EXISTS guard makes this idempotent, + // safe to re-run against data it has already processed, which + // matters if this migration is ever re-applied after a rollback. + migrationBuilder.Sql( + @" + INSERT INTO ""Club12"".""DivisionTeamRegistrations"" + (""Id"", ""TeamId"", ""DivisionId"", ""DateCreated"", ""DateUpdated"", ""CreatedBy"", ""UpdatedBy"") + SELECT gen_random_uuid(), + stm.""TeamId"", + s.""DivisionId"", + now() AT TIME ZONE 'utc', + NULL, + 'System', + NULL + FROM ""Club12"".""StageTeamMatches"" stm + JOIN ""Club12"".""Stages"" s ON stm.""StageId"" = s.""Id"" + WHERE NOT EXISTS ( + SELECT 1 FROM ""Club12"".""DivisionTeamRegistrations"" dtr + WHERE dtr.""TeamId"" = stm.""TeamId"" AND dtr.""DivisionId"" = s.""DivisionId"" + ) + GROUP BY stm.""TeamId"", s.""DivisionId""; + " + ); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DrawnAt", + schema: "Club12", + table: "Stages"); + + migrationBuilder.DropTable( + name: "DivisionTeamRegistrations", + schema: "Club12"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260907015056_AddClubParentClubId.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260907015056_AddClubParentClubId.Designer.cs new file mode 100644 index 0000000..a147dbd --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260907015056_AddClubParentClubId.Designer.cs @@ -0,0 +1,1739 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260907015056_AddClubParentClubId")] + partial class AddClubParentClubId + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ParentClubId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("ParentClubId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionTeamRegistration_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId", "DivisionId") + .IsUnique(); + + b.ToTable("DivisionTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WentToOvertime") + .HasColumnType("boolean"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("DrawnAt") + .HasColumnType("timestamp without time zone"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("ShirtTertiaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamStaff_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId"); + + b.ToTable("TeamStaffs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.HasOne("Domain.Entities.Models.Club", "ParentClub") + .WithMany("ChildClubs") + .HasForeignKey("ParentClubId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("ParentClub"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("DivisionTeamRegistrations") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("DivisionTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("ChildClubs"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("DivisionTeamRegistrations"); + + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("DivisionTeamRegistrations"); + + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260907015056_AddClubParentClubId.cs b/Club12-Backend/Infrastructure/Migrations/20260907015056_AddClubParentClubId.cs new file mode 100644 index 0000000..0903e1d --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260907015056_AddClubParentClubId.cs @@ -0,0 +1,57 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddClubParentClubId : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ParentClubId", + schema: "Club12", + table: "Clubs", + type: "uuid", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Clubs_ParentClubId", + schema: "Club12", + table: "Clubs", + column: "ParentClubId"); + + migrationBuilder.AddForeignKey( + name: "FK_Clubs_Clubs_ParentClubId", + schema: "Club12", + table: "Clubs", + column: "ParentClubId", + principalSchema: "Club12", + principalTable: "Clubs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Clubs_Clubs_ParentClubId", + schema: "Club12", + table: "Clubs"); + + migrationBuilder.DropIndex( + name: "IX_Clubs_ParentClubId", + schema: "Club12", + table: "Clubs"); + + migrationBuilder.DropColumn( + name: "ParentClubId", + schema: "Club12", + table: "Clubs"); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260907042600_WidenDivisionNameMaxLength.Designer.cs b/Club12-Backend/Infrastructure/Migrations/20260907042600_WidenDivisionNameMaxLength.Designer.cs new file mode 100644 index 0000000..ae29169 --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260907042600_WidenDivisionNameMaxLength.Designer.cs @@ -0,0 +1,1739 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20260907042600_WidenDivisionNameMaxLength")] + partial class WidenDivisionNameMaxLength + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ParentClubId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("ParentClubId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionTeamRegistration_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId", "DivisionId") + .IsUnique(); + + b.ToTable("DivisionTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WentToOvertime") + .HasColumnType("boolean"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("DrawnAt") + .HasColumnType("timestamp without time zone"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("ShirtTertiaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamStaff_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId"); + + b.ToTable("TeamStaffs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.HasOne("Domain.Entities.Models.Club", "ParentClub") + .WithMany("ChildClubs") + .HasForeignKey("ParentClubId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("ParentClub"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("DivisionTeamRegistrations") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("DivisionTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("ChildClubs"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("DivisionTeamRegistrations"); + + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("DivisionTeamRegistrations"); + + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/20260907042600_WidenDivisionNameMaxLength.cs b/Club12-Backend/Infrastructure/Migrations/20260907042600_WidenDivisionNameMaxLength.cs new file mode 100644 index 0000000..b1d41ef --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/20260907042600_WidenDivisionNameMaxLength.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class WidenDivisionNameMaxLength : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Name", + schema: "Club12", + table: "Divisions", + type: "character varying(100)", + maxLength: 100, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(30)", + oldMaxLength: 30); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Name", + schema: "Club12", + table: "Divisions", + type: "character varying(30)", + maxLength: 30, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(100)", + oldMaxLength: 100); + } + } +} diff --git a/Club12-Backend/Infrastructure/Migrations/ApplicationDBContextModelSnapshot.cs b/Club12-Backend/Infrastructure/Migrations/ApplicationDBContextModelSnapshot.cs new file mode 100644 index 0000000..c1b1f3e --- /dev/null +++ b/Club12-Backend/Infrastructure/Migrations/ApplicationDBContextModelSnapshot.cs @@ -0,0 +1,1736 @@ +// +using System; +using Infrastructure.Persistance; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Persistance.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + partial class ApplicationDBContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.30") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasColumnType("text"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TargetId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TargetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TargetType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_AuditLog_CreatedAt"); + + b.ToTable("AuditLogs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BackupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Origin") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BackupRecord_CreatedAt"); + + b.ToTable("BackupRecords", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPublished") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("MarkdownText") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhotoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Views") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_BlogPost_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("BlogPosts", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ParentClubId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Club_CreatedAt"); + + b.HasIndex("ParentClubId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Clubs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("IsCrossDivisionCup") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PointsForLoss") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("PointsForWin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(2); + + b.Property("QualifiersPerGroup") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Division_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Divisions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("FromPosition") + .HasColumnType("integer"); + + b.Property("ToPosition") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionPlayoffMapping_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.ToTable("DivisionPlayoffMappings", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_DivisionTeamRegistration_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId", "DivisionId") + .IsUnique(); + + b.ToTable("DivisionTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("GameNumber") + .HasColumnType("integer"); + + b.Property("HomeScore") + .HasColumnType("integer"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("MatchDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Round") + .HasColumnType("integer"); + + b.Property("SeriesId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VenueId") + .HasColumnType("uuid"); + + b.Property("VisitorScore") + .HasColumnType("integer"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WentToOvertime") + .HasColumnType("boolean"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Match_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StageId"); + + b.HasIndex("VenueId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("Matches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .HasColumnType("integer"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("HomeTeamId") + .HasColumnType("uuid"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("VisitorTeamId") + .HasColumnType("uuid"); + + b.Property("WinningTeamId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_MatchSeries_CreatedAt"); + + b.HasIndex("HomeTeamId"); + + b.HasIndex("StageId"); + + b.HasIndex("VisitorTeamId"); + + b.HasIndex("WinningTeamId"); + + b.ToTable("MatchSeries", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DocumentNumber") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("IsSanctioned") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("SecondName") + .HasMaxLength(70) + .HasColumnType("character varying(70)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("SocialSecurity") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Player_CreatedAt"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("Players", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppealDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealReason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolution") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("AppealResolvedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AppealStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("IssuedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StaffName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SubjectType") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerSanction_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TeamId"); + + b.ToTable("PlayerSanctions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerStatistic_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("PlayersStatistics", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyNumber") + .HasColumnType("integer"); + + b.Property("MedicalRecordFileName") + .HasColumnType("text"); + + b.Property("MedicalRecordFileUrl") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewReason") + .HasColumnType("text"); + + b.Property("MedicalRecordReviewedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("MedicalRecordStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_PlayerTeamRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("PlayerId", "TournamentId") + .IsUnique(); + + b.HasIndex("TeamId", "TournamentId", "JerseyNumber") + .IsUnique(); + + b.ToTable("PlayerTeamRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("MatchId") + .HasColumnType("uuid"); + + b.Property("PlayerId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Scorer_CreatedAt"); + + b.HasIndex("MatchId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Scorers", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Season_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Seasons", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BestOf") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("BracketName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("DrawnAt") + .HasColumnType("timestamp without time zone"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsElimination") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RoundRobinLegs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StageType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Stage_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("Name", "DivisionId") + .IsUnique() + .HasDatabaseName("CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"); + + b.ToTable("Stages", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("StageId") + .HasColumnType("uuid"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_StageTeamMatch_CreatedAt"); + + b.HasIndex("StageId"); + + b.HasIndex("TeamId"); + + b.ToTable("StageTeamMatches", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClubId") + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("JerseyStyle") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasDefaultValue("solid"); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtColor") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShirtSecondaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("ShirtTertiaryColor") + .HasMaxLength(9) + .HasColumnType("character varying(9)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("ThreeLetterCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClubId"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Team_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("TournamentId"); + + b.ToTable("Teams", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("DivisionId") + .HasColumnType("uuid"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamPointDeduction_CreatedAt"); + + b.HasIndex("DivisionId"); + + b.HasIndex("TeamId"); + + b.ToTable("TeamPointDeductions", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamStaff_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId"); + + b.ToTable("TeamStaffs", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("TeamId") + .HasColumnType("uuid"); + + b.Property("TournamentId") + .HasColumnType("uuid"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_TeamTournamentRegistration_CreatedAt"); + + b.HasIndex("TournamentId"); + + b.HasIndex("TeamId", "TournamentId") + .IsUnique(); + + b.ToTable("TeamTournamentRegistrations", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SeasonId") + .HasColumnType("uuid"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("TeamRegistrationDeadline") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Tournament_CreatedAt"); + + b.HasIndex("SeasonId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Tournaments", "Club12", t => + { + t.HasCheckConstraint("CK_Tournament_DeadlineBeforeStart", "\"TeamRegistrationDeadline\" < \"StartDate\""); + }); + }); + + modelBuilder.Entity("Domain.Entities.Models.Venue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp without time zone"); + + b.Property("DateUpdated") + .HasColumnType("timestamp without time zone"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PhotoUrl") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(220) + .HasColumnType("character varying(220)"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DateCreated") + .HasDatabaseName("IX_Venue_CreatedAt"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Venues", "Club12"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.HasOne("Domain.Entities.Models.Club", "ParentClub") + .WithMany("ChildClubs") + .HasForeignKey("ParentClubId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("ParentClub"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Divisions") + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionPlayoffMapping", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("PlayoffMappings") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.DivisionTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("DivisionTeamRegistrations") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("DivisionTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId"); + + b.HasOne("Domain.Entities.Models.MatchSeries", "Series") + .WithMany("Matches") + .HasForeignKey("SeriesId"); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("Matches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Venue", "Venue") + .WithMany() + .HasForeignKey("VenueId"); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId"); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId"); + + b.Navigation("HomeTeam"); + + b.Navigation("Series"); + + b.Navigation("Stage"); + + b.Navigation("Venue"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.HasOne("Domain.Entities.Models.Team", "HomeTeam") + .WithMany() + .HasForeignKey("HomeTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("MatchSeries") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "VisitorTeam") + .WithMany() + .HasForeignKey("VisitorTeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "WinningTeam") + .WithMany() + .HasForeignKey("WinningTeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("HomeTeam"); + + b.Navigation("Stage"); + + b.Navigation("VisitorTeam"); + + b.Navigation("WinningTeam"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerSanction", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany() + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Match"); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerStatistic", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("PlayerStatistics") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.PlayerTeamRegistration", b => + { + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("PlayerTeamRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Scorer", b => + { + b.HasOne("Domain.Entities.Models.Match", "Match") + .WithMany("Scorers") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Player", "Player") + .WithMany("Scorers") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany("Stages") + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Division"); + }); + + modelBuilder.Entity("Domain.Entities.Models.StageTeamMatch", b => + { + b.HasOne("Domain.Entities.Models.Stage", "Stage") + .WithMany("StageTeamMatches") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("StageTeamMatches") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Stage"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.HasOne("Domain.Entities.Models.Club", "Club") + .WithMany("Teams") + .HasForeignKey("ClubId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany("Teams") + .HasForeignKey("TournamentId"); + + b.Navigation("Club"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamPointDeduction", b => + { + b.HasOne("Domain.Entities.Models.Division", "Division") + .WithMany() + .HasForeignKey("DivisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Division"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamStaff", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.TeamTournamentRegistration", b => + { + b.HasOne("Domain.Entities.Models.Team", "Team") + .WithMany("TeamTournamentRegistrations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Models.Tournament", "Tournament") + .WithMany() + .HasForeignKey("TournamentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("Tournament"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.HasOne("Domain.Entities.Models.Season", "Season") + .WithMany("Tournaments") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Club", b => + { + b.Navigation("ChildClubs"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Division", b => + { + b.Navigation("DivisionTeamRegistrations"); + + b.Navigation("PlayoffMappings"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Match", b => + { + b.Navigation("PlayerStatistics"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.MatchSeries", b => + { + b.Navigation("Matches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Player", b => + { + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Scorers"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Season", b => + { + b.Navigation("Tournaments"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Stage", b => + { + b.Navigation("MatchSeries"); + + b.Navigation("Matches"); + + b.Navigation("StageTeamMatches"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Team", b => + { + b.Navigation("DivisionTeamRegistrations"); + + b.Navigation("PlayerTeamRegistrations"); + + b.Navigation("Players"); + + b.Navigation("StageTeamMatches"); + + b.Navigation("TeamTournamentRegistrations"); + }); + + modelBuilder.Entity("Domain.Entities.Models.Tournament", b => + { + b.Navigation("Divisions"); + + b.Navigation("Teams"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/ApplicationDBContext.cs b/Club12-Backend/Infrastructure/Persistance/ApplicationDBContext.cs new file mode 100644 index 0000000..dc51339 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/ApplicationDBContext.cs @@ -0,0 +1,61 @@ +using Domain.Entities.Models; + +using Infrastructure.Persistance.Converters; + +using Microsoft.EntityFrameworkCore; + +using System; + +namespace Infrastructure.Persistance; + +/// +/// Serves as the main EF Core database context, with entity mappings defined in Infrastructure.Persistance.Configurations and applied via ModelBuilder.ApplyConfigurationsFromAssembly. +/// +public class ApplicationDBContext(DbContextOptions options) + : DbContext(options), IClub12DBContext +{ + protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) + { + configurationBuilder + .Properties() + .HaveColumnType("timestamp without time zone") + .HaveConversion(); + + configurationBuilder + .Properties() + .HaveColumnType("timestamp without time zone") + .HaveConversion(); + + base.ConfigureConventions(configurationBuilder); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationDBContext).Assembly); + base.OnModelCreating(modelBuilder); + } + + public virtual required DbSet Clubs { get; set; } + public virtual required DbSet BackupRecords { get; set; } + public virtual required DbSet Teams { get; set; } + public virtual required DbSet Players { get; set; } + public virtual required DbSet Tournaments { get; set; } + public virtual required DbSet Divisions { get; set; } + public virtual required DbSet DivisionPlayoffMappings { get; set; } + public virtual required DbSet TeamPointDeductions { get; set; } + public virtual required DbSet TeamStaffs { get; set; } + public virtual required DbSet Matches { get; set; } + public virtual required DbSet MatchSeries { get; set; } + public virtual required DbSet PlayersStatistics { get; set; } + public virtual required DbSet PlayerSanctions { get; set; } + public virtual required DbSet Venues { get; set; } + public virtual required DbSet Seasons { get; set; } + public virtual required DbSet BlogPosts { get; set; } + public virtual required DbSet Stages { get; set; } + public virtual required DbSet StageTeamMatches { get; set; } + public virtual required DbSet Scorers { get; set; } + public virtual required DbSet PlayerTeamRegistrations { get; set; } + public virtual required DbSet TeamTournamentRegistrations { get; set; } + public virtual required DbSet DivisionTeamRegistrations { get; set; } + public virtual required DbSet AuditLogs { get; set; } +} \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Persistance/ApplicationDBContextFactory.cs b/Club12-Backend/Infrastructure/Persistance/ApplicationDBContextFactory.cs new file mode 100644 index 0000000..e827df0 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/ApplicationDBContextFactory.cs @@ -0,0 +1,101 @@ +using Application.Utils.Constants.Configuration; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +using System; +using System.IO; +using System.Linq; + +namespace Infrastructure.Persistance; + +/// +/// Provides a design-time instance of ApplicationDBContext for EF Core CLI tools, loading all appsettings files and environment variables so the model can build without running the API host. +/// +public sealed class ApplicationDBContextFactory : IDesignTimeDbContextFactory +{ + public ApplicationDBContext CreateDbContext(string[] args) + { + string basePath = ResolveBasePath(); + + string aspNetEnv = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") + ?? "Development"; + + ConfigurationBuilder builder = new(); + builder.SetBasePath(basePath); + builder.AddJsonFile("appsettings.json", optional: true); + builder.AddJsonFile($"appsettings.{aspNetEnv}.json", optional: true); + + if (Directory.Exists(basePath)) + { + string?[] extras = Directory + .GetFiles(basePath, "appsettings.*.json") + .Select(Path.GetFileName) + .Where(f => !string.Equals( + f, $"appsettings.{aspNetEnv}.json", + StringComparison.OrdinalIgnoreCase)) + .ToArray(); + + foreach (string? file in extras) + { + builder.AddJsonFile(file!, optional: true); + } + } + + IConfigurationRoot config = builder.Build(); + + string connectionString = config.GetConnectionString(ConfigurationKeys.DbConnection) + ?? Environment.GetEnvironmentVariable($"ConnectionStrings__{ConfigurationKeys.DbConnection}") + ?? throw new InvalidOperationException( + $"Connection string '{ConfigurationKeys.DbConnection}' not found.\n" + + $"Searched in: {basePath}\n" + + $"Files loaded: appsettings.json, appsettings.{aspNetEnv}.json + all appsettings.*.json"); + + DbContextOptionsBuilder optionsBuilder = new(); + optionsBuilder.UseNpgsql(connectionString); + + return new ApplicationDBContext(optionsBuilder.Options) + { + Clubs = null!, + BackupRecords = null!, + Teams = null!, + Players = null!, + Tournaments = null!, + Divisions = null!, + DivisionPlayoffMappings = null!, + TeamPointDeductions = null!, + TeamStaffs = null!, + Matches = null!, + MatchSeries = null!, + PlayersStatistics = null!, + PlayerSanctions = null!, + Venues = null!, + Seasons = null!, + BlogPosts = null!, + Stages = null!, + StageTeamMatches = null!, + Scorers = null!, + PlayerTeamRegistrations = null!, + TeamTournamentRegistrations = null!, + DivisionTeamRegistrations = null!, + AuditLogs = null!, + }; + } + + private static string ResolveBasePath() + { + string current = Directory.GetCurrentDirectory(); + + string[] candidates = + [ + current, + Path.Combine(current, "API"), + Path.Combine(current, "..", "API"), + ]; + + string? match = Array.Find(candidates, path => File.Exists(Path.Combine(path, "appsettings.json"))); + + return match is null ? current : Path.GetFullPath(match); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/AuditLogEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/AuditLogEntityConfiguration.cs new file mode 100644 index 0000000..f45778f --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/AuditLogEntityConfiguration.cs @@ -0,0 +1,27 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// EF Core mapping for the sensitive-action audit trail. +/// +public class AuditLogEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.AuditLog, EntityConstants.Schema); + + // Action is persisted as the enum name string, mirroring other enum columns in this DB, including Match.Status and PlayerTeamRegistration.MedicalRecordStatus. + builder.Property(a => a.Action).IsRequired().HasConversion(); + builder.Property(a => a.Actor).IsRequired().HasMaxLength(256); + builder.Property(a => a.TargetType).HasMaxLength(128); + builder.Property(a => a.TargetId).HasMaxLength(128); + builder.Property(a => a.TargetName).HasMaxLength(256); + builder.Property(a => a.Detail).HasMaxLength(1024); + + builder.HasIndex(a => a.Action); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/BackupRecordEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/BackupRecordEntityConfiguration.cs new file mode 100644 index 0000000..c927194 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/BackupRecordEntityConfiguration.cs @@ -0,0 +1,18 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +public class BackupRecordEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.BackupRecord, EntityConstants.Schema); + + builder.Property(b => b.StoragePath).IsRequired().HasMaxLength(500); + builder.Property(b => b.SizeBytes).IsRequired(); + builder.Property(b => b.Origin).IsRequired().HasConversion(); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/BaseEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/BaseEntityConfiguration.cs new file mode 100644 index 0000000..aec8429 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/BaseEntityConfiguration.cs @@ -0,0 +1,36 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// Base EF Core configuration shared by every entity, mapping the primary key, audit timestamps, and a DateCreated index. +/// +public abstract class BaseEntityConfiguration : IEntityTypeConfiguration + where TEntity : EntityBase +{ + /// + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(e => e.Id); + builder.Property(e => e.Id) + .ValueGeneratedOnAdd(); + + builder.Property(e => e.DateCreated) + .IsRequired(); + + builder.Property(e => e.DateUpdated); + + builder.HasIndex(e => e.DateCreated) + .HasDatabaseName($"IX_{typeof(TEntity).Name}_CreatedAt"); + + ConfigureEntity(builder); + } + + /// + /// Override to configure entity-specific table, columns, indexes, and relationships. + /// + protected abstract void ConfigureEntity(EntityTypeBuilder builder); +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/BlogPostEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/BlogPostEntityConfiguration.cs new file mode 100644 index 0000000..80eccd5 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/BlogPostEntityConfiguration.cs @@ -0,0 +1,25 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +public class BlogPostEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.BlogPost, EntityConstants.Schema); + + builder.Property(b => b.Author).IsRequired().HasMaxLength(50); + builder.Property(b => b.Title).IsRequired(); + builder.Property(b => b.Slug).IsRequired().HasMaxLength(220); + builder.Property(b => b.PhotoUrl).HasMaxLength(2048); + builder.Property(b => b.MarkdownText).IsRequired(); + + // Defaults to true so every pre-existing post back-filled by the migration stays publicly visible, and any future insert that omits the flag is treated as published. + builder.Property(b => b.IsPublished).IsRequired().HasDefaultValue(true); + + builder.HasIndex(b => b.Slug).IsUnique(); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/ClubEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/ClubEntityConfiguration.cs new file mode 100644 index 0000000..1e53d07 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/ClubEntityConfiguration.cs @@ -0,0 +1,38 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// Maps the Club to Team relationship as optional and non-cascading, so deleting a Club only nulls its teams' ClubId instead of removing them. +/// +public class ClubEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.Club, EntityConstants.Schema); + + builder.Property(c => c.Name).IsRequired(); + builder.Property(c => c.Slug).IsRequired().HasMaxLength(220); + builder.Property(c => c.LogoUrl); + + builder.HasIndex(c => c.Slug).IsUnique(); + + builder.HasMany(c => c.Teams) + .WithOne(t => t.Club) + .HasForeignKey(t => t.ClubId) + .IsRequired(false) + .OnDelete(DeleteBehavior.SetNull); + + // Self-referencing, one level deep: deleting the parent club unlinks its + // squads instead of cascading, and Restrict avoids the multiple-cascade-path + // error SQL Server/SQLite raise for a self-join with SetNull here too. + builder.HasMany(c => c.ChildClubs) + .WithOne(c => c.ParentClub) + .HasForeignKey(c => c.ParentClubId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Restrict); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/DivisionEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/DivisionEntityConfiguration.cs new file mode 100644 index 0000000..20f1a48 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/DivisionEntityConfiguration.cs @@ -0,0 +1,42 @@ +using Application.Utils.Constants.Validation; + +using Domain.Entities.Models; +using Domain.Enums; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +public class DivisionEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.Division, EntityConstants.Schema); + + builder.Property(d => d.Name).IsRequired().HasMaxLength(DivisionFieldLengths.NameMaxLength); + builder.Property(d => d.Slug).IsRequired().HasMaxLength(220); + + builder.HasIndex(d => d.Slug).IsUnique(); + + builder.HasOne(d => d.Tournament) + .WithMany(t => t.Divisions) + .HasForeignKey(d => d.TournamentId) + .IsRequired(); + + builder.Property(d => d.PointsForWin).IsRequired().HasDefaultValue(2); + builder.Property(d => d.PointsForLoss).IsRequired().HasDefaultValue(1); + builder.Property(d => d.QualifiersPerGroup).IsRequired().HasDefaultValue(1); + builder.Property(d => d.Category).IsRequired().HasDefaultValue(TournamentCategory.Masculine); + + builder.HasMany(d => d.Stages) + .WithOne(s => s.Division) + .HasForeignKey(s => s.DivisionId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasMany(d => d.PlayoffMappings) + .WithOne(m => m.Division) + .HasForeignKey(m => m.DivisionId) + .OnDelete(DeleteBehavior.Cascade); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/DivisionPlayoffMappingEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/DivisionPlayoffMappingEntityConfiguration.cs new file mode 100644 index 0000000..8fd6344 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/DivisionPlayoffMappingEntityConfiguration.cs @@ -0,0 +1,20 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +public class DivisionPlayoffMappingEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.DivisionPlayoffMapping, EntityConstants.Schema); + + builder.Property(m => m.FromPosition).IsRequired(); + builder.Property(m => m.ToPosition).IsRequired(); + builder.Property(m => m.Destination).IsRequired().HasMaxLength(100); + + builder.HasIndex(m => m.DivisionId); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/DivisionTeamRegistrationEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/DivisionTeamRegistrationEntityConfiguration.cs new file mode 100644 index 0000000..ec7b2bf --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/DivisionTeamRegistrationEntityConfiguration.cs @@ -0,0 +1,34 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// A team is enrolled in the same division at most once, enforced by a unique index on TeamId and DivisionId. +/// +public class DivisionTeamRegistrationEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.DivisionTeamRegistration, EntityConstants.Schema); + + builder.Property(r => r.TeamId).IsRequired(); + builder.Property(r => r.DivisionId).IsRequired(); + + builder.HasIndex(r => new { r.TeamId, r.DivisionId }).IsUnique(); + + builder.HasIndex(r => r.DivisionId); + + builder.HasOne(r => r.Team) + .WithMany(t => t.DivisionTeamRegistrations) + .HasForeignKey(r => r.TeamId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(r => r.Division) + .WithMany(d => d.DivisionTeamRegistrations) + .HasForeignKey(r => r.DivisionId) + .OnDelete(DeleteBehavior.Cascade); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/MatchEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/MatchEntityConfiguration.cs new file mode 100644 index 0000000..384a0ce --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/MatchEntityConfiguration.cs @@ -0,0 +1,55 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// Match has multiple optional foreign keys to Team, each configured explicitly to avoid EF Core relationship ambiguity. +/// +public class MatchEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.Match, EntityConstants.Schema); + + builder.Property(m => m.Type).IsRequired().HasConversion(); + builder.Property(m => m.Slug).IsRequired().HasMaxLength(220); + builder.Property(m => m.IsFinished).IsRequired(); + builder.Property(m => m.Status).IsRequired().HasConversion(); + builder.Property(m => m.StageId).IsRequired(); + + builder.HasIndex(m => m.Slug).IsUnique(); + + builder.HasOne(m => m.Stage) + .WithMany(s => s.Matches) + .HasForeignKey(m => m.StageId) + .IsRequired(); + + builder.HasOne(m => m.HomeTeam) + .WithMany() + .HasForeignKey(m => m.HomeTeamId) + .IsRequired(false); + + builder.HasOne(m => m.VisitorTeam) + .WithMany() + .HasForeignKey(m => m.VisitorTeamId) + .IsRequired(false); + + builder.HasOne(m => m.WinningTeam) + .WithMany() + .HasForeignKey(m => m.WinningTeamId) + .IsRequired(false); + + builder.HasOne(m => m.Venue) + .WithMany() + .HasForeignKey(m => m.VenueId) + .IsRequired(false); + + builder.HasOne(m => m.Series) + .WithMany(ms => ms.Matches) + .HasForeignKey(m => m.SeriesId) + .IsRequired(false); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/MatchSeriesEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/MatchSeriesEntityConfiguration.cs new file mode 100644 index 0000000..ccb7cb4 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/MatchSeriesEntityConfiguration.cs @@ -0,0 +1,50 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// MatchSeries has multiple foreign keys to Team, each configured explicitly to avoid EF Core relationship ambiguity. +/// +public class MatchSeriesEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.MatchSeries, EntityConstants.Schema); + + builder.Property(ms => ms.StageId).IsRequired(); + builder.Property(ms => ms.HomeTeamId).IsRequired(); + builder.Property(ms => ms.VisitorTeamId).IsRequired(); + + builder.HasOne(ms => ms.Stage) + .WithMany(s => s.MatchSeries) + .HasForeignKey(ms => ms.StageId) + .IsRequired(); + + // Explicit Restrict overrides EF's default Cascade for required FKs so a team with an active series is never silently deleted along with its series history if TeamService.DeleteTeamAsync's guard is ever bypassed. + builder.HasOne(ms => ms.HomeTeam) + .WithMany() + .HasForeignKey(ms => ms.HomeTeamId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(ms => ms.VisitorTeam) + .WithMany() + .HasForeignKey(ms => ms.VisitorTeamId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(ms => ms.WinningTeam) + .WithMany() + .HasForeignKey(ms => ms.WinningTeamId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasMany(ms => ms.Matches) + .WithOne(m => m.Series) + .HasForeignKey(m => m.SeriesId) + .IsRequired(false); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/PlayerEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/PlayerEntityConfiguration.cs new file mode 100644 index 0000000..2afce25 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/PlayerEntityConfiguration.cs @@ -0,0 +1,32 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// FullName is ignored because it is a computed C# property, not a DB column. +/// +public class PlayerEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.Player, EntityConstants.Schema); + + builder.Property(p => p.FirstName).IsRequired().HasMaxLength(70); + builder.Property(p => p.SecondName).HasMaxLength(70); + builder.Property(p => p.LastName).IsRequired().HasMaxLength(70); + builder.Property(p => p.Slug).IsRequired().HasMaxLength(220); + builder.Property(p => p.DocumentNumber).IsRequired().HasMaxLength(15); + builder.Property(p => p.IsSanctioned).IsRequired(); + builder.Property(p => p.BirthDate).IsRequired(); + builder.Property(p => p.SocialSecurity).IsRequired().HasMaxLength(100); + + builder.Ignore(p => p.FullName); + + builder.HasIndex(p => p.DocumentNumber).IsUnique(); + + builder.HasIndex(p => p.Slug).IsUnique(); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/PlayerSanctionEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/PlayerSanctionEntityConfiguration.cs new file mode 100644 index 0000000..b576dd7 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/PlayerSanctionEntityConfiguration.cs @@ -0,0 +1,44 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +public class PlayerSanctionEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.PlayerSanction, EntityConstants.Schema); + + builder.Property(ps => ps.Duration).IsRequired(); + builder.Property(ps => ps.IssuedDate).IsRequired(); + builder.Property(ps => ps.Description).IsRequired().HasMaxLength(255); + builder.Property(ps => ps.Slug).IsRequired().HasMaxLength(220); + builder.Property(ps => ps.SubjectType).IsRequired().HasConversion(); + builder.Property(ps => ps.StaffName).HasMaxLength(255); + builder.Property(ps => ps.AppealStatus).IsRequired().HasConversion(); + builder.Property(ps => ps.AppealReason).HasMaxLength(1000); + builder.Property(ps => ps.AppealResolution).HasMaxLength(1000); + + builder.HasIndex(ps => ps.Slug).IsUnique(); + + // OnDelete is stated explicitly because the existing migrations already applied Cascade for Player and Restrict for Team, and without it the optional FKs would default to NoAction and drift from the persisted schema. + builder.HasOne(ps => ps.Player) + .WithMany() + .HasForeignKey(ps => ps.PlayerId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(ps => ps.Team) + .WithMany() + .HasForeignKey(ps => ps.TeamId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(ps => ps.Match) + .WithMany() + .HasForeignKey(ps => ps.MatchId) + .IsRequired(); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/PlayerStatisticEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/PlayerStatisticEntityConfiguration.cs new file mode 100644 index 0000000..acc7d1f --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/PlayerStatisticEntityConfiguration.cs @@ -0,0 +1,28 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +public class PlayerStatisticEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.PlayerStatistic, EntityConstants.Schema); + + builder.Property(ps => ps.Value).IsRequired(); + + builder.Property(ps => ps.Type).IsRequired().HasConversion(); + + builder.HasOne(ps => ps.Match) + .WithMany(m => m.PlayerStatistics) + .HasForeignKey(ps => ps.MatchId) + .IsRequired(); + + builder.HasOne(ps => ps.Player) + .WithMany() + .HasForeignKey(ps => ps.PlayerId) + .IsRequired(); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/PlayerTeamRegistrationEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/PlayerTeamRegistrationEntityConfiguration.cs new file mode 100644 index 0000000..c6b839e --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/PlayerTeamRegistrationEntityConfiguration.cs @@ -0,0 +1,49 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// A player may have at most one registration per tournament season, enforced by a unique index on PlayerId and TournamentId, so a player can never be on two teams in one season. +/// +public class PlayerTeamRegistrationEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.PlayerTeamRegistration, EntityConstants.Schema); + + builder.Property(r => r.PlayerId).IsRequired(); + builder.Property(r => r.TeamId).IsRequired(); + builder.Property(r => r.TournamentId).IsRequired(); + + // Status is persisted as the enum name string, mirroring other enum columns in this DB, including Match.Status, PlayerStatistic.Type, and PlayerSanction.AppealStatus. + builder.Property(r => r.MedicalRecordStatus).IsRequired().HasConversion(); + builder.Property(r => r.MedicalRecordFileUrl); + builder.Property(r => r.MedicalRecordFileName); + builder.Property(r => r.MedicalRecordReviewReason); + builder.Property(r => r.MedicalRecordReviewedAt); + + builder.HasIndex(r => new { r.PlayerId, r.TournamentId }).IsUnique(); + + // Both Npgsql and SQLite treat NULLs as distinct in a unique index, so this allows any number of players with no assigned dorsal while rejecting duplicate non-null dorsals, with no provider-specific filtered index required. + builder.Property(r => r.JerseyNumber); + builder.HasIndex(r => new { r.TeamId, r.TournamentId, r.JerseyNumber }).IsUnique(); + + builder.HasOne(r => r.Player) + .WithMany(p => p.PlayerTeamRegistrations) + .HasForeignKey(r => r.PlayerId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(r => r.Team) + .WithMany(t => t.PlayerTeamRegistrations) + .HasForeignKey(r => r.TeamId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(r => r.Tournament) + .WithMany() + .HasForeignKey(r => r.TournamentId) + .OnDelete(DeleteBehavior.Cascade); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/ScorerEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/ScorerEntityConfiguration.cs new file mode 100644 index 0000000..514f8ad --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/ScorerEntityConfiguration.cs @@ -0,0 +1,29 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// Scorer is a service-layer read-model with no DataAnnotations, so there is nothing to migrate beyond the mappings configured here. +/// +public class ScorerEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.Scorer, EntityConstants.Schema); + + builder.Property(s => s.PlayerId).IsRequired(); + builder.Property(s => s.Points).IsRequired(); + builder.Property(s => s.MatchId).IsRequired(); + + builder.HasOne(s => s.Player) + .WithMany(p => p.Scorers) + .HasForeignKey(s => s.PlayerId); + + builder.HasOne(s => s.Match) + .WithMany(m => m.Scorers) + .HasForeignKey(s => s.MatchId); + } +} \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/SeasonEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/SeasonEntityConfiguration.cs new file mode 100644 index 0000000..d617b57 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/SeasonEntityConfiguration.cs @@ -0,0 +1,27 @@ +using Application.Utils.Constants.Validation; + +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +public class SeasonEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.Season, EntityConstants.Schema); + + builder.Property(s => s.Name).IsRequired().HasMaxLength(SeasonFieldLengths.NameMaxLength); + builder.Property(s => s.Slug).IsRequired().HasMaxLength(220); + + builder.HasIndex(s => s.Slug).IsUnique(); + + // Deleting a season detaches its tournaments via SetNull instead of cascading, since a season is a purely additive grouping and must never delete a tournament. + builder.HasMany(s => s.Tournaments) + .WithOne(t => t.Season) + .HasForeignKey(t => t.SeasonId) + .OnDelete(DeleteBehavior.SetNull); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/StageEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/StageEntityConfiguration.cs new file mode 100644 index 0000000..ff44156 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/StageEntityConfiguration.cs @@ -0,0 +1,32 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +public class StageEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.Stage, EntityConstants.Schema); + + builder.Property(s => s.Name).IsRequired().HasMaxLength(100); + builder.Property(s => s.Slug).IsRequired().HasMaxLength(220); + builder.Property(s => s.Description).HasMaxLength(1000); + builder.Property(s => s.StageType).IsRequired().HasConversion(); + builder.Property(s => s.IsActive).IsRequired(); + builder.Property(s => s.StartDate).IsRequired(); + builder.Property(s => s.EndDate).IsRequired(); + builder.Property(s => s.DivisionId).IsRequired(); + builder.Property(s => s.BracketName).HasMaxLength(100); + builder.Property(s => s.BestOf).IsRequired().HasDefaultValue(1); + builder.Property(s => s.RoundRobinLegs).IsRequired().HasDefaultValue(1); + + builder.HasIndex(s => new { s.Name, s.DivisionId }) + .IsUnique() + .HasDatabaseName(EntityConstants.Indexes.UniqueStageNameDivision); + + builder.HasIndex(s => s.Slug).IsUnique(); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/StageTeamMatchEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/StageTeamMatchEntityConfiguration.cs new file mode 100644 index 0000000..bdb305a --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/StageTeamMatchEntityConfiguration.cs @@ -0,0 +1,27 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +public class StageTeamMatchEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.StageTeamMatch, EntityConstants.Schema); + + builder.Property(stm => stm.StageId).IsRequired(); + builder.Property(stm => stm.TeamId).IsRequired(); + + builder.HasOne(stm => stm.Stage) + .WithMany(s => s.StageTeamMatches) + .HasForeignKey(stm => stm.StageId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(stm => stm.Team) + .WithMany(t => t.StageTeamMatches) + .HasForeignKey(stm => stm.TeamId) + .OnDelete(DeleteBehavior.Cascade); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/TeamEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/TeamEntityConfiguration.cs new file mode 100644 index 0000000..faa4e91 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/TeamEntityConfiguration.cs @@ -0,0 +1,38 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// TournamentId is an optional foreign key, since a Team may exist without a tournament. +/// +public class TeamEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.Team, EntityConstants.Schema); + + builder.Property(t => t.Name).IsRequired(); + builder.Property(t => t.Slug).IsRequired().HasMaxLength(220); + builder.Property(t => t.ThreeLetterCode).IsRequired(); + builder.Property(t => t.LogoUrl).IsRequired(); + builder.Property(t => t.ShirtColor).IsRequired(); + builder.Property(t => t.JerseyStyle).IsRequired().HasMaxLength(20).HasDefaultValue("solid"); + builder.Property(t => t.ShirtSecondaryColor).HasMaxLength(9); + builder.Property(t => t.ShirtTertiaryColor).HasMaxLength(9); + + builder.HasIndex(t => t.Slug).IsUnique(); + + builder.HasOne(t => t.Tournament) + .WithMany(tourn => tourn.Teams) + .HasForeignKey(t => t.TournamentId) + .IsRequired(false); + + builder.HasMany(t => t.Players) + .WithOne(p => p.Team) + .HasForeignKey(p => p.TeamId) + .OnDelete(DeleteBehavior.Cascade); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/TeamPointDeductionEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/TeamPointDeductionEntityConfiguration.cs new file mode 100644 index 0000000..e8f3c37 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/TeamPointDeductionEntityConfiguration.cs @@ -0,0 +1,32 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// Deleting a division cascades its point deductions away, but the team foreign key is Restrict, so a team that still carries deductions is not silently removed. +/// +public class TeamPointDeductionEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.TeamPointDeduction, EntityConstants.Schema); + + builder.Property(d => d.Points).IsRequired(); + builder.Property(d => d.Reason).IsRequired().HasMaxLength(300); + + builder.HasIndex(d => d.DivisionId); + + builder.HasOne(d => d.Division) + .WithMany() + .HasForeignKey(d => d.DivisionId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(d => d.Team) + .WithMany() + .HasForeignKey(d => d.TeamId) + .OnDelete(DeleteBehavior.Restrict); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/TeamStaffEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/TeamStaffEntityConfiguration.cs new file mode 100644 index 0000000..0dd3ce8 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/TeamStaffEntityConfiguration.cs @@ -0,0 +1,32 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// A team staff row carries no independent value once its team or tournament is gone, so both foreign keys cascade. +/// +public class TeamStaffEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.TeamStaff, EntityConstants.Schema); + + builder.Property(s => s.FullName).IsRequired().HasMaxLength(150); + builder.Property(s => s.Role).IsRequired(); + + builder.HasIndex(s => new { s.TeamId, s.TournamentId }); + + builder.HasOne(s => s.Team) + .WithMany() + .HasForeignKey(s => s.TeamId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(s => s.Tournament) + .WithMany() + .HasForeignKey(s => s.TournamentId) + .OnDelete(DeleteBehavior.Cascade); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/TeamTournamentRegistrationEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/TeamTournamentRegistrationEntityConfiguration.cs new file mode 100644 index 0000000..e88926e --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/TeamTournamentRegistrationEntityConfiguration.cs @@ -0,0 +1,32 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// A team may be registered to the same tournament only once, enforced by a unique index on TeamId and TournamentId, but may hold independent registrations across multiple tournaments at once. +/// +public class TeamTournamentRegistrationEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.TeamTournamentRegistration, EntityConstants.Schema); + + builder.Property(r => r.TeamId).IsRequired(); + builder.Property(r => r.TournamentId).IsRequired(); + + builder.HasIndex(r => new { r.TeamId, r.TournamentId }).IsUnique(); + + builder.HasOne(r => r.Team) + .WithMany(t => t.TeamTournamentRegistrations) + .HasForeignKey(r => r.TeamId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(r => r.Tournament) + .WithMany() + .HasForeignKey(r => r.TournamentId) + .OnDelete(DeleteBehavior.Cascade); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/TournamentEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/TournamentEntityConfiguration.cs new file mode 100644 index 0000000..fbdff79 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/TournamentEntityConfiguration.cs @@ -0,0 +1,30 @@ +using Domain.Entities.Models; +using Domain.Enums; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +public class TournamentEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.Tournament, EntityConstants.Schema, t => + t.HasCheckConstraint( + EntityConstants.CheckConstraints.TournamentDeadlineBeforeStart, + "\"TeamRegistrationDeadline\" < \"StartDate\"" + ) + ); + + builder.Property(t => t.Name).IsRequired(); + builder.Property(t => t.Slug).IsRequired().HasMaxLength(220); + builder.Property(t => t.Description).IsRequired(); + builder.Property(t => t.TeamRegistrationDeadline).IsRequired(); + builder.Property(t => t.StartDate).IsRequired(); + builder.Property(t => t.Status).HasDefaultValue(TournamentStatus.Scheduled).IsRequired(); + builder.Property(t => t.Category).HasDefaultValue(TournamentCategory.Masculine).IsRequired(); + + builder.HasIndex(t => t.Slug).IsUnique(); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Configurations/VenueEntityConfiguration.cs b/Club12-Backend/Infrastructure/Persistance/Configurations/VenueEntityConfiguration.cs new file mode 100644 index 0000000..74f80ce --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Configurations/VenueEntityConfiguration.cs @@ -0,0 +1,23 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Infrastructure.Persistance.Configurations; + +/// +/// PhotoUrl is intentionally not configured here since its format is enforced by the ImageReference attribute on the request DTO, a validation-only concern with no corresponding database constraint. +/// +public class VenueEntityConfiguration : BaseEntityConfiguration +{ + protected override void ConfigureEntity(EntityTypeBuilder builder) + { + builder.ToTable(EntityConstants.Tables.Venue, EntityConstants.Schema); + + builder.Property(v => v.Name).IsRequired().HasMaxLength(50); + builder.Property(v => v.Slug).IsRequired().HasMaxLength(220); + builder.Property(v => v.Address).IsRequired().HasMaxLength(200); + + builder.HasIndex(v => v.Slug).IsUnique(); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Converters/NullableUtcDateTimeConverter.cs b/Club12-Backend/Infrastructure/Persistance/Converters/NullableUtcDateTimeConverter.cs new file mode 100644 index 0000000..ca37f52 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Converters/NullableUtcDateTimeConverter.cs @@ -0,0 +1,13 @@ +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +using System; + +namespace Infrastructure.Persistance.Converters; + +/// +/// Nullable variant of UtcDateTimeConverter. +/// +public class NullableUtcDateTimeConverter() + : ValueConverter( + v => v.HasValue ? DateTime.SpecifyKind(v.Value, DateTimeKind.Unspecified) : null, + v => v.HasValue ? DateTime.SpecifyKind(v.Value, DateTimeKind.Utc) : null); diff --git a/Club12-Backend/Infrastructure/Persistance/Converters/UtcDateTimeConverter.cs b/Club12-Backend/Infrastructure/Persistance/Converters/UtcDateTimeConverter.cs new file mode 100644 index 0000000..95708b2 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Converters/UtcDateTimeConverter.cs @@ -0,0 +1,13 @@ +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +using System; + +namespace Infrastructure.Persistance.Converters; + +/// +/// Converts DateTime values to Unspecified for storage and back to UTC on read, to satisfy the timestamp without time zone column type. +/// +public class UtcDateTimeConverter() + : ValueConverter( + v => DateTime.SpecifyKind(v, DateTimeKind.Unspecified), + v => DateTime.SpecifyKind(v, DateTimeKind.Utc)); diff --git a/Club12-Backend/Infrastructure/Persistance/EfBackupCatalog.cs b/Club12-Backend/Infrastructure/Persistance/EfBackupCatalog.cs new file mode 100644 index 0000000..46fcda4 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/EfBackupCatalog.cs @@ -0,0 +1,50 @@ +using Application.Interfaces.Backup; + +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Infrastructure.Persistance; + +/// +/// IBackupCatalog implementation backed directly by ApplicationDBContext.BackupRecords, kept as a thin single-entity wrapper instead of routing through GenericRepository or UnitOfWork to match the lightweight port style established for the rest of the Backup feature. +/// +public sealed class EfBackupCatalog(ApplicationDBContext context) : IBackupCatalog +{ + public async Task AddAsync(BackupRecord record, CancellationToken ct = default) + { + context.BackupRecords.Add(record); + await context.SaveChangesAsync(ct); + return record; + } + + public async Task GetByIdAsync(Guid id, CancellationToken ct = default) + { + return await context.BackupRecords.FirstOrDefaultAsync(r => r.Id == id, ct); + } + + public async Task> ListNewestFirstAsync(CancellationToken ct = default) + { + return await context.BackupRecords + .OrderByDescending(r => r.DateCreated) + .ToListAsync(ct); + } + + public async Task RemoveAsync(Guid id, CancellationToken ct = default) + { + BackupRecord? record = await context.BackupRecords.FirstOrDefaultAsync(r => r.Id == id, ct); + if (record is null) + { + return; + } + + context.BackupRecords.Remove(record); + await context.SaveChangesAsync(ct); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/EntityConstants.cs b/Club12-Backend/Infrastructure/Persistance/EntityConstants.cs new file mode 100644 index 0000000..8f2c714 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/EntityConstants.cs @@ -0,0 +1,68 @@ +namespace Infrastructure.Persistance; + +/// +/// Centralizes all magic strings used across EF Core configurations: +/// schema, table names, column type names, index names, and filter expressions. +/// +public static class EntityConstants +{ + /// + /// Club12 database schema. + /// + public const string Schema = "Club12"; + + /// + /// SQL Server datetime2 column type. + /// + public const string DateTime2 = "datetime2"; + + /// + /// SQL Server decimal column type with precision 18 and scale 4. + /// + public const string Decimal18x4 = "decimal(18,4)"; + + public static class Tables + { + public const string AuditLog = "AuditLogs"; + public const string BackupRecord = "BackupRecords"; + public const string BlogPost = "BlogPosts"; + public const string Club = "Clubs"; + public const string Division = "Divisions"; + public const string DivisionPlayoffMapping = "DivisionPlayoffMappings"; + public const string DivisionTeamRegistration = "DivisionTeamRegistrations"; + public const string Match = "Matches"; + public const string MatchSeries = "MatchSeries"; + public const string Player = "Players"; + public const string PlayerSanction = "PlayerSanctions"; + + /// + /// Uses the plural form PlayersStatistics to match the value already baked into existing migrations, unlike the singular naming used by the other table constants. + /// + public const string PlayerStatistic = "PlayersStatistics"; + + public const string PlayerTeamRegistration = "PlayerTeamRegistrations"; + public const string Position = "Positions"; + public const string Scorer = "Scorers"; + public const string Season = "Seasons"; + public const string Stage = "Stages"; + public const string StageTeamMatch = "StageTeamMatches"; + public const string Staff = "Staffs"; + public const string Team = "Teams"; + public const string TeamPointDeduction = "TeamPointDeductions"; + public const string TeamStaff = "TeamStaffs"; + public const string TeamTournamentRegistration = "TeamTournamentRegistrations"; + public const string Tournament = "Tournaments"; + public const string User = "Users"; + public const string Venue = "Venues"; + } + + public static class Indexes + { + public const string UniqueStageNameDivision = "CONSTRAINT_UNIQUE_STAGE_NAME_AND_DIVISIONID"; + } + + public static class CheckConstraints + { + public const string TournamentDeadlineBeforeStart = "CK_Tournament_DeadlineBeforeStart"; + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/IClub12DBContext.cs b/Club12-Backend/Infrastructure/Persistance/IClub12DBContext.cs new file mode 100644 index 0000000..04a24bd --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/IClub12DBContext.cs @@ -0,0 +1,49 @@ +using Domain.Entities.Models; + +using Microsoft.EntityFrameworkCore; + + +namespace Infrastructure.Persistance; + +/// +/// Defines the database context interface for the Club12 application, providing access to the DbSets representing its entities. +/// +public interface IClub12DBContext +{ + DbSet BackupRecords { get; } + + DbSet Teams { get; } + + DbSet Players { get; } + + DbSet Tournaments { get; } + + DbSet Divisions { get; } + + DbSet Matches { get; } + + /// + /// Best-of-N playoff series where each series groups the individual Match rows played between the same two teams. + /// + DbSet MatchSeries { get; } + + DbSet PlayersStatistics { get; } + + /// + /// A sanction's subject may be a player, a team, or a staff member as defined by PlayerSanction.SubjectType, despite the entity's name. + /// + DbSet PlayerSanctions { get; } + + DbSet Venues { get; } + + DbSet BlogPosts { get; } + + /// + /// Seasons, called Temporadas, group tournaments. + /// + DbSet Seasons { get; } + + DbSet Stages { get; } + + DbSet StageTeamMatches { get; } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Seeding/Assets/ficha-medica-generica.pdf b/Club12-Backend/Infrastructure/Persistance/Seeding/Assets/ficha-medica-generica.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ec1fcc01bad8a3f33211dbadcbf3958b986700b5 GIT binary patch literal 503180 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�)fu4}Zrjje=M5k&+{ zzDiTDZpnxDq79=A%X`_XRfG8OOOR|9EBmyc5l5>udL2{IQXW8%g?)`q>FfOAX z$D=@1opbhHd#$a4l*&&WL^XTMWk(4Zyjlar49V6n%#li7> z6Bf}YX-q5Hh)uC?&7{sex}=QCPDe$hG9=vW=VY9D#yb`cx7X3p4kL2I z$*RW!PF~D&-w0V-+$t_MAwSLoe3N1syd$} z(p^%ont7mAb_I#>RF*zp=+UBB?$Z)(8@)G#^8M__=zDV3dVZ9t@7i?)jIWp%%CAG2 z4P;iY#Wf*Do*=%U+pTf-pZ#Itk2*WI0*M2hsM;w1{@cdR4&osVG|)hwmzS>r4!-ki zh;luubQ#Ndv?d&69JZgSZq2-miYf+)=H$w&$lzf6xq;6BB=Ar`)&mT> z)p^_rz#1A}<-w`wq81evJ|Pei;^NL_Lo10j5BC3vt+ToLR6V${z;Z_^*aKz1eE9-! zUiW%1S|xvMA5p9KzAfN4U487EVbTwC!r{!HlT{rHej4X+Mlz!pHz4Lp0a2&k_U1QB z%jVMcDsRryni)b`f>RClv9GBbD}DX0=sf=Q&tNOT=?Jf!jlp&wq0sM^0X?16_j`R7 zWUjPQj_1C{lG|97?Pj+U)N!7xx+!S>h1aD1v)_w(bcbK%E=Pp(t-jaw1T56SXJXsb z^f)-U#wz>g&v3Tjg7-Wki43mM+Jvl2SpOsCezf%T^q|ygJHI7Si{7d7X zE$mK9p+0)l+10iA-M%~ietn)`Dq#uQ(h6-a^!I!cY~2%x&Y+94Gy3Eb0u2te!}ETd?fd*Qfq#rpTFR<>l$V?s&=jHE`(d=dA5v(Q_L|nS$T*%rWJ3cK6mH$ z`}zbvy=qh+?cQzecjvm)8N`mA94s>M8C56lyWwM^Rp;R$z9k(M9W8FvCkjRz7913B zazI&ThY>Bw$;r`oPYVdBhaQ7T@%8xNAg{wz$M^5wp}7&b-$n|%5WAzJL_t9TNyz~+ zvYNWOzK_m)ThZKyed=N)X2DyVoD}xl-o$NhGU4Xn=qa=tL+t7W(6qS;`hR^?2ZWf% zIByTn%#=9I58^!VKOyj}S{ZCYgs_#RrPutXH=yDbyT%9wDB_iBg1n8RV}HJt?xjng zfMtwJf%*>zzwWY3X#w!ns6&OfEpy5HrfqxbA*u1dQ14ci1_P+p};yKnu z3I$NRBSrZ67PsY_0!H|LYOVt!z-+*C7iuu6^d`S6f-B2^w&%rfuQJ zxVT|mZe-*8_%pOiJ=`G^zMe&RtxRKS_8Bv~;|rr1*J`y+bA zoqvoZ9A_DiyI|4pxoMCP9$p$1wQIwcB9mrP?b5@e=t~^Jd$Zq4y#T&=`zA5-@#EcC z4WOEijgB^@Z{dyvXSj2q+hAyHoQI19{ix@IJ-e`QS9iAv3riC21{xJdm=(eOx~8Z1 z16!`9wiZ%lkX-n9d4ce6fYgRYCDxyah=`1ol$htX8z3=soi1Lyh$9(&F37JQ^XU|1^4ryt@I##f4 z=h50Gk`(IH)YLtaklSTtX3j4zUcYvYq$XNl{)u;Gyn6@g;;JB4e30aK@8lE}R~8lk zVIJAP8!XMm`T1*#id@{>rQkc{m^Rx|#mxNsFGzUJPz^QRCg$cYhx3^r)1f$dVPJH0 zbZDqqo8SVboP~{K<2jaKIh@@EXawE95Q&WiO4#DU0u*XnTU(xBso<3|H9q3F#f@)} z7J~+1Vl8t<)eL42#xcFE+3ukd(#v((N@=i(eq1n70#TyUkL&P>5BZy|5!a+3U3!iBN zBO{Bfe|#Aj7#L0X1W`QreCWd3^`n!Wg-GlOiM!8LTc~9LOxkiD0%aUnBR&}f>$Cl( z5~fY(BKAX&zp?yNCchAZcyL0OkRF4T{A+Cu@@QYz_qbpP-$R0eAd4Lbow;WV+qF0M zeyBDVB^lY~bbfz9r=*^q9@y&%G-hBMlAuZlA3s03i?(q$#-8D%1G)4ufv^zM2*Oxf zZ%=P8qFkBSgY8XcNFU-KoC=nM$PqVl!tFiY8S+kaqMKzH#9RzbOhO|fBAUAn zeEPFcJ32i4BI16cQVfpnxVSiMJsZ0qT&&D}Nh}s{5Si55SXo%0@JA?wmMt6_4_tmh zGXk48I&Gk89XmFLCLekRrlzL&=*GsQSbI=-yNq7G44}+16R&+Mk8x>#Z>>W# z7?4m{K2apE{y%lQABDh}2a$=V-n@-j)E$!}7OFF&~tDm>* zpvyk1Q{q;L01m2lIbXoI`Vs;@#AnvC-diaz&;Qud=3yChDXIb){(e4>&m z(p<1Ia((8*Mq#CNGk+$#)Cc`3^2|T}W!W$IPn^#ijX%8JCOv)1a_Y`MffP+N1Rdjt z6D>+(rLR{FFTJeII{&k;^ryDYh1VAsU-OpP)vH25mjqTl@NH;F@!%nT?}%`Knm91< zkbUKlaT26|xUWshr>c|@`_bh_$5gUzKb~@TTiY#d?US^$u8273Q6X_fF0gN(_nSAT z9CE*Wxnf{21hg7~pk90f4G_OAQn64K%ziK^zif0SH9TAsTUfu<8Fr%ZRu?piKkow%&+1VmCgI6IIG%{igr~v=l&f5A-{|)80Ol;u8AeWMq zF(9!s_zP&*&Yy>q%j+%zp+*@p!KkPxFmm!Od-a7}k*&vH52fg&Fw)nzMg9ifo~vAE zXJ^p)PBN)swQ_FuG`^`2Iaj=typuP%Io5Uab39Mh}Z+Ht;NZ3F&|i$mM4>+|OyOAiqM z(B=&d3VPt^7<0j95L6rU&_Yi(Nnp~DTRx8@;y^`&|EfQa=+h316RamRA*EzwBE!Q; znuPrP_VRM1$9z-kEoa)0n?kCKD3(QjX(b`#jvm` z#)!K^tAv1_6N!G<)cEOJFyug#$B7K~!ZdDU@Y3>fa%*Z_oFPI(&-`U1=4h&va!3mB z^SgmF8AK!_Cx>LcqZT@zUSz*>)|?Tl`rkF)y(Cf{kU}T%?%e^14DnN@6w-Nglb;WO z9E}*D7o(WZJ^S-#b!}}gy4qNT>FHKjwJ|X<#l>PI^RQ3p#$ja3oMjr^8X6w6I$C$` z6vXteGPv{0TzvR6>Q&EN45c!mYR0o=*NYE-O;Va${P^TXb#U)?O1)+X*;TgKN8hXm z_8HVsN=V4lkWcx}e5$U!wS97Y_$xchy_A&2oSfG_WP3_OprJkS^s9TuhYv2;phzIx z#659d;T{`#wl+7TCwBU<51s&49gcDdW=Wi>;bCFQtAe&D&42-7nL_@D$kQ;@-x6A1 zU!PuD+K-eL0n>uF2q*koDPL&Vjvn~dzrB)==;WY^*XQHsA9mOu_d|_c{r#}u_-pjj zAd@dG)n%a~B~Q|K4iS|#V5?#A^N`O~5DymS=8}?1+a1ve{ zKRSiyf(kPsR?pP*+Q<)Mqz>oM#g{Wfl7@p!@3$IZdE|)iH_!uct!zb|C`*qVc_riL zePW?ctEa>bFk?YMSTr;&ooTglyr7-GVjQEJ2UM8W&zgo#&5l7jN>E6HJx3zJY$isg6^JS#~rhWsyx>$gumumF7gUN!b|9xc4 z(fcU+k?%f_u=GvG(}}shmIrg4-bCFyZSv&FYo>D!0ulcP=${ZuwMpE~PtOt;Zad<< ztC?7D7?i|0*U|l8O_gnKQrj8J$q@ea%nu2n{y=jdHwKC_WtNYgJ08ehQU4{^ zF&sXuucxP?q0!OX%X!!b&kBeY$e98KbnoKhjo=5K?8s$EsvmJUPgvB`m^xIXpzT)DY*OjuMbBo9UNAWPN?3vVaOYP>C^?p4Q$=y z^auns_&vnbtFusnrUe2mUW#q?&P&;IIH5=Mh+`c% z$2c5`%4+H)4he^5*6=ZtB%A^GG+5K$M;1_oAWH)48SF(t$z>dy!^841;$mmdN^*tz zUVS?^XAOxK;5&E;3=G_LnZSvQ;~Cj#a4N~_Zb!V#kVWpFe%|M`n`@Pa4)F#))xGU9 z`svRP_YaSp3NAk-$Ub5BSKS%K2N6QnMJy3<`hO<2n)ZG2wBNTaaPr%D%1*z#Q(>X$ z=TDoIeETdf3wWMd&5V6bZIhtUPRBB*zfz*M>$AoCh3${tZK<3e-HS@oC79384@3y) zvI>>_F|9nuJ~;0L{0mPPDGH*jZoXv{q=Py-^a}iqEiJKgm zj}j~qv=>)bP56SYp3%%RN1mXG)6~NQcSD?<0&s->aRRRz6g+UYNy=qpX0E`~Y;6^| zaKQ<=7eXpv=)yvrL7fQlNXxJcWCG}O^YV0Z&5laFo12?Mz6a)b3zkS{Cy8%jX>J}@ z=Y-Hm!k9o8!~elDfEg1{LB5uSMH;vx#l>65;6XUd1$>Nh@5W~^#T^jB!4*V#CoCcY zz6qy5gsI1tzOq@Nn_3W(I zF1iXjRZ}}2dDTlGL`v-R46c^8w>N5jD%3mDn{9fiEVWIKqPSc`alPWYa(bz=d2>_P z`Rlsw_Cq#u54U`@}1sDTbLq*2UJSqHTOtwN^PvK?;VRe z&0x5drRz6Ey_fyBE0cw(=|pK(pr>jlp{2Wf;}z}H!~~67leZfQn)KL%1J7Av=0d(K zO;8B15z4x{tvh$7IXOe!+;rY>NY>@YgHk*Teqh2-obswm9dd)b$av(iJu&gZyaOTUKLSfF5a`t z+3Ym)Kb-DkQPaN1W_5~%XW_tmv~*^MD_ow9+PAA4>yq5fL&w(ET)82$HZrPY&G<>y zqp5T(8F`y?_7s;ITZnR<-=u#j1|1ik^=;7wLcKDZiBcP*ha7WpVC`p0n}j^uc3Xbiz~_M!rRb&Qdzt zWskbY>bXzc>$_b8vc2c2LJqpJDbHXVk0+g5yhGc2kNn~qTBXk3Ici*gy{1n+TwrKN zg~yD(X=JG(vX*C-M?T3*66@RKehS!`^$7}NtFuZ6$ctk_+Hkr^%n4Y3x(?3iA{n~<4(vt~$M>(RXOA3K^jnvqG|%Yp*$kB6S#2xeF69ypt?3QiUZ zz0wOQp3~QteOizk92yzvwld|+%uHFv*e$XBHRy=gDA{gC145;Gox1kR2w~T|*}fNO z*aj5l|_pY$BldoH|2Z?`n)_mv=$V<7 z4O?l+>?_RnnClu5s0=}b*AOjjKs@xH2ddiH0L z-AI*g(9eu)t#JxNuB}H|?y(%xJG?bARi<3VI~TZWhvX>>tluOb{2y;!jk10Qv(t%z zPotg$L^%g|=%gm(X((6(+BT}H>w+X8Q_W)Es}j4!&yW8e2=G6UNmeomJIfw&UFxmn zlQR$cOJ*h3@6BA2Y~6e$e>L4`Rn%@O${YM_*P4>{<3o)*_Z~&m`zS6RFJBr7-36ig z{re+_uRV<&3fjc?uv0jFql+~eQ`q|xsj~X?40yPirO_+rPD`}Pf(Ka`&+|!Zu zLWTAo-woF=ZjfwSTd3cqaNbgHcKwwu%=sa<#ZN$bf-Saru-;jF-*{Y0e!YF4m35u1w#5E^eN(*e-kGmo>vSyNe0D1RR=30|&?k5vAqX`-pcq-T26pde3 zT+;|s{qLg|5Gs{@Tf)AXRehGJ3Q71z6+!y24AO~Ds=nsP-npZG=ZW;3V9O>$fa?v@`v$w{W+?ybK(o;Y6GzWK-!5xolvcl^pu45# z(xj^H+99U2HmmRFJ*|K{9D46LZ$kr5!|`0h9K!ZJ@?Eu!3AZB%w<8h^DvtZL@;99@ zNKG?(n`R`aSS>G8t!+}XFn_y0pq?YX}hr7B4H-j}ALOg~DUhij8AGsO6uRQL* zUP{gAZg1RqTv}<1VRd>-M{8sB$w7-;dqh?h=D*rME^l0X(9B}=)nF`QVCAAV|J?U8 zb-nh-C?Y<(ZwJI=AzS^uM0mdQ(8#HGN(M*^Hoi?KzE31t^4xY0EN3y&Z_;?1OiVYo z;yoE_$|J3+d%aoK^?BgR@go+Tu{H)Z6#)Va5^eflI%b*u@5~3SR9)-pmx#}Gc)8Mb z%|P9Zm;HFTL3n3zL1fd)%7=IFUTkd~V^KXE>G!~C)wGrS$+qJEwZkg14 z+J?=eRdtUERXR_6Tj(hWH!3gtUW{6z-g4q;(N}am;(CzHvYM*=KUUHo?bbAMlAV2V zn~w8k9xjvX5wS}G@P&yke=gY2r2RJDbRnbC^f__mxTFQAYE&lEP(_5&$@_vj)#ig8 zH(!;fg~w&DgxlPYZq(U&zOr5^oD$Qh!>8$5mOa!a&gU@Q9KlXwys=Z_Ip1(uk5jj+ zPv=E+$$a5h$)F+2Gg001&o7pplfJq?GGgRR zn#(o6mh1g>S5xZ{S|U6b&z`taeC|8sr6 zH@D3FaWk{y{*64xV>7Sb&k4*My+K#YXds}FBT#E!9x?G;ppgboNUL)$&O%VdbwX8` zJ%*;#cH!}COk<{-%^+cn+0w(~OZhP-a}U!m3CEbtoi3X+`wpe)v}AfnnfyFBl=tyX z@qA%sh-!|YB2Bj5LQ*bS_H^2@fcM&64@#48MZ|=1&*p@9GCO76Qhs(Q8q$m33%aGh z8u6Z;a}^UWcW_Y2G^#&zrU1ij`kAc|PyNd4!JB zfX<@Xr>kG-M85vZyfxJ!4yFkW(b(+u&cA;~M`lc}{mmZJ+PS&#*pFK=$77i_vYn~x zqo7T9$`K1sp~lhHd53hrd}pKATPZ_Rx{ZAc|F*3h%T0}L?^$@A(7G5aq+rNFQ*JA2 z*J!Nj${D~(RBLcpd>XpEVfFqlJq3}s+2evu%r_0=2@L^0P60bkd;F)$Y5(f{WVu00 ztw3w&y`+f%moOq(P?`#%>d+0}i!^d0Ath64hrK+DoH|^m+RpyB`8HR7godU&PsE?* z3nJzvG+s~`vWZ|4#(xI#OX#e>t#qSyFp%Qs_Ek3z3x!y?vgmYt<5Czp>T4t?B{wPO%#fMOgfnre!gw=;C3w! zdro-nP;1D~q_hSVEpf$ZSBqSuuF3xUDctS1@*C)l#2tbH+meh7S`_$OL+&MUs;rDR ztL8~hygTo*OEvZ<`>Vh}TfMOejqg5cQ}R@00p7wHE&^Tuk;JwSwyNK>*y`>#rU<4Q zCA)1*xw(-vQuN5PRF%SLJ!EkgXZGIhsO6!GQo5Y&P_jiKLO4--QKT+}VY#X2Fhya8 zh`?$^z~;Y3d&^Gt7v*Q?F5ATXH+|w)?llo%u3u~JE8M%E?R*)rBNU!I+0)*=QBeQi zBnZ*Hj)xDIsN@ZZu9z<{WV%)Juy1dUy$%YhaK}(Qd=1RsOHNtL$XZ{pF?qG~)X3kK zM{S_5_(0|_n;5un;LFK;roYH!t=Ex{-to%XG`B8KO`eqR^!VUmLaly zu&XdznX2E;t)z|~Frp~ilpu7jjD&t%Sg3IS@cV@Z=70X2FnyOs4(03-!djS~c-a?C z@w;L9G)(Km*Dl*F4vAyzD8A-s&xFbfiwN5uKK!M(hl@`9`AE}`vSZqJu4iQ8^WiS5qg|Lq5cpPNjkzjp0ewsf-Pm)5P?c*&|Edgy<>#K90| z+sciXrq4^33V!y^4PPRBfz$7QZ?Is<{au+SYw~RU$j9%)TSxz|zhqbG9GDz%RagA@ z`Gq`c6=$EMEUN$Q`>fo7`MIJqF8@8d|7UK({|m3OIW`ekT_h~>K`Lshsl6Y=aq%^0 z==}SBwaC1#H4f_u2&yiU?~|847wY)?6Np}h|IE;P$Ff(k=htuah01>(^q~yhBTrmY z%hYcC9aQIbV=6^JQ6ISyh2d`+oYUZG$8pHoH+T z8Yxk@rK=nkQ>rRaswPob8^bYIUwY58vt(!3e{=KaWK0(ZX@4?!Ot$Cjc&HKF9t7zF z)cGE!s{z<*Y&=cv>+SYT_8d_5gk>19bWDceFF>7~bAZ+O`%^fKfVcoj7Jexn6t>=u z4tP!Nt$2)oMMnxuE+B!R643`hh`*UM7zp?m^0(kE(2gz4%m&+-v6DczqrW)-?j=!K z)@Z;|!N|+&XSeyI&O7(b%m5U7RikZ+k`R9be1cv$z`u!s6!VPaWFLyVT3WQL51WTT z!}H1qD%aWk*`50OseiCXA&LQ7CjRc<+UW7q02^)9sSiAH6OVC-gAqaf7&LB_b`elt zkf7Sw+q$|EzkJ|_UOy)B?i^5|oP$@08Gx4Y5`~3pp?m%W?GIkeGLd-=&{A}C$hJ$m zx{4GT%gF4zBdT_HMjFgs`~w~NLQ?i8%V&zz0Trs9RIX-Q&?b!a&%m9tvPw@& zGdlQbZ1KsJ(~d@gvMoToY}F>H`1$#hQZY(tuKN+`NgN|tb_IbM1jbDEp8zTEBkeeoRaWaphPTGwgy*#Py@dQ`vJ{pak!STswUJ#3Xw))pzY>$KN74u zH~>Nw2XoWL6o6w|8XD+B4IZSUi!y;H+0XRSmHByAPR^UjpB;V!`{+AB z_&L1XuFC)#Tw;vJ2XHqYKSuj~@5(6<1kixRYC})Ir@+Rm_o*5iSVRJRd}x{)l(FU? zNApLJm$#|889;rITzt>bbB8POu5gk8&<7)V3fMT!&DkG5M7Wk<*}vBTxPKM{vB zTxdVd0qF+rYGqIlYR;PXe#p(qQHvLr3AS^HA|dA^BPw^)xk5>%Q7T$zao2 zTGUTos0acr_KKFOg1r3ZWJ>zDy63Ml2H1GWOJHRI`=r!VqW1lUFMv}gZ8t#PJH&V> zQ$t%>IRhTOGQlfFvnA{-EdTg^JMiSor%!jow)A5aTwPtw%qFo^coZb8>$D`jrt- zx;7d)RS(J$Tdi3SuXc&snhLEh+^^sPO!p@i5NL4n*PyI|sg4>OCI@V6^zYR1)PS)? zAR~}g2Q1Wh$@uQKaUeuc`=-2mhb8%qr$%>Yy&c^ow!^s}NMd|SitPl2$Y;L3oo~hW zSCi23>V)Lv%I!^PFu}r2IY|yO5*^)Eoycs1Fa|>)$lkbk2+dQMmXe}^0yWT@OC4Z{ zYy04Vp*n21^*sgo_}DRisIG5FN0gKqCp0@DwlO(e>QQ zEiLuL3c&hOQ=_u~M@~T@e!4#FJc&eyj(&iT&)*aj+x<>cgqb~>hYXk|(uV7=IC03!fSyr8qt5E`4CAu6u{|G(`eb;7WBlWM>@ zi*tB^6Y8A=?THg4j~NhZ1qDOW8fLn3eVd+XaB#7)-9|G@ti-BcTzEg++)XIAK`Kd- zjDXgo(2kjI@$Vx{CnY>yuSD?rqoMkOh7J9l>({TVs@g$T@3}d|07g!2ZDdQ%d}!(B z$Ylo+c%;Pg2BC#n#$#2y9Fm>A2np+%i`YdZ$4oYOXPX>{e9{D*HqvcFJb3<`WW0S( z2JI5U^AABo#|h3O!RqGb=AfM-9Gsx0CeiA~qFw#d_zRN5 z{a4!W#5bPj>FL~eJV+!1?qJNg$RG~|14S++At4XsMVR`PfBg!&nFaW7AY~Fys>3G% zT+G9jXtA&ql8Am+uWH|ft4t3p*G;SOG)q%2f_6X9zmF86O2dda2aXu^eT$wEy<%r7g$>1Yf#680(5Ur7FGqZy)Dh<=p(!g;}oGnI+XT-Kp&BvudC9)1| zJCO04dwS$caiY%8&o9MqVeMc!#N9B3o5{_4;K6nD^}SbSG2}%buv1;vT>w(%V5L4HuKO9q^7#e-vpu9FxXvFqnB+fAmq!FAdA<2|NcVuTtF2}RSI%)9$<}=mEk-W+nCA*Ik_*}BtVxB0}^Y;N`Ttb3ePzRYkY|8OUpG}@wXgEKokwZW&u>SGnn9iG2 z#G-FHE1)q!pG>(>7sRSszhVNY0w;gR<}P`5EO1f+aF){ z1Y5pEh95wO^!$zIc}W$cVERxwl3DwbldSB1t*F>KEAEeTU$C6vAL0U+WP)A-f+vOv zSu%zbo;4hs#YqqDib1~ot`*a2P?NC%drB-YD{3+vUiSe30il(KXflErW1y9aEt#~f z4O$ACwrG^MwFx?v7SNa>Fi(BrD%4i=yNZ^(-?L|f#z~sl#+8P=YXO|>wU9ch`=9}y#Q~4h zERPIKlFhZnASTdbCaz}Cu$?*cZ`Wu6qh4eRy%hmT{ewry8-jy^GOP}CZQl2BPXZzrHpFKmi^)fQDo0J#7Ik286vE_3jZTDZF!Azhb zAS)zU8{v|ROq-dG9Sa>Fzn2&9Giuk++#LIvyQh#mnI2`@Iw;J1WOgvzL}~Wq%4M3# z%MICic?mjp2vRTP;z1B%qj^SA5*m6e3Pc|!;fGdeGFn^nxPIMECR;{ICjLDS`I0fE zKWG-{E1Eu_t-X8cn!@$#KVK<{pGVI8o4SmA0fcl9HO5U98aeD(6BC@*V-MWac!Lmz z(b~>Ghr|>UEM^R5tC<63&FLb{f6hE*%^jzsQQKNLUw*F`TZUlr{Wza*X2F@=dyyUq57nB*?<~~=* z;m4z{*t78XWhf519-AFcgPGMQBDD>b+@W*Us3 z;MLPRE%KBgy4quhLtqi(Lc>o#a{b_4Lpm8ge%vIvKwZC1RL-DzCcTvO3n~WCEES`g z=?w^P3G@+_!`)V;H(981xWPR@#J;~bEHaXriK$8C3R^IY&K+*aB10R`znOT)Z|G5KC22lhiNH>wPbxEYBjd^r5 zFH%pu^QifWQRzdU zKyVH4+&n8Z2Xhq)33@YY0vjtUvGO=ZSKv&k2|JJMR}+c3Yr?Hm4LvKXnBd^$^c+c> z*}3?a#H(i-m5h_Xpi?HWA>AJqHz)^{Ez`iaBeg?uU`tBd$@3|9Z&u~_-Q z_RHvi$S0t}ASJYHX~aMmdA(}9S(-8nvQ-5iCVqZ3rdJq?M$H&O@Sn^^tymdL{`zcSpRi_OfZJz*YrW zw0>;1Gkfgv`H--pw1!a;bER1c47(UTTXxOefYo= zB&uLA@jL$&NrXwaOk#RM0>Svf=2ElLO%Ho;|O@e$7aCw&5dDPVL6 zWd}ybkcLfoI0g~N4Y!ulJ?U^VVCSzD^>2~NH&X6{eGW68VJvxY0UNkLYK3>mc*aQ) zhv!^J&B#fWJEol7lxEG#&u`9IgsKG}r)ORn_}Bx@Cc1s%Ys0UQzT3`9lLGL6z?Ng|vXAdoQtJ+qJ%{_S6u3E1*!N2vAt-~h7%#*uoHQ( z^BO=4h4Bi-f%5m|#z`2+7||>4n|3LS#td)DuLtR{5Y=m5d2B3n#|83;w&9xHscneFdF%!KMLk(xSL?VU2qy^Js*kV<)yFlAAtdfPb-uc~BT(8aHZGDH$lW6AOj zQ*!YLNn%`A7VU7zt&w#rQ&VVFYIL_3j$tpFUB_bKS5r_@3gA^UFo=zbVNej26;gj& zoeTEbfr+Up`9K`1A z@45;>|KHePxOrgze#l@dW<7UY33|98rUkV;G|zNeIy=Kje-tJqJ*^)D*mX-yOjHJs)s&TSib_rY)icW17CSmZ`|(61u>_?O39V*;IbVYFjv6o3 z&F9cZ!AweVkFl#sZ>eVtJw7g^U=VANIe*s3K zX=+xsJp&jEN83WXNL5nO3zK}@JgU3NY2Nx$1_oyps^$?2kjB7XbzuAt;XGjKR5J+S zm^p}`z$SvZ2*b7)jgpWNaGyK(n2Yijq#>Aia8`ZWKly zU)GF2yRrlH1(S$O@7YjcgL4As6n z?4$W}Vx*b341Zgd`jjU9iB)F`8Zp23ALNfJ!P>Oi=(JR8nMUGTTUO@v<&YG~)&9pU z_WV)1vSicj6_pL&S2j3(b5 zQ;BnxR&D=^>6b8Vi`w33FWnYE5#D zCf?ud*?#TCM*&+9du@pT6;FPpbWwK0i12 zX6L_`PQ96tYzb>^Y`omc6H##&Cxq9S#>~_dGC+HXJTuj_(U2%W$aB0`!x!C_zEEO6*a7P=D3_v>DyjEs$CE6_{6ov1x}U{5W@d04E%>R=`#9TLN)2xwj6 zHpT$9i^)FW_t_l^nZ$Cefg?QQM3H?7f3!)vojVDV`qXoB+d?-uX0k1kq45DIX7ai; z4b;=pSFbjBp2bK~q0?{qvOq;&x4@{wE5xU`A{BMXs_JSi2J?q>O)nX*cicY({9{J9 zW*MLfrh#D!gxKV^GWtplcAE`Q7c-yj0WZp=&{ODETJ0mDHo7l}Ao&9e0hTu8NuoFl356d1llB^va_Bc{ zYHO!v?TL`${0-F#va|b|MgR3^0QwRHap2MjuwE$*E#*P4U%SBuQ9_~=*yCew@BR<= zDG-6g*Iqdj2$cF9Gqd-TCrE%!V(=rhkryvIhv`W3_enrej3f$^Y>XDHXUqURBd>v7 zz`WO2R+Z`#TO9@#xI(D!ob2iMi{s^eJE-x?Yx&0BK=h0vI*B0gYtoy)@ zU~d5!cNJeNE8=#e@({S9P;;F#&4tgta9E4Gzn>I7_EHmWEv^HV7On$t*kj7|6qB96 zTz>_(2(Ca`w1kcvKD>8UWZKEyU930J^t+d23dRCPioL|BY-Z*$D2>tRLnoFbQvkSw zG^N9UHw+*)w4w_OsQc+@^*U0X99T(uiKf!iZ-Y1%feXn@x4dxNh~E&0pbPyuP_3h( z7sVy?s9pJq71c-)4HZLmb#;Hg1WEaT9}*4 zZhT!H2E5eM{^CG1KG6Fy5xy$qW@G8TTl zj=`O5050-B1#}6p{jujQ&cO&K_qhnk65;$gwkI^Up*Ize9M}JA&Zzs&h6rGjG9^Pr85MPO%?;t7qc?Mq)@9y3jGyY9fco_3HYO#O* zqc4ljlfHHOH-GBW5z7CMzM(9+^zGw>{O2#;*SAFz8m5l?4^!eLZ+L>(pU!IYGt8Fe6{BuM8zyW>IZT|~5;1h{F6p+yH-l>?-fSI_9h)Qv3y zpzSDgf&-1E9sngEZQOMuCDW!fWO%s*V!pb4vZ>Y@yjzQrL&|wzhj7?i4%Q#!vX3rM>_6CBdTyQY1 z1Tc3&z}(sp#6Zg~f0Wg6On>h;$k+WwFzB}$-4G<6hfvAfnQqNJ8 zs}oj2S|71of{(>35%IZ_Me~tO<>Ulygu`hV=@rV2y`fjn1ojlW3Za|9#q~Ze?(r61 zFVwoBp%o>isAy5GcH}cBr>4q7PedslE*}E>Snkt-vK>mtNdjrvb8!7<^EXg!p(QhS~eT*5v?HjDrVD%1fdLW&(Hg^#_q{qNi5o&U5x`8K7{i zb}dro4Rjhh-K90^!-of96g@pX9S>RJ#O$o@Z%%|A9yj_~KHv0b+VHrL0ipe{x6}nh z3%o({VUm>*&I07txRs8Mt3YYdF1FqoM2DUvE&le6@xp~)m{w+OZ9TWLLa)t;rXLnQ znoLt?4?H;|DAT#p`+NzOPQgX!10z6sAnb#-)e-m>4a(}S8 zwB+RCV%e3$dhud-Q7xJc@Jw95!Kn7F7kxt%J)q`LGKf#XodMQAfwEVXkR5=MBu=C{ zejJ#OrInTE;;2HGqJ_mY`WBel*daOna?Amwf9IK6?D*^~1dC7)0!!*g3k==5X}mi)L&0+UTN98l zySuq{ylBFs4hb2ZnW@`yc?4PtI6l_9)bVHl!%eK4!14a^lP6MFuf9g_{>+&xBT`SU z5HYPIHZ~T^I5BYyL)q(lg@38+4DouVryCMKA~TwLsqM}r}6Sn^6y!cxl}xI|=z;Dcm- z&PLwE%{_~;a%$Xu{{CQP7-4kfYOU6-TX{f9kyIwDA+(Xac7}()ZfTQKP$YappR>@i zSJ+Yyl^f<2k%($-ZNRI5Q0PIdgc2Q2%9vG%)q)Xf!n3%y4+J51CT+K=sn>u{`QCH< z%0(XM2_OTTXi8vUWaK7Z2FlW-M~w^&%G`TuYtyi|qC1eULWT^maPOWyQ5Wru(IkZ6 zm8uVZawvQ|N?Bne)YaG+xMc;39V)+D@3jF9U--2_?m~%Q$@E;Jq!%rKs1)m{`CCtW zd+5uTS$OoghuMsHqIESjYMuV_OlVJ|3!^6?20sMsR5UWGx8w_uSYunk<3C~H0mwwT zfuzafNXy`GgSo>@uNq^1A~ka3d`Wj zQK1(yK8}&tPAHgOiis-vMKbyOFHHu>!I2 z{5&>1G>>SvA&CZba>F0h2c8CeZs5yKS*HxU6FBTJY)+E=1Niu2>x*IIfLnx_?TxX6cpmvjlkjDtCR;HXB!(1b{LX7Ba0Z|kcXH` zr~MGE6^y;tE_MlaAI9`;yi=kCBlaQQ9wWI!sz(mQqnUwqvu-^zF=1nGkA1F+{#mzS zf-(yzC#A4FftkX(8(uLXJ=Qc4ham~|QYv*>eg|6z1YK6dNp$bfqgp?LE-dn1q|OtC zH@sdhHk^9zNKDth%eSvoV+{P?lxXn?S&!qIz=@-6;~sq(1{t0ZK!#Yu-?^ zVD-Y-9VAMt_W-^QFnQ@_zI#`NRx>o*+psc$ubsoY%#@A-n^-8g(dFKd)z*k3@Uh zI3*lrdMd{jCx+s|Q~BI&4H(aSdK|Dc;yMoEzA1zU9Pnsxnm6-2X@F0`ItxZL_TWX) zHFFL6>#x5+ajUCUMp=c&+#>4^E*%Ox&{iV0Mqi(SEo3Er5M5rNn6a@2=H?t5#)(n! z?hLR?}Z4p%g-n-s5|dx>-a zIY3Gd1lsEn}9T1 zsNYxa++l9+bUeVBgNTpj7;UhXe(BVH3JTJAq4f0G7qvk^!x0f-*kr7W!8%PXEg*4( za4n-p3@`_|Ut>I&vga{nXmBtKo@6Ay0AS8S7cZ{C#%e0`B0O2p2gHh*#OEvMe+*4C zHej@<^9>H_W)n5wCSZV#r+>s6aCB6XlY6^>PMlTm2LWE*m+ry=6<}-igW)~S*^Fmz zP(iUS0ym4<;bAomjqhpo-wm_S7C|fnyA(5BzPGjp*Sfoj_t(|IPXk3uz_vA-8{J)9 z@NaLJL9S)*=*SF9$ljuSWHE}*w7Dc;xQ3gt*OBn&!yVF889*9Sv$Hs6KiUlK-EfkV zd*cu{Dfk1qU+Jpu6$Zw&4vAm;yia%j3jyJ}8YSY94+U`DI55q=G5 zPt{+-QwvVF0b33pKGM?V1DqIuie70!VxkOD`Q!!bQ=+17bHXUvfcT-T#S*;^WwqFG zxc41DE*rCh)#T;7RYoZRi}lDWFTA}gFeDv9?vBw7#0G#yTAG@fG!Wmv2amyh-G)(6 za?jqqwnDIogrSo3w7w|46@ospaIfnWZ{A|`3I5hYuZ^dJ$Qo~TnnhB@$ppWGY&9PW z9-vnXdsg$22q7+exsETdu2y{g$^r8laRyjM!E-{;Fc4WMFy11JUV>MI%QAN1l0^E53U8IoOes?)cbd%gC1uUrU`X)2Z&@wEO|5hA78&di3;2@Un@{H$92`x zC1zpG4M&)nnF;U|&>0#Jz-s`phs`@5thMv!17FtR>;Z8Coq2EXeZ_;IXi8qWg6Ued zd5lOz@KHe_@%Hx4reOo~@%tBGGN&=ZrHnv6MSP!@2IR|F!r?pib8=2xaxzBeNFqS@ zTxAWdhMf`|6+9N6eojw?H3;%IuWmb5Ns`ancN0vaN01D*+7Cj|(BR;>5@~_2zC(6G zTGqf~I(KfVt24mu04X5mWp}q#z9oxakRgq$M4(;r<^_?Gg++E`cKhysvmX&y_;`5U zFx@pP1KmYm;t0~Q5Ux92ApTld)VjCBw+JYLZEVODB8eq*GEN!udqk7|OhOrss{u-c zgT&l^5UC>A-6(cov74P8yDDPD63n#~my*H`(|dIN5LFHASYqly6Hg>~oH^`+#X?Gk zWl*GJ82dxNH7h#84?R5ph`+gckjMIRwnRs z-|pR4Wn_TX3t>bvyGpLfRo);u?s1^Ss-;oDL+}g1DvDTF=&;geg${UG2qRHlUq=>A zC+ege-zCi=84t6h@^T<{O^IiWSk|A_WBtU(=y-a1qBz6aMB@Q2S6Jc5iSbxEW`Dpz z5*O$=)p6Mq6toDinIc}lt{GcI4+AJ!q0Nx=Gb!N3s0on(!;1y!U4DM(0_z639-v?B zZ(KF5dE$cV*5ky}yf&t;6hTHkwI1r{Z1kd;xm7bs15U)G!>dXgc;qvg9=! z9YGzzlZP?YdJ>QfAxn8#Sy?3|5F+7+RSR~bG7A|QnJ9YRu&DvctoGrD3PE2+;U!$n z;8%dHTUVzD)Et=*Vo^J#GEIyui=<7dG3;bWXI}bOygKTGn`D&IF_`%f?>fN66crOQeFCx)@F!#<-~?1D!(hbc`E$emDeP`wjpDB`fX)-F zC?M>xF`C+`?{j{buhQqa$@yUOx>nZ}4ZX*Kf1JK_YWH7HxVmoK7BnKr?>E#F>C?)~ zGk2aX&Og#U4;B$;wX2e52uEj#`L%7kRpE-$mHw|T*pzQLFU|ea+t9n#H}CG5Lez^o z0TqHOOVIYV$AUOpFeZ0FG(nN(t!qWX(k7N5#ef|#)d4&ZJHvz9m15>we|JGC(MvfpFe+yA`6u_Zezp^*%ZP$kx1%y z@DwD!dGiU}@{|u4Vw#EQW!ADd`S%r#?SdZmfVUR%`MW$qIWIxIohBmw*X ze3MCmTtGx5C4q=o17nnyGo^PWJ{f1XyB+*z|5v|9zmp7L4uCC?&Y?6ow>gaYfAGq} z;`~3T`^vVey6@{lOG^mSiZn=fDyc|GgT%q0yE~<%1f)w;K%_-VxW}@{= z4YGO2+Sw97AHpsytjpKi4BO4`vaK2KeSEw_7khh{qjq4zfQ=1jmH%?a{(%9YWP&fHG``ExJ0kkT!Y$Oq!rzB(3$%(8blzhpz{ zysx+SJq2k;KR5+Gq*8=1p)bn-R%oJvg4*i?Q86*=tE<-~KJHmdAn$4^GNQjHW zH<{FZ$_)uz{sh|?Wd3v$(GyHX*Ru%{Ph$?Uf&PAIz5{u}ueP?=f1`o7FTf?QAp_ef zY~>F+r=gBS8I`-*H9R&3$7^ALu%R`~ix*Cel-DH5&IJfIZ*Q;z0LudZ8x<8y`KEOa z{Au1)69L==)Y-8l5E94~flC`CRlw>35;E79YycBJYqDvUyo8MeGM@_l+eB%Pyu4~5 z<%7LmvLEJ$$$Aj9Q-?w3xd|sS@PK_2#{9LaYSh5+^#ieY02*YdF~9;0aWs zCF4+I06_EnE}$BKa+6T9iHSYSQ3q-yoMb+0et}bEgymS$B%jV`5Ev;ArXMg1`un2K48y;L#%8g8z~OVoRRtL_YRAe z=AVHs+G<-0&4LPOAPH%{pBOCT%wLx5X`K?fNhItNYKg@hJ8J~;8(bPWz zgX3U(#hL&u+Q76~KvEKDZKB{Hq@e*FSMoL4;B1y1LPC{-zp`6$47jw2`{Ulu4gHenhL^fKwSOsUp01*oOcB(Ehk%X+Co8rwHi_!-ksW zu9`l1LB&KiI2j_hcU#Xp!DMNOKucYH9`K_CMG$vEAnV@hQxgSX$6S^G4-e25W~OUlGGa@XigaRlYicsh!(X9d{U$O}2_n>^N8J4U;$BCE6D%LKWI%cZv!ki1 z0GO{Z{P$@U`Iv|-Sw0&;Fo3xL5viLJC&bk3q|=! z$uMM-p`c{FBxhg*EzO|>?weenaF;w6dfwI-w zdX1i0S$(FXjew<+>xTrEDeyMG&)h&qb;y8%KaAY+_I6fyJRD(@lg(eeK&x$o7Qc@D zoV?Q6gPolULqC%Sue(88{-*z&J&fvi$Ep(+U?321NQ<2>25G?Y^>eWFlbm0`%G18D z9T*9zqV^_c03{9qK5q)1=kM{!Nk~X#@R!!s{iL#-6JUZ0e7WqD_sNLm+-D1MH?H3`6zI^Mj80Ea4MRfItrh?j0E)E^En)jhUK24I zT3XXGT}wfMa9IepXmk@zP5#RZaLk7XRg09JzZtKZ?80~F4x-;BJ4Br=0n#>r9>r-1 z-~5KI!O}8CM1&Xuq_KZHz)X&wzNNq4)&^fdL}Yb&`PyP?s~Ifuf`WqP)Cc6~DChN2 z7qa;`gVbaIDm0e>C>JVb=+R>601pjXk^VQ2AS9_l(@I{*3gNXU4@g1A&cf2w)bzmS z@93BR$W_jwnjY&hq4fOk)bo3P{Ft{F23i~3E^+uv2Zx`4;eh%B#t$|h;CF+-t-Blh zM=NA2@CSPbNCyWV@RDL?HUrngf{?)VdWc)_Vc4wzWwoc;b2kVB!}Blss4 zr|!c>0wH3>UKpmvVmSm8$|y4MN;}e!zH@U_YA_>6{2|D~fZk*;kK&|gUPQ(t#(s2s z%*DduAI*=>O|A?>2DX+bC)Kj_DX^R(+8t{Qa?~>o=2vQE%cEBpm-CZ|Z4Pq>uKEXy$Fs*2HR$+E-u1vqFHZ>$L zC>H2g(5m5j#FyYW(o(@IEZVfDDf>q1b2bv^H*9L0N$az-h@`JjQ^Cf+HVXqWYJwu< zCQ#G^sgQH4zX&7X3}F<}vh?8Xz2aE2GYXRyc46R$iIX`#-dAlaYW3tNjU_e!_0$@a zA&7{0L`1Mx-J6!E0yj+U zsMr86WEPeioHDTjq*3xc{ryZru45%%EMOQC($cOeC}a^8mDh6}PV3YJdHMO+oU>1$ zvE&<^zD4n{b(GK9{4`BSaadNstD|pRe4PLs(f*zu+(dF%v4Q=jgM{`r_!vVSamFF5 zs}=x3(qrT8=2TU=0rP?Scd)dHfiHy|*3-vtd72oxYYm?OB^=a5pEJv#}#W#KLY zKt=*<7C<0?7555XI68v;BhEjHmh*Hd_}Wv*awf@4@(i=4Qhv$KeweQbCW~q;@1ST5 zRUH9-(x7Jlk>6bl8xS^NVo0n3f+bAdKS7C;r~yda(ysfCE@%ZcH6c3*^)@F8OY;Y} zM$V)suFCKXKYDv#mg$1F7fKy{rdFa}VY0u%{)#(2iT)U5_{iS=j>>}v4?v^=^sMyI zbjYEgI&2H5L@*6fNvRiP7gWT=sEx|lMMUFuV2I&Dxo#~KzoCkdP7PcGKXq!2}wy{+GjzI+lr$GOHhf4q(c{G z3>clliE}Ry5TRojm-m+-G(FjAB?S0wu59NiO!to%FJCi(u`?znrlr#YtTgkIl&ma5 zT->454C00rSYME0z!f1{L2(dtEf7?#@@Q`0;|DVvM*()+q(=rLi=~|rR4@hE0Q@w0 zeF;nk0BUf8i6h;FfpE)Dd0$9~oUaZjPC_%T2X5LQ~KCzd*GTW$ch0>hTS|F8~yO^=v`RNvPZXgA&A2B z!maNl_``XND9Q@nFhBpho+YN)!PNsuK5y4TVDDUV?AB{A@eP5rP9_Tn_|p|T=>W_^ zR=X#J8qfqJGU@E5(O&=rKuZaBcH9YFEjBHvUIFhFz?85a+#5FrouRWw7W@P=`71zV zMP1h55DN?qILm-%Cv%A}lu0|=+kuwS6t1YMN?7Oz$R?&vD5Pt1{_G%xuomQ~ey_r3Q7w zc_K)~V2Oi7_Ury4VVpJq6!)DyU0nqk8H0P4I@;TF^778k&PZY*{t&o+~GEs&0_l$+#o!6w-0 z5&h8k_+z7!K-?~vzl*4UA+A7H2DvuCmH?>8hLa^I0#6dl&VVT%06A3^l@>Gzp>qe1 znc{ctXQSMlo$&|>TYWUyMqnqKSOVfLB>C0af>Khd;x~CecW_F_!<>RjodDlbSJ#bR zd_cvK>LnutI{BJ#LhM|C)vKkc35DsAJ!bPvZvnvRyYD*#KgZnM9En6!zK3JJWB=6% z6d*u)EXxiBez;XxDv+^mtgmZWT?h44i6E$_uB`=%?YP$g@}WByTsHuz2#h;C$Mrh2 zTED(M0{Yk+M^Zol@Rl_0QzslL;_#Zgr$@)eZvWhYw~}r1Bu8CYMMZi)Krx4Y zh6&{yd(4m|gZNvE)n(0`a{l!7;ru%Hu(5%4=;X8yk43CD$_yfqswy46qBr1!OZ8xG zOwsioxGuw?2(~pyX5c?$7hq+oV>-F2TXp!yY@8UN*)%+gZ2r=!q5 zMX+5B(eXM^6-<@?y0W`K#yGwaa~nA>>chM%ILmRJO#`>a6@K4y&-kaUdyW zy|++-RV-qCq*X|spy=Y`dgBKA{KR|pd`YPd{7Rmtg~->RzHUs3+G8DlFFtCrbl4uKRA$**U^m}i??O}iX* zOZTg_Dtc)?DkVL~G1feF%3SLv<>GgQe@1Yr-=Fw@R|-?lM{mNhHhcYgg`JI*w2~>Zo z@z8tZ&WSKZs3EwEzsy+}jpck$oAu3}yuG`k_{B=>ZHV+8PP7zjqNDh_Xq0?S392K3 z+!DXSpqN8Ln)BxDxeCiScdv)QVSQ(V>gZXf*)7sb-5{}h1(-vuItoBZ)tQTTr9$*` zaCD72yk4GrSK3ua*sxRWCUo{^dIP_)r62&Quaw=6Kw|rgaO(C>PWY)Cpjoo)2;XC^ zc|I^xV|TuyZ~nRR$QQxEJy)55;;Yau!4O52_Pt~=U9P=vR)tbeW>;(k_uZYFIx+u3 ziu)Q~v&&_xb!6V}IK!brL<$OhD6O*F;JR{Y@w#uVcCu!b)%@^~QtL*WOHb2m+s4L} zsqr<^ft_Ep(72Xh%>?b15`^tg@-@m;#^5vlnSNznur@auq{6*Kxh;`w4Sm(Kg zpOcAW70FfpVg}GeHrB7#&FNInDJk`j@y>G({keBAeDLM^F|Xu~O5DmU*TuqpxbcJK z-1T0r{H{MI3>Es*utsU=>Pis7G1-jX#B@iv_QThUav7uJ4bvb63LQs%#6g+V)br8%ZKUCBsq5w%pvpCgx@V>#B85Q3cl@W32h)=*)lDAzPMrdn*CQ ze|Pq#ezm-T3ngXk)oDOa;xxyOLZP>_oKwkzbx8?yh^pFnyNWx^5?Cw22G8!kA4{ zQY;ZCBr&QtnqNeExt7ECa-DrcB>OnAAxty3Hz@xtYa$TZtt=s^4ha_4YrbbMbf%Ic zd}uww_YbUG^+a6Kd<>KDhSgwiaB{+3x@9w4rHSAOQONzaHT7%a?2p-2@-fG6Lk({< zUM^4H@RySJ3QRM|Nr(~%{a?M%5c(FJqVd5uAch=!rAJ%rrK4h-i^|^K6RDFTE(EEQ z;&2EISfZepZ~wqQXT8BU^H)f_8_ zt{cca{p+0o&hMC3`;I zFC&b77SQ}(sXJnvp6PW{BYuZA92#B>R+Q$p`)56k$*oF*A;OV%6te+%n*E@XWaKp_2mUj2hl4B zDHbTt|Mf7oXHt=uv}LuS{kez9ahSbLqx_pv@j+&26* zR~_;d<9J9xzD#Rer^HSSa2W4pms(@VR`kXk=~jy7`FUc5>GC_46V;8fDHV(+`{$0! z-+Z~<6>GV&@>D0}F~~MOTWJxK{FoPuVwh z(!TC#BdqDhdr;hlKK;Tt7m&pEuL*kc%hY&+asS+S<7$VRouA&$27jcP&if_TO6K$i z&DjQ`^mMwsklDCm@@v{F
y1bFsER>gsY@d*-$CZj_gD;2NjH-6{{EDcV9=c3PeL-_`CNNt!5p<-32AT4LjeC`unX8S3{Slpf z=~Q;h$rG!ziv@2r)qBs3$g*(rFt0VGS?sqUcR z%N!>4#8IvD73+ocbr}m`LQ;Lj_GIFMdE3!7Nn0HSR+G$%i{AG`o3s@hY4iuO3x@c7 zX01vFLcvJ6(~?Bm+w#MyY_Pb3gZbFw!6ULpih_d0+XXX`W>g0B$y`LC-(Ds?qm3rE zY3w?@A)?=<7bQXW_WAVD#mhPGo`uHikwm_7N_CMu%VpvjyR&SzlTe#rtUfR*&ce;h zy!O@Z&ZsZPQlNT8fzpgEXNp37@3TYF+E|X*VEm6!F0>oV0~xX^=Bsl81bM zh`R;Hm2U6O3?@ApzGYR??&jVt`*wNLM9p`W)YoT4efQ?gtKu7VWq*ofeKmAt_K$zd zJ06!7M7U}d!EtoozK~nie~N~WTyc~*Wgp<~?(Y1wv2ZPZ_2fkKU-`s7lZO$?d$KCn ze&(K>)RdEMynTJ~x}eI$RgYJfN&MJ2sOuk`S~gi{t$yqKZrpj0G5_|IB1t5B;GVddj&>SIuX~^RBbtBeP*`rM$D3~8G0YLW85Tfv14Ek+bNY44kgUG;TF!- z^0d@kx1`@+XVwCF^GFRmPpG zd4Gs#WnUDhQJ;GwW%%f)3`yj|kATY82E3imJN7c|sv%L{~Z7}jWZetBP)K(c05 zR`ny9vrsnUm4kHD$-(2letrlfiFBNwsu*I*ZSU`?`u(K$T&jZ6@7%focX52bc`92XQnqAVtsZ{;Dwj@l z;P!BgwwOs~AP?10>NC33?=dD4h8rC<$$EwouldgkznNb3&axB};H|VWJ+#~T%XxRW zHD}Z}?`FA`8Z&bpTd#QPK!irco;oIt*RuJ^Gwwb@)(*$w{jJx|`1xGqZ7Tfz^;1%; zKSPhf!nfVozi#uZCa$}WdGCLCjfXZcuycl*Z{Vu&_R}9zRa)0gf;g(uZ-}vG9^F); zUz|F)f8v7TGtpOvy?Va}&5mw_*0;9U3ks!^?YL`2y{5GJ^S_k3AB;E^OYHpOsyKf{ z)OEUR!PWQvDU+l(Z>Hy%qXJrj8^38zM;{~^iE7`sR>}8NTvtci>y6jc*uUj5VKluF zs!-zbv?rNiaDQJ~Q>jZ}!^{*#IPIR0&euH4OxbpGRfl`JuI9*!9O_;5_tG7GzL0rv`N zKgx!UZ+isW`|RK3zq;GQHA*idqAPxZD8r$m_ZDYZ9Y?>;pXs<_~6I5 ztjetZ-JRnf=Msnmhzr>%!aImUI(=ELC97~hn>-n&XyDRQu41#ySkUi&Pc8Bnk??)Z zM>=UMK4$y>^X>dBCO(-05g*q@J-4R5lWO~3-DM64@n2N@C0ctc%_PIzdx`Y!T=%S+o?Zm*%7j`hS@==+odD5!i+-Ig3E9S1=YC2~nkW3{ z8vvO2@8$j9{nwf_;K-o)xM6t<-)x$kUD@ck&eGA*LA?JK|Ayz0@ENM$a{{umyAmW8 z)y5r@i#6W|FR-Mlt@-Z&oIWG);0XUg9AM-4+~z;upuiT-+bf2UDZpekX(B*8lElt*vv`T|z=aDCuXUAaar|DjiP@0i5vhjanujMe`EujtvXHQo!04J^Ycf%fGM1C zJ|mVqPiHjcSp~oxz=5Cv0Az|a(M#s%=LdkPe%{x)Ht+{Ndy|C;3ka}KA6|^l?fC)p zdLIb)!V(hDE?6|r)3yUmW|8)L2f$BJij#wKnHsoJO<>c9hTfKzPM7wKJHN`$cdZz* zlA4)Ri zfI#KvR}TNOD%rEhU?!9VusWVige>kF&YS*jLymv9wzj@~%W9pzWdbo00|SPT_rMbm zj*P4-u>m!}NR|x(T&Pj2QhL-j?Sy*pg|GcM68hM?wg~k4k&u&%2fcru8BlrFG_Vx+N z901i_Tpq4{1v0F&^XgV(^!@w5Vc`YmuoEAc1hxc=ARu7-=TF{6(y*GN5ek6R9S8|t z+x+%7{Xo~ibd}1$_+~=g4?S|g~*RSqw z=QE6En!wX@adJA_^bZc6qCR{*wr*V4*tl_bKM*EibrO=2f4rpuvNWTQm<@~_Ycnmo;ol)0Lex+O!SC7 z>pXMjjP8HP3ZT#a^f>eOHjrrbSy0kx|MY1h0k~VB$VUbT>jqU)BJhLz_wI4g@T@Sz zi>ntjE7ganOF+Qc*cL#dmY=+z_uK@6PE(WW;JJ*Lr`oyWF6Ybv{s782AhQcd7AGfu zL5E|CCLnve7(#$$cEuOC%*slots_j5R|EcBc-HCeSk#-c(gd z$;j;7i)W|k_(y=ljf>Dnsb*vU0pVO)Qc`Z1kAQ;^4L}O;+aNCiS$Xp&flw03-t*aA zaR~{4vwXM;bQSm|g@w*RLCmUB;^MT7j45$(Jp%*aBz>Ei>Fw;?ldQmY6)>b;1?Pp4 zYT)B3_5xH6yf{KtKQl$(VDWb#@t~i00elwzdBo>$%A>;R*OXF2roz&sx>FG$tQdu z!IbZt&;vBz9)5UxXLR+vHHE~_a8q# z;+EUnAti7>JRG1(V9|gSE38{x6^l>+9CG>3rDG(ac>8!P*b@LPzIgEhbfxl(*e)BOuuY!Qk+nKOi{;2k&w2MZm3v;tI`J0aM=myU@|z{$4vmAto{L5@#%s+27rL6%`x7 z(gMG6TpE-BV@1BSjvxpcojXp?%*=oK^opq38OBc_PC+UmJf1V5H2@R~$N_9&jTsmi z?AyEdZe%3>06Nyfa24oW`wak-!Uyiiy|?-o1%Pn}gnHu6{=M&kq6Gjt+~{T3ETE9W zLPDlLut0nP!Uvi$VQ}8(23QA(Bq05qDTBbq`3@kn5a)c4BlrDTTB`f-Aw1U7)|N*` z4MDppnpu;P`;@I^8xto3d8&2_EuIiAip6&;2%8e6DMvrm>o$g9d-m79W=AF zCr>y=K-kd+aTZ$zWCW}UXc>Ta0O0W%dEh3Qq`e;`Cli;^QByxkOoUPtag%(5C3@`1Gpy?)W9SMKHo;w6tNezwX^5`F;*OqwNikjYEYIN|~xED(CEulU)SsN?n}-IRGe00M@Tu zfli3?4t{aL0lWmPL#hi!Dx6i$<8tTE?{N|Em1Y9!^ac+b1^+>k1J6KPZzPH;dd|J_y?3W?tFI-T?V$od|xE z+bhn&=;eLIXVew;R2Uj-XzYzW-dUHW#e?V!iov~I#Ik_( zKR3*n#x^z(vQB{nIyblE@ih4U@W8;wY1i=%tSSV8(cwOOPe(_-z)8=lYQ%=|JfmJ*1}4?8#$V#>+2cW9fu%-Bv+@v~5U;l_Ma89=qb2!7_1@Ni{Tm=v zkJKIZta|d^6v_O3B}0F4QPHCe48#SHO{nr;|MUGjca6vO>*LS}TC>}No`X-n zI(C6TpoipeoTt31AseqG2aBLiJ+&z?Q#KkAF+yAu10)$_Hfmt_xf!DJzRIx9k z!$L?f2~1w;iC2hCzDwV(oUw3oi^Yu^)u5pOP<`wVA*v6cwNX$iDjwiF)n+Li+wwMG z)ARD>LyIe@|H(*6IX;Ex=YcX%1)dl{-!+eq6_%GLK7M={FV9U&t2^hu2~+gn@15)! zb0L>N+yetEpADKVgkoDx{F24HOiH?3l8){Lfg9A_YWG?!QPfbH{CCeu zkDkkavd|1&M9$b21;M#RZ)*{Kka3C~xWno?2M6akO3{meyUJ2WbUP-2)MIniA%pae zpI*dG^DxzHS{j-+Wo03auaWb{FK( zK>wWZyHKhh0B$uu(F!ot6BBHF;mGBiLOtny{->A0d_77yF zr+2Lyt`Y10_)!UbW%LA5icmVaDbYN+*xlO;K}7Ex#%LQS#W+>&qc;P4xTU2L%eeLN1-aIn<6q3h-B5*@|e3C$@%IO9>vixLN-Bkkv(6KKcgw+GUQ7(hTD!0im<*8Hu@yL4jRPy@nxGo?6B;NszNG(#GU3079t z`dnS!`1N(4&x^pYLlupfp8*^};<9wS8~<44;13_<_!%%*zfcPD2Y|EiThYGK8#)6f%HlU6crlW(L znfBd}L!YmzW)~yYq9t~6>QL4fI(Dz5L}VfuEC^5q##UBdr#~e{_p{M%FIKzsNy_;R znw{_8t<5gchu^<{;3_(5@UN!g3jao$Y?;~GesnBLvz15OobMN-PHsm@2Hl&wqZcX+ zQ{&6XB^QIYKfPE)4Ma{~=LHJFHAODZB@koxC7T&I&n3l3KF@!9YGQPQ1)Kjpk zP(9!Y?HU?lq@t4Fy1-41cTpD(d6RN*gnMnu-n@Z0(fKj-dz*Od^M#-ZS64^vqIEXZ z*3Qz%L3R>+2#p;q#q%KuccweGc6P+rV~$w(xT~A2_C>mmBX19hL9HubYfZ2-eg8k9 zH|JtB`k;3h*AYpdrm*cF8ai>H;wE_JlsXhB0+>~_;^QIc`LB9v2cxZ~V2`8x6Wef9AxkqI zewQl{gzwxTOnit8@ILfU3c=m|ouRH3hNwqSw&Go(VLnB)2Ifh<_~Bsh^Dpx95POb9 zD1bF)R^;vEMBYt)=_N)SNMaa7xL0BThEh$}3vvh$6<{f**EKK*xolF%(8^)cn z7orrOwOg_esE?hRo1Z776^T;ZC*L^liEdqHGCo}v8bxTZAQH0wp#NMZW`1|?9yW~g z4JAdVsl^jFyZ?^d!?-v?mlkj$mY0{2G7}SzeXb@wAa85?0|SIRa;!3Q_$%qLJ8RU7 z-qK+B5)u*$aEIIW6_li7W(1NRv_JmMiM+fb<_Mu#QT>RHEiIpvgZeP)WDB7rEgmR( z*010l#$lZmE4`v{;C7aRkcV%B0mMe+W`U6mddu+EY#I&rexZ;!`L;(>rf~&${yMs+@WJId? z0HpbY2Y=`F3Q9=$pw^Uj@x^o#d6)%;?`?{ornt@?6{SG*O!D-CP*{5oA=-V5;h=Oj z&-)+XjGfhD;E|SQ3<|@OWKAM{WP2cFJUQP~EE)PKrZ0KGMN;SPD1Xs4>%Xh==pLDH&VoQzk@mY6YtR9OV zuvjKt*s6vd&!Q@qGspUvoS1-!!nq?~tVQg_qyz*g+$o()(B6lzpz63kX!mbx){zg-1r#LFU2|HK_&U5JCP+ zV~JBrFGbaZHWnq7LIX+4RaDj9jh}L${elUJnyTvk)PLn2qG4Rzok4YEKy*W6eU~98 zqR)RhQ={Yc1m@L=m6ej9o}siraKgV)AE+B4cj6{8GBRRfV?lC<%w#)O?apPy8-^V&2o!`-^^{X|5Vb>ao0 zql6~S)b#ZEd*uF*PX^nQic^RwTF2O!?9`R;4oPuw1o%}tqq}PsXBh8eH-7r0S~W~o zqlsZ-$MS!^Y^+8hUA+(7*tj?-?|qBJCSPN|*g2{kLy z%_iH=D3b=|Am~qEnFIyN3LZ4p)O3RcG8+Edw{IgijJ)-UZLydb2rnQdoXm$vgJT($ z10r=E2>FKB>*(m|*&bfvOHgKe_~6oWF#7QNK}h6@gR%(OWxWSimRsz(n;ju0{@|`2 z)HfKToAdBs&jL(s;5c`zlkUb}j= zb70`G=iw)+L5&wM8ABz5or5+5b6vzB8Ck?4IWA~#4!`wT+S%U3EXLb1vVb%b4x>v; z?<3Baq(eXf(P%MwLL!|DzMyU>4tLxP)boH-pe8%#1qQuv^JF>VC18TM4U~C zkLRU<3c=S*cswf8mjwzh9+Vr7#D3^I@(L z+%jw-6wE-?NI3Ne6WW(rFClA(avaOhkOnUoyl{+0b9-zz4}NZ9If)nSO}IGiDshSD z;iaj#zpj87ppVe)-skcHDmB?%hqA$HeflYwlu*z;N--9*&HjD~S=pfR4}|K}II2K- z6VSt@ZXYi8sA&@Y)Pwk;zCLsW=%(iwq(Wl-8eSm<%cT=kA9;DX#n;(DB>Y+sHh+r{}IEQQR`DFG;cLQ^$}M0SK=F^L-Si><^nyTW0_inGfFjEJ?0>H+?vxH%Xoo!u?c5o5b_`^0d$U4lzSDY)5L5LtUcVRxCD7^19h@As zlRp{v5iyr1ZoqvnMra8q&T*zD9KSA>d|n)3q0seiyQN>0Uby^ulH>N=eXQumo<1e8 zcv5gd09g;g>*$v+b1Q321*j^q!JrQXQMSOq9ZWPA{ZAYSInVYGMG!_Y*!;*8=UCod zh%NY6RkeQni1XyG*%Jfm%rbW%wXwE(9TRx&U`Vp5aQ620;!S{;ifQOrMH5YACPv}a z)lazZ;t7v^>3EZlDXXbO55fn;4FdyfI;~;T!13_X=R^2i#=|$O!8{T3q{xPUTUzu_;h_n2v`Zkdo}i422boBvpwgj35TD0Rlai9c zVOg5N_#v6WP+NO@_X@+c=Mm(=0JcuN&1`8X~vT;YMV zVw8ja&q@@Z9kG;DKmFTXkMZF2yLt7TZiKO8G7Sw4C~514Wsu_WXZk2hXe*=lP|bF- zw;#(VFkv{XbTv3S!CLndm=p#(z(JyaXSoDxeB2Bs`eoDw%R zavPGw#1qr9K?z1xYinx=w4s0!IM!QW(scG1Y994h81Lcq1iBgr+iqA{O%|`B?K*ev z926L^#G!hHv21{wHZn(q!iT1lufibSbMtdCs#sm#&k_i=5I?dI8h5(iV|!-=K1R}z z1WPcz?N3>b0||wK$CUr-0=_h*Ow`31#>PzDx4KShfl2+C6&?ialER`SXT+ zFdZcw9R#X~W7e`IQ4;$m6c&(axgEss&sD zM5+(&$D@Q*L~$`{aZ2V|oqR*q{PSOJOA(b?r!fbG+(Ei}_~AJ1TnG>oNxc9s+U}vy zqll2sms&iKs!%9#gbqDAt~DF9QGepcP`pPs35$zgW?nPW3iI zFZaM$Dbr!a+X)}fii;zm4!*h*Of)EeHPcW%P^KwUSyh?*ohHcEV>Kadv-$J#x zkkBpid{lDC6~@*RzW;(EE|`icr1ufc!TsV+BPT%BrTKRxRT@5*{`5tbPPAnr7O)=Z!QpCgw83L~p2@RiZ}C%q0A14NcRK$LLBR zScJ)LHDw6SlDsd4i&6Vm-a?$t&3vV8hb@b_XIcja=8%U+h=rQOP3nUME9y825)_0Q z)gQuj-o5q9YVC%E3?Kj2QJKo0Uv5f3JPr>(IMHocHi%a5dVa9Ur`PGfSJWNLFxHmj z;X#}@bf5C@9lV7_AvIHt1HrW=CGJSH8Ky307|{bk3r{t>%T(Yk&PA}c1^ii@ATLNg z#96zIT$RUMe8MjgSy?DgsFkFrRx|zReu&dB$D#7^%wm@c z!?%_;W@Z@)|A)G_3ac{O;(ienr9(*Lx>OL6Zjf$}F6oe#7Aa{F0Rbs-(IG7@ z(p}Q5MR$F}z4v#n&)s-R)qpl*ut$(|eJTu*KM3~2Xdoh+ zSATQc-<=4JYJF&n#(j9{Hg@p8Yb!)d7`nc-73yNA?j)bRO89^KJ*Mjzs1noy#R$%7R{Gcgg@g1JLCQAv5EO#;J!b6B`nOEyA8a}-918CS1xQ?xN zdX39S$jKp*ws3c(|K@FJ|}Q{rk7%1>w=rP@q&T9L5g?#~wxoz+8`QlP9)&)zyL&-IP(w+uMY0 zqpvxG}X=CGkynXK@E;T+Agvn5+4w(nH8BjgUQTlxGD)~@uH7wKOir;iS zYiFeoh(V8uANi@8=jpV+4ATX?IkQ0AZ(XP!P?yZiv4qO2l7z! zHMp38T1nuUgKG$|32)IMz~VhM2d58gBrsXP+gCCN&unmw8Yr$x%-&*jQeU-;0Kkuf zg>`KWTcT&9g+t(z4gi@_A@x4YLKXo9EW;a&Lwt}zg>`wXO+Xl?G=C>C4bq-~a}1Q4 z7#h0H7e9%FV<|EsBK6}(wCbY4HOOz+LsF}9%So!FG=A{5w3X?#{fe3q}BNaNw0eVZRh;0JXXtrCCrn+n$|g zb~h69UV``vJ+l}(gfIiMN=g858Kh;6f`E+THQ>v^_QNWZ^q)jxHuj=W#gj;AKmh9n zqR6bQYs>9ap#}SxuNd8u?udNZne`5CtZj~p_H|~JI8hZ9~sEQ1KPV_ zT?ZoCV9bllDMZ6fv`zmB<${8)mvrety#ziJ!yztPcx$=X&k&(Pj#*fwPz%}N<)Xkf zR0ozVq|O7l7hQzFql3%0|9pFS53%OKZv_SO#HpEkpRC83TrvU(s1zMv@JPd1wW&Vf~trH_{s(r zKQ*s=iDbZ)holDhh7eZ3UtrgI1NJBi>Yw=du&6rv<+7Au5KOOmi*wPn^6|9z`29b! zoK2Zv@gYKo?5&_+HSl4jvY$&yEzku)NC{^I*BW#;z?L`O@3njk<^g=$b2(Z$4tO(& zk%O6_$D#7a4{BOkUjb{t&w4E-Q{iHM9m6f=;nd_|r~nTP3Ve`F1ArjViCXXg^_vhM z4_>SPx4>6uYQ|sJHxoW-X=q^5c|gVvx&(e}ZetNZO5$cIhe5+8<7;TIgL?d<_3Po_ zV2gw1Q?kDK6xb66S94wA;3ut z(pk_h0d5wAsoS`sUzw7j6q6|!s7Nq3ijJTjS^SyGMtIAxP@}NkW6vB6**ApT%a{4U zO=)R`0z^OY6u|el=lnIJ0-&V&ZwdhyHa-CXtp86JK;MCs5g3Q#qb$HThmFe+hkb#& z#>kih$pQGw!-x5mmE!(&M3sgcsZ1 zPL<2u^1C{7m&q8mqM@aQYA(EHA!Hg~Mx6Zx;H~6M1)oPytqN^{%(6d=2YhUBpFbT6 zr1!xU|95NQtuVl2Lht+y>67;8PE+u~;aPX7p`F6rT_A0I-r-5xNAOZ1ngi@JRJ%nC z)UpTO1H!z}+s|(nFxPy1Wc2qLh&#K-#kjb(Pd7@SwSu`t8W#@|Oi;yggOt=aUdR61 z5Ill+@3;FGON4|%02%{IyCQ+~bau8FcmG=gq3rhTSu+6GTjm{Vry-M~&v*;)PU#9z7I3SbhTqj+!v} z|7$q#yA)C$CR)cJ58M+ zaiXkD6#OhKG7`GAf{eabRFDBm?XmMG5BNyLVGK~sM<9k-QU4-p7^Q*GmX(pYdk+To z{7X>gAfvy_;R zt>1t6paLmn3!c_Vbl}$jxHP8; z*o!SK5(sUiYb_oohO*M48(xM);V`rpZc{;#0et$c>y?v5&}|qDWZR>~5Acr1dJ>iI z+7@6rK;=bbatitmiWn=PnbgDQCpa?D2)QWqMZW|_pS1Sf#KKkopwN3868sR@8B{=l z^+`mGG5Vz+61afcZOQRpevQiR^lknvs&LH&+snLP;4RIEFYikks zwEA4J(q9HF>)PK*WslFy0DBu!8NeVuCe`C0-(gIW$&tw+CVyDvM)M^~C4ax>YsX@2 z_fi!HtOp|sCq)3@Ptg+&ABBx8r+$J=edprhyO!HMg1fl)7DFlZ1>maBl5DJ_Nh7u%OX()CFngYB;0!#sQPiqCz)vp(b4!!fD*hHN6_$VEHZ-9H@M?8mE9Q7mR*w z`hPXRqLPwD=j*t(CF@e}UtHr1+4^QtIcby_$ckNF zHrVg70Dw#yqOhCm*c`M^iFQ4AT!hk}WtD0du_SaI*$~nMdfcJJwU!1hVcnh;f-)Xf zISdI}^1>nu>liqn4dmZSJ1P2BAmvJ9_+Cg12_98 z47rXqrqF3HTZh1&Ko3dZDF-RK|8eWFwngLRNI$BkpRuu%Jq8-2U0uZ_Zl9?^n)Kk} z58lGwU;aBW218nxt>5_k zCp)Kw|7`%j)H`==&)S|i`~SS(eaB`h<9^f#L&9tPFIM?-g@dVjb_9+$Q>PgX=q;A;wS4CYEVs~EKs5FW$BD6Ye{4-G9(@E4xc~xERoGiD~-sr z9caCjN}Vexr%)N z$T0;$_W3{Ss+>rd8iY`}TydamH>ptE$-d^Vc?VW?%&IiGV%`*+{t;F&cAMWkbkn)d z^2Muv93jBOKFin4Cx}60FM26AiLZ^v{1cQ}Qpi_;w#m*xG^EW$i#a5=c|oVLO~(5s zVq|El^K2q!m4eKpuH{PlWtU+@#Zqy3LRzp{=cw<;1XU4BsXb2g9cz2wlzgc$en}W$|b8Fu&m^u|dOpfy9a3obu2|U)) zZ`8PCP7~xVOO74fJ1SW=9=nZ;(^Pu=cGY~R_>c9~1%|waulV8cP@K;K{YzV~Gb``H zX%uBTA*Lj+k%4TW>{Xj=a?Y@ccG19whzufjn&;?rm>V~ut<+(&n?!qHF28*)FG9u4 z#%j@bdD?TKTdepO_{=uM{M{lv?f}FAs{i_?`~u^ zJYq{TqoM~H)Aj7mKFg2Im)7U9;@uCK*KBQQvzOk~dlcDNx{$h-PNe0JPg9UNE-852 zFnK@DMNBmN_QGH%CVM02J5Syw$VWF7D33!{slQ(`B$sD29Lv4$z6f8;!(Sy(h2ut z{N{r|f1-P+o~t3pYEc+gKT21&8-CVZfN88crjA!08e&rKX1i!;XgZ7%@O8LasvR%V zi^v>#Ug<^x+S`WkqY_15M+05$n7vl6Eh=q&-B@Uj?`5}{#WH${uMo3(&zJGbezEw8 zow>Bl1^ZPO=hf|;{Fbxn45CdVzvmu=x~f_elVdI-Ck^L9cxUxN^JVIv%NCtH1blF2 zeWD&{5eB|?<$vdIg!Ol5q&>InT@}bg<@;8-bTVpaZ1^k`cJ78eZ+uf!74Qg@!j!4M znlHKLeWiPgC6U7+^I-`4OYkTP@51HC8C?r|U4(ek@+Rs&-SuHZt)XE87QFVDS=OD5 zbzf<`5XSe+?BABlejP8$#;H!qE$1caPpZ}VWbc14c*tH7F)X5?GH_HbdqQy-q%5w0 zJ7sy0xcY?;H=^$FsmH6~C!*HzlCOOaoW_eK5AKMB#tc~Y5@8ilkX@ptcNcaWYT9E1 zK4gSu@s82u+1Q|NAGruj+v=Tb_APPX5KIW?Jz~rK@j0)^}S8+mCi4#9pwZGXf6INie5=V~fyfqkbe6Ief5tmaudFN$6lNxy`p?x=6*M zN;icAyuQN6xp_yv)ECN0YSG?{37d)d#ck``RicOk3BQY$p0UsyrjnZ>>viZ%X0;bQJb4yA&uLP=g<8t@1*K(81|RedRV7&&;FoG ztIqM+q{`R5tms|db&nRdHS*Z{ZTO~IG~lEcBOh~<4L3qZPxI$Jud|el%M;f_m#g2O zFBxRsSe70h?(tQ`r_C%D>u621$(!d5@;oXZAK-0Sc1Q|7v#ab_I%F%^e0LO&iqp6g zd3ZUWbJ(|*aVfa-B4+DJE}g)oV?kjkr=URFQNsJFu?fW74)syz?_X(4f1MU@cgKDi zX~M2~Z8&D8vPY-u z9pL(H{N9!oW2{!WD#~0=(7@|d&;2&Vu}MIjz#?iR^mwAny8RJbL4o-;k1abmrMBj> z-)e3vg!+#a=ad<7OVMahVW{RY=2y_RLIJ zw`>+Sx7AIl2cz=lUKU3p3H&NLK-yAQ``*?_N5xtvzou=SsLdD4vjkO-yWJwTa%tdLDem&;L-zOKlC*Q;J z4^;aNxhEr7L-)dKn(h}3yfP3@8lEZNL{RzZpBps3*n8ucMlH5~!S5tP5tn_t@LFut zWvqPZYuRs+^oYMnQMmY=QcM+On$fSm9Tw5xg*>0dv9{Vs(b1kEll}3034^mSK~q_n zSK#}3#0KLXLDAQO7@`=mym%%_TN)+B-FMZ7ruc4CO!INojZJiVU1*=2e`qy#=K9!T zuh0J4qMw5e$vm99AP0CG{4RlR5D%qWVAzAeny1ULL!_35?1@t)KaBHyi|g%1UJ zf?hbX3j)}+S#MlzoDS?9B%ZeIY{`ur7dHN)rqx;A9Zg`Ve`Z$WTj}yfe)n;+!bTnW z%%5rgew!2yi@r^nADec*hH}&?`z4nbFvX0#&nl-lKt0i!@#heoi2t@yo!UdxY^lzQ0jtUiPvgCSdgEcAy+bI*cyCml~}~hMB~YEWNQsBEv#HrgdN$5 z%U>V9us3D@MtxHzw>s7DKt)?5XZl!Kte|`8p_$zI{H*F}h{jw}al;a)U(&NIzi;!C zo5eEAtyh=5KdR%T$rNzBF1xQjim)E%&^gXjtL{b7DPeZ-&Go-(Sha7+q)qyR>8MWO zwuDfoOVv6?EFSg9amDp&U+3l3u4{wTkw<8AVDlXcvd>~B z>EzLq$ozs-&qWo~X|A{C=Xx5Yi^E>*gww^2%2Kwb()Th_R~ECT*U@)$uxmqOE|T1> zE`RbIzIqksw>7*!T0(+EZ7AwSj3o9|mt4I|F_=)q-MKCJel5_160T-^fl2b9ctx0z zGS(d*$CvU3I`*4BGNC-IYxTR!s_DwlPYtOVfHl}=FK+o5@GCdxD}P`r$P>88(lw^&X@NQl^$=6tekA!m4S znw%wS$4=TrlmDpou2Vcud&7PUbxH8NRnbGS9QN?3f`ODee9f=8G4!vhpHHvH&GpTV zVIB@Y*6K&mHFa@NmukhxmxrzOjuvZtRrBn7OW3lY5pa=gOd#jBm&jVzm>)=u|uFkM=pX>Z=8{gv}O zC|X*jyDZc~)^!ad*U*vt+sAsJ1D@f1Joda?J5;T$jLfczQWI3h z%UCnUxxp0+?&yDJ@=n3{2(QR91LeGZp!q!}TBLeuQQVi++CmYzLyeSqyd`Sf*Gm^l zFj2GIuv_ABle?&U$x&b$I{{)C8=Hs4(e`ibjy8j_v(6DT>v+2P%tI*ucv!-M_bOy5)uEFy*vLT6N|6AhO3nru;}C!BBhCb->Toql1>`ocV&el@J(5EYh3N}^5DPNaP{?g zS*2})9bu0ZWljHJZGKZ`NZhZ{-1sz~@y~eedG9FpUBioAs>fx2U*nJcS#MJ0Ok!5A zN&7I*Z>P2xYL8_0bu0{F9SaaI$HwHCGHbp{V7W-;zw$do2{jCCD3DyfsyXtlL3$m{ z%q*bD{_xqx$xl8WXOO3~DxH@zyxS>f2(>S-pFNQ#=H?%gJD6X6XVBs9{9x9ZXj*2f zWAmskA$qy2-8FaI?vs4q5szOaov1^`-rK&d_vamOmtO@JAk3QCZ#^EaXi8+Oj+)+nahTyxF5!h!0Jf zldew`sp+}v-5FuOf=>9?saj>X{WgzhDM|GFs~7#1Hii3Aad_u&XhUe>i=q z2s3RMx8}&RS@$-KvcUZ{LPa-xJ%}%cNWVQa!RinN_qdtfk!ie z4)X^G4?i2<$_w(YGy6?j^|bUw8qryL65BhjHi~sF>2iA&4{Zu@H9yzBXkT8%p^B;3 z`1pQ6F+;Sf+Q>5A@mQB)!zuJ$O&asyc|&k$bOrkc%jbr=>;TKvb(jaqC{ zclKFGld^nZt?mV?^K;yatYSK6^-+H0qD9yI$8-t0k?SFdNFsq;@ub%VcIa8yZ{H7X z`is{tFMQwLNphY!Dx4-nx_Wg#Y!cs>qfA=iI4s$T-^wVu{MC4E!-l}V`cz|`-!D~2 zsI!%;7ZS^BrkNM^azwv&1kON(YziC~2}6W#Wt)sXSsusCl^}AYac!huso*!w*;k6U zx6+%mbwx=jr>V&XRrFs3O~;5^caBS-Nj9A58%fPLT;b(vxo%H=Tstx0Y)B|?m~X%y zM7hc0hV_Kdy?&F}?mCf9kaxL*^qy$$z_)lp__sT%y!iZA<6-A0&TOO7O<$6g2H1+ifciF|ZMu0kR1S5pL0Jmg!7BXKK^f9Xb&z zN@9+uRyS>Q1vU&qiugp!52u8ASrC_2lFrJ*k1M=`*}eNqV!g^=F34z^I7S_*R(qcv z9I*;)$b`u`udA>})axYp@Kv$ix0kjrm3(kS5c6Afu&LAU%9xK&K5@p-_lmf85KQ~kcLo+Igi)f*Cxxm4{PzSL>U$5c_~AeQB>rYe{b7}lDWY>yrum%vh_ zg*lws!AXf8ZhalSGm$noriFqinYf(62aair*|B7WVex zTpoz4W(w=}7$Ozy3?GkQdl&X{uiaoT!h5@WsK7H|E7E~1f?Xv4ilAgk*>XmK(kVxb z!w1cqseN#x<)X-EQ*D@7cG*iHZrt~3FxjFQfm94ru#b}2ygsL`eQ9U^^LaaFm2lB# zGJ@2%_X>w;V@0-)S*r(m>MqXaZNH*?gf=<|@}d^T$DW<zYrq(67IAYkY@sWgv%v>ep4e{4CWEGoOc(sT|7ZCLNmwY)hREo|t0eyls6RQrWk+ zdh^B{)5``{t)l4lnlit-E1|SVn1+1Kp>4|vu_e?iK#yi*T{kWDC|CufA@0wJg2TOE zNkoB+zK2R2p({uWm01DDb(vKAS*oa!U4?+eYeX4B?CcvY@%VwS+gSXQAtc%jBcaOtkqc>x){59NCu+P$qbh-xV@g z{c(`^7V6XPo-^+h>;=6=>__q*xQ0|vrcPl+teQt$Y=@nDgiwgh@$4V`{wmcHt)BZ^ z57$b|+!M!(G9Bx@ziQkg_F2B5YgX`T{-)ldz3729K3_m;cbvG-*SCmuU(&}2IW>M_ zqoikU-*%!}JJ)#2+w_&hlt`PmAJX;6Iv%&mm`ik8bK8=qq&C?amv}jjR-B5tk&AX~Wp^r-?U8#w9f$tBJI>6* zR~K}c6<(O*Ynh&mIt#C{61v{_8}(}C`lJQLSA4Mgqj^|~Ueb1WwL5RRyNS$wM@OxL zdG~y32@~GnVV4QF-T7d@38~J^3#X6@>?h1-%ZY1qau2#=X!~p3i!_)uT9Qr0s+4Z{ zX8f5b|1|OuS9oLu<*|KnEZc;KK93@a@L0Nz7GTy@Ul}J|Am~+%|8~3mQ0GB$R=?x5 zTj{x_-sCCl6?EdUewR+YA?{m}jC$w(+%1ZQL7DM`HUfl-_s+h}`o(MBB)( zCDWugQSWZJwO<{__Pl+^aV+$V@GonGjLuoxBFquhEjV0D$m6HD_O2#lC4+Hz&4n}( z_Znf#>=;tSW(QaP##M%7hTyT6sy$cIu$+kBm-up&Z|66x3OcWQi&j1COV`_S#U4~I z{yrO)>Ax#~`r-Q9(sS-}l=8Re^My;{j9;TxZi5fTl+XyL#m{F`PAQ&VT9Ilh8q{c* z)e07kGdLdjiM+>n@q5e$zrKliOlb;0)tYp@rr*a7FFpa*NwJ%zlYO)8GlOnF^|9Y; zILq?`QN>jG_fyz40-L@LQXSdbagVdChYi1(na~UzI7M~R*W}%dma280-8*?x#G_K3 zTNmxVnk|$shu|2yoGAGq&)&Xmmkb{IjuQ$;ko-K|puIdz|&-jnGdxo@Y zsU&+kL>}x4n^}tlGmYG0E`$O~%c+KwWY_j)nxU7DV(w0ckV<;uaQ3iHyiQ0V5j#$X z6o*@2U+;EQZ^HR*e%%CFxMXgI#XI^BH{WpT2Vm-}7G|a?6r@5RlAR zdKjOQk~n_lzEIQTF554Zuj_T`B?W9nf{2rR!ar zo1izDc0-Uxlf3=(`TIRB1LX~r-ksjV*T?0P>GDXPxk;==cY6zQ$a6NQzS0yS?;hO^ z-~aW3)J;6&Y0egnn_PAYx`VB+|3>Zm-h^v}@dnQryyJ1I5@l*ovbZkw zXLReO_nv9`R&#LM8?@$|c0Dv>ok4PD*BoZ{TI7<%#~Y8zsz>bmEx(h-$6z=_d6!Q{ zN*7$a`}u45tt*o7%KqzobS3CB8exAFKCp54gxai~F<7Hy{*st_78_taUuM)bpS5H}+hLc)SG^@RYUm!O>?sYwo zYInQoh9g-p_Ju4l>>WRbj((jO5{4FlRh+&0{r8WjPee8lL z1yqW~yEKI)-wsd4FXmwgegbtX6KisI_;y=P>h9FV)`mpUnZ)7h)r=-o&yJL()%3N} z+$x~WNa1tw^VUU<8^i6n`)rS<@BO_VoBDAEPxJSko}%>lJ1IwItg#!rGE$uS`Qb8~ zZ%rQkm9bS`V(?NADtiB6*zaB`H(!pmt3I0FH3tV5LADCyAH+mZq}WXR44v14y^R@Z z^iL(953Ny(8@uN!<<$)d6ThYGP@hiqio+E*Vt>{31|aKkc=6)KmMM#{M&)bC2HYQh z<>2+A=&QivVLvs`W#?*DaFh9#6j+#~(V(4vI#m=t8{x5Hy5^sWqF{gRk&V`>HhjBu z?FARs9gI1;#RK8mN1SgG@t*v~wLPl+WA0qHGP?hB@Xqa4bqb;vtq9S5l~|d2VUx&S z)aq(B2B9$DS`-3@^}$cKIFi)bC?(*K$gI@6f7`DwPnbHuI9>NL{(3iDQ8jjYUc|R- ztpvNyy%^&8FOQ(tBc7YnmGO?o9O*f#g*J8T)V64{By$V zCo)E*tzdWX!`9v!MDwLeFeo$X(N%2>{0Mt^#HyV8J6MuG&>*i&gR#{0m*sa3ExFt?zrk-adMb0*P8W>8z+mMx@tO+L1m^Y-+W4V{plqomnP}la)bFutx4FMLO!F)x4iWm0_pxvPjEUQovHO|%eDC@%T(oBkgEfZy z%1gOo961pmlJEuGZTKOiYPW;8P2iY(63D>qZ20lAuUZ@JGj^BM zn~bN~_@U*+9@yTT%WZC5o;NK?)@6KfcFHudfA%k?I+)sI{1L6%AVynge^CFXFKkVZ zp#J?)%dDCCUjju{Ej^#|4h7AN3qJ<8!-2BHlDt`F_tOL2+4M+xZnSqM#hdtuu}3L% zo(KlP`isTrh`YA}?)u@i{;{2)wlIoK)f2%+MdC%TY3XXixHrB{ zMXapery`+Zl`@$+IRk<^lR}|`39T0T#3^XK{? z)nfORx4|h-=x)uad1B&-U9RaHqt}%$G9`1d!kYZ;+sQQ}72;mCCEP}ntUoZik-8!J zL#95?KnT+}WqYPNre(3qjB9p0|;U3_)?kv)t15ZP}BY?5iC$zAB!q8voT3QX5TOtD{Eo54Lr$+8qS+ z;#9lQf=>9(@7)S`5)joJw6DM2!q7h@kQGxGZ!r5T{!H!>OM-PcE0gbm3m@7&G~H`= zPggs;ip3^I8VnNsH}O~glH7i)B_R5Jz=q&fftU}%7PlY0QRb+Jh~%>U%xwC?c`Rl> zAMpTe+)tEt%K82|J?qx~uL3-}Sh=8~xO0;)xgTvkW)$pdYTEnkr#6hU?Vl#rSYa}iE-`FVp<=GV7t{rw16Zl|jr7lAjT75* zJt&R6Q{K35j~0ry{3a>iSDz&YNtL*s%5S zL8O$0@%OfE9WOhh^jg$KA8*RH=S$h*p7e?gGwDSG#)#fO0*_DGAMP-O^lB$;3jTOf z6-=XDT$eMNsgf_a9l5+*tuj9I>g<>bR94#1r>QO0O~!1$fW%LJR$E7(Vxa3r z?!GL5gWK?=%Isj@edrEb>`v90KHJE;E7fI>IaTWYWF4(h%io5~*Pn{nXS*MQ7Wz)+ zDG$%=zZG>0-GXwQkmY)_eC?ttZJ-Ha;`xI7Wyqfzd+ojwf%5SOWAE*6@u&Bdtm2;E z3OGl+ZdOKnr)J~3$@vnt!#_g;e?CTT+MzY$hV)lt^8DZb`+pw%fBTnRMrcg^D7TCq za5wYXgSh;`HL<3^<5?h&K=z2-r{>n~5&%bG$n?tg-3=F^{iw*hnMCkBC1$bEn|Dsa z@5XgP+xXh80B_8N#l;)wP4GNmYXV5cXCN=)u7LuywSx#dvn3GZ4DgJ`)xqcQIrBi_ z%AI^JDDvx)U|kzqkH(mi_<#&q>L`-U)~tfnNZkVfxrxI?wp6dbwQ$kROWC`>58zBU(4YVl3K<7ftUn+x9v*&vmoQ$2 zu>u<9fokxQK!jEt6z9MQuFV>@N>5G(IT}zMU@AM@0}g%)Tu_!|P(FV22!N%~OW(aT zF*XM5>FqjD*Z>zWw54nk$(0T2pdRz`E#?2xkbx)S_kG+3G}j32qs(_&Yygkj0$%K$ zJJ2sa<^~=kX9WW9dUASoYDdlVPVJpBdv7FDEzxQ4Y6Gx{GVmXodUcAL~=su_!*a= zO+s=qyAGH30$j1~C0rtAlJKoAnVb*LvVNvZfbd6>{=}TT?y)=c>CYc?fE>d`$Okl8 z|IrW#r8kI=Z#Uc8+bhb+9n-d%a)UVIME}A6)|fv4qDu7ZwRLr;pr;uqie z0=gXJfTe(+?rxAt)cl97_!ML=K0ftoEP6URvLLw!#mg=;NLI120bettI%a2YFYdhD zv%Ouq^$b{^nVFe@79AS{t|AQ~;qRfLeaBAFf51HP0#N(RJHYON^#INL3VF9Nje)xb z!ZQ%b07(HAKOE1(bo#j4zcmJcf~LLj&XKn|`-yo?zDhmv_L4qrF} zM;l}tASV%^=3ruCVrI5stQO}1d}0~uC>EfZv?0rh}T-fZgY{nWoHAp(1Qn3cL{-Sks7L9 z1oVM@yd*&PfZ#@0SQy^r9Xp5t+(jC`d;!qk;JgDo5!hwtd;L7gJr|u+)YRv5fdnMj z!P70K+(fs6tVfFh?*sw`jY7E8ALSm%1xjVV-hDfd8zza3i#xP&;95I9?;y?a0ffI= z+hh@KAcF+{0GN1t*tuMfsi4cWs|$#F4wV{(q-1S^)X};e03y=Y)^1i=br1m20>Cim zGJ$~%)FHT9hK6V;yalIfxKyBu*p^IFV8Ra-c3cPojj_Beh|I06txM%uL`CaAe{MD| z``_U+2#YN}E3bl>5jcmbtaE0`tNT(Fi5R{qv zz+E1P&zz-H#q>|n3*<*cO%1jt=!l@N8m_lWK1nel?3uwqP(&7q&7A<)BlpXfsmo#j zy@sBY`#Dnt2Zw}!ekX4b_DgbX>^#*EU~vK*&CSJygpBMIQ^OD23FHXTw4#tMoF}fG z7K1_VfR+Lb$=FX48k%Wf9L_o3!ELX0UQtLB0K-^X%MaS`2xp@I*#=h%RyH@| z)98J|omdE3TfkHTFiaK~w*fYkp-S*GBiMtHSs=bE)~)`YS9>?60|y^p|J5u1&hEoo zI5>eorT7CU&X_T}3G+9TEY7&j_@pF$;Ho}nH7`$=$pI6$^-8s5lupc>!kRpAGBGI$ zH{ec;9^T)Q_d-#fuj>*U@c2?JDSdUbQ8}6#_J5o@6s z=?4_wRp>Sc6jW58`vcJ$crguUzypi{UuRO0~ zr1zN$-j2$G8w-}n@M{3@xx4!MLWH4zTwX@zyB1rXVrB@Dzubeps4R4JbU49FfzEz? z?DswTSa$%a0zeF28ICcGo3=)ehB5|J;4T3Wsx*p|i>tJuL7zU>FjR>pfy?|n=#d(5 z=Cs(_`G9D@kM|dJ_lh2yTUPD|>%#!duAPQa2Y}-$E&TxsZXTZ7t{;PBfnft56h9QK zYv3a_;1fT89&G=e6#ItF7ZWau7tz!}O>lH{gzH(#Nf(~=U$^ePD2VKUQCQ}H*0Q)` z=pDp9PA5?|zPY`<&8k@(1YE=G*Xz!AyTO=gi8EL20h~7~3a@Y&Ow~Um^=#sUFv)J7^qH8#O+ET`-FWA5Fj9?17Q|c41WkV{3iiHhXOl2eKS^GIqZ9Q6-J&)d8D=06a2mbdG*hqu&EB{yP3VmbJWw{?5jXg3Hc;llfbc2ZMFw6(SZXQp$>2}}SW2dVK_CNXS*=L|0ZYZG9W|D?{|+t=_z z1CgrW^-0NT6sq4P1X*>apfhGn$JWbs(7FSOxNM&NBWz?4_$SB!5)95%fK|aviD4`C zd^K3wuphaWw2R0h8iC128v{PyCK3s!v~igLJv};~!c*{vadg1x1F9p;2#|XKVp_P> zAiZq}Oa-$Da>O}-hkky3Rar7Qzy*+(mE8aY`P$k)9{R<>==>Af1et`iw7SNZU>klC zi8ee^10*x}QyCOiio$AudyJfedkrrLGSXs$#^^RBkmnJHwc7A)3Gx;WhnoRm491JR zwziIljD&#@Uc|13xWki^sp;ua2noP`0NC)N`Uq<>3#bePQ`Gc?w*aUCVHa$ohP^ES z^QQ?qn}hKOQENlPzonz42?IkKAn;1+PKb-+dHnc`MjA*6%yz8fAIXfJ-PXt`3l5PsrT?1Jcbu@QwY4W;-hi+|hZW-R-~f52WZ%)} zq%@Zim_b`xvw)qX;sx+5sIo!)3aAP1jyKyS90k%qsop$30YCD(H#5#b!5w`vT_%$i9!5 z?BTEl0|3mf3I~8gfz1S^{8*rD_Q3Jk8B7%badnhbIh=aH8-m#i;y_|2z`-$rOhjC# z6{5@Zy9NP(Lt>>3&mzDoPJJ0%Gkp6`w;dN382lcfJmB;5@(#OA9D*f*RrU`I!U3>G z*lKWiHn2NhN1q?cS9K5v-xv6yXQT`W?dkFHHF%fq!-hxk3VGd~ov$q|-|pm3+X4R( z7BOsi2TgbgrtD~I3xUT7#5Tjfw=SxD@}k z6kS!dRCcv1QX2-7hzJX%)84*~Vb8*y?7iG`XEr0$TTC1nZGEfn+LQoF+Lgcxm$`J_ z-q^UglBOUl%c$Sc=92G2J#pE<+EUB8^N@FFrA`1~?UgD+mWKFK+?_c&)Jc zV3C2u8lDd<5#YfA$NVF>*c4=BiShAj?y3+ysjHJl8pERiTK36fjF-s)2%G>le(I(1 z>(^_|!8maAZ7TwCiPSWf{VDZz78-*II_S?j}(Fzh0i_rFeWnc@A7hUE$idQLy&bjs)D=a z@M{>*JaLL*VaRdw^7)-(i%cJ~UfbyZu82(-hV<7-5KscK(rjeFbl%FGfzaaka5DgL zah5j(3?g9az}fmBu4(pY=fcI>T9$#>s22}ng7Wfm5^OU7$Sdsl-2D&D=MFArvK$1S z4Y#YtZ7P8KagYj04siC;1D^rg@p=H{EUJ(&@~I7Tg%NB3J-_R87?;4>!3K=l2P1Ut32y&`xG@X!rqk=jMe zJMQ2fK%{XUeOA=o-abN-ezuO60vGT@#Xy??>OjUnAG7qLd)|RLp$iy5a8a`d`1tq^ zZz^%&`k#c^HdxGv_&<5;gAJCR;tdN4_G}D)V%)AA5_P7wOMN-0D|g zCP5{KOs}~Bv^=cMIJFmZi=&EK=T@z-F}VT*F;bkP?r7ihB4ft@j_z*t|HWp4zb{Y5 z#jNpT6dQI8yyN??hV1?ib#ECJN7MEF=1M{cPS6nC-Q5WqJOpRZ;O-1gNRSW$1a}DT z4uiY9ySuylTi1O*&)2igdOw}B)-2W}Jzd?sYxlpZtM~rx^=79&kF^m+q=&`T;jhF0 zZ`VoxuNPSVA9w6g4;eJ6o;}cWq7$N5D;Qk0^B&E+5tunlZF2@Com{}`u*@j#unknh z>#W0({b4X^G4{T-L9oF3S_rHWfH1~gfg9SU#~eM>)RWPs$M$>NWd#qS^k(zWl7~h= zlDB7h6|)2uA>NRz#U+<#C+mZ$5S-*cM{Jl7CCI}`F0hoT&z0OI@J*tUBxJNp9Zfq zF>tA0)#hM*1Q8K4ZRJ?sPSA#;1EskA#HWY_HJ=VfW!f(G^n8*VKvf$OhDP*f+~a>d zxnbqLcX?EMC7rLs&o}xt=?OYbE1#WHkiWfd;=Dq^y`PqGB zyLKgI;5KWqUZHrRo4T5Tp}swxFrx|<^qydmN_pqX){;ZhGT~=h8NAP3^{0&4e@JRF zY~MlBrxNn_C2i$cY?o6|*{$tw4GnvJYN^vQV;*oRdY@%fKee^!m9FCU?>aTQQ(S9* z;)ZnqVOYnLx2CA-BIIklR~p^5Os-GWI=G=V5ksyx$b59iG>fj7Ov^N2n))bCu?m%ghta{&nZ9~)o7R0CR0t`hF-3odPs zF^GTiRofp&n)h4X=F~%)KEpNP@XF8Ml-STj@9Xj>Omm)akGluI>#rOp?mHq7sKF5> znBxPi*G3?JqhEeLl#;^4%ZOuIXK0+7@wIhsq{y8_?%+@*C~tf5{8* z$)Av%6F?KL5+`AMFPCOuT8-7G6B}--wkfm7;f$@Qk(6D^B@Q8uMa~(gM7NPwp1sMV z#F%Z&wq4ASv`il?!u*N+GW?_7%I<{;4n##m=uH#8j%rvpCe9X4jY5uW))J}mEN0|6 z<_+A#1X+At{>+3SKWltpKf^oUo)$$$`zfPEF}af}n&eh|0j)w8E_KNyleL@}8uo(u zz2teL1i7?~fkFigoSunj&J+hFt|J6jI9h%kv`;*#Dr^| z!hOU$E8RvGG_Z!ib;!+rn1xxTgkW9G3#Z2~2_2Gq7kjEKJE6(s@-A5+RxT}T$N~IY zIdc;(VC2Aj`hBuCVyKPBp&3zeuz(ORGZy{g^e586p6VvRgRqA<})h)7VtpKok>(i<;=^qVJ)|yzf?KQr|Kpa`F zTYMQZx6W#@DDmvHAqV{gb)GN5onD1_b9)PGHZ?_G@OvgE2N_Ww*qOXehPq4IpYdG= zR6a~6Qe1oW<489^>pQ<@4A@P%vH5?IO)`1+v;R#yqmDH6_m(-LhnTA@uU$MPSwsw4 zp$zwdMobK}q^(-g4yClOKyerYM&uM>;;^EWL9CewB?{Q{KPz~0>L zm2l(J0|X8^rrP_P48)KHt_j|l#sBs2W=dMo;rm5RQ}BoAt*FM;QO{baWtg<+1(Nxbe{xkLvtMw2zkVcPJ8(Vzb=&DoXN>}pFJO-_3aaik znKTVfd4qL?BU}(vVO)KzOFAbI##K#)Rgj&n>6b@&_fTihUC14`>L{OJQoQUbXu-ih zp*i$Hv8jQqO_4SG6Y}!BTn@LADep|9cy@W`myjIXR4+TDCE?uFj`*=+-MyQRq=j=y z<5KZ)EZg9Gjq=A1eB(hLj70KF?VZie%|%4wcDJ<9~lc_t*aTNbnE)LLBLFBUQ^tLDv=g%#4!uuxSj~0wz zLqkQ&4h279UiGtlNeXGMiJs@b?543(x+C!u=e(99^W?2N5FP|fcKjmY{PJ-JJp@IJug7|fEc2kui35aP>@7w%SmlLMa z{+!6@f*5jr)tf(OvNkm6z1#e3xK%YHWaafn`K@;fC;#?Q2aqGmu;^9GZkTm{>_r_yeZq!4nzIs{mk+D+U+dGZ~ipa(! zN@`tHTj0azk!PDysQBsFx2h(9(9v>xAglE14u`Bb7z1nGnXhzrJIpP3kY7?(ikXon zy8sqH!_RWRnzfa=@8#a!y7`AQg-sxhog}>gs-Io>=DEq|yqy^rw$jjeDuLl_b9yqn z5*kWmp^SgJiOHGV$j+h&!eLsPDFVB$rz$_&{2sc6tM*<|?_WNNk%)%e?Y(L9{}bA! z_C@h|z|dAzbwc4Fu3@>Ey9>8fk-gs%p-9K^QLIw--O(sng=0gR{oPH6_VbFBtLXJC zy*ybE)ON)q&p$dfH+CJ?_jz6%F?8SjF!SPKSje%mu3-vtIfzNKHg>PVLHzU7tN3ZM4>_M?$ul_etYj&aaHB$!kdDlbvnyedx~?VJMZsU|G`TQ; zRaYR{`cYk6qK#_J<)nJ#)cLrg;%H(g@~v@T>XwJv^qn71Y{Z7nSS^uFZ%nY=n43sX z8bPkLpFyB?L$HXE_rC7{y#WB2jT@gG=DQ2jFe?K3L-gnxC54%8cI6W!kT1Ce1&)oyb(YQuV# zz4U~~yQ$K>M0{Sgx1mxMQHcqO++rp&QzQ@1Td<~lZ#s%%`0352g|KE-)$vcI{7G#7 zTinc8+|}<&$BGqf-L8t#M`~;|o<*|k4N@*pYf{OZSGKIHS}{Y8jBt?sscMLaf#ay` z=`V*EyY8w_N3r%MdmHF55^lg098~&NUbf@IHF%|;Wc1g&mG?z|uP+ZWf=ct0n4QcT zPO23Uc71N*cGGxZAH=M{8r3iq4QM#@yGiwtl@`h(B%Zyo>no^_fMGG~;=wyP~(EA<@l@Y`Cz^ zbN?vA8*2mued;S00If=Sa9=(zq=rW=0dZM;pVfPw?<&ZokMt#;nm81v&_(;4#!go(#S=RVpTlL>ni*5!p5aS$)O z5v->8Ew_XG>#);VDPb5rnH&$nRJO#L=cxp3Z(qlEUmKav|K z_9WLfbBWqR-C~*m@R)6xR~1a8e*Yd-uO*A^pZrbk>){A>89>k^e<5z*m<}>{UN76rR!_3dh`#0C@C|9;;Cp3#S%AGmW;U4I$jec_pu_{uqf@xnEGd$E}HRy<@ zs=|lp8;vV>Tglce?@~$XV0WaNz_Jb91J7vYzyeVpZjH*%pNNx+9S)A@A$Yrp1Q$W3UmTAy! zc$1E9EM2WuFW0l^3F5cud$D5|-gu{QHTMLDpq>Xm5DQ00q!%A5c45e#9Y7=|L&QU- zK#jLxw~y~oK#IdkwY25*!v66Fef5PxPW@+!XiF~fBTQO%2-4%RxKZ_ zL8)f8vWKHt3$l2_vbut@!0veH6Vq*bi`vY~N$!T5C;tx^(Ma=}YSSA~!3YO^=1|5- z1q}u!p$s&beC|Y--*0a_#lD``?aHLKGX1+g%O$?`#h}yG5??7A37t!}Mwl)@b;e)c zc%Oj)#3>BhpRkr{%=$Mt21qLM>l0<{oQ}aSU8g`?Tn)e5ueHJAp(FDbYxVcLrRiT7 z79j`&o3@RY-lr=x?S!%xzh8)h{H?1TAGF5SFZ1HaZ>=@8aptU!?jGR+-hQ(~lm|?b z@?gp0?aQ_;mf%TJbepzyP$kb!PffNPc>(0y^)eCg*{7upM$aB9Brbl;EG72hg?dZB zhLB;fyBjKd+~6msd~oe<3Gp{Y-7Hf>+o>GqZfn>NH)Bgcq(D|H|hJ&XluBsBCZnRdga>{h~Q+X8IN}qqm%N%JVSRo|{U& ztcYeJx*ILpVS2Ol@G=OOUB&{Hh?RBt&qF)&x;c`lVyiRKw;sPrqBITTYW`}~H_KPd z8gM#VAn0`zGYNe;)#hQJ}a4j+>R+Pi3klCil-bZ08kjx#X*ve)skK>TYUr)iStN ziR4LrHM47x*0ab{Rb~BY=-?k{4ha4HM*qj;X60FYaG>5c2&90%_?o{@9Sz}&YcENq z5q7WJYT4(hCwWN{QAT!7p?hBx=qN=UTc${uV!%M_wr-c=R|`k<;+@1M`7in8@$oUI zyDx_sfih)^scsc^ZW0zVWjHY*P0*&$TC%cPLKhHr54j&4pElh_+u2~iD$o;Bi%@3E z|Ga1AlGFKh8!4)E(N3rIafjZmYH<97pjsuM@Zo7D>c~18Oc;}!G*FL*R*L%vajw0@ zNZ6jw{l8}!kNW292G`R0Y=ljpa>FYyvH00G>5cx(u*yS54fIf%rc*~;u3N#yi-#j` znvaP>OGdNdPPG29Zgo;b!?Zk4QP~yQ{9toC((`?hJ#qob)+7g4uI;DC?-o9nx?dt% z{>QiB(o?o6pZGmtr!AF?Y+1r?H1Trl?Kh|XyY6Es8=VX2Im(J&??*9x6XbjG)6$Bv)j$fVKO$A|&&@)a43VBe z1uqDzRamtX7logKt|f=bJnb%Vz;Y-TkL(oCUIV+T5{ z*RALwmH5F$4vah7I4JQ~&PY%`9h92*YlY}fLfPzeK&fN#{h4kCBX@PYoP0%KLy>af zYD3!QsKJ(jAAi+P)BhizPVjTwL<4DFmD|=gb^p5h5MCp3n1C9yq4g z#?O~cphWkjETonbP+`6wZgx@O@!aTs7IoW0gKE%WKp)k5RCFy)6Bnz|gI!lk3QfD~zrGR1oLJ z+QoUDb z8knm)SQ5qwm0ucjB>FZoHfP=*O39MGK9IF1k?pD|n4wbq<~f{AXcI;Hq$B!RFsim~ z_HfW_hSg_3yy0n8LJ@+ozf3%+b9w6o$$GV{NbteXJ;D69;18r--bdCTMpguQZ}t3C z;i#e+juxV?vv5Cby;R`vBN}iO(%^&@KXg{Crn31nvD7pb`y(eZf?gDKE}N??dE@Px zgd#Eno5LrJ8-QhVjZOA&&Cu0#L_C(%ubFN)$^(j_*Y}zPRTiw+1)RtcEUeKNA{~1P z{B?LlhD)dQm8Y~<*5ytB?(-aW3-T~>I%wQaW(N{X`kp3FGNXxw6WXciL=pQ>L^zwlGx{F0=gZn``Gk87dN`n!ML{UPgMiiuCV+XRZy zvvAP1TC)M$@ylN6d2`aZLz#V_KxGmlbl3fxwl86G%9r=n7;_~QtMGh@aa*YIjraTN zd9g8z?Kt4OJe zn7I0NF9x;wY2v=aovh>GNRWFpy~N0sfcpMA*2!zdN7$9n@}-M#t)D*_cHef+zYM?I zRWFI1_){8&6Fc>MBFTFG7va?*>Nb`!W4##<6W(^CsyEiF0_8%r`%@&8AaflJWuz{n zvRCL^_-gb~cYk}b^CcrDx6q}lUih`=3qT*faN!Uwqe6_PZXnQwR33#5_u{D4&FlR9 zJ?b(X`rGW)+Ua6bY>#c;$~l5a3I&(6;f%+SPgL`SV??~l#K{xBix_lOyA~Q=;Q7c` z`6b4u1AbI@SM}P6WdS`N-p&i1w<2~Dg0nWzsp>xpchmZ>LS`)!pWSUmUk%M)6mo0y z=a0V;>j<5V?~C)HxL@kgp@zYdoqL@R># z24^>*#xXJ0_~rO=R!a#iRnR?|tv{PE)@G>;4N=f7;<*i9bA`Wm z-4B~d@wRO=0`CJnea0VCS2Ur-f9SK|ZAboj>#;gm2o-X&b=?qunm^wB2VG}T{4@$P zDo{}k=JMH7ipdCDzQMCFx6UnKFx62{FA& zH8+4G`X+v$xS9Q=rQ+c={ye;t+}&;W?DOIoYw|~ax&-a7FHl0&3C*awN$g59tfF+9 zkLW>fYbhdrz7Fc~>hoHLf@}z{2ic81RcD0*-( zV9J#`?HP*jz0&v?N3jQ6;T~l>K(aW!?*7RzEVZV?*d%sxVq#z5gnT36?<49nr|NK| zk3ezmfz4I^??Q}n5qdVdpFJk2oyR)xCQ}NR|pV zyv0yIJJS&;=wr3jjST0b?Wk2JjdYaSLtjtCW3#q{lNzn827#1hugmwS@*F588F~wu zzi!5FS>*QrQ}D-gAD<`3D0RVOWhm*hsAhlSZv9c0^|)%oLFaPq+InE8d&j%)%jIiXWb?$ya3S4v9sF zY;hTh65ABu&hnIrr@${Dou%DT_qG8b(nOiPe6!s9)EDdpsOY zX_ME*Fe8H1t*7Nb_4)f?ILL1skSxI8c!t<~a_werPE=$gRbS~+F>%?Y4z^{l=iAtr z9?;9ez=(gf?jFSGDQZ`8R$Szac1`lf|B&|Z{?$4ygLgP>j@M}zyD#2d>2=zV7~|LTbW*XjBu41t*{qu* ziDO1Zfr}GediCtNyu=y0gi)Ahm>S{r^(z=3@k$i#YSM}vkrz?a1h)sUJ;cbF@8nBp zG>#r8F@DX~WNE$0CS07rRQ?tAVB?@R#L7WdP?}qwp6*fwtu^60rh+}VDNj) z93L#hqb(u%n)}d4L*>glev-nQzVTWOxR+_RB5ikq*YVZXP=b;9%L?gxg15c%-Xo?i zL=~kzl0O+@=NHtA_iaRFOhZ4(G2%Ci(DJ0SJNoX;QSnOPSJgLF_M->(3p-4y ziOnmGj7^0rD%}QUr`YNcLN?+mtMDXDQ6TA&(TlgVAKZNdLw6l(UbrqZp+VP9u$N#gi1~-S9(O z9|HaHo7c`N@kcwi5ySkFU4mi5iDfe69OaJW)i(Udx`nR-*Y=N5Y%KYqJ}OAR^1gq(z^G5bV8%A%VLxP0%{E*K@9|3h!+HGFPse-x;Xk;C1Kfx=^Jh>=6g3~KGr2+y$c5(3YcP0j zq4}F87_VEq%V%1I>cz=hv@@;>HT=qdT$WU~R&xk1SYun^a!ig^GBg83Zs=a}(Bl3u zHV3>V@XmSUpo(pxE6^7_*sn(yW1_3ex0qq?j#ywvrXYs?UdYR{zVSR1w45I@dF%W4 zvL*b!+lXK;^{GAOa?#7O9yX5zby~DBuTxj!{PeJ^-^W-;x$(Bec;dHp z$|&VzmAuRz)7+h|e3FS_Kg(Gj-=e-X;eAq4V_I3*P1LvD_^emkG#>CyTuwrs2VzYV z-|G!VhtG4El|$T_4CqwEf^&zPRE*wq{VkoS#<9T0a(UnxxK(&gf51eozU?8Nx<2w4 zlQuv4@cPea{vWlMqF=a=M&)G=f3t8Yl=*2JKrieWlWwZqKWl79r@KvG7D>!mLs27t z3C9&-5iQPAy?-C`F`Ir|(e!)pb-+w#|0)M$R^s6-kk>KgS81-NC{vEHs+vmqmQBO) z(c~;oZq~8lr|rkyhVOm1iO2A-q2k_{`#I><{M^{_e1;)57VgrjocB3;MDI1l+n9@b zh!akB7J`B&&(8Sa-vCV=oOfOBY&?X`jK?+LHg9P1CI{x`*Lk;Y!CxsP3&wLgHTE>X z5??sv>)vNx?$76}uh=CLf4C)hT8!W=fg|NU!n>&^^BZ|i0TseJAyfNTc%#ChgWqwM zTVAutGSX#}3nfLf@;TRL$l}LRmqOhnvkUyBs+^L**m(-(g^x+DJRWLB|g)vJs{d<2z{Jz?C{>RHM z2F2wkeFpkvR+9cMp@Tp@`S^^aIH)jrN_HqDEiz>`zWIAwh2((JuOQ~{K3$YJHVXwl zJDG3`A1KFd^LJf{EDZQhOU9q#hAJodFAd(}dtTc@tE}Ne1svtQ#EzM{1iuCD70;R$ z+7+x{kq6R`c&*rLcg662#PrJfJY!)%=z5X~XC0Vdvi+x81^M?69_>~_LFU@T&}e3< z>}BpC60uF7obAG?ohHAv@{^O)Iu=5z?n6rGT!y%Jb}-7c_TDX!nsReuIup7(HaBiVAxlX%}=OsKO5%M*6Y*rF>>rFGn zdS7d9@ra9-U@+gttMq!^MLV?Y<;IODTxYuMVf^0*+FoZ!kcucytg zooG17&9Fw73FsAsdCm#_`1sV~5B<8%SFl<5hKMxwHv>PBW0%dEFxx4$IPJAD*tlm~fI7iEz z%85e|Ugfl&tT#M=diydw0-GR#!zIf`_amGh$wH0td4kq91<`;gO^KQP!}nJY!OIJ4 zYj{#ri)Uyy1+Ix&H@3V+h~!Q$+be_;GO!{?lwPDzD;F*6i24@v%_&3Y6&` z+40)sEGis7iJ5saOhbD9?|FD(YZ|RlikgLcH~gW%U+A5MsqoTF%>Zq|Fts0ce7UHQV}qQ#c|xR>N9`88XS7es1GP0J79R%D zv<1^Uh1(XX%_n2{oxIOVb7tvbX2kraNX_`2Sye_S&QRhl^wQ8|LFZ2U^%M`ExhT_% zKAnIpV?ucy+~|eoWgl(j5+aO$9 z4e`EekE0yZ=tk|OI-|&=GRG+v_@0%LBR_E`sv5`gt$DP>aUT;S>@eMYvFEd;F+0i) z&&&SSAc;=SL->uhkZq~;`G?G$a_aNoyO2fO`>12`FB2fcvvw@ z-rq5L3g=lE-bKYu|6%5M_|8wwWrdU?hRs$^k5IdZH)6k?*-*Z^hr%D?Ayf&ceU-i4 z!yI*>!+*5y{EW=(hrVZ6?oe<=*yOWU?&a((7(5x5sCU{!wl;38So~+lCs94?nQp@JH(Ls0Mc83Rq)Y$%CmsJO0#tnBR{@lgixX= z&Yd>Fio+Xn)>i0ln=VG#)$lVf8efh7lCk(QCPNf2%n1t(=-ckZx52e_6;PyMU@W*q zQi$aHYGr}lwThS|xZaBu9e2}k=gxJ(i}E>==c+&=tV!5~Y?8S}Q)kY0tXG;?ncU31|^ox$;s2)0ohx|T(L zn-u|P`oNt&nV3MIy#-t6Y({m*tDT#5eDowXpQ4y-ZxI(=2?M1nz0Y@yjL&`>Uxk`C z%krLQ>0O*Y!A-nKgUZYcj%9x5jPtz=~ocqF%3ixubI63!Bnn2b;#4fDW+a zy*I1UmE1wL-^Lb-7E=8&X}v=rum|6{5V7(n&+A= z&mHylhVS`z?y%1^$RVGeEgE5Wg3W~sXX$Hu-~J#|$~VvWg{uADnMnc?P7Dc%jhgD{ zz(v)`_t=NAH#d^fvheIa`hT#|wXeNZy(S7Qn#q<8=z){k5W(g2*v<)5h{Jif<^8z5 zp>sv-76SfzM%en+p7aICHCL$K42Cts1dN!RWLg@296c_Mern7j3 zDA=2ujvFc2501cD>_HpP5c9PgFs=zWsW$S{+#oRm9)p{Gj3qT4Z4u>KsVx(W!osl9 zkZ)QsOBp0=o7s`3U!*k;$K$khGVJCg3vGAAs0suXdzcxDr`S_qR)bbE( zdZ{u0k7sVF%e5G8SWWtx1$lo?@Ra$@Yv@Ge_*gLLAsc;+B*_?HmJ29Zr2#6fk!WSpKfib%Mfxlc}??Emypp` z87-IK@S}dPbF-FW;vlnbPw~jZBNTpH=zbLX$j8xxvwekxw$#)~_D?Lw@z@HfiFU2=3A7*!w%G=SL{2 z4C$8bHY8~IDH4zeRCcypEIiN|Cx0{VpNK7NM!7yE%V0;SMaMbK@zsAzIuS^j6K%Fr z(?=VZ+IVvFd%!Th-(-)ENYU^tjyp!SZ_kS1Og^^NaO)7pG3 zn{8ux=3dA?-WwAm=)xL3t?@3tYoPF8CZqNywliY&!CGW6#etN^__tb}s09=VX!xv} zfM9XWOj$ufH6zV@Mpa_|yxbZy`aUqTeJ75}rXpUx!@poFXIR+9>UeCbXRG#}Lc7++ zYc;=n;#ZJsF~W}6Kx1mJG<4=` zZ^w-mxONU%Xu#OOvL4c5o8)|JcP*`3p|kJyb^F>KC)NkjEw)uYE5S>j4DWp+RgoQI zZ9=%Wi7#c!y=Iqa%#beqM`BC7}1AYA+H!pEZAVYi^NhxX$`rZgk;vRZb4++!X`PD;~9O`g_lur zTyGyl820T6>9 ziIK~QS}T{2LtkWo{a4Q6eA9LvxUHDAS*Lxl zR){0=iZ@>$I<>B9ewb6p`pSL~bT8WwyqIi_V&Tp^BfT`doSijY-!;h7-RWE)XC zc4|+z3^VhH-KXHoB`B$l>g#`S&w4yl`QnI*JgDC^y2Pn)6tKvv$*#(HUDGl2&rKFK z3K|i7k2Y6hp}yhzdrYht6a5WkrOD4~Nz%QdMZ{=D8`9vYzV_L z`UH4GaGn+>>a!RVhDfy8R%xl^+dO`x#NT9b@I9CX&`8`M5hbIq%_e`e^60Yb@-a z?LUi{WtZ0pK86o4=6?AC<@H!C`(AE%-+;ntkq~-%$i9f6<=5m(ix$$*p5bV_@0wI^ z~oKRCvQ@`1U@EdbHeR{!gO8v`Smqb$J@lKCsK1Y7g8KpI0p6di-F+jWcyQ- zZI`E{4-iQas1%Xj%2BfZS0WMW#sT2dYSm$idozwq)wA==qXTSjvx9n=^A};q#kQa8 z(llpyHOFNgVy3Q?IIDaT>PSgA&a>SidB8A+W*kAYhuV~tpQ%v~y?+l8v`&Sm zJuQnTZbSO^EJ{-1%E7CvQzXv$_%05Ik+=a87}R;D;{yiA;hs>|C8;l{V|ypRNGEc*&`( z5(zgoAN4W&oMxX_wYZmIp#Jq%f7zbM(Qh6{S$BCqBqX~%SzwJbiKcat$a@;?1p2hM*|G2;tH#2JgoBlC)bTbaG)X7o%? zux>@tLd3=dS<)^lDQE#)=Ksx~on}pdkOC|j)c>~Nr2@VKrc|@ev80!jxVT>gn8f|h zrYKN$8UTF$y9of20E>m|fA<34RaGbd-SmHXBL6?%E$vwm)Sj1Bf4P@Kbd^=YKbr5UV&55)k}%9}=;5qJU0Q ztIY)epRP?4^6lRQ+!rw&Ty`w?5VL@MP=2EN8t%Uvr}LVch`|3JngswL&GS_g$$#A8 zg0D=2Vc_#lRXei(f867aZs2n+w|>C?oDH@K%>+J+*nit-LS)CiRCxj5mbLXEz_x&lbsHBk8Z3auF)=dA1WjdTX6EFu4}SuT4ke}V4#K~k8Tt7# zBT#Uo4Le`1T!NKtp_x-<^ zg5qL@wHznau@z-M(X8yf{hMN?gQjAq>PZ>6(VO#f1m<_BkodwRZk(a_MK z>oI~${QLJ?`|76$K5eGz($YUqzeQRxn=KHLkj~G}>X`R*%q=Xw@3R6H&6*9%wPxnP zh~ChDVF0Hw9mQO_5C|ZREKa`rfr98S$`nig#YtkyHv+)+R(@iuxZVU{wbh9~ zWTX|Usi-)qs=d3T{vUsOgr}gOIGC+4ZT$|{9)J@3^C!9+;Ju8tw5(JN3qcladLt1>+e691PdA1DX>e^RnmjT(YCo1K4e(*Yb}QdH2=&jdub=Y?4<4rVd>S1#`%v(? zl6dUfIy?QRvH+>4JCv|rfZvV?fSsP_ZJR-l1F+#Q+Xo5fyI4K%TR^D*@RGuUf_c@y zmKGKOh4L>L4N&4%tpJYf4uGrxiowp#!qO7>z3FfYW0@5Luua!*JOCI3ehs_^SOuTo zg@HZEp>|)RJL_kr^bSuG3j|;Wc3Z5^04$TpW&Jg#vZVRxF44AW zw#qU#Awdh=CLKRjw6!0=wH0YrnCwmD0i5JoY=8(!&(Khl$Mq4g@=myBxls`CYe~s~ zo<0T~dcgVuB#=ld`E+{Q<)+1f0S0>d;HeNdfu+TID79kB!(sKZq=W=(^=F_UZ{xYF z0jA=O=itc52%w(*(*Af+iApRa03?K&`FN%E2k47f*AX0MUAeaac4%pB{RsANx>-Zo zlI;f&Fd%7_m6ZVb;A+9d!~`Ppdso{7;gR3J&@)}Au{Rw`vWG%T^7FqIy8-6gar3jT zq$Cv}WG1$egjZKz=PG63FlYc&n{Y>0*RK*5lY#d)yNT?kp&1$E=agUq0ibY9x(%)X zCW7TS=7(MmJ=P7!)?ffyhC^z!Gm<7g0B9woE&wLP z9*bt(t<45*s9YLgW$VQ{RZr!XsVP{Z%rUS(0_kz%bxOze2RuAhb%jr#W=aiSJz4;g z5*Y;PU)W8>@RH~GV6jVAON&TmRvsYwaBxJ~IPOmsEiRnHgS(B&?>7N28wbY)v!axe z(rA;{{mvD@o7Oj1cZn`h-cvJ@r#X6=3|GmAMccNHWSRIKv!22ZMx@{*zbtfFM zGBYVmTDG>f0?~+F9JyA9u7FU)^)LH8dxftV7#Jw2s!~!@!#`*CX4TdL&RK%yp{e`D zPPl78NLbkOS}?P|Ke!D)6<&V<_^94UY8~w3Jmnk!G96(v8yf{t2gOQo|2vmRTLM$K>+@mZcH%WO;fFuyWMdSqU(3vd5Yf;SWn=)9 z2#^+q?blv22tbbjuSyf3Z$#?>WGXjGRGx~J^<4}Y@5P$)vSef13UU;Z5m8X&0enkA zfs2EKfZyc+U?4xfwRd(tw6Fz=hCm8{VStaMAT1pf76x#8fIC7g>@D#6wR>u9RuiqeR{4FWyZrN)T$I#GF zu>j|08#SbNqv!!O1c(AjdFhmIFzm0#HkU zUFzuANQN%qfi}RH;pOEe;UcZ9>juH&F!sxs!%ez!%E9PHIf*Z zn1&#tYOx+Zl?+U?A`AeKCT(LRLeda*jJ+mB*u8z?cT`JAu*AqXmTSr2b7hFkcas4EaDAac3S-=e&<&H9I{G1!Ku)-mtK+ z99(`ulFP#t25Nvzm5^Z6YijU$eggcz%8CkE1%-e~5+ZLPuYP|df^h&@GzW+Wz_U?O z!o7K80|XwJ{a;?(qj7G2K5&D;vj z2NXPL*yJ8DpZ<;DK0Q#eiQq7%1n9LvBJS(Qx<}E=hEAfK^`|;S`FE?l-rC+3|Nn5; zpSKwq+_JUzw{GvqHKP8@gMSrsTu_)UI}Aw>rwRaIS|P#;5C-9>zK}E;a0Qhx2W$F^ z7s{Js!pGy!pFfX;LwaWH!~U!um>v~cR2WL^cb$jgg&^~<@yZm%Zp*J~i-f(-Zkbu1E}dChU%$DXBd#G$Mj5lV%#v;vU~*$< z@w+!~v^eE3s>tGftjMiM~`wQm%q}JJX|!7u1#d+CQBrVJ7&gJ zUaBXMNq$MbeMMh?a(sMO9mW?L=dmbW)-WE9#gucfKU>Bpl}iXkjzj*}gy z`kk(|WoW)>WKy759(xvYL_LwvV7-ux>l4BtY`!!*Wc%%$gk0szKi575;|1)tl3-&y z>&Dt?CCa>rQCWcm#^($w>gu_1HglE?6N^(l^^Z^z7#7=+X@6~Kn3`jkAItr(;GVdQ zDi70TeL^vm@r;-lGv>j5VX{AD?q|AqBr`kP*U+-*n*VYn5{B+=9G2gK8ZlSiTE%?q z-sbbld48l>dW?fjZV%(h0CO(XoXZ{hHhX2?6cm^!V}j!Pc?`|-J+q97QuX(m367Z3 zHX=yR5_03_&0~oe6IV_o4h2oAB}w%G8Y?UB>exF&R?{|501-sagfLS6`0>}GJ3lDc z-}H0uZP4J*{7_jr^ejY*!)I+}GPj=aJULlITl>@1u*HwAvx8ghi(_KCjwZRuQZ%=; zc$c)kx!Eeu(~ik#^{Jo26B4FIMzl3G*@XmTZr#el1jBc#sp33|#IdcKT3U4D!d*>G z#sDuE-}cHK!)MPO7qF}dB}0@!ifV7)YG%&;M1LzHC5<8Ke5z}gE&RI6T<_^Hg$I~t z0KT7yLY*?xUlvDWH`c^%{wKQTN2PPjiEDmOo;3fN{ANNO^W%puFa61DdsrlN4GpC* z?6_R3?{T%MOv@by2Opy4fZP0|rE(OLGKz_!*4@W%WT-~*{}~oT`ADHcTiRZ!daL8* zUWSO9#>P##MinMieyHUu6~PMKY%nt0!kE-pSXk(cSg0>q*epj+$4>N z>4Zj}>k-R=h-7bWgQ>tc@j29-fu~pnNs|S^QmCTdx3bX!lWU%_;yUsK6z3^WIwrSfV|$`lX86}jzRopue)d-Rre6_(PA>H zkE{uPBnB~Jcsy$2)jyNC+C@k?O?-&TR{{Gwdd7E&2nm@+Ze0q0a#%^_m%!0GcYP}& zi1G39gGsbUbq)-aYjFFxuKLhK*HCX5DJiAr=WBld>JUc6^BP;?6}uNoB&T97!ieER znD|Q$D5bC8ld~cw<2Qj&iFC!=9TOKfPxXX#&=VKWoE`a14RyE zwBdXsDo6=QNd>=si=1)|31@U{P)A2D^`e%R zkG>oS!xxm|sG#z;;lp|4iIJ<{{eKDww`eTmg z`0?zQFHso=ZLF{ElPxKUrEwAYU-?9x)QI7?e%r|R**d$p$ljwHsi&ntp9NZ-s*WTK{)H0@-;j+AF%cf`_*sir#XG3*gnN=k1ihwF@{|n5DlU z`Rw&p40U|}$;6H?&{(2vcBo3{rS6@LwPghNO=ONFkCkHyySMQeXzXDUvDDHT(7JHp z@#zR9V`H|HMfh2J6uo2)SvACp)Qi@%NVOh5$ zl;V{2{rxN@IoAE}uDnadw=m|)N5{l8Ymh%mZ+fEcHh{-%62L02BbBsyPq+=xn)8B= z9Z>j_;-8yBa(kG?Xg(Zn1#uwhFs9Kx7>)Vhh-C`va+ZV6)6IPgMB*Go)EYM1gLH5KYs>8 z2SQ?29rGHWe3*@5m>|J7OAadCsr^E9y}VCRNP~PiOi{-8AIV&nUQ)t%lx9}2X*-Yf$v0F__egPg%@pW(D?4z zHCi9D-QewxZ8G>Eco!BI{n$BC2MQ|r;B@yfnq?rcK0--@LeTc_A0!(_Ve{bd@Nhug z+Ez2R(>c2N+sJ?0iZORju;<^UbcrTH&@jFf!Yb|82-zd?lno0ZWMaYqNE;V-qxQ++ zp`H^B@u$NSsWbe5Q0+eW>;m#+miAb~wERCq{;X6CLS|Fl1t4KL?AcqI6)}|4!NXPe z`{bnRnNx`p&a|IW&u080Cf?xgzL(+8fJ?ir!(XlDr`^qT0n~1RuO%Ilh!%SQ#{dG$ zcI`WY0rpd7orozYt4m^oSpGRBDD6s~_n{yK*CmZg&|=yw&5@`<5qPsb^YUylGu2K% zsb=~UAXPaP6spTy|MKg2s!J!}@9-H70Dgr?tyrKU0Zg066`tFx`H#p!(kR+ojYs@}%6@

;EIDFi#)<)8op*7}dQC*J>VH5I#Q?k&6%{ci`u;J7do%reo_HmmVN~#yWw-uS`{hf@>XxHa zF01 z)!JBSRmb?>qM{|_k-Vd{c>S&-q?%beAvct$rlyu0)?fKxCo0#nckeJv|EHPtZT;7J2zWJ94*oUgOWMxp0|~s6uQ-1g;+?qota8$c&YVq zI7aCTZv8q`<*y(_S9TD4Tuj)f>hAsOJOKsxGxR)?>nNFY`@Y>P`Cjsrusldi2Y+r%Tsu;tvS9M zT=hfIyIY}zPZOC8XgTjrbWq4H%dg+VM7JEIZU=D1!4tts0ud4A_}vNGRhZ{XZ&`)`S7c#>7)&wj{^1qePVW+v+Q^_RJVZmAyio_6+2 zxh#)+^W=#LDrsaDTtxrTfm&mj{~WX?hRxc<89%^yybZ>ieY)}U63aUTm5kLw2-G;m!6 z4cJe5ov1vfaq(h`_2Y#v*hEG17(^(=wFG0?gJW{4!djpsF@%|dU6B5=+YcxRW?y-m z!w8PJ9ksPlb43_xzhuk3(EY!9 zkgF+E2mq5LB;elfG+G%?PZ~j@$JbDMiiwLqG|OO#thy~kx1;btggwC1h}tb|ycif@ zwR8aoT=uc2uHJe1V<7?c9fSe)VT@SrLKTNI6>aoLTfw-Ku0ex@E44aXTE74MN#-hc z{Y&`ltZj+g$AG;vpyz@agypHe{r*|UK4Nk6rgAS=t;VG^H4{-nprDYDcmGpTrBo}0 zj9YG0;Q_LNE-5eQboxiA4<+42wehqLhUK-jwd1Yn70V-WD3OW8h-%ZywRuz+T@{|v zePQ5t!K?58U4SZ(6QPE&^B2xk%#YOQ*C(!)-aT)2!-Z6yO7&J@uS9mO{Fp;tCq`5g z3)$7o{(^*3RaQQ6ZxzAcKQ?bE*ph=mW%yXVgQYiV$h`es!`0pq+$W9xb*|$q3^JP4H39rR0RN6m+H(crG4CALAk(G1OBMp43^8kf6`2j`Gocq+CX45 z&cULlozyw*bE=??%`(7f3EmL+rD$wCh~fs%t^ri9P@{KvcpFLM0wY6pQ1HI!o~j$i zxVf9y!$3*;5A5jNDt7Cn+%iVb$BOJ0^$3+wQL%(pn0IGn*Oao~gvV`C5=5GknNb=! zoWATPRli3JsK&)Tu(9r2Tbq23JxLGS$g}g=VE-R>^O0h_tg3n(n)KB3`g%1KEG{=9 zYJRkT`=%P-^NtDEGn%6aOPT53u%IpabBmNQ`DW_<23!wmy6xT}^v+;Y|+asj17)M%n@7m>MXb7rs-!Wi@+YXsNfCNyB zyW2JJ31P)RqWJlcPF&f+c*-5yRs;v2gFtIU7$hyNfD)HyJ3CkjmN%6Q2zz@XL*bnt zinne}jy5J$RlU^7x!Q$D4|LXa<-|=taofQy>xT8)pGHO1SiK!n_R~Wh41#P(1shVZ z#6Vb3@cwb+=rUVLfQ*(K`x6kL+1R$QgiihZslZy4{PLxmXD*&VFMcS5WD@UjEDe0V zd|D0GCb)q5vHIBBD*T9@i_7;y6BzH^Tf+>m%(fyPzJqMC#cp43mRCZB)0`=wlYbTQ z!5-vgdC?0>IdCmBlmARKBQZGG$`-`Di6>PXT;DZ_Wpa!(G+WfipNG9inrofZ`rbQ& zn7j~Z13N>X^j>OW#qR$&5CN}Wd3HZl=57lEMqA#|e`|%g^d{yHJrjw^Qp$$0!23A{ zrN+}nwve}PtE?KCbGztlBCmb#TT2e46kmSdXFf+DYZUE9c$z6Uo01?b5YUWk(8A8%nN9qo+5JZe

zS9c2DxCpLV)X*Z2a^|k&_B!lAhPX?Ckxj)f7F7Lt%qURs-b;w?i)&bEk|lP%=ha z%&j6|LwTnxdzw5GgB=WND(a1;$Zh9r6#1i6XHq~C254B%zl;U?jf)t@XB+kjlCy8wVBNu((hrJYDa*H_(T3k3z~~XD#lWus_WO)MK@w z>u`}4tiL?0TAmwrYWJ!IYlTYObpo4!L$UEM#iMZ*K& ze%D{QK}CNUbk+-gO)P5w;Lf_4jqISKD5L)$OR`-;o7{z7IaopPhKSnKWgVTJmm~uu zzQ1N7y{jq!TyOQg5 zkuaOwzrST^l$5yVpiNZQ)c8*yEX$X6UHpZOUULmC9r62)UI+*&PE+TPj*irAvDil* zoCP=qG4wl`RmaOK>WLD2S!ERUldAWyO09L~Ut6fO#cg+}-`fhz7t>nnuvumtPi23K zk%`IV`#L_J_^ua(_o}oFNzwy#bwLa@U=*+bY+c5<<=Zy_9v;*Ftz#ZX$E{dwr&=cs zAS10=!GzP~k=aB}xk+7i5;nYrI^Xm=GJ;;7EYcqDUcRhvZ{NT10#u5JBbH<|Ef!$0 z-qHzTyQ-7!L3?-cm>W7{Rmt!XL2bVI?5K!E6C0kHdF(T5llo(7#JuL6j~s>9_7Qbp zoFm!nGERZ`3@@C_G{827N=huTV55QB5dp~_~6i}SEh{~oWdUjPb(^%iI$WbKK@?SHhgfi)MZ5- z^r=pdS7hu%eh@$QV(~a3OX=sHN2N>#7WFKxea;&aR0;v9t+=%9Uf~{(a0nIUdY7 zGpOr-eE*K^ni3KckZ4J7E_Z$U-qYfvZzM>Y`QSrKI#1L%=gBhUlBz@B$E7RFD!)5v zTU7r*pIDg_)bXsu z8$k;dp46MJYuPE@O`co+XIF%QllcD?Gbik+|EE=Tj1_xQ3A15f2#JeBomzpfF%ER7GF)=Y9zT=aBtY}F8c(d+*X1jftFZ0TpeKzBg<^S~2 zd~JSDKST%`A*;?j9G!3Z4@i#Kl_bCayqgiIWGPOIn0D~V4$D@BZ7h>xw;5x!p=1I+a?1-Ku&IZQsV*V-T2SxubW*48=FdOG);#)nwsAK zwByP-g55F@GQ6NJS>3)(e+6>%i~NLIrsAu&@tyuvPjuf6g$EkH?)p9{XA&CFU@ojP zGQW(v_~l&)IrYD>r41TqQ~O<91r!TzSLWbPi|eEa^UIsD#Z+dy;;SQ?$WGt= z!yC+l-l?wrm3Jc(yUKA?i;X6)>laE%mlaNrOZX{lzx#EHilBA|RZnDIopXAJzY~q) z`(15xfXcs$*HwZ=ea-a`5BEbW(NdIH`}?WLl)u{;=)jJ*5oScQ9tH0*iPqcUIe?|T zK`t+git>_^y+gBX)QXE;<`ZC;ms}2CS#iOROr&bHM2Q%hS6IS`QWKl1T3r-C#`xzNk4q!VV z5}1-w4ayh5iDm3X(k*u@h86zy?VNKvRy~QMO8cp-+}+oYyQ#ou1f2`S&(ZU-Crv*F zz@RnV{7eaZemSxb5>b61M!&pT{2SCNDvnpstFp3goj<=vxEQ=*6(I#gXKS8@f*ZI^(nhwo%zW2coOx4#e$7f~9) zZ9?)U5eTXKjrpUpo;|Y%^9;VHvx&V=2DHU{rnlp=5CU^kL(agW(XOPp`mPd$PjnkR~G?sCE?|bn-Z+>K+pvSQ0E9d z%O)r&*jeGZi7E%iQDAdiM3M#uf5*dttz&zzI}L9hxR8>P64^I5&lm3-e^|<%9FnT| z)1i+VqLP0Ph4H_(cuQ+*aNb;=joV~%3jTf%tFRgD3Jb}|%DSYj?RScrMZyU}*z6`M z+86%*J9`ZX)YO~sNl>uE_BOdLu-@GZ4&8=D65Q>;{8V~c8a$EFG(zyN!6RNGfqE81 zy$=o8g^Vgp^1;SB*iQmkEwK*O!qwu5)#~h4=V@itE$huJB{mT zY-OcpG%-*iy}pT4AoT5>-A_v1sIahSU72s+F5=MngtB@p$-FBg&n)P36M#g(xCaGB zcSCF_M_parJ7K&eto?Vv;BHNY2L=iR zeG9jypkV$1>px$qZNs@hbniyEBAo^DwS6hJ_3ChYH2x#`Sr+(Y`!FzYIcrqiBX1q##nD zt>*vs;fI7A^?}>D2I%Nd?$WAj5uQISMSQ!h9A2`(v#uxE-OQONwSBSRVVy2>7KI+ zn`9_<7qEGFYAd$g<3gB!`n3zoLKnuG-^Oj*O>cpEy#S{+QRw=YpzaN5L&@p%QFV@X zcHT)}a7bYTb;3SG7Z=M7R@c9WST9HXrCxj2kFNh>W)P2x&6UTE@L#50477q>B8;f?&Kg9e9vw|U0Pm%Qg_Ku17>*=3LAc1W?P5=I`>l*cciYapxw)B|ax6rPojGG<-D%UA2S?c9TaUxS_FYYOnkx8~ ztgshB4az}$JQC+dRK=Pvjj6dgxlKZJbZ3A6S2H2hwel2Q`PY;*G&Jy|B%r_VNb0Vx zR^X(?((r&SS|q**)sIhT4XbDG$B&#q=zM$)Jw2P&-U4f;VkF*w(2vt1-1*UebL@MPa_^_s}Y^6TXaW@eo6H`R{pdmA9nVPs?^TZw^2a&g9YtUi=EfhCFXpP6Hms|35Ba2d7zpLp(6tT%JCSBUa-dU zPAcZ)*xZWUTCbf?1FIpK-oCks*yt0Qp6CKoM9j(_SU`NF9*-t@vXC47SzMH5S}mO^ ze`dP(O>6R3c6qt(i?7*7gfBIiE7H%`X(29+WBwuZ8Z1jH=N`$-|xpbA3 z+uLWlkz$e?2!|(eHaGryZr<~#i>Wj|t8^K~rTc1A6U|2kB>p8jV_RDqzaJaeIgiu= z2Ohf|zFe7v52I~c95(zt?GtX4*nY*s%8Jl2Et#(i1#EG_Mmv0#SH3K&>e#~0%PjV!sx?S#V7qyvvgLxV2|#LkNb8Q9ATv;#y5 zvpU`>3TkLTyZv%;6!AUWXW>nAw8$V7fVjZYafCm#$i>Gq`$}-p2laG!4;~(clBcbG zQH~p!71i67D-21)*YiQ`WBp z(1zsw%hLP{8-;J}i%=pb?XIh%Q2v+JN^ezKWJMXe9K*6jdg%e1@kk*p$TivK8Gs0~ z-!8&p;_iy#4z;+wy>NU6xCq-te4OmZ>%kP3Kw5#;!654n zYXg>QL+#(Qv^4oG7u{MjPS2$`#&ExMW@o1%Wq4#}P=$Q$v$!FJCg#(<`#( zFhdET(A3dkWnnR#ZA#u``R{bzJ|=giQ$Fmn;@>jU#a}%g&z|i*B}$LXykkmSwiY)t z4IdJ2L3p@)g=i3irA#Q1f*qbK4KH|2cG6v2(`fszg>0UcOCBBd3zAw|zI6Q_6zdfz}AHEG7T6 z4)S%n+qM!4&~*g66>c^8gC}8O+xwISv(>RnIQVDSXR}Z$u)FvvZIVAgfvi&5v3t3w9kYqptwG14Eb?V$6oPX>W)^-Z7`B7X_BFoD9 zhWrJgy`zJC_1>L3<1;h&?CeTz`nik|{rl14ao%1Er7Y6Up+%a#du7f)AAoBHQIG8p zHwol?%9iI)R#(??T0oP9Ca`7Ft=qB0jg@Z|z~eQ5=-owk4j0H*p^PTRT{AcLOC+XV zJhqT{R-m1B|LzC96zr->e7+*}9&FRoEOXu=S`(a|I+_gI?WBXKsk zH2^wa8#V6rIezoL6>VW*akjCq-APwdbL;s1SLO||s0WV`9;D(J%5W0XIMDY#MRElH zhS3PytKlwxf6pFvE7a16-$ST3)f!MvB91|xcPr4z)+2L|1=qT|IQ8I#JvL{X=0{hX z#>B?rNP-d$s5YGlcnJyUGy`c7I_d#Dd2qlNU!0VbbcYwSt@A?WK5dv!pFMl<+3ah@ zAQaOfV7`=;i!eUZ2I5^azk)eEB#~&mH6F1BN*eg)&hLYRTMl%AE(W`U7lV-CU*)C5 z?uv;1fx7`BQ@}YRw>h%lY-<}Ee17Ze>so4QA+A1&iZ7CO6pU9_1`Xj-LJ0KwRZqM zjI?iGK&cCJdv#siWEuk{6@L*zOQOfh&1=_w;8LwH(~SAA^bBm=h48ceB;fI-u_ioI z(l+`z9S2Gq_f~=e2?GPXdsP~1#N77q^KQR}!cE%K-KhlnlT&-ktpdJCA~Q3yE^o)$ z(W+;7Fw9i`Uo`S`#U;=ZOwYu$iU`m+kdDu>fF5ckc3-sFvb8yH4*F0u(ivtq+7<-T|!yaWB zNiWMOohIpJ;|voo(i2lWA82RmDUfXPDKjQO?#GTPD=FqDIYBzNo~FkyFh&iA9RXYwWWue z*Ot*0{?pDGN*?=geho_x0*zIYG^=I42v^2SnQt8(cfWt!r7PTuhVczmkOs+b+S>Rm zwfG!w@vM}%flvX2O!C}VF8kZB+Gs&@3ZM)R9C+0@+YWz-X66k042md&N%8%wP-=KT1(G zk0UlHRqN&DwIC$1&?JX`Rqsir1p-%u3f{^L3Kpb^*nXBA&>|kr`uh3-kx;Vfr7O*a z|H}*V*E6tUUEkkHc461)7qdPK!CBddw!i9LNH{O*Rz9oEREQ&<=e=-mz+K6vUpS`z zo1yC-#_cEt-n^j`ZgZn#K)L+o0%_y7{X)w|=+oGl|LxX#Zr(}`4LZ$Yk; zfCLWAcK=7xs~zJxQS}tfM4@3Guq#4Yd^?Xe)rB&Bx50H+BO3*)C(Z= zlNxWl?rN)Egb5>GHyDT&_mV#fcnU@il2ge8(QE8h+@&xo0)c=wNik-ssJiR{qhb*+U zXz1(X$K#7)%#X9OCXwaOXJ1je;^CJ0oCN9=5E?@Q$Z1t_t)(+O!=Tl0l|_9-<&hXjkqW#hnA z2rIgKa-TmZ6AFLyXwTKB-a$MF*G>DrjLw@hFIB8h+0=d3nB8oX_yWdHH)X5%OtLnq zxEhO$Rp+Fw6sX6~l~>?AcnxWvmAl?Qg}&q21u(2}pFe-Tlzw5aEMd={p~IKvPmSK- zesb<40)5?In1B50et!6Hr0vmz7s^3xy5%UI$W$M1xPlM;m-oTrta_eoB}-s&Zio-H zpVcthn~p@Zx`<1le9ciD(mMR@doyK>Zp%VNkrp5tFf~_UYA2P z*yPGKRwi*=8Bd9$5!+$xtU3+vFVs?#Gc)MoIn6zFI7A|4_LiqA!^3iWNy#$MM9Y8u z@i9V8$oq+7RR9!FSeUL|1~YhSWIA|TKN`xd12r|H0D-fkpsP&=2i9DFT||FPRy8*%W+uhG<39j-t$uw#O$dp3K%E$>%Z-gvt5(oTVBU)Vgsfo)Q+-gi zwFn@Y!UkBYp82{GXe8o$>_#(<>P*T}&jA@spi6UBmki4GErC3m+q^Jn1 z`;d@O0#1x$SjWJVh_4z=X^!^x&$dZ9{mQSw-;URWH;xJ)3=TW0&ZG4Usd#dF`htzLh9Sk{}b&}w@^b3Mj7iI?GeE22Qry(XjfmqeihVy zNNUWV5O}8XSL;iCed>)2QiQK^gkH09=q~D)3}X+tMM0%P_{=y}TkfL+(98 zQJ_sO^6{~rjjc@6G;L5dtk8LiJNo?gFh%MXY;iFgJG>5A!g%klS)3}Iq7NR)5-<&t zUT-%|FKTh*IUxRp>uUEgQUC!cl^pg*_4U(IQWvswJ#ayEk3A7*8gi<%OoCf{KLszK zgrFkTTY(b%_86!my)?MpauuA! z;4tO^i1oT2xZrAOASD`S4ckXn`6Z-)aa9?ig9KfpveHu5{HM=0Y5zmFbR9o$S^Zl6 z#kBKx$}#8ulT!IUG}*-$xU)Z`Fx{425qNp5a&#WGH|U$PvNBYU+qQ2vxlh?X2+;Ti zgeN|PM!KcO!WITgQ`2pk3MM8~;PSyF!CeK)apkUPoPnWXVs!Kv*e5m;$B)jQxg*y=2eQ`YD_A?G$(XzAOU`uu!TPd z0i7~1;1Ww%*D>@ZVPw97#_>X(n4&7^f|ICHDN}PF#qQf4eZbX8rF+I#* zdVH@MTt*!oU@DiFmhgJz6%;^S@`?3;9LR-V<>bjW#C~+xe^JgBJ4zdf_wagzo4Ekt z2uiJ|r{|3uqwn9p@9n+nJ5!6piMdpYisX_-GI0{9bTP|nLefm|U{E{a(vfzG0~9;( zcH8_ZDJbChzF#`PPhMhNzA|y#_lg#VSO7Y~ii+6`hZq<>!x=4jP|F$QFlJ(aL`J=Z z|3WGmyZo)SH4LA?&z}(JDUbJ|J)x$i1}c;TIS6yRA|$JyB{ zYif>??DtRcnc>qrBQ9 z-cX)XvQHwfY~hb8HE!SK`(fEA3vUZ&b}T5#Gsz-QK1G8%9IDF(sdO+mAIbKl zb4dU1eabV{-OK)a!|v(ow|jguuGLneXC-f2P0|R^sn8kzp8O_SaqX<1US5JQYf|Ip zU$cxeReTBNh%6OHdTA3<**7$g?}m;>*M-6_$_5$AmO{E7nWiRV|BnpD{j^`WhMePp_ zzSE0Rtoz|)qM#mEwNL&@6I6|p^sk13Pi({b@qv9zOhI*X?3ADYY5zCB?+m8-{r~?z zlHKb6=}BY|s)uYnCSUzK7}4Pjk|hQszI`U7g)kY{VE)haLUsowxT3(AA)i?9{D*yk zeSkFdu_?V zRtyR(f+cbW49tk*d5v)2p-e*zo#4WkSXfwqSOD6Dgjy;qxAr$<=tawORd6kNa$aXc zU!^)#YqyslpQhHoxV#JU1Qv z{{5Iv2cUtzJPhChdU!8~S#6@C9hH?{;<|WA0N83rF7Zb}`dVID`LKDZ6j{;2#wPu( zU`%~&?K#xF1_pgd!I|+W6p<)!xE5qDsHwrWh>Xp@r0gq8EG<2w#Tc)H9E08imWXql zNY=LBHJ=x}dgcB7M2s3x!Aib<=s8Ygd-UUXw6_=J<#F@z`92c}`;5R30(tBV)Q{{J zFT4sD7Z!Y4Tkz2}H1I(kC@VAIiTEIs9tGXJtGRg&pU72y9ZuS=&Q1^*@3)dPX~@uv zHrKATc6M?{)%pARja@#9{|hEYPVUdByu5t<=2djf;HfR}f$;>2LXq)haC(~MzyVaq z*|Q(eumBK&+)VP%>E!gj`SkDo$vsLRgj_h3V@q zP#NeffFgo7h#Za646oVBsPyKpW#5_~WIJ~r<>Y){3Rx;SIT>xtN4dD*$HCMMbh(k( zCZ{JV&tZfp$B+p858fF|OE{=V&I+@K_}yqx!2q?YoSYFxBo9=Na2^011_T6v4~0Kp zCiwfD6R6$?M4}10OD~@WWWewDHoJ!oDA2&9nO|OjWa#iFYT)rgsKgY*i61|zj0Mrm zgGOdasXlSWZOYRO58jE~36Osbu=9UY>mW8Y$68OAU`OT_H~pY?Ni z7=Qqk;n^R~W@ga;J^@jQUqqt;9s{0=9cjFe1IY%m{(cm;Z;S@?mxiSXzYvH5wFt^+ z+<9>B13f*v*_%IoN`p9QSQ{N3jaRS&o>-d7w{aA0`RHI0!Px~JCMc)@H5W5FF=%BJ zND4FU)KpYbRG9YfCsUZ3o|d}v{iCVlxhZLW{%H&`!fU}31+k5$r`-fX_-8ZQ>w~}# zkAs4q-DNp+XwOYeUiwWKCJ{Vr$?>*UmX=_isvr_&_(_g=3?}2H(o*VC?q7FE!z;XlgQ{B(sj~`RvmSxW3K21zapoGF@pz#3h zyOW$egZhJMKJGC|qk>s}^!-P+!5qg~;{k)5y}?53OJTc>=i{1_9#^p>Tln z6;p6f-7m-o|8usry>)pP=jVYNpV{!jUE#6=HWi!=dMnOTqM|K55ABL2pzUCy$Obd{ z0~2H8vw!x;jQXOdLN+9%w6qkk>MDOyObq&m&yyZt3Z#1*TL4PoAV>T*lDLZ{JP{ z3evpt!}rP76H$&}`dgDU`lxRJ03l#GrD<>nL&V#mvNup3Xdka;fiLvW;Fi;3VmL@2 z(lO28#HmyM)MOyp5U4dZ_c{#d>gn-!Jmf!qycD)v_+i;cFD7NBqW0Sl3+fnO$UcJi2nte z98Wm8xD1Nzd?$*->VEeB)1;sG{PpWrSYw7<>1b(9uU{9bDE)URa-X7>Ukc-zX}*|% zKqF!x;{@Iu0umxSXd?+FjwkQKN=r(>DKZeqQBwDwkd*BE_DyNP)58PDP)f4+tY69~ zp7{5nArotBNohIc3=AZ#WIN7ALs5c5(ZdA`D;wK=sWNjL{nz_;fL+ z&zHyhQw0VY!_BmYu}jGy7?ZpciGKe6NPUp?_b>Ji4B&|oNRn|TZlIdnPj4JTxIFRQ zh~XAPt-|Q(NXW^V9vozIZv=hNbmEk>9N1C}V^vpE^YQh?h`Xkcy@T`1=ttEfipAe z`_FPb!2~TAXXobm09ikT8XqypU1+&ujZQD>U;>V&i zh9MV;3PA!%CiziRVj>1J?Pq08LQF;@*(IN3cWye$pr9bs6OitJD)2p{=?}<+;gz{; zY;24>iHZ{cH8o8Em#Y>C*@Qmmy0vwKvEYdR-r8Hpq|3|8^ZGOQ6N#t~AQO#U@;WOk z>kZlSWC%(fl8;A41z{!GCMc1JzsG9>QE+J#wlTSg?BdMAiA^JxqZfx+&7B4dqEd5*6ma{;YsUB;D@p?<8( zD~0Yups#o@yhyY{A&cXbwXsBo5fM4z{scG!&GP!ys|=P?E`DFWe!c79a5d#x1}ti| zwG^3s@U?Mr`tZD8Be{`q`0-m6I5c79R8u2}91h_jq7T8e-TR+-eS3GlE;Cw=tgPUD z1Un7;{0Z!I#^+hz7U`V`CP09ox4*fV*yG<3ODT zq8SX(Nb^dMne#zUE1ES6w_(!F^XJ! z2zolYtJ$H;3i|`)@e#lj%JIa)&8_#-CqG6`#YPPjm&g`voz+Ny;FvHO&DC|atxYRX zej5W18ynkt3H)c7)<*b%M@BxGzeYN|kR*kMD&DZq@Z4RzXnO0G>Uq{nFreXc1Rc5C zAQXoYT+IBc6n(u0q7UXfqzi?EjDfM>avPYwpEtN+sPR3Kv5@IUh=`0-ysUuE%=mWN zz+w1h&?1UQg>;4I2rM-;F=t>==S0LMYBusgabX-h~iBJqqA|C(3oI=o}pRkGz%`d~2m6z9q z&rt6rt|a_|yh@7d%Cf{f_>|1dulwvq#`N@UJW3wJ^I#p3o(c-kZvr|mKt83pS@U4s(c%k`>tT{*PQXle2_Cc2GvQ?JK_Z@?{ZBx-ii!A``|`>Ua!?|DapG ztYGj}R|@!ax%CAO?BH1XbIc7cuGE#X6l1-NX(^dFVYcM3@b`3e0krg4{u6)8&kZqK zZY~h2jRg@z_bc`S8>93Ito{yYp5=@4>k|u0#2MJ@A+nh=OiUF1!t5&BcX)-UE%&dS zbe2fW$+?X%ZTiRWpNr`s8BLAXD{FHzNzygaasn>|c#ns`kdg?ZM%AYJyqX$j+V#ooi@E0|C$@-zBMvOo-!)QxEaISSI`wY1v2 z6{g0=Ph+|P@|t&u>@owv&)1i%?1z<|-9@xq{B{CD^7pUt&h;fI7;w`B%7a$MFJ35m zQ%@)uG-X^MIlR)IJwpX1J76y_zoUK_2mx>&hC;MiUKff%9!D7q9K-R1LP*QWNo>6o zgQ7tEZB@!EFE`ZDi7F4~++lL)P{OjD<-aM@TK+nBX7kq{bG;+)W61?OP-eOhmcTt@ zQ3S~h)pxMmjgQ}qQA7QE9Z4M;nX^wKk?kaM zDbk82!vw}C;TVEn){=y{AX9W)Kwtp9{k-g7&0nJx)X~8K0?K&MyUGuHQOyKBlJHT; z122F)J7#SR5qi!Qydx?R#Gh?mXt045a0>nYIBY^O!Jo}gX`#WXnHueRG^wZY??2{a ztPYw=&iZ*f9yDH-d2)^uORgt(b0v#t%fT)QjWq-`~eH zEc8P2(guRUc5x{wC~!p6+}b?75ZwZgHxMv{9!vff>w3~57Z=x)llx&=f)^*jA^^`3 zrp9gr2L@`MQ$~G%^g{f77mzoLzkhq@;-l6vH6=lNlALLY?Gfei)Y?vfBUFoZ_(H; zk&2D&>2FNj#p?)7CxCna`&_r$`Q!bq%AS*mh2;VF;}L?=1wtV@H5E{NQaqW>>Ojaw z2aK2$aKDOybGu3l11KtBB#|Z10KUS-^#bUy0k$n*3TfOP7`$eH@Bn`1k^G%?2EcSc z)f8Z+yu95&ghCw1WgtM#{T47pRdT&c~Nxyiuq?<+cGiF z%2r^4b#<0~oTDnM!{jgSy?@Y+tkGy^0H;P3m%J*90L$*B@Xoe!6%eCYTxZI!w_~Jh zmLDNCP2sXzfdM^&g~Y1)R!dAnpK1NJ+E_$&Yg==tFI4VyKYpxp)#w>2Sfa61==1yRT#%L(xaAYjrj{LJul6{S53c%}oG)b?=RA zhnDvIEaOK*TwaUkFFBgLdcE6*Q;EM0*N5cAg0#6%)t6*GyOqlUEjGo4hmDH#zmEFD z2MXq9oi`ryL4s%hB6!;jIENb1s5ax(B_@8ZgY~;}wT2Zb@p5^-2Q9=@*-ncnJsFh= ziOi*?F~Z)_85+I7PV3UU1hka`1luMr=VsWx4>)9|HzCOvo(Cre-a5!;+K0@qPr2(Y zRFUOpew~IBdra*@l285uO4BUN_1_S5vEWihP=kvjcr@C0GZ+YW@$rm2IkEqVS#y1i z`$&WUQ=Xx4Fs*ltw0NFB_x-&MeU@oy8`^rN?d_*Vo@&Q;|0F{7>_?>t&bJ_mCTK%} zw>)$-r+d{sRqqw-@XWheeFNWyzSWt%*g4%xX!H@1HK4^p{BRr{WS{vwXk%~QtQB#& zINaE{c!1KdR?!{YH=X{X5mh9(n$T|J!mE^7w)lv@kEW#X0`0Oo+fOA#+;cAQciB2M zJZ%lAF*s-`M*r?C#?{TUTC=P6AZiwaV2z?mMnLSZ8U!sh=UY} zG2nb->BlngsOni5;^3jLJ|CD#lvEGSdg!9c?r=$h)A-T3x}t_-b|B8n_=RG!KJ+iX z{7il5kC-E)-YkEjgdctV1U98^CrKO&6QIkjMH)NQrwf%yvwIT&=3{&$R;ybbH-Q@3 zH@&0*#n0gGqfFVnCF2v^P>Oj^6g~X8PH1boU70dk)IiJqqI%1W1Bm+jA(CH9gAB

6(%@z!M*mPGPmCy5 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

T(3C3GS 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 $!~L#viC?!-A=)#QQHa$)yVjUQCAPukM95a zPvKN`!EWP+i6xv$q@T;-0~1jPZ>dR*$BGv()F+-jOska3Xr81)#b02#_*7Si%C?}8 zb6e|5BnEP`F)yK`bqeAK24_GOfaSM|^nX~bXYOK1$7%(`nWOQ64 zqGqY+d2~e092Oqc8NsQMDIHZ4!AX%k$G3qxXF|G7zJFs#tNBiQmDF+gHjOm3p#jHj z+cmD*W^)5WCjU8#qfKgShveAXtKKMt%L#|ENK@W z8fE`SDe%jP%xO1*ykYk|SNOvpcgkr97&=Mloe1~NY zHD|XYm8mts;eK4pL_j3?yLJ<$F^Qo05vOBivs)-xtX$raNmcS{5-es=aFi|ZKu0(ej(2Wecy+Ex0Ub>JT|w_zV2`d zTj?Zo37f}*)XESMCjl?oYT&JucjPjw8;;E>zYjb)lu~w*@axFQA@LnbOrg()n7#S- z(vcmiksvQ8ifzo#V1pve68HPo4{v-n=)FkRZC3ZcZ~X8kpe(iWmc*pW+F{nWUksLP z)()d5FmuNqZwPpiuz+7IsbzO7BRa}bDkB7UA}a3)Y>0W$>!P-Vf4;dUdvv`>tMHhg z>2u=>Ikp;~IZe^f?IFGmK32o$;|?H+Ix@sJ7$WgPYso{j{#uR165|>25eaPbh8H6hW!4qK`;ImVFyi-N%UniqquYvVZ4_BBIBV?b|0mmH};Z5=3i zA?8UPStRC3=4U1}X|@wzd0TAbq!-z4E#(@`nlZN*^>fQr&7FYC;DIG3@W-Un8pnt3 zFV)McYaDA6WZ;kb*ttV<17G%wcDPRph}OAJvZt95A>o-kUmL_c-^f}*!Db}nMaecvzji9Za+UUVYg%27XSi5U z*V1_}N_o^4?q^3@18mNu>m$YMmb8XM&sed5=LP9v0h8D_X*C0$cf|r|ndfUp=B!pf z9`|BCx>%6omhyA=%_F~W2KK$XE17m%+c^a@i+)Xy*u(>s{T4;gKStS%da1q-t;Uns zZjFF99c<7)<`)N71MVqH40b(YduO{P+3|>N&Z2j0D?(Y~!O$uRcxTDLve#gH*R*&2 z3tGQ)X4ehCv^#nKtoAL}Eo($#D`<6~k!CH4!{tg$`gD>3e; z%D(89``OQn@&UHxB=*l}IhjOmH%G!m*=dAl{QFKpqLCw4KjkEDYr zSCX85Vq;Nm@zaMKFV?T$SF74ESA*9kgK7LC8-wxsai`!+CadmT^f8_aHye0Z zIhbtW3PF#Tcvy}h%2UJqg+89YGOTfAmAJLIh)$|9k_ELlEbPaf zq!`@O)XsPx)0vWxZeMq8zA@*2)A;*Sh02ea*#+Hi%eC+D6_9-#WiQix{Tc1Mb?1(K zoLgjP*PzlySJg)ATaW3;2!;sWQ^h_5`T9HSwibo!^)+u*OpVT@c61FGjZ3`zR?xS| zNm}j$bI)y?LAHNu80-Xjj!b)Nv4ur8o@Z8Y)>rhcT9kUsK)3#d_q<$O*P|7#ouP4>AAZsHaxpz~ z1#$ihK7Qjt+f3H$IjXp5cadqc5!=Nw!G7NxSKM<|yM|S}6dQT%(`iT< zrngDG1=+sawV#H;?I}ub+oE;Dc=X*)2lX*qD7kxu@ubX3<=KB&j<`^u;Ld0N^#jxO zZ&&TaWnOVcH}}{N6yBj{Zj<1dU9iGl?$7)F_KSD?mB)(8KRB5hDcPGTZ7G`%Cx@NH z*z3_9<5Q4E-YX0|-t(1-mG)Z+zdQCVydf!S;3c*8GvVs1F}a)Z1xU{JC@9XYGTgjmTp4YLQp7(m=z-u2&!XrqKqYf%U6jeLud+ zT%Z4->NPeyx|7Hv)sw2rw)@>J1-(CR@v7n_E0HkAZg=tOwxF8@Lfbb!)T*p5fAV#H z!X|WfGyi-nZ_mtOSPhdhdSr}7C!*;{;j>QDf~&g;&0a{}tdguKZ$6|pO@PJf8Kyy+ zu)ZjsT4TJhdJIe?rcXgJ%&Dc$t$xC_M+ey`KCdWjU_BrZUYMyEXYkbgWKN9Sir!}a z=A%QOWbyAaPLSy__eeS_ zm)XG|ayFnt;U?3zy3@CdG(|lL=E&j4?2be1g0!YR31>smE(vFni;nuAmP_O_PqJO) zi&J}*sx29;i7#xigpnhx>qkHOUK-?~fyo6M=M~N)7fQ)a+SNBs3g$SKMqe|sMW|2i zV%Mua-Yttj=Q4R08#VOYp|7&n^4K!e^1yhTWzUS71u5!}X6G+_fBRTr3O+D7E5BK+ z_Ttb&Ow9NECN>YzpBp!LStB~R`<}X)zr2U?ao;(gq(8=W{!pxwh{%Bgp_gqpbM<o58u&c7Ah$hLJ%+J`jpbO36b;=0DeDWQR0yU+S2sBF z#?}qHQrBP0iI@90yPIbe|JX47X6e9{s%$^%-t&FH|3OkEHxfTGaW(+ewrHKP0;;~O z>ke-pF0D~2e>q?Pj_YF6;I~E=CqLWzy!gZ4c+&&dF7&AYx{F-B_ zc+Nf_5t#R$rb5p(q0v#=HoWkIpm<|HlgN9sm(0kJj}x(N!|XXUI8A&W-cE^L1J9%< zpK?Z2L`yR4hi>80X2{pLyi4|(Wtz^YTQY$WvaGzuH$Pfunxwmv%xpBHfgW=e&{OY2__O_sN>0;GIzNA_dOcTT;lHta` z5n6VUlZS7ly!U%8g~%xHhZqG@SMiA-(bNV{=&LV%8ny3!{NU21%osUk*xD1CsZ*sA zcXa%3t&6yM+mP`yqs|@+3^6G2?F{5HjuqgQRS8!>@2@usY>{~!h`FDxY*HL6Dm5E* zfH6;FuAAKdH0;fCum4iXsSc6WprYd3V&sRUJDxUMJ}^<~@&oUz6f;kpbqb%i9??M8 z*rph#e58{LKJ68ec@3+jXJRm`?SRpFVtTGH(oHF|Pb&KoO!=j%v$lO^DMcx>18S6e zr^QksLnVmQ;GtEFj54}H%mAK^k7+^I;R1MWtH-w37G9lq52|ZudIHN92z+FlvtFYj z=b>1{D<|hrm@I(jOf9GnE$`&uE8)wnf0f!_Bbl2TqhwM)JadMpbXB)KLthMSpLALz zu_BmHZk9D6TkeT6O%_w7!wWB|cZ?_I&JQ~lXS^H8>~OcQjzGsfTEEL`Js$hI_+8(k zf`^-pyWJ17dpf#)Lz9^+Jr-48cgas~GlL8(ZoaSm%1i4Mojl6j{$jg;RG*=Vf+d;x zXFuFaENmXsN7M2YHVvlbHE>1v8;6g^=zeT&5x{%P`%Z@*&b1uR-?!m8ly^4i%+D8` z?~;XGcX%fAABkKL{ZQC&W}NaBKB~-tQ@W*mGp)v?S&u{Pt#0X)(rd#%zvp05FS+D6 ze%vr!9(vvse5C4=L?J4ulKZFpjT4(vM?Ifyg*Rs{*exo=bu! zyVT83WVk*S=12DH(B&T0obE`N^?xCCH}CEo<9R^^o8`P0*KDf?uP57l@9yvJldAa6 z>{YRHV?a~dkM-4%@3WPDNgXncn3JSArnh@LCaBLG4O391rjQHjIY92U@G8s(ZYkGn z($&Gtz;U{T(MZwzOdnzHozhQk9*&(7BqV95Io4# zEGg{pr3Z&;nD5OO=pIwQA_qPpe{3>*=1k>CfZqI?B;v6kM=bMOg@oAf`U}P~nzrRS z&emVEbY2^V?AC^-HhF5oYiBIJ1ulN!WvHq*9lB~kX>#yHO(I87Kqqs}Nal%k>X}2g zvRqgc&JWxwbE$kOcwFW5L>$w$T3h_XV?p{RG^}ch0_jx&@|g75yZ7hM4-0>fD#==w zowKcajZz91nc$g?h;x(I6FU|yegD+7*!0@BG``bdck>v`UGbj167`e1L)%j(XM`4fEiTz<@{?0hP_ZyjM)ER?loY&~TxPCw8W@?S zu4L_Ew`)1YoVHTnv!IbE;ALpuai6U6ZjR7>zs;P|ouAL1WzTm8Eod$Bo{+!x%%NiA z>vO*q(?yHYSS`Ja&m}C>ZOr<19+dK`?`$WS`K6aGmGiL-d~lpD?LKd$cVVQxwho59BW6)s^WBqs84(@G^Ol$XOFAA1*}ZD~u1jI?aG zYP;?;IPpflCNikJlC|yu)BG3av}Efuu&tXPW1=JF_~lQ!e>}uQotelh$cyN;deYii ztRosgE*+TQtW|x+5OLSLbs)yy)LYigx(oJ;WeN4wRM>kX$GtTSIY(}|ya&AS6% z`^G!{sqNLk8}7RUnc2$~+I$8a&`OSGtb!_X%DQ2$ht_f#;Qo${oecj2wZZipt9}>&ChXpSI)O3cYc0` zNEv8NZn2qqXFpT^Ubqd1jd^iS?egSZnNIP0_uY^2(YzSw>m8f6)4ueSjFN>RE2caQH$4xsqcq;ypF*TEoUAtR_AKo92s^6Cl0Pc)h8N_P7wL04#6wWxw5zIIj3HY46E>O zIj;{N35*t6c)$GKL{+r0vNoM0OM95|mfn-=fykMIqzunqzE3NQ7U^+#BGKcKW1;L? zX83Yw>$2CD938&uSwv{aema?o$}yc-o0Bm+b#8C9#m&{j=HKgy<||O#ZLF``=~)&Q z$qH1oj4EyB-OR4Y`VoUw9u{JWK3H9>XC0e#hUSe_;Bm>1N}`t!(?{^WqA=x-SYjiI ztr>ejgZeBmk;bZZXNmXdrIj&(*XI;^cSLE^>&Msk-%DpS;hz~!SL0{)DDk|ZT>Da0 zB{4o)fp={6T<_WD*2VIW;iDA$*KZ&(54(S?2>M>Emp2zOP*jnKf1)+hxX0NM8^e1dq{gIlkOeJY{w$qs z;)_R*NK9K$jLv7KuNt008+0G6XB|7@RVOQr!V9VBOFzA{`C;7Ak1URtsbEx`^f3u9 zd``!=cnaW0XX|XXral}!%^PTPHz{72uP|+Xax&@~vP9uK%W}_jRfW$}haA;avSP<5 z?(3;uFjO<_WwRj_A_r~*Hiq^r)Cs|=Idy)9K+Co|Rjb|1u zWR4|TBj>(K^Hw)sj7)gNd`0H)nbpx}%i--sMfM(Kc-|AK28AliOz+L_wip+a9@ZMP zb2L4k!^IWAF2xZYt>rv>eBx7$ZK)@JJ-Y%g>~ytDVO%ju$OIdmLB43fdr6N=-W|@* zcSpbags;CV*fI^}T_bHvM7ltv zub0P&raX&JB7E?O4{c4{RK?fFIg3Ry5|Op_5v%$&f+=}p$`3!rD-|Ritv9@7ipJko zl-?v~pO#&lcb*@a7UY-F(K)erKP;$8;o#5$&Bd_FCxfG0=BkS3(IK?VCTzAUE&LoI z-83f8&wa|hKz+yJkmp>1aVI5xO)SgD!h!g%0o1FyP^yI(UfM&JE%zW3^iY{GX_vC6Aujb%0O?0+7!M~s5J z+G&jv0?#K65~!9-rfp8WoYH33@m|ut!)eP~q7+~ZyQmnNqoAI<_V6}|RRXDJuhB%} z&DbtJiej$7^+So-mZeEF8TaB8V>(*bW!7Zf%Qq~vOtlVP9~LJYOBE<#QhpRQ6l0Fo zWmjY7P~bcm({SMKkmf z_PX9q&0}jk?M~VqSA+G_3+V}2ad8inD$>vKxg{Qbo&J$k`;64#g|!E#hdr9^6>5e? zqDnQ)ymwDuAlKMDrfPlqwBL;WPO0?iYN2lvB_Fbl%RV!%B=+TJoUHO?xm|6=Ge=$L z7!_IE7q}5K5`5MjYjpjT8!v3DIOI{3Kb=D~Z)2KPJMRY$IE?R&$*x*wJ2_qWgSw9E z`84Xr$x>%~TORR#vdYJ0CG_7jPqERvi$~a@1>go!SCR!`1{d?;`X)a^Pi3D7P&R_s z(!mmw7EqN^VfjoWn*}y%)E#(USkoXYSCYEa7ta@g_xKZzXIR}WPGXa1ebX%_axFKjAdrkxhFBSK|`unlnXhk@piqFPg|ye4yH@@RwmI@ zQJ+Ni#q^VicN{!&7pdEA@`b5mGuqRbGYdNw;0W0dlVX)#75Hn?U6awI`1SHf?iCjm z%pE@EafZ*=JbohZcfchUGvb3?Dz4B@RV5oc{FRFjxu^Z(C+wBl==i$UGt4^d>qUn ziQVg@%Vc$d$7=2TEork7jG=G!aPqx?J+)=sO*RSo+hFG3#S(D=KdAeQ8LxU4h}mvh555{u!yH*aIYnEIc*%VUiyaiz5s@zb_|uWG0HQ zf^ifUtXHp}Xv_itglR5T!%_xkCwWU`|1gudR5n+_wj>hs^A3tvc>mfhn)_K|-HYAp zB7a)=IF8J7R|ob8t+78kfaBD{ip#`(7Gd{BxVST9bIs`I|1& z4`aMvT{7L^&CB6VK3(NVgZl2zG1}WW!M|@_o0lG??l8zzYrh>+z?0A2^muTP+v?;N zd`NW}EBE_~cA2)}`18+4b|R+o<*e1mm9|)(YzQLlObpI$^RhPmQKj$0D1`Sf=@Q=e zF~!l9-3+@Gur$UnBWP+tlAI z(sm{@e;Bvuhu7dG&yBMY)}*mYX+h)l;fH@Nj6;k~)xt= z$BXT6tum!2x_m2D*=m@Q!SeGm9hf>s7|qMz(dit|V={lMRaVxirbXgFzYv4s?Q*;% zT1`~VKejm4C;7l0rVqD@oqkDRYs$f|KwkUC>X)FSiM;Q+oc?I-%bWJ6A%C^H%)zfh zE_Xna&`Km*h}Lhv+s5t>=GouY@lzL;^XltVsl;{zIqifMzJ}+)jR#q5p;-KETijmN zed@>+-<$)7r%^j{Usgp#q}rRDz= z>q0A-RgCP+*o9>ODlwLHwlcC+zWjILFS(GUi;0=ND<}*_34($&B$8Z+FaUuBhrtAK zFr+XVl>fS#*X##0X=AL6MrUFrnNPf&1$%PvFI9Z#;(^Lflj3HGK3Zc)xm*Cc$v!%@^~3# zBS&^2yeZItmFx9iWR2@CKz_WvxdXeXsF1pug_Vn|^L2I}NiZ`rUUDH-XHzq0D|-ue zo3#Y2Cr1^U1SxZ*>SUpUwzANhwHpUw zTsp5yt~|23=YKO{w=(g1{a5LmfD(>xF!zudls6ciB}k5O$98we@ovdZ*{Z ze6n2B=L9cVed_bnd|W?p(Wi3Czg1nx;56Sd$9hD7{BGoS8e*pbcX3UXu3_EMuS(T$ z?48T&kLCiFL9_MI3w4*7(@tXU4!v0W%t&L9LPETg36-1wVcLHiD#1d*hWfvow5&a- z*(6xt--auo@Mj~N8i7p(%-jW-#IH@H4p#oMgQmR|q0SRn->(AKfA2Jc#r&Sm|Fbd_ zz6b71IYN@Mj{WGtMc)SBeJ!v?x1SoCxTsx&XKwUgLd@K?o$g2(J+-vpx#s4>CiSq) z45cynLpDvqrj;up)IDjEfi2{dj+oV&H^-sj!ui2gwMpr^_Xp-x(sq;`UnP`}y*~2k z=@SN4hUvps85X~)>K&Mm^xn_=Qn-#-op3CnF7|&Q0b(zLxxR*^djmV^doOZ9Lss_S z``kW0`dRB^YZ*yX1N8o#M+0uiUtrgI+<#>7!sDr<2A8oh4@s+Kqce(-WuYH$w&t-d zERe)4G3B9dwaz`ylJlS7FU6D{hB}a=i(`3WM z&;3|Q+mbWAKgy#Q7#+dNM+3h64|Tvf9nLv~lXku&tm+n0|L-9Sy0Lv__ z1#93w)f>R@!{IXcM15$&hPmq(xBxO~UMk*;!91;Avr)n2bE0;p_dxjD9+|hBj%PZQ zOFXZ|&R;synj-L09bKNlBn#nh1HwtI^R7hX9P!4m(tM%7uCQCGazIR;Z z7@OZ{*%v33exnj32dzH0q@G~R=}o^1}OCtAI((q>>f!_lOC=nt*QM~^r zC%~bBxB8tkU_nNq{X17cVhL%1t&s%))BeZd0CRZ=g^E&!zv@(C+v7guRZBWFT7qGa^@H4Fv&YplOeM_}M<1OPY& zsF)W3^9H^CFNwgvUFPpndxARu(LOu;*ZP3s{6B~zU_3DN|Ni@*YTz&Z{~gRgvh|la z{Gou5W@T<}=4@tfVg|z5A3%=({r^9xDF3UA|9`r`Vg4H}(1dyqCf zoz2Y2g@HGKk%JS??;mzF3I$MP%-R2@5%7KpKkW8@(U9OML;xH4Hx0Z-5`4(NX-GH> zg#b`0duX6DKs(t>Lt(K1>1PiO{DCAK2naua^TlAn!2?3WAu$AalYh4ZuTcPd2ui~e z;FtEcgTcTF14=^@PIQoV7y{M{ga%HML^J}(5~SU)!y^%m0DH1`JU9|b#24VE5XpfM z#u4#FAPDeDd&dJyL?kZ)Lx8q{v;${5C=Ch25v?y0MS!E)+ZPEI1WH4}ki;}3KuUu2 z1*dExouW{Lyninb6pjGlvzLa3d{2_xF8z`_YwNqc!9f8nMPsS44A0p^4={;SfZ+LJPy8dj^Jv5$!D)8c6_q+B-iqmS|tXFfd}>V?h22?F-y4 z(VAm01Z=0hJb=?CqG930bH<_w$W(j#Vu^h=3; z{UN|qh-g3%==>0H7?F=f02DML9&juH)oTwA6abTe`UngXNi-J>5-d8TFEAG(-D6?E z@j}{R;lcz&wSV&^xF+Zt0rW#OR9+YyxH<@5VBk2YO~OD}g+cftL6#5o*)Rk^dxXvf z0XRcwNMIUJe+5z>4AlORFaRS5z4=fHm zDS*&$U`2>%7$SM$!UT}ey?sGu0gb=lT=5G!agS_pkOM)|lBIbbs2V3ZP zAbp0~3jzxsDMH(UGzaRh02D7YM@J%X81T$(uT6sRMznvC!U!UJ20Nda2Ea|B@&aRn zp23g=V}r^D7Kvy*kRXjE8V`Ykp0SZ&K%#w#1lI>py#VhFC!(Q{M0N$b5}pn1StBGG zN#qleXe^N*LSkUh+yYn-hG_32vA=MS_wv93i-z*WVu*49B#!6|21G+Z&*y-KA)=wc z`3<@kfXjjAFG!T|uRGj5^8;alC^n*ilZExZimJ~?^Td!3=Dez0%%xbJ%a=v>c`Po;@k(EIH7k1 zNHjQtLDvJ^(h&Jw@JS9LzlH&30JT3bW#V=qcth6+g8)xwAnS`kfWS{g!$8mbNDLBA z6e}>m=|FW4ye^R)U{E-sJ&ghBDUlp#;9#J00q&Vd4zTs1{s1fyk#EO>V-!@+II#Dj z^5O`=2f`NzA{S9#04)!d7l#5j%h0~ycm&lm4h0a^Mfr&0LcFJkr>d0o$x^&7gr-^*I#$vz~KVdN6y71t13tSe*pTD& +public sealed class DataMaintenanceService( + ApplicationDBContext db, + ILogger logger, + IAuditService auditService) + : IDataMaintenanceService +{ + private static readonly string[] PrimeraNames = + [ + "Atlético Central", "Deportivo Norte", "Club Belgrano", "Unión del Sur", + "Racing Porteño", "Defensores del Oeste", "San Lorenzo del Valle", "Gimnasia y Tiro", + ]; + private static readonly string[] PrimeraCodes = + ["ATC", "DNO", "CBE", "UDS", "RPO", "DOE", "SLV", "GYT"]; + private static readonly string[] PrimeraColors = + ["#1E3A8A", "#DC2626", "#16A34A", "#EA580C", "#0891B2", "#7C3AED", "#CA8A04", "#0D9488"]; + private static readonly string[] PrimeraStyles = + ["stripes", "hoops", "diagonal", "chevron", "sash", "sides", "halves", "circles"]; + private static readonly string[] PrimeraSecondaryColors = + ["#FFFFFF", "#FFFFFF", "#FFFFFF", "#1E293B", "#FFFFFF", "#FDE047", "#1E293B", "#FFFFFF"]; + + private static readonly string[] SegundaNames = + [ + "Juventud Unida", "Sportivo Oeste", "Estrella Azul", "Náutico River", + "Ferro Andino", "Talleres del Norte", "Huracán del Litoral", "Vélez Serrano", + ]; + private static readonly string[] SegundaCodes = + ["JUN", "SPO", "EAZ", "NRV", "FAN", "TDN", "HDL", "VSE"]; + private static readonly string[] SegundaColors = + ["#4338CA", "#B91C1C", "#4D7C0F", "#9333EA", "#0284C7", "#65A30D", "#C026D3", "#B45309"]; + private static readonly string[] SegundaStyles = + ["gradient", "vneck", "solid", "halves", "sides", "circles", "stripes", "hoops"]; + private static readonly string[] SegundaSecondaryColors = + ["#FDE047", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#1E293B", "#FFFFFF", "#FDE047"]; + + // Playoff cups shared by both main divisions: Copa Oro takes positions 1 to 4 and Copa Plata takes positions 5 to 8, each a best-of-3 SemiFinal and Final bracket. + private static readonly SampleTournamentBuilder.PlayoffCupDefinition[] MainCups = + [ + new("Copa Oro", 1, 4, 3), + new("Copa Plata", 5, 8, 3), + ]; + + // Historical Clausura — two small 4-team divisions with single-bracket playoffs. + private static readonly string[] ClausuraPrimeraNames = + ["Independiente Rural", "Ferroviario Central", "Atlético Cordillera", "Deportivo Litoral"]; + private static readonly string[] ClausuraPrimeraCodes = ["IRU", "FCE", "ACO", "DLI"]; + private static readonly string[] ClausuraPrimeraColors = + ["#0D9488", "#B91C1C", "#4D7C0F", "#9333EA"]; + + private static readonly string[] ClausuraReservaNames = + ["Newell's Barrial", "Talleres del Oeste", "Huracán del Valle", "Vélez del Parque"]; + private static readonly string[] ClausuraReservaCodes = ["NBA", "TDO", "HDV", "VDP"]; + private static readonly string[] ClausuraReservaColors = + ["#0284C7", "#65A30D", "#C026D3", "#B45309"]; + + /// + public async Task WipeSampleDataAsync(CancellationToken ct = default) + { + await using Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction transaction = + await db.Database.BeginTransactionAsync(ct); + + try + { + int scorers = await db.Scorers.ExecuteDeleteAsync(ct); + int playerStatistics = await db.PlayersStatistics.ExecuteDeleteAsync(ct); + int playerSanctions = await db.PlayerSanctions.ExecuteDeleteAsync(ct); + int stageTeamMatches = await db.StageTeamMatches.ExecuteDeleteAsync(ct); + int playerTeamRegistrations = await db.PlayerTeamRegistrations.ExecuteDeleteAsync(ct); + int matches = await db.Matches.ExecuteDeleteAsync(ct); + int matchSeries = await db.MatchSeries.ExecuteDeleteAsync(ct); + int players = await db.Players.ExecuteDeleteAsync(ct); + int stages = await db.Stages.ExecuteDeleteAsync(ct); + int teams = await db.Teams.ExecuteDeleteAsync(ct); + int divisions = await db.Divisions.ExecuteDeleteAsync(ct); + int tournaments = await db.Tournaments.ExecuteDeleteAsync(ct); + // Seasons are deleted after their tournaments so re-seeding never collides on the season's unique slug; the season count is not surfaced in DataWipeResult since the counters are kept stable for the admin UI summary. + await db.Seasons.ExecuteDeleteAsync(ct); + int venues = await db.Venues.ExecuteDeleteAsync(ct); + int blogPosts = await db.BlogPosts.ExecuteDeleteAsync(ct); + + await transaction.CommitAsync(ct); + + logger.LogInformation( + "Sample data wiped: {TournamentCount} tournaments, {DivisionCount} divisions, " + + "{TeamCount} teams, {PlayerCount} players, {MatchCount} matches, {MatchSeriesCount} match series, " + + "{PlayerSanctionCount} player sanctions, {PlayerStatisticCount} player statistics, " + + "{ScorerCount} scorers, {StageTeamMatchCount} stage-team matches, " + + "{PlayerTeamRegistrationCount} player-team registrations, {StageCount} stages, " + + "{VenueCount} venues, {BlogPostCount} blog posts.", + tournaments, divisions, teams, players, matches, matchSeries, + playerSanctions, playerStatistics, scorers, stageTeamMatches, + playerTeamRegistrations, stages, venues, blogPosts); + + // Records the destructive wipe for traceability, written after commit so it survives the wipe since the audit table is not part of the tournament-domain data being deleted. + await auditService.LogAsync( + AuditAction.DataWipe, + detail: + $"Se eliminaron los datos de torneos: {tournaments} torneos, {teams} equipos, " + + $"{players} jugadores, {matches} partidos.", + ct: ct); + + return new DataWipeResult( + Tournaments: tournaments, + Divisions: divisions, + Teams: teams, + Players: players, + Matches: matches, + MatchSeries: matchSeries, + PlayerSanctions: playerSanctions, + PlayerStatistics: playerStatistics, + Scorers: scorers, + StageTeamMatches: stageTeamMatches, + PlayerTeamRegistrations: playerTeamRegistrations, + Stages: stages, + Venues: venues, + BlogPosts: blogPosts); + } + catch + { + await transaction.RollbackAsync(CancellationToken.None); + throw; + } + } + + /// + public async Task SeedSampleDataAsync(CancellationToken ct = default) + { + if (await db.Tournaments.AnyAsync(ct)) + { + throw new InvalidOperationException( + "The database already has tournament data — call WipeSampleDataAsync first, then seed again."); + } + + List venues = BuildVenues(); + + SampleTournamentBuilder.TournamentDefinition tournament1 = new( + Name: "Torneo Apertura 2026", + Description: "Torneo Apertura de la Liga Club 12, temporada 2026.", + TeamRegistrationDeadline: new DateTime(2026, 5, 15, 0, 0, 0, DateTimeKind.Utc), + StartDate: new DateTime(2026, 6, 1, 0, 0, 0, DateTimeKind.Utc), + StageStartDate: new DateTime(2026, 6, 1, 0, 0, 0, DateTimeKind.Utc), + StageEndDate: new DateTime(2026, 9, 1, 0, 0, 0, DateTimeKind.Utc), + Divisions: + [ + new("Primera División", PrimeraNames, PrimeraCodes, PrimeraColors, MainCups, + TeamStyles: PrimeraStyles, TeamSecondaryColors: PrimeraSecondaryColors), + new("Segunda División", SegundaNames, SegundaCodes, SegundaColors, MainCups, + TeamStyles: SegundaStyles, TeamSecondaryColors: SegundaSecondaryColors), + ], + CrossCup: new("Copa Club 12", GroupCount: 4, QualifiersPerGroup: 1)); + + SampleTournamentBuilder.TournamentDefinition tournament2 = new( + Name: "Torneo Clausura 2026", + Description: "Torneo Clausura de la Liga Club 12, temporada 2026.", + TeamRegistrationDeadline: new DateTime(2026, 9, 15, 0, 0, 0, DateTimeKind.Utc), + StartDate: new DateTime(2026, 10, 1, 0, 0, 0, DateTimeKind.Utc), + StageStartDate: new DateTime(2026, 10, 1, 0, 0, 0, DateTimeKind.Utc), + StageEndDate: new DateTime(2027, 1, 15, 0, 0, 0, DateTimeKind.Utc), + Divisions: + [ + new("Primera", ClausuraPrimeraNames, ClausuraPrimeraCodes, ClausuraPrimeraColors), + new("Reserva", ClausuraReservaNames, ClausuraReservaCodes, ClausuraReservaColors), + ], + // Historical: the Clausura is a past, completed edition. + Status: TournamentStatus.Finished); + + int playerCounter = 0; + // Both tournaments share one transaction, so their division and stage slugs must be unique across the batch; a shared registry disambiguates repeated base slugs with numeric suffixes instead of appending GUIDs. + SampleTournamentBuilder.SlugRegistry slugRegistry = new(); + SampleTournamentBuilder.BuildResult result1 = + SampleTournamentBuilder.Build(tournament1, venues, ref playerCounter, includePlayoffs: true, slugRegistry); + SampleTournamentBuilder.BuildResult result2 = + SampleTournamentBuilder.Build(tournament2, venues, ref playerCounter, includePlayoffs: true, slugRegistry); + + // Groups both 2026 tournaments under one season so the additive Season grouping is visible in seeded data; each tournament keeps its own category, since the season only groups them. + Season season2026 = new() + { + CreatedBy = Domain.Constants.AuditConstants.SystemUser, + Name = "Temporada 2026", + Slug = Application.Utils.Helper.Slug.SlugGenerator.GenerateSlug("Temporada 2026"), + Year = 2026, + }; + result1.Tournament.Season = season2026; + result2.Tournament.Season = season2026; + + db.Seasons.Add(season2026); + db.Tournaments.Add(result1.Tournament); + db.Tournaments.Add(result2.Tournament); + db.PlayerSanctions.AddRange(result1.Sanctions); + db.PlayerSanctions.AddRange(result2.Sanctions); + + List blogPosts = BuildBlogPosts(tournament1.Name, tournament2.Name); + db.BlogPosts.AddRange(blogPosts); + + await db.SaveChangesAsync(ct); + + // Unlike DataSeeder.SeedAsync, this reset skips the medical-records backfill because that would require this class to depend on IMedicalRecordStorage, which its DI-resolved tests have no override for; seeded registrations read as not-habilitado until reviewed for real. + int teamCount = result1.Tournament.Teams.Count + result2.Tournament.Teams.Count; + int playerCount = result1.Tournament.Teams.Sum(t => t.Players.Count) + + result2.Tournament.Teams.Sum(t => t.Players.Count); + int divisionCount = result1.Tournament.Divisions.Count + result2.Tournament.Divisions.Count; + int matchCount = result1.Tournament.Divisions.Sum(d => d.Stages.Sum(s => s.Matches.Count)) + + result2.Tournament.Divisions.Sum(d => d.Stages.Sum(s => s.Matches.Count)); + int sanctionCount = result1.Sanctions.Count + result2.Sanctions.Count; + + logger.LogInformation( + "Sample data seeded: 2 tournaments, {DivisionCount} divisions, {TeamCount} teams, " + + "{PlayerCount} players, {MatchCount} matches, {SanctionCount} sanctions, {BlogPostCount} blog posts.", + divisionCount, teamCount, playerCount, matchCount, sanctionCount, blogPosts.Count); + + return new DataSeedResult( + Tournaments: 2, + Divisions: divisionCount, + Teams: teamCount, + Players: playerCount, + Matches: matchCount, + PlayerSanctions: sanctionCount, + BlogPosts: blogPosts.Count); + } + + private static List BuildVenues() + { + // Real basketball club gyms from Paraná, Entre Ríos, with approximate coordinates around the city near -31.73, -60.52. + (string Name, string Address, double Latitude, double Longitude)[] specs = + [ + ("Estadio Ángel Malvicino (Echagüe)", "Av. Almafuerte, Paraná", -31.7398, -60.5060), + ("Gimnasio Estudiantes de Paraná", "Gualeguaychú 100, Paraná", -31.7255, -60.5150), + ("Paraná Rowing Club", "Av. Costanera, Paraná", -31.7150, -60.4890), + ("Club Sionista", "25 de Mayo, Paraná", -31.7345, -60.5250), + ("Club Atlético Talleres (Paraná)", "Av. Ramírez, Paraná", -31.7460, -60.5300), + ("Polideportivo Municipal Paraná", "Parque Urquiza, Paraná", -31.7205, -60.5050), + ]; + + List venues = []; + foreach ((string name, string address, double latitude, double longitude) in specs) + { + venues.Add(new Venue + { + CreatedBy = Domain.Constants.AuditConstants.SystemUser, + Name = name, + Slug = Application.Utils.Helper.Slug.SlugGenerator.GenerateSlug(name), + Address = address, + PhotoUrl = SampleArtwork.VenuePhotoDataUri(name), + Latitude = latitude, + Longitude = longitude, + }); + } + + return venues; + } + + private static List BuildBlogPosts(string tournament1Name, string tournament2Name) + { + (string Title, string Body, bool Published)[] posts = + [ + ( + $"Arrancó el {tournament1Name}", + $"La Liga Club12 dio el puntapié inicial al {tournament1Name}, con la Primera y la " + + "Segunda División de ocho equipos cada una. Los primeros partidos ya se jugaron y " + + "prometen una temporada muy pareja.", + true + ), + ( + "Primera División: se juega la fecha 1", + "La Primera División puso primera con una jornada inaugural cargada de emociones. " + + "Los candidatos mostraron sus cartas y la tabla de posiciones empieza a tomar forma.", + true + ), + ( + "Copa Cruzada: los cruces confirmados", + "La Copa Club 12 reúne a los dieciséis equipos del torneo en cuatro grupos. Los " + + "partidos se disputan los miércoles para no superponerse con las zonas de los domingos. " + + "Ya están confirmados los cruces de la fase de grupos.", + true + ), + ( + "Copa Oro y Copa Plata: así quedaron los playoffs", + "Con la fase de grupos terminada, los cuatro primeros de cada división avanzan a la " + + "Copa Oro y del quinto al octavo a la Copa Plata. Las semifinales se jugarán al mejor " + + "de tres partidos.", + true + ), + ( + "Resumen de la fecha: goleadores y resultados", + "Repasá los resultados de la última jornada, la tabla de goleadores y las sanciones " + + "aplicadas por el tribunal de disciplina en la sección de estadísticas.", + true + ), + ( + $"Se define el calendario del {tournament2Name}", + $"Con el {tournament1Name} en marcha, la Liga Club12 ya prepara las fechas del " + + $"{tournament2Name}. La nota está en revisión y se publicará con el fixture completo.", + false + ), + ]; + + List blogPosts = []; + + foreach ((string title, string body, bool published) in posts) + { + blogPosts.Add(new BlogPost + { + CreatedBy = Domain.Constants.AuditConstants.SystemUser, + Author = "Redacción Club12", + Title = title, + Slug = Application.Utils.Helper.Slug.SlugGenerator.GenerateSlug(title), + PhotoUrl = SampleArtwork.BlogCoverDataUri(title), + MarkdownText = body, + Views = 0, + IsPublished = published, + }); + } + + return blogPosts; + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Seeding/DataSeeder.cs b/Club12-Backend/Infrastructure/Persistance/Seeding/DataSeeder.cs new file mode 100644 index 0000000..7ad69b6 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Seeding/DataSeeder.cs @@ -0,0 +1,802 @@ +using Application.Interfaces.Storage; +using Application.Utils.Helper.Slug; + +using Domain.Constants; +using Domain.Entities.Models; +using Domain.Enums; + +using Infrastructure.Storage; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Persistance; + +///

+/// Seeds the standard sample dataset for the Club 12 basketball league used in local development and demos. +/// +public sealed class DataSeeder( + ApplicationDBContext db, + ILogger logger, + SupabaseHelper supabaseHelper, + MedicalRecordSeedBackfiller medicalRecordSeedBackfiller) +{ + // Fixed seed keeps logo-to-team assignment reproducible across reseeds. + private const int LogoShuffleSeed = 4212; + + private const int LatestSeasonNumber = 25; + private const int LatestSeasonYear = 2026; + private const int MaxSeasonCount = 12; + private const int MinPlayersPerTeam = 5; + private const int MaxPlayersPerTeam = 20; + + private const int ClausuraPlayedRounds = 5; + + private const int UpsetPercent = 26; + + private static readonly string[] FemeninoNames = + [ + "Patronato", "Peñarol de Paraná", "Bancario", "Sportivo Urquiza", + "Belgrano de Paraná", "Deportivo Libertad", "Juventud Unida de Gualeguaychú", + ]; + private static readonly string[] FemeninoCodes = + ["PAT", "PEN", "BAN", "SUR", "BEP", "LIB", "JUG"]; + private static readonly string[] FemeninoColors = + ["#0F766E", "#BE123C", "#15803D", "#7E22CE", "#1D4ED8", "#A16207", "#C2410C"]; + private static readonly string[] FemeninoStyles = + ["hoops", "stripes", "chevron", "diagonal", "halves", "sash", "circles"]; + private static readonly string[] FemeninoSecondaryColors = + ["#FFFFFF", "#FDE047", "#FFFFFF", "#1E293B", "#FFFFFF", "#1E293B", "#FFFFFF"]; + + private static readonly string[] ZonaANames = + [ + "Echagüe", "Estudiantes de Paraná", "Paraná Rowing Club", "Sionista", + "Recreativo", "Quique", "Olimpia", "Talleres de Paraná", + "Central Entrerriano", "Parque Sur", + ]; + private static readonly string[] ZonaACodes = + ["ECH", "EDP", "ROW", "SIO", "REC", "QUI", "OLI", "TDP", "CEN", "PSU"]; + private static readonly string[] ZonaAColors = + [ + "#1E3A8A", "#DC2626", "#16A34A", "#EA580C", "#0891B2", + "#7C3AED", "#CA8A04", "#0D9488", "#4338CA", "#B91C1C", + ]; + private static readonly string[] ZonaAStyles = + [ + "stripes", "hoops", "diagonal", "chevron", "sash", + "sides", "halves", "circles", "gradient", "vneck", + ]; + private static readonly string[] ZonaASecondaryColors = + [ + "#FFFFFF", "#FFFFFF", "#FFFFFF", "#1E293B", "#FFFFFF", + "#FDE047", "#1E293B", "#FFFFFF", "#FDE047", "#FFFFFF", + ]; + + private static readonly string[] ZonaBNames = + [ + "Regatas Uruguay", "Estudiantes de Concordia", "Social y Deportivo Colón", "Neptunia", + "Independiente de Victoria", "Ciudad de Paraná", + "Rocamora", "Gimnasia y Esgrima de CdelU", "Unión de Crespo", "Sarmiento de La Paz", + ]; + private static readonly string[] ZonaBCodes = + ["REG", "EDC", "SDC", "NEP", "IDV", "CDP", "ROC", "GEU", "UCR", "SLP"]; + private static readonly string[] ZonaBColors = + [ + "#4D7C0F", "#9333EA", "#0284C7", "#65A30D", "#C026D3", + "#B45309", "#1D4ED8", "#B91C1C", "#15803D", "#C2410C", + ]; + private static readonly string[] ZonaBStyles = + [ + "solid", "halves", "sides", "circles", "stripes", + "hoops", "stripes", "hoops", "diagonal", "chevron", + ]; + private static readonly string[] ZonaBSecondaryColors = + [ + "#FFFFFF", "#FFFFFF", "#FFFFFF", "#1E293B", "#FFFFFF", + "#FDE047", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#1E293B", + ]; + + private static readonly string[] ZonaCNames = + [ + "Atlético Gualeguay", "Capuchinos", "Ministerio de Villa Elisa", "Litoral de Colón", + "Sportivo Diamante", "Atlético Federación", "Defensores de Bovril", "Unión de Nogoyá", + "Ferrocarril Concepción", "Deportivo Chajarí", "Sarmiento de Villaguay", + "Atlético María Grande", "Unión de Viale", + ]; + private static readonly string[] ZonaCCodes = + ["AGY", "CAP", "MVE", "LDC", "SPD", "ATF", "DBO", "UNO", "FCC", "DCH", "SVI", "AMG", "UVI"]; + private static readonly string[] ZonaCColors = + [ + "#0E7490", "#6D28D9", "#A16207", "#0F766E", "#0EA5E9", "#DB2777", "#65A30D", + "#EA580C", "#7C2D12", "#4C1D95", "#0F172A", "#B45309", "#166534", + ]; + private static readonly string[] ZonaCStyles = + [ + "sash", "sides", "halves", "circles", "stripes", "hoops", "diagonal", + "chevron", "sash", "sides", "halves", "circles", "solid", + ]; + private static readonly string[] ZonaCSecondaryColors = + [ + "#FFFFFF", "#FDE047", "#1E293B", "#FFFFFF", "#FFFFFF", "#FDE047", "#FFFFFF", + "#1E293B", "#FFFFFF", "#1E293B", "#FFFFFF", "#FDE047", "#FFFFFF", + ]; + + private sealed record ClubSpec(string Name, string Code, string Color, string Style, string Secondary); + + private static ClubSpec[] Zip( + string[] names, string[] codes, string[] colors, string[] styles, string[] secondary) => + [.. names.Select((name, i) => new ClubSpec(name, codes[i], colors[i], styles[i], secondary[i]))]; + + private static readonly ClubSpec[] FeminineClubs = + Zip(FemeninoNames, FemeninoCodes, FemeninoColors, FemeninoStyles, FemeninoSecondaryColors); + + private static readonly ClubSpec[] MasculineClubs = + [ + .. Zip(ZonaANames, ZonaACodes, ZonaAColors, ZonaAStyles, ZonaASecondaryColors), + .. Zip(ZonaBNames, ZonaBCodes, ZonaBColors, ZonaBStyles, ZonaBSecondaryColors), + .. Zip(ZonaCNames, ZonaCCodes, ZonaCColors, ZonaCStyles, ZonaCSecondaryColors), + ]; + + // BestOf 3 applies to each cup's SemiFinal and Final only; every earlier round always plays best-of-1. + private static readonly SampleTournamentBuilder.PlayoffCupDefinition[] FemeninoCups = + [ + new("Copa de Oro", FromPosition: 1, ToPosition: 7, BestOf: 3), + ]; + private static readonly SampleTournamentBuilder.PlayoffCupDefinition[] ZonaABCups = + [ + new("Copa Oro", FromPosition: 1, ToPosition: 4, BestOf: 3), + new("Copa Plata", FromPosition: 5, ToPosition: 8, BestOf: 3), + ]; + private static readonly SampleTournamentBuilder.PlayoffCupDefinition[] ZonaCCups = + [ + new("Copa Oro", FromPosition: 1, ToPosition: 4, BestOf: 3), + new("Copa Plata", FromPosition: 5, ToPosition: 13, BestOf: 3), + ]; + + // TeamPoolSize reuses real rosters from the Zona A, B, and C teams in build order instead of inventing a disjoint set of teams, mirroring how real leagues share club rosters across parallel competitions. + private static readonly SampleTournamentBuilder.CrossCupDefinition CopaCruzada = new( + "Copa Cruzada", + GroupCount: 6, + QualifiersPerGroup: 2, + RoundRobinLegs: 2, + FinalsBestOf: 3, + TeamPoolSize: 23); + + /// + /// Seeds the sample league, skipping the work when any team already exists. + /// + public async Task SeedAsync( + bool reset = false, string? logosPath = null, + string? medicalRecordPath = null, + int seasonCount = 1, int playersPerTeam = SampleTournamentBuilder.DefaultPlayersPerTeam) + { + if (reset) + { + await ResetSeededDataAsync(); + } + else if (await db.Teams.AnyAsync()) + { + // The medical-records backfill always runs here because it is idempotent and a no-op once every Approved registration already has a real stored file, so a database seeded before this feature existed self-heals into habilitado players on the next startup with no config flag to flip. + logger.LogInformation("Sample data already present — running the medical-records backfill and skipping the rest."); + await medicalRecordSeedBackfiller.BackfillMedicalRecordsAsync(medicalRecordPath); + return; + } + + seasonCount = Math.Clamp(seasonCount, 1, MaxSeasonCount); + playersPerTeam = Math.Clamp(playersPerTeam, MinPlayersPerTeam, MaxPlayersPerTeam); + + List venues = BuildVenues(); + SampleTournamentBuilder.SlugRegistry slugRegistry = new(); + int playerCounter = 0; + + List seasons = []; + List allResults = []; + + bool autoDetect = db.ChangeTracker.AutoDetectChangesEnabled; + db.ChangeTracker.AutoDetectChangesEnabled = false; + + for (int offset = seasonCount - 1; offset >= 0; offset--) + { + int number = LatestSeasonNumber - offset; + List results = BuildSeasonTournaments( + number, isCurrent: offset == 0, playersPerTeam, venues, slugRegistry, ref playerCounter); + + Season season = new() + { + CreatedBy = AuditConstants.SystemUser, + Name = SeasonName(number), + Slug = SlugGenerator.GenerateSlug(SeasonName(number)), + Year = SeasonYear(number), + }; + + foreach (SampleTournamentBuilder.BuildResult result in results) + { + result.Tournament.Season = season; + } + + seasons.Add(season); + allResults.AddRange(results); + } + + List allTeams = [.. allResults.SelectMany(r => r.Tournament.Teams)]; + List clubs = BuildClubs(allTeams); + + await UploadTeamLogosAsync(allTeams, logosPath); + + foreach (Club club in clubs) + { + club.LogoUrl = club.Teams.Select(t => t.LogoUrl).FirstOrDefault(); + } + + db.Venues.AddRange(venues); + db.Clubs.AddRange(clubs); + db.Seasons.AddRange(seasons); + + foreach (SampleTournamentBuilder.BuildResult result in allResults) + { + db.Tournaments.Add(result.Tournament); + db.PlayerSanctions.AddRange(result.Sanctions); + } + + db.BlogPosts.AddRange(BuildBlogPosts()); + db.TeamStaffs.AddRange(BuildTeamStaff([.. allResults])); + db.TeamPointDeductions.AddRange(BuildPointDeductions(allResults)); + + db.ChangeTracker.AutoDetectChangesEnabled = autoDetect; + await db.SaveChangesAsync(); + + // Runs after SaveChangesAsync because TeamId and PlayerId are store-generated, since EntityBase.Id defaults to Guid.Empty, so there is no real object key to build from before this point. + await medicalRecordSeedBackfiller.BackfillMedicalRecordsAsync(medicalRecordPath); + + logger.LogInformation( + "Sample data seeded: {SeasonCount} seasons ({FirstSeason}–{LastSeason}), {TournamentCount} tournaments, " + + "{ClubCount} clubs, {DivisionCount} divisions, {TeamCount} teams, {PlayerCount} players, " + + "{MatchCount} matches, {SanctionCount} sanctions.", + seasons.Count, + seasons[0].Name, + seasons[^1].Name, + allResults.Count, + clubs.Count, + allResults.Sum(r => r.Tournament.Divisions.Count), + allTeams.Count, + allTeams.Sum(t => t.Players.Count), + allResults.Sum(r => r.Tournament.Divisions.Sum(d => d.Stages.Sum(s => s.Matches.Count))), + allResults.Sum(r => r.Sanctions.Count)); + } + + private static List BuildSeasonTournaments( + int seasonNumber, + bool isCurrent, + int playersPerTeam, + List venues, + SampleTournamentBuilder.SlugRegistry slugRegistry, + ref int playerCounter) + { + int year = SeasonYear(seasonNumber); + string seasonName = SeasonName(seasonNumber); + int seed = (seasonNumber * 7919) + 13; + + ClubSpec[] feminine = Draw(FeminineClubs, seed); + ClubSpec[] masculine = Draw(MasculineClubs, seed + 1); + + DateTime aperturaStart = new(year, 3, 1, 0, 0, 0, DateTimeKind.Utc); + DateTime aperturaEnd = new(year, 8, 1, 0, 0, 0, DateTimeKind.Utc); + + SampleTournamentBuilder.TournamentDefinition femenino = new( + Name: $"Torneo Apertura Femenino {year}", + Description: + $"Torneo Apertura Femenino de la Liga Club 12 (Paraná), {seasonName}. " + + "Zona única a dos ruedas y Copa de Oro para las siete clasificadas. Finalizado.", + TeamRegistrationDeadline: aperturaStart.AddDays(-14), + StartDate: aperturaStart, + StageStartDate: aperturaStart, + StageEndDate: aperturaEnd, + Divisions: [ZoneOf("Zona Única", feminine, FemeninoCups)], + Status: TournamentStatus.Finished, + Category: TournamentCategory.Feminine, + RoundRobinLegs: 2, + PlayersPerTeam: playersPerTeam, + UpsetPercent: UpsetPercent, + VarietySeed: seed); + + SampleTournamentBuilder.TournamentDefinition masculino = new( + Name: $"Torneo Apertura Masculino {year}", + Description: + $"Torneo Apertura Masculino de la Liga Club 12 (Paraná), {seasonName}. " + + "Tres zonas a dos ruedas con Copa Oro y Copa Plata cada una, más la Copa Cruzada. Finalizado.", + TeamRegistrationDeadline: aperturaStart.AddDays(-14), + StartDate: aperturaStart, + StageStartDate: aperturaStart, + StageEndDate: aperturaEnd, + Divisions: + [ + ZoneOf("Zona A", [.. masculine[..10]], ZonaABCups), + ZoneOf("Zona B", [.. masculine[10..20]], ZonaABCups), + ZoneOf("Zona C", [.. masculine[20..]], ZonaCCups), + ], + CrossCup: CopaCruzada, + Status: TournamentStatus.Finished, + Category: TournamentCategory.Masculine, + RoundRobinLegs: 2, + PlayersPerTeam: playersPerTeam, + UpsetPercent: UpsetPercent, + VarietySeed: seed + 2); + + List results = + [ + SampleTournamentBuilder.Build(femenino, venues, ref playerCounter, includePlayoffs: true, slugRegistry), + SampleTournamentBuilder.Build(masculino, venues, ref playerCounter, includePlayoffs: true, slugRegistry), + ]; + + if (!isCurrent) + { + return results; + } + + DateTime clausuraStart = new(year, 8, 2, 0, 0, 0, DateTimeKind.Utc); + DateTime clausuraEnd = new(year, 12, 20, 0, 0, 0, DateTimeKind.Utc); + + ClubSpec[] clausuraMasculine = Draw(MasculineClubs, seed + 3); + ClubSpec[] clausuraFeminine = Draw(FeminineClubs, seed + 4); + + SampleTournamentBuilder.TournamentDefinition clausuraMasculino = new( + Name: $"Torneo Clausura Masculino {year}", + Description: + $"Torneo Clausura Masculino de la Liga Club 12 (Paraná), {seasonName}. " + + $"En disputa: {ClausuraPlayedRounds} jornadas jugadas, el resto ya programadas.", + TeamRegistrationDeadline: clausuraStart.AddDays(-14), + StartDate: clausuraStart, + StageStartDate: clausuraStart, + StageEndDate: clausuraEnd, + Divisions: + [ + ZoneOf("Zona A", [.. clausuraMasculine[..8]], null), + ZoneOf("Zona B", [.. clausuraMasculine[8..16]], null), + ], + Status: TournamentStatus.Ongoing, + Category: TournamentCategory.Masculine, + RoundRobinLegs: 2, + PlayedRoundsPerZone: ClausuraPlayedRounds, + PlayersPerTeam: playersPerTeam, + UpsetPercent: UpsetPercent, + VarietySeed: seed + 5); + + SampleTournamentBuilder.TournamentDefinition clausuraFemenino = new( + Name: $"Torneo Clausura Femenino {year}", + Description: + $"Torneo Clausura Femenino de la Liga Club 12 (Paraná), {seasonName}. " + + $"En disputa: {ClausuraPlayedRounds} jornadas jugadas, el resto ya programadas.", + TeamRegistrationDeadline: clausuraStart.AddDays(-14), + StartDate: clausuraStart, + StageStartDate: clausuraStart, + StageEndDate: clausuraEnd, + Divisions: [ZoneOf("Zona Única", [.. clausuraFeminine[..6]], null)], + Status: TournamentStatus.Ongoing, + Category: TournamentCategory.Feminine, + RoundRobinLegs: 2, + PlayedRoundsPerZone: ClausuraPlayedRounds, + PlayersPerTeam: playersPerTeam, + UpsetPercent: UpsetPercent, + VarietySeed: seed + 6); + + results.Add(SampleTournamentBuilder.Build( + clausuraMasculino, venues, ref playerCounter, includePlayoffs: false, slugRegistry)); + results.Add(SampleTournamentBuilder.Build( + clausuraFemenino, venues, ref playerCounter, includePlayoffs: false, slugRegistry)); + + return results; + } + + private static SampleTournamentBuilder.DivisionDefinition ZoneOf( + string name, ClubSpec[] clubs, SampleTournamentBuilder.PlayoffCupDefinition[]? cups) => + new(name, + [.. clubs.Select(c => c.Name)], + [.. clubs.Select(c => c.Code)], + [.. clubs.Select(c => c.Color)], + cups, + TeamStyles: [.. clubs.Select(c => c.Style)], + TeamSecondaryColors: [.. clubs.Select(c => c.Secondary)]); + + /// + /// Re-sorts the club pool deterministically to produce one season's draw. + /// + private static ClubSpec[] Draw(ClubSpec[] clubs, int seed) + { + int[] order = [.. Enumerable.Range(0, clubs.Length)]; + Shuffle(order, new Random(seed)); + + return [.. order.Select(i => clubs[i])]; + } + + private static string SeasonName(int number) => $"Temporada {ToRoman(number)}"; + + private static int SeasonYear(int number) => LatestSeasonYear - (LatestSeasonNumber - number); + + private static string ToRoman(int number) + { + (int Value, string Symbol)[] map = + [ + (1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), (100, "C"), (90, "XC"), + (50, "L"), (40, "XL"), (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I"), + ]; + + StringBuilder roman = new(); + foreach ((int value, string symbol) in map) + { + while (number >= value) + { + roman.Append(symbol); + number -= value; + } + } + + return roman.ToString(); + } + + private static List BuildClubs(IReadOnlyList teams) + { + Dictionary bySlug = []; + List clubs = []; + + foreach (Team team in teams) + { + string slug = SlugGenerator.GenerateSlug(team.Name); + + if (!bySlug.TryGetValue(slug, out Club? club)) + { + club = new Club + { + CreatedBy = AuditConstants.SystemUser, + Name = team.Name, + Slug = slug, + }; + bySlug[slug] = club; + clubs.Add(club); + } + + team.Club = club; + club.Teams.Add(team); + } + + return clubs; + } + + private static List BuildPointDeductions( + IReadOnlyList results) + { + (int Points, string Reason)[] specs = + [ + (2, "Incomparecencia en la jornada 4."), + (1, "Inclusión de un jugador no habilitado."), + (3, "Incidentes de la parcialidad local."), + (1, "Presentación tardía de la planilla."), + ]; + + // Prefers ongoing tournaments and the newest season first so a penalty lands where the standings table is actually visible, and avoids ranking a finished zone's bracket differently from the cup it fed since that bracket was seeded from standings without deductions. + List<(Division Division, List Teams)> candidates = + [ + .. results + .Select((result, index) => (result, index)) + .OrderBy(entry => entry.result.Tournament.Status == TournamentStatus.Ongoing ? 0 : 1) + .ThenByDescending(entry => entry.index) + .SelectMany(entry => entry.result.Tournament.Divisions) + .Where(d => !d.IsCrossDivisionCup) + .Select(d => ( + Division: d, + Teams: d.Stages + .Where(s => s.StageType == StageType.Group) + .SelectMany(s => s.StageTeamMatches) + .Select(stm => stm.Team) + .OfType() + .ToList())) + .Where(pair => pair.Teams.Count > 3), + ]; + + List deductions = []; + + for (int i = 0; i < specs.Length && i < candidates.Count; i++) + { + (Division division, List teams) = candidates[i]; + + deductions.Add(new TeamPointDeduction + { + CreatedBy = AuditConstants.SystemUser, + Division = division, + Team = teams[^(1 + (i % teams.Count))], + Points = specs[i].Points, + Reason = specs[i].Reason, + }); + } + + return deductions; + } + + /// + /// Deletes existing sample domain data in FK-safe order so a reseed starts from a clean slate. + /// + private async Task ResetSeededDataAsync() + { + await db.Scorers.ExecuteDeleteAsync(); + await db.PlayersStatistics.ExecuteDeleteAsync(); + await db.PlayerSanctions.ExecuteDeleteAsync(); + await db.TeamStaffs.ExecuteDeleteAsync(); + await db.StageTeamMatches.ExecuteDeleteAsync(); + await db.PlayerTeamRegistrations.ExecuteDeleteAsync(); + await db.TeamTournamentRegistrations.ExecuteDeleteAsync(); + // Matches carry the optional SeriesId FK, so they go before MatchSeries. + await db.Matches.ExecuteDeleteAsync(); + await db.MatchSeries.ExecuteDeleteAsync(); + await db.Players.ExecuteDeleteAsync(); + await db.DivisionPlayoffMappings.ExecuteDeleteAsync(); + await db.TeamPointDeductions.ExecuteDeleteAsync(); + await db.Stages.ExecuteDeleteAsync(); + await db.Teams.ExecuteDeleteAsync(); + await db.Clubs.ExecuteDeleteAsync(); + await db.Divisions.ExecuteDeleteAsync(); + await db.Tournaments.ExecuteDeleteAsync(); + await db.Seasons.ExecuteDeleteAsync(); + await db.Venues.ExecuteDeleteAsync(); + await db.BlogPosts.ExecuteDeleteAsync(); + + logger.LogInformation("Seed reset: existing sample domain data deleted before reseeding."); + } + + /// + /// Uploads a real PNG crest per team from logosPath, replacing each team's generated SVG crest. + /// + private async Task UploadTeamLogosAsync(IReadOnlyList teams, string? logosPath) + { + if (string.IsNullOrWhiteSpace(logosPath)) + { + logger.LogInformation("Seed:LogosPath not configured — keeping the generated team crests."); + return; + } + + string[] files; + try + { + if (!Directory.Exists(logosPath)) + { + logger.LogWarning( + "Seed logos path '{Path}' not found — keeping the generated team crests.", logosPath); + return; + } + + files = Directory.GetFiles(logosPath, "*.png"); + } + catch (Exception ex) + { + logger.LogWarning( + ex, "Could not read seed logos from '{Path}' — keeping the generated team crests.", logosPath); + return; + } + + if (files.Length == 0) + { + logger.LogWarning( + "No PNG logos found in '{Path}' — keeping the generated team crests.", logosPath); + return; + } + + int[] order = [.. Enumerable.Range(0, files.Length)]; + Shuffle(order, new Random(LogoShuffleSeed)); + + List> byClub = [.. teams.GroupBy(t => t.Name)]; + + int uploaded = 0; + for (int i = 0; i < byClub.Count; i++) + { + string file = files[order[i % files.Length]]; + try + { + await using FileStream stream = File.OpenRead(file); + string url = await supabaseHelper.UploadImageAsync(stream, Path.GetFileName(file)); + + foreach (Team team in byClub[i]) + { + team.LogoUrl = url; + uploaded++; + } + } + catch (Exception ex) + { + logger.LogWarning( + ex, "Failed to upload logo '{File}' for club '{Club}' — keeping its generated crest.", + file, byClub[i].Key); + } + } + + logger.LogInformation( + "Uploaded {ClubCount} real crests from '{Path}', applied to {Uploaded}/{Total} teams.", + byClub.Count, logosPath, uploaded, teams.Count); + } + + /// + /// In-place Fisher-Yates shuffle with a caller-provided RNG. + /// + private static void Shuffle(int[] values, Random random) + { + for (int i = values.Length - 1; i > 0; i--) + { + int j = random.Next(i + 1); + (values[i], values[j]) = (values[j], values[i]); + } + } + + // 8 plausible Argentine coach and assistant name pairs, cycled by team index so every seeded team gets one DT and one Asistente for its current tournament. + private static readonly string[] CoachFirstNames = + ["Carlos", "Fernando", "Miguel", "Diego", "Sergio", "Pablo", "Martín", "Gustavo"]; + private static readonly string[] CoachLastNames = + ["Gómez", "Rodríguez", "Fernández", "Sosa", "Benítez", "Acosta", "Ibarra", "Peralta"]; + private static readonly string[] AssistantFirstNames = + ["Javier", "Alejandro", "Nicolás", "Ezequiel", "Federico", "Rodrigo", "Matías", "Emiliano"]; + private static readonly string[] AssistantLastNames = + ["Coronel", "Duarte", "Aguirre", "Medina", "Bordón", "Cabrera", "Zabala", "Leguizamón"]; + + /// + /// Builds a DT and Asistente staff pair for every seeded team, scoped to the tournament it was built for. + /// + private static List BuildTeamStaff(SampleTournamentBuilder.BuildResult[] results) + { + List staff = []; + int index = 0; + + IEnumerable<(Team Team, Tournament Tournament)> teamsByTournament = results + .SelectMany(result => result.Tournament.Teams.Select(team => (team, result.Tournament))); + + foreach ((Team team, Tournament tournament) in teamsByTournament) + { + string coachName = + $"{CoachFirstNames[index % CoachFirstNames.Length]} {CoachLastNames[index % CoachLastNames.Length]}"; + string assistantName = + $"{AssistantFirstNames[index % AssistantFirstNames.Length]} {AssistantLastNames[index % AssistantLastNames.Length]}"; + + staff.Add(new TeamStaff + { + CreatedBy = Domain.Constants.AuditConstants.SystemUser, + Team = team, + Tournament = tournament, + FullName = coachName, + Role = TeamStaffRole.Coach, + }); + staff.Add(new TeamStaff + { + CreatedBy = Domain.Constants.AuditConstants.SystemUser, + Team = team, + Tournament = tournament, + FullName = assistantName, + Role = TeamStaffRole.AssistantCoach, + }); + + index++; + } + + return staff; + } + + private static List BuildVenues() + { + // Real basketball club gyms from Paraná, Entre Ríos, with approximate coordinates around the city near -31.73, -60.52. + (string Name, string Address, double Latitude, double Longitude)[] specs = + [ + ("Estadio Ángel Malvicino (Echagüe)", "Av. Almafuerte, Paraná", -31.7398, -60.5060), + ("Gimnasio Estudiantes de Paraná", "Gualeguaychú 100, Paraná", -31.7255, -60.5150), + ("Paraná Rowing Club", "Av. Costanera, Paraná", -31.7150, -60.4890), + ("Club Sionista", "25 de Mayo, Paraná", -31.7345, -60.5250), + ("Club Atlético Talleres (Paraná)", "Av. Ramírez, Paraná", -31.7460, -60.5300), + ("Polideportivo Municipal Paraná", "Parque Urquiza, Paraná", -31.7205, -60.5050), + ("Gimnasio Central Entrerriano", "Av. Ramírez 2100, Paraná", -31.7502, -60.5188), + ("Club Recreativo Paraná", "Sarmiento 800, Paraná", -31.7310, -60.5215), + ("Club Olimpia (Paraná)", "Bertozzi 1500, Paraná", -31.7415, -60.5402), + ("Club Bancario", "Córdoba 400, Paraná", -31.7290, -60.5285), + ("Gimnasio Parque Sur", "Av. Zanni, Paraná", -31.7562, -60.5121), + ("Club Neptunia", "Av. de las Américas, Paraná", -31.7480, -60.5480), + ("Estadio Ciudad de Concordia", "Av. Monseñor Rösch, Concordia", -31.3930, -58.0209), + ("Gimnasio Rocamora", "25 de Mayo 200, Concepción del Uruguay", -32.4835, -58.2320), + ]; + + List venues = []; + foreach ((string name, string address, double latitude, double longitude) in specs) + { + venues.Add(new Venue + { + CreatedBy = Domain.Constants.AuditConstants.SystemUser, + Name = name, + Slug = Application.Utils.Helper.Slug.SlugGenerator.GenerateSlug(name), + Address = address, + PhotoUrl = SampleArtwork.VenuePhotoDataUri(name), + Latitude = latitude, + Longitude = longitude, + }); + } + + return venues; + } + + private static List BuildBlogPosts() + { + (string Title, string Body)[] posts = + [ + ( + "La Temporada XXV ya tiene campeones", + "Se cerró el Apertura de la Liga Club 12 (Paraná) en las categorías masculina y " + + "femenina. Con la Copa de Oro femenina, las seis copas de las Zonas A, B y C, y la Copa " + + "Cruzada masculina ya definidas, conocemos a los campeones de la temporada. Mirá el podio " + + "completo en la sección Campeones." + ), + ( + "Torneo Apertura Masculino: así quedaron las Zonas A, B y C", + "El Torneo Apertura Masculino se jugó en tres zonas — A y B de 10 equipos, C de 13 — todas " + + "contra todos a ida y vuelta. Los primeros cuatro de cada zona avanzaron a la Copa Oro y " + + "el resto a la Copa Plata. Repasá las tablas finales y las llaves de playoffs." + ), + ( + "Copa Cruzada: la copa cruzada de la temporada", + "La Copa Cruzada masculina reunió a equipos de las tres zonas en seis grupos (ida y " + + "vuelta), con los dos primeros de cada grupo clasificando a una llave combinada de 12 " + + "equipos. Los partidos de la copa se disputan entre semana para no superponerse con las " + + "zonas del fin de semana." + ), + ( + "Arrancó el Clausura: así se juega la segunda mitad del año", + "Con las zonas del Clausura ya en marcha — dos zonas masculinas de ocho equipos y una zona " + + "femenina de seis, todas ida y vuelta — la Liga Club 12 encara la segunda mitad del año. " + + "Las primeras jornadas ya tienen resultado y el resto del fixture está publicado, así que " + + "podés seguir la tabla en vivo y ver cuándo juega tu equipo." + ), + ( + "Los clubes de la liga, temporada por temporada", + "Cada club de la Liga Club 12 tiene ahora su ficha propia: el historial completo de las " + + "temporadas que disputó, los planteles de cada año y los torneos en los que participó. " + + "Es la forma más rápida de ver de dónde viene cada institución más allá de la tabla de " + + "la temporada en curso." + ), + ( + "Fichas médicas: qué necesitás para estar habilitado", + "Un jugador queda habilitado cuando su ficha médica está aprobada y el archivo cargado en " + + "el sistema. Desde el panel, cada delegado puede subir la ficha de sus jugadores y seguir " + + "el estado de la revisión. Los jugadores con ficha pendiente aparecen marcados en el " + + "plantel para que nadie llegue al partido con una sorpresa." + ), + ( + "Sanciones y descuentos de puntos: cómo se aplican", + "El tribunal de disciplina publica las sanciones a jugadores y clubes junto con la " + + "cantidad de fechas de suspensión y el estado de cada apelación. Cuando la sanción es " + + "institucional puede incluir un descuento de puntos, que se resta directamente del total " + + "del equipo en la tabla de posiciones de su zona." + ), + ( + "Goleadores: quiénes lideran la tabla de anotadores", + "La tabla de goleadores se arma con las planillas de cada partido, así que se actualiza " + + "apenas se carga un resultado. Podés filtrarla por torneo y por zona para ver quién " + + "lidera en cada categoría de la temporada." + ), + ]; + + List blogPosts = []; + + foreach ((string title, string body) in posts) + { + blogPosts.Add(new BlogPost + { + CreatedBy = Domain.Constants.AuditConstants.SystemUser, + Author = "Redacción Club 12", + Title = title, + Slug = Application.Utils.Helper.Slug.SlugGenerator.GenerateSlug(title), + PhotoUrl = SampleArtwork.BlogCoverDataUri(title), + MarkdownText = body, + Views = 0, + }); + } + + return blogPosts; + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Seeding/MedicalRecordSeedBackfiller.cs b/Club12-Backend/Infrastructure/Persistance/Seeding/MedicalRecordSeedBackfiller.cs new file mode 100644 index 0000000..56abf45 --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Seeding/MedicalRecordSeedBackfiller.cs @@ -0,0 +1,213 @@ +using Application.Interfaces.Storage; + +using Domain.Constants; +using Domain.Entities.Models; +using Domain.Enums; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Persistance; + +/// +/// Backfills a real, stored medical record file for every seeded Approved registration that lacks one. +/// +public sealed class MedicalRecordSeedBackfiller( + ApplicationDBContext db, + ILogger logger, + IMedicalRecordStorage medicalRecordStorage) +{ + /// + /// Embedded resource name of the generic ficha medica shipped inside the assembly. + /// + private const string EmbeddedMedicalRecordResourceName = + "Infrastructure.Persistance.Seeding.Assets.ficha-medica-generica.pdf"; + + /// + /// File name recorded for the seeded ficha medica, whether it came from the embedded resource or the last-resort generated placeholder. + /// + private const string PlaceholderMedicalRecordFileName = "ficha-medica-generica.pdf"; + + // Flushes progress every N uploaded rows so an interruption loses at most this many refs and the step stays resumable. + private const int MedicalRecordSaveBatchSize = 50; + + /// + /// Uploads a real medical PDF for every Approved registration whose file reference is missing or legacy. + /// + public async Task BackfillMedicalRecordsAsync(string? medicalRecordPath) + { + bool isConfigured = !string.IsNullOrWhiteSpace(medicalRecordPath); + + byte[] pdf; + string fileName; + if (isConfigured) + { + try + { + if (!File.Exists(medicalRecordPath)) + { + // An explicitly configured path that is not there is a misconfiguration, so warn and skip rather than papering over it with the generic one. + logger.LogWarning( + "Seed medical-record file '{Path}' not found — skipping medical-record seeding.", + medicalRecordPath); + return; + } + + pdf = await File.ReadAllBytesAsync(medicalRecordPath!); + fileName = Path.GetFileName(medicalRecordPath); + } + catch (Exception ex) + { + logger.LogWarning(ex, "Could not read seed medical record from '{Path}' — skipping.", medicalRecordPath); + return; + } + } + else + { + // Falling back to the generic ficha médica keeps the seeded league coherent, since without a real stored file every Approved registration reads as not habilitado while the same players hold scorer and statistic rows for thousands of played matches, the exact combination PlayerStatisticService rejects on a real match sheet. + pdf = LoadEmbeddedMedicalRecordPdf(); + fileName = PlaceholderMedicalRecordFileName; + logger.LogInformation( + "No Seed:MedicalRecordPath configured — seeding the built-in generic ficha médica so " + + "approved registrations end up habilitado."); + } + + // Superset filter kept EF-translatable: StartsWith on a constant translates to a SQL LIKE clause. + // The per-row IsStoredReference check below is the authoritative skip-vs-upload decision, the same predicate the read sites and the approve-time write guard use, so the three can never drift. + // The CreatedBy == SystemUser branch lets a seeder-created Pending or Rejected row self-heal too, safe because that value is never written by a real admin action, only by the seeder itself. + List candidates = await db.PlayerTeamRegistrations + .Where(r => (r.MedicalRecordStatus == MedicalRecordStatus.Approved + || r.CreatedBy == AuditConstants.SystemUser) + && (r.MedicalRecordFileUrl == null + || r.MedicalRecordFileUrl == "" + || r.MedicalRecordFileUrl.StartsWith(PlayerTeamRegistration.LegacyReferencePrefix))) + .ToListAsync(); + + int uploaded = 0; + int failed = 0; + int pending = 0; + foreach (PlayerTeamRegistration registration in candidates) + { + if (PlayerTeamRegistration.IsStoredReference(registration.MedicalRecordFileUrl)) + { + continue; + } + + try + { + using MemoryStream content = new(pdf, writable: false); + string objectPath = await medicalRecordStorage.StoreAsync( + registration.TeamId, registration.PlayerId, fileName, content); + + registration.MedicalRecordFileUrl = objectPath; + registration.MedicalRecordFileName = fileName; + if (registration.MedicalRecordStatus != MedicalRecordStatus.Approved) + { + registration.MedicalRecordStatus = MedicalRecordStatus.Approved; + registration.MedicalRecordReviewReason = null; + registration.MedicalRecordReviewedAt = DateTime.UtcNow; + } + uploaded++; + pending++; + } + catch (Exception ex) + { + failed++; + logger.LogWarning(ex, + "Failed to upload the seed medical record for player {PlayerId} / team {TeamId} — leaving it without a file.", + registration.PlayerId, registration.TeamId); + } + + if (pending >= MedicalRecordSaveBatchSize) + { + await db.SaveChangesAsync(); + pending = 0; + } + } + + if (pending > 0) + { + await db.SaveChangesAsync(); + } + + logger.LogInformation( + "Medical-record seed: {Uploaded} uploaded, {Failed} failed, {Total} candidates, from '{Path}'.", + uploaded, failed, candidates.Count, medicalRecordPath); + } + + /// + /// Reads the generic ficha medica embedded in the assembly, named by EmbeddedMedicalRecordResourceName. + /// + private static byte[] LoadEmbeddedMedicalRecordPdf() + { + try + { + using Stream? stream = typeof(MedicalRecordSeedBackfiller).Assembly + .GetManifestResourceStream(EmbeddedMedicalRecordResourceName); + + if (stream is null) + { + return BuildPlaceholderMedicalRecordPdf(); + } + + using MemoryStream buffer = new(); + stream.CopyTo(buffer); + return buffer.ToArray(); + } + catch + { + return BuildPlaceholderMedicalRecordPdf(); + } + } + + /// + /// A real, valid one-page PDF built in memory, with a correct cross-reference table and offsets. + /// + private static byte[] BuildPlaceholderMedicalRecordPdf() + { + const string content = + "BT /F1 16 Tf 60 760 Td (Ficha medica de ejemplo - Liga Club 12) Tj ET"; + + string[] bodies = + [ + "<< /Type /Catalog /Pages 2 0 R >>", + "<< /Type /Pages /Kids [3 0 R] /Count 1 >>", + "<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] " + + "/Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >>", + "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>", + $"<< /Length {content.Length} >>\nstream\n{content}\nendstream", + ]; + + StringBuilder pdf = new("%PDF-1.4\n"); + List offsets = []; + + for (int i = 0; i < bodies.Length; i++) + { + // Every character written here is ASCII, so the builder's length is also the byte offset the xref table has to point at. + offsets.Add(pdf.Length); + pdf.Append(i + 1).Append(" 0 obj\n").Append(bodies[i]).Append("\nendobj\n"); + } + + int xrefOffset = pdf.Length; + pdf.Append("xref\n0 ").Append(bodies.Length + 1).Append("\n") + .Append("0000000000 65535 f \n"); + + foreach (int offset in offsets) + { + pdf.Append(offset.ToString("D10", CultureInfo.InvariantCulture)).Append(" 00000 n \n"); + } + + pdf.Append("trailer\n<< /Size ").Append(bodies.Length + 1).Append(" /Root 1 0 R >>\n") + .Append("startxref\n").Append(xrefOffset).Append("\n%%EOF\n"); + + return Encoding.ASCII.GetBytes(pdf.ToString()); + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Seeding/SampleArtwork.cs b/Club12-Backend/Infrastructure/Persistance/Seeding/SampleArtwork.cs new file mode 100644 index 0000000..63f11fa --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Seeding/SampleArtwork.cs @@ -0,0 +1,101 @@ +using System; +using System.Text; + +namespace Infrastructure.Persistance; + +/// +/// Generates the sample dataset's artwork as self-contained SVG data URIs. +/// +internal static class SampleArtwork +{ + /// + /// Widest a generated data URI may get, matching the 2048 character cap on BlogPost.PhotoUrl in its EF configuration. + /// + public const int MaxDataUriLength = 2048; + + // Court floors: plausible hardwood tones, picked by the venue's name. + private static readonly string[] Floors = ["#B4763A", "#C08A4A", "#A9682F", "#CE9856"]; + + // Painted key colours, likewise per venue. + private static readonly string[] Keys = ["#1D4ED8", "#B91C1C", "#0F766E", "#7C3AED", "#C2410C"]; + + // Cover gradients: pairs dark enough for white text in both stops. + private static readonly (string From, string To)[] CoverGradients = + [ + ("#0F172A", "#1D4ED8"), ("#7C2D12", "#C2410C"), ("#134E4A", "#0F766E"), + ("#3B0764", "#7C3AED"), ("#450A0A", "#B91C1C"), ("#082F49", "#0284C7"), + ]; + + /// + /// A stylised plan of the venue's court, with hardwood floor, sidelines, centre circle, and both painted keys. + /// + public static string VenuePhotoDataUri(string venueName) + { + // Floor and key are read from different slices of the hash because taking the second from the same low bits as the first correlated them, causing the 14 seeded gyms to land on only 9 of the 20 possible combinations. + int hash = StableHash(venueName); + string floor = Floors[hash % Floors.Length]; + string key = Keys[(hash >> 8) % Keys.Length]; + + string svg = + "" + + $"" + + $"" + + $"" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + + return ToDataUri(svg); + } + + /// + /// A 1200 by 630 social-card cover banner for a post, with a gradient chosen from postSlug, a ball outline, and the league's name. + /// + public static string BlogCoverDataUri(string postSlug) + { + (string from, string to) = CoverGradients[StableHash(postSlug) % CoverGradients.Length]; + + string svg = + "" + + "" + + $"" + + "" + + "" + + "" + + "" + + "" + + "" + + "LIGA CLUB 12" + + ""; + + return ToDataUri(svg); + } + + /// + /// Base64 data URI for an SVG document. + /// + private static string ToDataUri(string svg) => + "data:image/svg+xml;base64," + Convert.ToBase64String(Encoding.UTF8.GetBytes(svg)); + + /// + /// Small, stable, non-negative hash of a string. + /// + private static int StableHash(string value) + { + int hash = 17; + foreach (char character in value) + { + hash = (hash * 31) + character; + } + + return hash & 0x7FFFFFFF; + } +} diff --git a/Club12-Backend/Infrastructure/Persistance/Seeding/SampleTournamentBuilder.cs b/Club12-Backend/Infrastructure/Persistance/Seeding/SampleTournamentBuilder.cs new file mode 100644 index 0000000..11d4a1f --- /dev/null +++ b/Club12-Backend/Infrastructure/Persistance/Seeding/SampleTournamentBuilder.cs @@ -0,0 +1,1443 @@ +using Application.Utils.Constants.Stage; +using Application.Utils.Helper.Playoff; +using Application.Utils.Helper.RoundRobin; +using Application.Utils.Helper.Series; +using Application.Utils.Helper.Slug; +using Application.Utils.Helper.Standings; + +using Domain.Constants; +using Domain.Entities.Models; +using Domain.Enums; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace Infrastructure.Persistance; + +/// +/// Builds one fully-populated, coherent sample Tournament from a declarative definition. +/// +public static class SampleTournamentBuilder +{ + private const string CreatedBy = AuditConstants.SystemUser; + + public const int DefaultPlayersPerTeam = 8; + + // Offset mixed into each division's variety seed so two same-sized zones of one tournament don't replay the same season. + private const int DivisionVarietySalt = 7717; + private static readonly DateTime SampleMedicalRecordReviewedAt = + new(2026, 8, 1, 12, 0, 0, DateTimeKind.Utc); + + private static readonly string[] FirstNames = + [ + "Juan", "Carlos", "Martín", "Diego", "Facundo", "Lucas", "Nicolás", "Matías", + "Franco", "Ezequiel", "Agustín", "Bruno", "Iván", "Santiago", "Tomás", "Gonzalo", + "Joaquín", "Valentín", "Emiliano", "Thiago", + ]; + + // Used instead of FirstNames when the team's tournament Category is Feminine, so feminine divisions don't end up with male rosters. + private static readonly string[] FeminineFirstNames = + [ + "María", "Ana", "Lucía", "Sofía", "Camila", "Valentina", "Julieta", "Florencia", + "Agustina", "Micaela", "Rocío", "Antonella", "Belén", "Candela", "Milagros", "Paula", + "Martina", "Victoria", "Carla", "Daniela", + ]; + + // Eleven health-insurance providers, led by the Entre Ríos provincial IOSPER since the league is from Paraná, kept co-prime with 20 first names and 21 surnames so a roster never ends up with one obra social per first name. + private static readonly string[] HealthInsuranceProviders = + [ + "IOSPER", "OSDE", "Swiss Medical", "Sancor Salud", "Galeno", "Medifé", + "OSECAC", "Avalian", "Federada Salud", "Prevención Salud", "Jerárquicos Salud", + ]; + + private static readonly string[] LastNames = + [ + "González", "Rodríguez", "Fernández", "López", "Díaz", "Pérez", "Sánchez", "Romero", + "Álvarez", "Torres", "Ruiz", "Ramírez", "Flores", "Acosta", "Benítez", "Medina", + "Cabrera", "Sosa", "Vera", "Ledesma", "Quiroga", + ]; + + /// + /// A named playoff cup fed by a contiguous range of a division's final group-standings positions. + /// + public sealed record PlayoffCupDefinition( + string BracketName, + int FromPosition, + int ToPosition, + int BestOf); + + /// + /// The cross-division cup is an extra division whose teams are drawn from the tournament's teams and split into internal groups. + /// + /// The cup's division name. + /// How many internal group stages to split the team pool into. + /// How many top teams from each group pool into the bracket. + /// + /// How many times each pair plays within an internal group. A value of 1 + /// plays a single round-robin and 2 plays a double round-robin, also + /// called ida y vuelta. Defaults to 1. + /// + /// + /// BestOf applied to the pooled bracket's SemiFinal and Final rounds + /// only. Earlier rounds, needed once more than 4 teams are pooled, + /// always play Bo1. Defaults to 1. + /// + /// + /// When set, only the first TeamPoolSize of the + /// tournament's teams, in build order, feed the cup, instead of every + /// team. Null, the default, uses every team, unchanged from the + /// original behavior. + /// + public sealed record CrossCupDefinition( + string DivisionName, + int GroupCount, + int QualifiersPerGroup, + int RoundRobinLegs = 1, + int FinalsBestOf = 1, + int? TeamPoolSize = null); + + public sealed record DivisionDefinition( + string DivisionName, + string[] TeamNames, + string[] TeamCodes, + string[] TeamColors, + PlayoffCupDefinition[]? PlayoffCups = null, + string[]? TeamStyles = null, + string[]? TeamSecondaryColors = null); + + public sealed record TournamentDefinition( + string Name, + string Description, + DateTime TeamRegistrationDeadline, + DateTime StartDate, + DateTime StageStartDate, + DateTime StageEndDate, + DivisionDefinition[] Divisions, + CrossCupDefinition? CrossCup = null, + TournamentStatus Status = TournamentStatus.Ongoing, + TournamentCategory Category = TournamentCategory.Masculine, + int RoundRobinLegs = 1, + int? PlayedRoundsPerZone = null, + int PlayersPerTeam = DefaultPlayersPerTeam, + int UpsetPercent = 0, + int VarietySeed = 0); + + public sealed record BuildResult(Tournament Tournament, List Sanctions); + + /// + /// Hands out clean, collision-free kebab-case slugs for the divisions and stages built in one seeding run. + /// + public sealed class SlugRegistry + { + private readonly HashSet _divisionSlugs = []; + private readonly HashSet _stageSlugs = []; + private readonly HashSet _playerSlugs = []; + private readonly HashSet _teamSlugs = []; + private readonly HashSet _tournamentSlugs = []; + + public string ForDivision(string source) => Register(source, _divisionSlugs); + + public string ForStage(string source) => Register(source, _stageSlugs); + + public string ForPlayer(string source) => Register(source, _playerSlugs); + + public string ForTeam(string source) => Register(source, _teamSlugs); + + public string ForTournament(string source) => Register(source, _tournamentSlugs); + + private static string Register(string source, HashSet used) + { + string baseSlug = SlugGenerator.GenerateSlug(source); + string candidate = baseSlug; + int suffix = 1; + + while (!used.Add(candidate)) + { + suffix++; + candidate = $"{baseSlug}-{suffix}"; + } + + return candidate; + } + } + + /// + /// Builds one Tournament with every division in definition. + /// + public static BuildResult Build( + TournamentDefinition definition, + List venues, + ref int playerCounter, + bool includePlayoffs = false, + SlugRegistry? slugRegistry = null) + { + slugRegistry ??= new SlugRegistry(); + + Tournament tournament = new() + { + CreatedBy = CreatedBy, + Name = definition.Name, + Slug = slugRegistry.ForTournament(definition.Name), + Description = definition.Description, + TeamRegistrationDeadline = definition.TeamRegistrationDeadline, + StartDate = definition.StartDate, + Status = definition.Status, + // Every division built below inherits the tournament's category so the one tournament, one category invariant holds in the seeded graph. + Category = definition.Category, + Divisions = [], + Teams = [], + }; + + List allTeams = []; + List regularGroupStages = []; + + for (int divisionIndex = 0; divisionIndex < definition.Divisions.Length; divisionIndex++) + { + DivisionDefinition divisionDef = definition.Divisions[divisionIndex]; + + // Every result a zone produces is a pure function of the seed, so two same-sized zones sharing a seed played out identically, once leaving Zona A and Zona B with the same scorelines and final table; salting per division separates them. + int divisionVarietySeed = definition.VarietySeed + (divisionIndex * DivisionVarietySalt); + + (Division division, List teams) = BuildDivisionWithTeams( + tournament, + divisionDef.DivisionName, + divisionDef.TeamNames, + divisionDef.TeamCodes, + divisionDef.TeamColors, + divisionDef.TeamStyles, + divisionDef.TeamSecondaryColors, + slugRegistry, + definition.PlayersPerTeam, + ref playerCounter); + + division.Category = definition.Category; + tournament.Divisions.Add(division); + foreach (Team team in teams) + { + tournament.Teams.Add(team); + allTeams.Add(team); + + // Roster source of truth for the division, mirroring the TeamTournamentRegistration seed step above, since a seeded division's teams must satisfy the "every StageTeamMatch has a matching DivisionTeamRegistration" invariant. + team.DivisionTeamRegistrations.Add(new DivisionTeamRegistration + { + CreatedBy = CreatedBy, + TeamId = Guid.Empty, + Team = team, + DivisionId = Guid.Empty, + Division = division, + }); + } + + Stage stage = new() + { + CreatedBy = CreatedBy, + Name = StageTemplate.Group.Name, + Slug = slugRegistry.ForStage($"{StageTemplate.Group.Name} {division.Name}"), + StageType = StageType.Group, + IsActive = true, + StartDate = definition.StageStartDate, + EndDate = definition.StageEndDate, + DivisionId = Guid.Empty, + Division = division, + Matches = [], + Order = 0, + RoundRobinLegs = definition.RoundRobinLegs, + }; + division.Stages.Add(stage); + regularGroupStages.Add(stage); + AddStageTeamMatches(stage, teams); + + SeedRoundRobinMatches( + stage, teams, venues, definition.StageStartDate, isCrossDivisionCup: false, + legs: definition.RoundRobinLegs, playedRounds: definition.PlayedRoundsPerZone, + upsetPercent: definition.UpsetPercent, varietySeed: divisionVarietySeed); + + if (includePlayoffs) + { + if (divisionDef.PlayoffCups is { Length: > 0 } cups) + { + SeedCupPlayoffs(division, stage, teams, venues, cups, slugRegistry); + } + else + { + SeedPlayoffStages(division, stage, teams, venues, slugRegistry); + } + } + } + + if (includePlayoffs && definition.CrossCup is not null) + { + List crossCupTeams = definition.CrossCup.TeamPoolSize is int poolSize + ? [.. allTeams.Take(poolSize)] + : allTeams; + SeedCrossDivisionCup( + tournament, definition.CrossCup, crossCupTeams, venues, definition.StageStartDate, slugRegistry, + definition.UpsetPercent, definition.VarietySeed); + } + + List sanctions = SeedSanctions(regularGroupStages, tournament); + + return new BuildResult(tournament, sanctions); + } + + private static (Division Division, List Teams) BuildDivisionWithTeams( + Tournament tournament, + string divisionName, + string[] teamNames, + string[] teamCodes, + string[] teamColors, + string[]? teamStyles, + string[]? teamSecondaryColors, + SlugRegistry slugRegistry, + int playersPerTeam, + ref int playerCounter) + { + Division division = new() + { + CreatedBy = CreatedBy, + Name = divisionName, + Slug = slugRegistry.ForDivision(divisionName), + Tournament = tournament, + Stages = [], + }; + + List teams = []; + + // Feminine divisions draw first names from the feminine pool so their rosters don't read as male. + string[] firstNames = tournament.Category == TournamentCategory.Feminine ? FeminineFirstNames : FirstNames; + + for (int i = 0; i < teamNames.Length; i++) + { + string jerseyStyle = teamStyles is not null && i < teamStyles.Length ? teamStyles[i] : "solid"; + string? secondaryColor = + teamSecondaryColors is not null && i < teamSecondaryColors.Length ? teamSecondaryColors[i] : null; + + Team team = new() + { + Id = Guid.NewGuid(), + CreatedBy = CreatedBy, + Name = teamNames[i], + Slug = slugRegistry.ForTeam(teamNames[i]), + ThreeLetterCode = teamCodes[i], + LogoUrl = BuildCrestDataUri(teamCodes[i], teamColors[i], secondaryColor, jerseyStyle), + ShirtColor = teamColors[i], + JerseyStyle = jerseyStyle, + ShirtSecondaryColor = secondaryColor, + Tournament = tournament, + Players = [], + }; + + // Season-scoped participation source of truth, mirroring the PlayerTeamRegistration seeding below, since the denormalized Team.TournamentId pointer alone is not authoritative. + team.TeamTournamentRegistrations.Add(new TeamTournamentRegistration + { + CreatedBy = CreatedBy, + TeamId = Guid.Empty, + Team = team, + TournamentId = Guid.Empty, + Tournament = tournament, + }); + + for (int p = 0; p < playersPerTeam; p++) + { + playerCounter++; + + string firstName = firstNames[playerCounter % firstNames.Length]; + string lastName = LastNames[playerCounter % LastNames.Length]; + string documentNumber = (30000000 + playerCounter).ToString(); + + Player player = new() + { + CreatedBy = CreatedBy, + FirstName = firstName, + LastName = lastName, + Slug = slugRegistry.ForPlayer(Player.BuildSlugSource(lastName, firstName, secondName: null)), + DocumentNumber = documentNumber, + IsSanctioned = false, + BirthDate = new DateTime(2026, 8, 18, 0, 0, 0, DateTimeKind.Utc) + .AddYears(-(18 + (playerCounter % 20))) + .AddDays(playerCounter % 27), + SocialSecurity = + HealthInsuranceProviders[playerCounter % HealthInsuranceProviders.Length], + Team = team, + }; + + team.Players.Add(player); + + // Most seeded players' ficha is Approved and the last player of each team stays Pending to keep the upload/review flow demonstrable; Approved rows are seeded without a file reference since DataSeeder.SeedMedicalRecordsAsync fills MedicalRecordFileUrl with a real uploaded object after Build runs, so until then an Approved row correctly reads as not habilitado. + bool isHabilitado = p < playersPerTeam - 1; + + team.PlayerTeamRegistrations.Add(new PlayerTeamRegistration + { + CreatedBy = CreatedBy, + PlayerId = Guid.Empty, + Player = player, + TeamId = Guid.Empty, + Team = team, + TournamentId = Guid.Empty, + Tournament = tournament, + JerseyNumber = p + 4, + MedicalRecordStatus = isHabilitado + ? MedicalRecordStatus.Approved + : MedicalRecordStatus.Pending, + MedicalRecordFileUrl = null, + MedicalRecordFileName = null, + MedicalRecordReviewedAt = isHabilitado ? SampleMedicalRecordReviewedAt : null, + }); + } + + teams.Add(team); + } + + return (division, teams); + } + + /// + /// The team's crest, generated from its own kit, is a round badge in the club's shirt colour carrying its three-letter code. + /// + private static string BuildCrestDataUri( + string code, string primaryColor, string? secondaryColor, string jerseyStyle) + { + string secondary = string.IsNullOrWhiteSpace(secondaryColor) ? "#FFFFFF" : secondaryColor; + string ink = ContrastInk(primaryColor); + + // Each band is drawn across the whole square and clipped to the badge, so a style only has to describe its own shape. + string band = jerseyStyle switch + { + "stripes" => $"", + "hoops" => $"", + "diagonal" => $"", + "sash" => $"", + "halves" => $"", + "sides" => $"", + "chevron" => $"", + "circles" => $"", + "vneck" => $"", + "gradient" => "", + _ => string.Empty, + }; + + string gradient = jerseyStyle == "gradient" + ? "" + + $"" + + "" + : string.Empty; + + string svg = + "" + + $"{gradient}" + + $"{band}" + + $"" + + "{code}" + + ""; + + return "data:image/svg+xml;base64," + Convert.ToBase64String(Encoding.UTF8.GetBytes(svg)); + } + + /// + /// Near-black ink, the alternative to white for a crest's code. + /// + private const string DarkInk = "#0F172A"; + + /// + /// Whichever of white and DarkInk reads better on backgroundColor, by actual WCAG contrast ratio rather than a hand-picked luminance threshold. + /// + private static string ContrastInk(string backgroundColor) + { + double? luminance = RelativeLuminance(backgroundColor); + if (luminance is not double background) + { + return "#FFFFFF"; + } + + double onDark = (background + 0.05) / ((RelativeLuminance(DarkInk) ?? 0) + 0.05); + double onWhite = 1.05 / (background + 0.05); + + return onDark >= onWhite ? DarkInk : "#FFFFFF"; + } + + /// + /// WCAG relative luminance of an #RRGGBB colour, or null when the string is not a valid one. + /// + private static double? RelativeLuminance(string color) + { + if (color.Length != 7 + || color[0] != '#' + || !int.TryParse(color.AsSpan(1), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int rgb)) + { + return null; + } + + double red = Linearise(((rgb >> 16) & 0xFF) / 255.0); + double green = Linearise(((rgb >> 8) & 0xFF) / 255.0); + double blue = Linearise((rgb & 0xFF) / 255.0); + + return (0.2126 * red) + (0.7152 * green) + (0.0722 * blue); + + static double Linearise(double channel) => + channel <= 0.03928 ? channel / 12.92 : Math.Pow((channel + 0.055) / 1.055, 2.4); + } + + /// + /// Builds a real circle-method single round-robin for teams, so every pair meets exactly once per leg. + /// + private static void SeedRoundRobinMatches( + Stage stage, + List teams, + List venues, + DateTime anchorDate, + bool isCrossDivisionCup, + int legs = 1, + int? playedRounds = null, + int upsetPercent = 0, + int varietySeed = 0) + { + int n = teams.Count; + if (n < 2) + { + return; + } + + // An odd roster gets a bye slot so the circle method still produces a complete round-robin, since running n-1 jornadas on an odd roster instead dropped pairings, replayed others twice per leg, and left one team pinned to extra games. + const int ByeSlot = -1; + int slotCount = n % 2 == 0 ? n : n + 1; + int roundsPerLeg = slotCount - 1; + int matchIndex = 0; + DateTime lastMatchDate = stage.StartDate; + + for (int leg = 0; leg < legs; leg++) + { + // The circle method rotates slots 1..n-1 each round while keeping index 0 fixed, and resets at the start of each leg so the return leg replays the same pairings in seeding order, where a smaller index means a stronger team. + int[] slots = slotCount == n + ? [.. Enumerable.Range(0, n)] + : [.. Enumerable.Range(0, n), ByeSlot]; + + for (int r = 0; r < roundsPerLeg; r++) + { + // Global 1-based jornada number across all legs. + int round = (leg * roundsPerLeg) + r + 1; + DateTime roundDate = RoundCalendar.DateForRound(anchorDate, round, isCrossDivisionCup); + bool isUpcoming = playedRounds is int played && round > played; + + for (int i = 0; i < slotCount / 2; i++) + { + int first = slots[i]; + int second = slots[slotCount - 1 - i]; + + // The team drawn against the padding sits this jornada out. + if (first == ByeSlot || second == ByeSlot) + { + continue; + } + + // Alternates home and away by the jornada within the leg, then inverts it on the return leg so the rematch swaps venue, since deriving this from the global jornada number instead cancelled the leg inversion whenever roundsPerLeg was odd and both legs were played at the same team's home. + bool homeIsFirst = r % 2 == 0; + if (leg % 2 == 1) + { + homeIsFirst = !homeIsFirst; + } + + (int homeIdx, int visitorIdx) = homeIsFirst ? (first, second) : (second, first); + + Team home = teams[homeIdx]; + Team visitor = teams[visitorIdx]; + + if (isUpcoming) + { + stage.Matches.Add(BuildUpcomingMatch( + stage, home, visitor, Domain.Enums.MatchType.Regular, venues, roundDate, round)); + } + else + { + bool isUpset = IsUpset( + upsetPercent, varietySeed, stage.Order, round, homeIdx, visitorIdx); + bool homeIsStronger = isUpset ? homeIdx > visitorIdx : homeIdx < visitorIdx; + + int marginFloor = isUpset ? 1 : 2; + int margin = upsetPercent > 0 + ? marginFloor + ((homeIdx + visitorIdx + matchIndex + varietySeed) % 13) + : 4 + ((homeIdx + visitorIdx + matchIndex) % 9); + int winnerScore = upsetPercent > 0 + ? 63 + (((matchIndex * 7) + round + varietySeed) % 29) + : 68 + ((matchIndex * 5) % 22); + int loserScore = winnerScore - margin; + int homeScore = homeIsStronger ? winnerScore : loserScore; + int visitorScore = homeIsStronger ? loserScore : winnerScore; + + stage.Matches.Add(BuildFinishedMatch( + stage, home, visitor, homeScore, visitorScore, Domain.Enums.MatchType.Regular, + venues, roundDate, matchIndex, round)); + } + + if (roundDate > lastMatchDate) + { + lastMatchDate = roundDate; + } + + matchIndex++; + } + + Rotate(slots); + } + } + + // A stage can never end before its own last jornada, since playoff brackets anchor on Stage.EndDate and a short end date would date cup games before the zone's final jornadas were even played. + if (lastMatchDate > stage.EndDate) + { + stage.EndDate = lastMatchDate; + } + } + + /// + /// Rotates slots 1..n-1 by one position, keeping index 0 fixed. + /// + private static void Rotate(int[] slots) + { + int n = slots.Length; + if (n <= 2) + { + return; + } + + int last = slots[n - 1]; + for (int k = n - 1; k >= 2; k--) + { + slots[k] = slots[k - 1]; + } + slots[1] = last; + } + + /// + /// Builds SemiFinal, ThirdPlace, and Final stages for one division, seeded from the group stage's final standings. + /// + private static void SeedPlayoffStages(Division division, Stage groupStage, List teams, List venues, SlugRegistry slugRegistry) + { + List standings = PositionCalculator.CalculatePositions(groupStage.Matches); + Dictionary teamsById = teams.ToDictionary(t => t.Id); + List orderedTeamIds = [.. standings.Select(p => p.TeamId)]; + List<(Guid HomeTeamId, Guid? VisitorTeamId)> semiFinalPairs = PlayoffSeeder.SeedPairs(orderedTeamIds); + + Stage semiFinalStage = new() + { + CreatedBy = CreatedBy, + Name = StageTemplate.SemiFinal.Name, + Slug = slugRegistry.ForStage($"{StageTemplate.SemiFinal.Name} {division.Name}"), + StageType = StageType.SemiFinal, + IsActive = true, + IsElimination = true, + StartDate = groupStage.EndDate.AddDays(StageTemplate.StandardGapDays), + EndDate = groupStage.EndDate.AddDays(StageTemplate.StandardGapDays + StageTemplate.DurationDays), + DivisionId = Guid.Empty, + Division = division, + Matches = [], + Order = 1, + }; + division.Stages.Add(semiFinalStage); + AddStageTeamMatches(semiFinalStage, teams); + + List semiFinalWinners = []; + List semiFinalLosers = []; + + for (int i = 0; i < semiFinalPairs.Count; i++) + { + (Guid homeId, Guid? visitorId) = semiFinalPairs[i]; + Team home = teamsById[homeId]; + Team visitor = teamsById[visitorId!.Value]; + + int homeScore = 78 + (i * 4); + int visitorScore = 65 + (i * 3); + + Match match = BuildFinishedMatch( + semiFinalStage, home, visitor, homeScore, visitorScore, Domain.Enums.MatchType.Playoff, + venues, semiFinalStage.StartDate.AddDays(i * 2), i, round: null); + semiFinalStage.Matches.Add(match); + + semiFinalWinners.Add(match.WinningTeam!); + semiFinalLosers.Add(match.WinningTeam == home ? visitor : home); + } + + Stage thirdPlaceStage = new() + { + CreatedBy = CreatedBy, + Name = StageTemplate.ThirdPlace.Name, + Slug = slugRegistry.ForStage($"{StageTemplate.ThirdPlace.Name} {division.Name}"), + StageType = StageType.ThirdPlace, + IsActive = true, + IsElimination = true, + StartDate = semiFinalStage.EndDate.AddDays(StageTemplate.ThirdPlaceGapDays), + EndDate = semiFinalStage.EndDate.AddDays(StageTemplate.ThirdPlaceGapDays + StageTemplate.DurationDays), + DivisionId = Guid.Empty, + Division = division, + Matches = [], + Order = 2, + }; + division.Stages.Add(thirdPlaceStage); + AddStageTeamMatches(thirdPlaceStage, semiFinalLosers); + + Match thirdPlaceMatch = BuildFinishedMatch( + thirdPlaceStage, semiFinalLosers[0], semiFinalLosers[1], 74, 61, Domain.Enums.MatchType.Playoff, + venues, thirdPlaceStage.StartDate, 0, round: null); + thirdPlaceStage.Matches.Add(thirdPlaceMatch); + + Stage finalStage = new() + { + CreatedBy = CreatedBy, + Name = StageTemplate.Final.Name, + Slug = slugRegistry.ForStage($"{StageTemplate.Final.Name} {division.Name}"), + StageType = StageType.Final, + IsActive = true, + IsElimination = true, + StartDate = thirdPlaceStage.EndDate.AddDays(StageTemplate.StandardGapDays), + EndDate = thirdPlaceStage.EndDate.AddDays(StageTemplate.StandardGapDays + StageTemplate.DurationDays), + DivisionId = Guid.Empty, + Division = division, + Matches = [], + Order = 3, + }; + division.Stages.Add(finalStage); + AddStageTeamMatches(finalStage, semiFinalWinners); + + Match finalMatch = BuildFinishedMatch( + finalStage, semiFinalWinners[0], semiFinalWinners[1], 82, 70, Domain.Enums.MatchType.Playoff, + venues, finalStage.StartDate, 0, round: null); + finalStage.Matches.Add(finalMatch); + } + + /// + /// Builds one full elimination bracket per named position-range cup, each seeded from the final group standings restricted to the cup's position range. + /// + private static void SeedCupPlayoffs( + Division division, + Stage groupStage, + List teams, + List venues, + PlayoffCupDefinition[] cups, + SlugRegistry slugRegistry) + { + List standings = PositionCalculator.CalculatePositions( + groupStage.Matches, division.PointsForWin, division.PointsForLoss); + Dictionary teamsById = teams.ToDictionary(t => t.Id); + + int order = 1; + + foreach (PlayoffCupDefinition cup in cups) + { + division.PlayoffMappings.Add(new DivisionPlayoffMapping + { + CreatedBy = CreatedBy, + DivisionId = Guid.Empty, + Division = division, + FromPosition = cup.FromPosition, + ToPosition = cup.ToPosition, + Destination = cup.BracketName, + }); + + // Positions are 1-based and inclusive; standings is 0-based best-first. + List seedIds = [.. standings + .Skip(cup.FromPosition - 1) + .Take(cup.ToPosition - cup.FromPosition + 1) + .Select(p => p.TeamId)]; + + SeedEliminationBracket( + division, groupStage.EndDate.AddDays(StageTemplate.StandardGapDays), + seedIds, teamsById, venues, cup.BracketName, cup.BestOf, slugRegistry, ref order); + } + } + + /// + /// Builds a full single-elimination bracket for seedIds, adding every round's Stage to division. + /// + private static void SeedEliminationBracket( + Division division, + DateTime anchorDate, + List seedIds, + Dictionary teamsById, + List venues, + string? bracketName, + int finalsBestOf, + SlugRegistry slugRegistry, + ref int order) + { + if (seedIds.Count < 2) + { + return; + } + + int bracketSize = PlayoffSeeder.NextPowerOfTwo(seedIds.Count); + List<(StageType Type, Template Template)> rounds = RoundsForBracket(bracketSize); + List<(Guid HomeTeamId, Guid? VisitorTeamId)> firstRoundPairs = PlayoffSeeder.SeedPairs(seedIds); + + DateTime roundStart = anchorDate; + List? advancing = null; + + for (int roundIndex = 0; roundIndex < rounds.Count; roundIndex++) + { + (StageType stageType, Template template) = rounds[roundIndex]; + bool isFinalsRound = roundIndex >= rounds.Count - 2; + int stageBestOf = isFinalsRound ? finalsBestOf : 1; + string stageName = bracketName is null ? template.Name : $"{template.Name} {bracketName}"; + + Stage stage = new() + { + CreatedBy = CreatedBy, + Name = stageName, + Slug = slugRegistry.ForStage($"{stageName} {division.Name}"), + StageType = stageType, + IsActive = true, + IsElimination = true, + BracketName = bracketName, + BestOf = stageBestOf, + StartDate = roundStart, + EndDate = roundStart.AddDays(StageTemplate.DurationDays), + DivisionId = Guid.Empty, + Division = division, + Matches = [], + Order = order++, + }; + division.Stages.Add(stage); + + List roundEntrants = roundIndex == 0 + ? [.. seedIds.Select(id => teamsById[id])] + : advancing!; + AddStageTeamMatches(stage, roundEntrants); + + List winners = []; + + if (roundIndex == 0) + { + for (int i = 0; i < firstRoundPairs.Count; i++) + { + (Guid homeId, Guid? visitorId) = firstRoundPairs[i]; + Team home = teamsById[homeId]; + + if (visitorId is null) + { + // The top seed advances automatically but still gets a match row with no visitor and no score, matching the shape StageService.FillStageWithSeedsAsync writes for a bye, since otherwise the stage held more StageTeamMatch rows than its matches had slots and re-seeding from the admin panel threw SeedTeamCountOutOfRange. + stage.Matches.Add(BuildByeMatch(stage, home, venues, roundStart.AddDays(i))); + winners.Add(home); + continue; + } + + Team visitor = teamsById[visitorId.Value]; + + if (stageBestOf > 1) + { + Team seriesWinner = BuildDecidedSeries( + stage, home, visitor, stageBestOf, venues, roundStart.AddDays(i), + seriesSeed: (stage.Order * 97) + (i * 13)); + winners.Add(seriesWinner); + } + else + { + Match match = BuildFinishedMatch( + stage, home, visitor, 79 + (i * 3), 66 + (i * 2), Domain.Enums.MatchType.Playoff, + venues, roundStart.AddDays(i), i, round: null); + stage.Matches.Add(match); + winners.Add(match.WinningTeam!); + } + } + } + else + { + for (int i = 0; i < roundEntrants.Count; i += 2) + { + int pairIndex = i / 2; + Team home = roundEntrants[i]; + Team visitor = roundEntrants[i + 1]; + + if (stageBestOf > 1) + { + Team seriesWinner = BuildDecidedSeries( + stage, home, visitor, stageBestOf, venues, roundStart.AddDays(pairIndex), + seriesSeed: (stage.Order * 97) + (pairIndex * 13) + 5); + winners.Add(seriesWinner); + } + else + { + Match match = BuildFinishedMatch( + stage, home, visitor, 84 + i, 71 + i, Domain.Enums.MatchType.Playoff, + venues, roundStart.AddDays(pairIndex), pairIndex, round: null); + stage.Matches.Add(match); + winners.Add(match.WinningTeam!); + } + } + } + + advancing = winners; + roundStart = stage.EndDate.AddDays(StageTemplate.StandardGapDays); + } + } + + /// + /// Builds a real best-of-N MatchSeries between home and visitor and adds it, along with every game it plays, to stage. + /// + private static Team BuildDecidedSeries( + Stage stage, + Team home, + Team visitor, + int bestOf, + List venues, + DateTime anchorDate, + int seriesSeed) + { + MatchSeries series = new() + { + StageId = Guid.Empty, + Stage = stage, + // Team.Id is already assigned when teams are built, unlike Stage.Id which is EF-generated at save time, so the real id is set here directly since SeriesDecisionCalculator.DetermineWinner runs in-memory before any EF save and compares game winners against these ids. + HomeTeamId = home.Id, + HomeTeam = home, + VisitorTeamId = visitor.Id, + VisitorTeam = visitor, + BestOf = bestOf, + CreatedBy = CreatedBy, + }; + stage.MatchSeries.Add(series); + + int gamesToWin = (bestOf / 2) + 1; + int visitorWins = gamesToWin <= 1 ? 0 : Math.Abs(seriesSeed) % gamesToWin; + int totalGames = gamesToWin + visitorWins; + + for (int gameNumber = 1; gameNumber <= totalGames; gameNumber++) + { + // The visitor takes the first visitorWins games of the series in a comeback shape, then the home team wins every game after that, clinching on the last generated game. + bool homeWinsThisGame = gameNumber > visitorWins; + int margin = 4 + ((gameNumber + seriesSeed) % 9); + int winnerScore = 68 + ((gameNumber * 5) % 22); + int loserScore = winnerScore - margin; + int homeScore = homeWinsThisGame ? winnerScore : loserScore; + int visitorScore = homeWinsThisGame ? loserScore : winnerScore; + + Match game = BuildFinishedMatch( + stage, home, visitor, homeScore, visitorScore, Domain.Enums.MatchType.Playoff, + venues, anchorDate.AddDays(gameNumber - 1), seriesSeed + gameNumber, round: null); + game.Series = series; + game.GameNumber = gameNumber; + + stage.Matches.Add(game); + series.Matches.Add(game); + } + + Guid? winningTeamId = SeriesDecisionCalculator.DetermineWinner(series); + Team winnerTeam = winningTeamId == visitor.Id ? visitor : home; + series.WinningTeamId = winningTeamId; + series.WinningTeam = winnerTeam; + + return winnerTeam; + } + + /// + /// The rounds a single-elimination bracket of bracketSize seeds needs, in play order with the first round first and Final last. + /// + private static List<(StageType Type, Template Template)> RoundsForBracket(int bracketSize) + { + List<(StageType, Template)> rounds = []; + + for (int remaining = bracketSize; remaining >= 2; remaining /= 2) + { + rounds.Add(remaining switch + { + 2 => (StageType.Final, StageTemplate.Final), + 4 => (StageType.SemiFinal, StageTemplate.SemiFinal), + 8 => (StageType.QuarterFinal, StageTemplate.QuarterFinal), + 16 => (StageType.RoundOf16, StageTemplate.RoundOf16), + _ => throw new NotSupportedException( + $"Elimination brackets larger than 16 seeds are not supported (requested {bracketSize})."), + }); + } + + return rounds; + } + + /// + /// Builds the cross-division cup, one division whose teams are drawn from allTeams and split into internal round-robin groups. + /// + private static void SeedCrossDivisionCup( + Tournament tournament, + CrossCupDefinition crossCup, + List allTeams, + List venues, + DateTime anchorDate, + SlugRegistry slugRegistry, + int upsetPercent, + int varietySeed) + { + Division cupDivision = new() + { + CreatedBy = CreatedBy, + Name = crossCup.DivisionName, + Slug = slugRegistry.ForDivision(crossCup.DivisionName), + Tournament = tournament, + Stages = [], + IsCrossDivisionCup = true, + QualifiersPerGroup = crossCup.QualifiersPerGroup, + // The cup lives inside its tournament, so its category always matches the tournament's, the same as every other division. + Category = tournament.Category, + }; + tournament.Divisions.Add(cupDivision); + + // Cross-division cup teams already hold a regular-division registration; this adds their second, cup-scoped one, matching the roster invariant of at most one regular plus one cross-cup registration per team. + foreach (Team team in allTeams) + { + team.DivisionTeamRegistrations.Add(new DivisionTeamRegistration + { + CreatedBy = CreatedBy, + TeamId = Guid.Empty, + Team = team, + DivisionId = Guid.Empty, + Division = cupDivision, + }); + } + + int groupCount = crossCup.GroupCount; + + List> groupStandings = []; + Dictionary teamsById = allTeams.ToDictionary(t => t.Id); + + // Distributes teams round-robin across the groups using index % groupCount so each group mixes teams from both zones and stays balanced. + for (int g = 0; g < groupCount; g++) + { + List groupTeams = [.. allTeams.Where((_, idx) => idx % groupCount == g)]; + + Stage groupStage = new() + { + CreatedBy = CreatedBy, + Name = $"Grupo {g + 1}", + Slug = slugRegistry.ForStage($"Grupo {g + 1} {cupDivision.Name}"), + StageType = StageType.Group, + IsActive = true, + StartDate = anchorDate, + EndDate = anchorDate.AddDays(7 * groupTeams.Count * crossCup.RoundRobinLegs), + DivisionId = Guid.Empty, + Division = cupDivision, + Matches = [], + Order = g, + RoundRobinLegs = crossCup.RoundRobinLegs, + }; + cupDivision.Stages.Add(groupStage); + AddStageTeamMatches(groupStage, groupTeams); + + SeedRoundRobinMatches( + groupStage, groupTeams, venues, anchorDate, isCrossDivisionCup: true, legs: crossCup.RoundRobinLegs, + playedRounds: null, upsetPercent: upsetPercent, varietySeed: varietySeed + g); + + groupStandings.Add(PositionCalculator.CalculatePositions(groupStage.Matches)); + } + + List seedOrder = CrossCupGroupSeeder.ResolveSeedOrder(groupStandings, crossCup.QualifiersPerGroup); + if (seedOrder.Count < 2) + { + return; + } + + // The bracket starts after the last group jornada actually scheduled, since SeedRoundRobinMatches pushes a group stage's EndDate out to its own last match, rather than after an estimate built from the average group size which could date a knockout game before the groups had finished. + DateTime bracketStart = cupDivision.Stages.Max(s => s.EndDate).AddDays(StageTemplate.StandardGapDays); + int order = groupCount; + + SeedEliminationBracket( + cupDivision, bracketStart, seedOrder, teamsById, venues, + bracketName: null, crossCup.FinalsBestOf, slugRegistry, ref order); + } + + /// + /// Deterministic draw for whether this fixture goes against the seeding, with the base upsetPercent damped by the seeding gap. + /// + private static bool IsUpset( + int upsetPercent, int varietySeed, int stageOrder, int round, int homeIdx, int visitorIdx) + { + if (upsetPercent <= 0) + { + return false; + } + + int gap = Math.Abs(homeIdx - visitorIdx); + int chance = Math.Max(6, upsetPercent - (gap * 2)); + + int hash = 17; + hash = (hash * 31) + varietySeed; + hash = (hash * 31) + stageOrder; + hash = (hash * 31) + round; + hash = (hash * 31) + Math.Min(homeIdx, visitorIdx); + hash = (hash * 31) + Math.Max(homeIdx, visitorIdx); + hash ^= hash >> 13; + hash *= 0x5BD1E995; + hash ^= hash >> 15; + + return ((hash & 0x7FFFFFFF) % 100) < chance; + } + + /// + /// The gym a team plays its home games at, picked by a stable hash of the team name so a club always hosts at the same venue. + /// + private static Venue VenueForHome(Team home, List venues) + { + int hash = 17; + foreach (char character in home.Name) + { + hash = (hash * 31) + character; + } + + return venues[(hash & 0x7FFFFFFF) % venues.Count]; + } + + private static void AddStageTeamMatches(Stage stage, List teams) + { + foreach (Team team in teams) + { + stage.StageTeamMatches.Add(new StageTeamMatch + { + CreatedBy = CreatedBy, + StageId = Guid.Empty, + Stage = stage, + TeamId = Guid.Empty, + Team = team, + }); + } + } + + /// + /// Builds one finished, decisive, never-tied Match with a scorer and statistic for both teams. + /// + private static Match BuildFinishedMatch( + Stage stage, + Team home, + Team visitor, + int homeScore, + int visitorScore, + Domain.Enums.MatchType type, + List venues, + DateTime matchDate, + int venueIndex, + int? round) + { + Venue venue = VenueForHome(home, venues); + Team winner = homeScore > visitorScore ? home : visitor; + + Match match = new() + { + CreatedBy = CreatedBy, + MatchDate = matchDate, + Round = round, + Type = type, + Slug = SlugGenerator.GenerateSlug($"{home.Name}-vs-{visitor.Name}-{stage.StageType}-{Guid.NewGuid()}"), + HomeTeam = home, + HomeTeamId = home.Id, + VisitorTeam = visitor, + VisitorTeamId = visitor.Id, + HomeScore = homeScore, + VisitorScore = visitorScore, + IsFinished = true, + Status = MatchStatus.Played, + WinningTeam = winner, + WinningTeamId = winner.Id, + Stage = stage, + Venue = venue, + PlayerStatistics = [], + Scorers = [], + }; + + AddScoring(match, home, homeScore, venueIndex); + AddScoring(match, visitor, visitorScore, venueIndex + 1); + + return match; + } + + /// + /// Builds the match row that represents a first-round BYE, where the home team advances with no visitor and no score. + /// + private static Match BuildByeMatch(Stage stage, Team home, List venues, DateTime matchDate) => + new() + { + CreatedBy = CreatedBy, + MatchDate = matchDate, + Round = null, + Type = Domain.Enums.MatchType.Playoff, + Slug = SlugGenerator.GenerateSlug($"{home.Name}-bye-{stage.StageType}-{Guid.NewGuid()}"), + HomeTeam = home, + HomeTeamId = home.Id, + VisitorTeam = null, + VisitorTeamId = null, + HomeScore = null, + VisitorScore = null, + IsFinished = true, + Status = MatchStatus.Played, + WinningTeam = home, + WinningTeamId = home.Id, + Stage = stage, + Venue = VenueForHome(home, venues), + PlayerStatistics = [], + Scorers = [], + }; + + /// + /// Builds one UPCOMING, unplayed regular match with teams and a future date set but no score, winner, or scorers. + /// + private static Match BuildUpcomingMatch( + Stage stage, + Team home, + Team visitor, + MatchType type, + List venues, + DateTime matchDate, + int? round) + { + Venue venue = VenueForHome(home, venues); + + return new Match + { + CreatedBy = CreatedBy, + MatchDate = matchDate, + Round = round, + Type = type, + Slug = SlugGenerator.GenerateSlug($"{home.Name}-vs-{visitor.Name}-{stage.StageType}-{Guid.NewGuid()}"), + HomeTeam = home, + HomeTeamId = home.Id, + VisitorTeam = visitor, + VisitorTeamId = visitor.Id, + HomeScore = null, + VisitorScore = null, + IsFinished = false, + Status = MatchStatus.Scheduled, + WinningTeam = null, + WinningTeamId = null, + Stage = stage, + Venue = venue, + PlayerStatistics = [], + Scorers = [], + }; + } + + /// + /// The players of team that a seeded match sheet may list, meaning their season registration is Approved and they are not serving a sanction. + /// + private static List EligibleScorers(Team team) + { + List eligible = + [.. team.PlayerTeamRegistrations + .Where(registration => registration.MedicalRecordStatus == MedicalRecordStatus.Approved) + .Select(registration => registration.Player) + .OfType() + .Where(player => !player.IsSanctioned)]; + + return eligible.Count > 0 ? eligible : [.. team.Players]; + } + + /// + /// Hands a suspended player's scoring rows in the games he must miss to a team-mate who is not already scoring in that game. + /// + private static void ApplySuspension( + List playedMatches, Player player, Team team, Match sanctionMatch, int duration, bool active) + { + List afterTheRuling = + [.. playedMatches.Where(match => + (match.HomeTeam == team || match.VisitorTeam == team) + && match.MatchDate > sanctionMatch.MatchDate)]; + + foreach (Match match in active ? afterTheRuling : afterTheRuling.Take(duration)) + { + ReassignScoring(match, player, team); + } + } + + /// + /// Moves every scoring row player holds in match onto an eligible team-mate who is not already scoring in it. + /// + private static void ReassignScoring(Match match, Player player, Team team) + { + List scorers = [.. match.Scorers.Where(scorer => scorer.Player == player)]; + if (scorers.Count == 0) + { + return; + } + + HashSet alreadyScoring = [.. match.Scorers.Select(scorer => scorer.Player).OfType()]; + Player? substitute = EligibleScorers(team) + .Find(candidate => candidate != player && !alreadyScoring.Contains(candidate)); + + if (substitute is null) + { + return; + } + + foreach (Scorer scorer in scorers) + { + scorer.Player = substitute; + } + + foreach (PlayerStatistic statistic in match.PlayerStatistics.Where(s => s.Player == player)) + { + statistic.Player = substitute; + } + } + + /// + /// Adds a scorer plus Points and Assists PlayerStatistic rows for one team in a match. + /// + private static void AddScoring(Match match, Team team, int score, int scorerSeed) + { + List eligible = EligibleScorers(team); + + if (score <= 0 || eligible.Count == 0) + { + return; + } + + // Spreads the team's points deterministically across a handful of players, with tapering weights and the lead scorer taking the remainder, so the goleadores read realistically instead of one player scoring the whole game. + int[] weights = [5, 4, 3, 2, 1]; + int scorerCount = Math.Min(weights.Length, eligible.Count); + int weightTotal = 0; + for (int i = 0; i < scorerCount; i++) + { + weightTotal += weights[i]; + } + + int[] shares = new int[scorerCount]; + int distributed = 0; + for (int i = 1; i < scorerCount; i++) + { + shares[i] = score * weights[i] / weightTotal; + distributed += shares[i]; + } + shares[0] = score - distributed; + + for (int i = 0; i < scorerCount; i++) + { + if (shares[i] <= 0) + { + continue; + } + + Player player = eligible[Math.Abs(scorerSeed + i) % eligible.Count]; + AddPlayerScoring(match, player, shares[i]); + } + } + + private static void AddPlayerScoring(Match match, Player player, int points) + { + match.Scorers.Add(new Scorer + { + CreatedBy = CreatedBy, + PlayerId = Guid.Empty, + Player = player, + Points = points, + MatchId = Guid.Empty, + Match = match, + }); + match.PlayerStatistics.Add(new PlayerStatistic + { + CreatedBy = CreatedBy, + Value = points, + PlayerId = Guid.Empty, + Player = player, + MatchId = Guid.Empty, + Match = match, + Type = StatisticType.Points, + }); + match.PlayerStatistics.Add(new PlayerStatistic + { + CreatedBy = CreatedBy, + Value = 1, + PlayerId = Guid.Empty, + Player = player, + MatchId = Guid.Empty, + Match = match, + Type = StatisticType.Assists, + }); + } + + /// + /// Seeds a coherent, varied set of basketball sanctions tied to real finished group matches and players. + /// + private static List SeedSanctions(List groupStages, Tournament tournament) + { + List sanctions = []; + + // Sanctions are tied only to finished matches, since an in-progress tournament's group stage also holds still-to-play upcoming matches which must never seed a sanction, and matches are ordered by date because whether a sanction reads as already served or still being served depends on where in the calendar it sits. + List matches = [.. groupStages + .SelectMany(s => s.Matches) + .Where(m => m.IsFinished && m.VisitorTeam is not null) + .OrderBy(m => m.MatchDate)]; + if (matches.Count == 0) + { + return sanctions; + } + + // Every played game of the tournament, playoffs included, since a suspension handed out in the last jornadas of a zone also rules the player out of his team's cup games. + List playedMatches = [.. tournament.Divisions + .SelectMany(d => d.Stages) + .SelectMany(s => s.Matches) + .Where(m => m.IsFinished && m.VisitorTeam is not null) + .OrderBy(m => m.MatchDate)]; + + (string Description, int Duration, SanctionSubjectType Subject, SanctionAppealStatus Appeal, bool Active)[] specs = + [ + ("Falta descalificante por conducta antideportiva.", 2, SanctionSubjectType.Player, SanctionAppealStatus.None, true), + ("Expulsión por doble falta técnica.", 1, SanctionSubjectType.Player, SanctionAppealStatus.Pending, true), + ("Agresión a un rival durante el partido.", 3, SanctionSubjectType.Player, SanctionAppealStatus.None, true), + ("Reclamos reiterados al árbitro.", 1, SanctionSubjectType.Player, SanctionAppealStatus.Rejected, false), + ("Falta antideportiva reiterada.", 2, SanctionSubjectType.Player, SanctionAppealStatus.None, false), + ("Suspensión de cancha por incidentes del público.", 1, SanctionSubjectType.Team, SanctionAppealStatus.None, true), + ]; + + for (int i = 0; i < specs.Length; i++) + { + (string description, int duration, SanctionSubjectType subject, SanctionAppealStatus appeal, bool active) = specs[i]; + + // A sanction is issued on the game it came from and never before it; a served sanction is drawn from the opening third of the calendar since its fechas have long elapsed, while an active one is drawn from the closing third where the player is still sitting it out. + int window = Math.Max(1, matches.Count / 3); + Match match = active + ? matches[matches.Count - 1 - ((i * 3) % window)] + : matches[(i * 3) % window]; + Team losingTeam = match.WinningTeam == match.HomeTeam ? match.VisitorTeam! : match.HomeTeam!; + + PlayerSanction sanction = new() + { + CreatedBy = CreatedBy, + Duration = duration, + IssuedDate = match.MatchDate, + Description = description, + SubjectType = subject, + Match = match, + MatchId = Guid.Empty, + AppealStatus = appeal, + Slug = SlugGenerator.GenerateSlug($"sancion-{match.Slug}-{i}-{Guid.NewGuid()}"), + }; + + if (subject == SanctionSubjectType.Team) + { + sanction.Team = losingTeam; + sanction.TeamId = Guid.Empty; + } + else + { + Player player = losingTeam.Players.ElementAt((i + 1) % losingTeam.Players.Count); + player.IsSanctioned = active; + sanction.Player = player; + sanction.PlayerId = Guid.Empty; + + ApplySuspension(playedMatches, player, losingTeam, match, duration, active); + } + + // An appeal can only be filed after the ruling and resolved after it is filed; both used to be back-dated before the match. + if (appeal == SanctionAppealStatus.Pending) + { + sanction.AppealReason = "El jugador sostiene que la falta no existió."; + sanction.AppealDate = match.MatchDate.AddDays(1); + } + else if (appeal == SanctionAppealStatus.Rejected) + { + sanction.AppealReason = "Se solicitó revisión de la jugada."; + sanction.AppealDate = match.MatchDate.AddDays(2); + sanction.AppealResolution = "El tribunal ratificó la sanción."; + sanction.AppealResolvedDate = match.MatchDate.AddDays(5); + } + + sanctions.Add(sanction); + } + + return sanctions; + } +} diff --git a/Club12-Backend/Infrastructure/Repositories/AuditLogRepository.cs b/Club12-Backend/Infrastructure/Repositories/AuditLogRepository.cs new file mode 100644 index 0000000..a33989f --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/AuditLogRepository.cs @@ -0,0 +1,16 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for AuditLog entries, inheriting generic CRUD from GenericRepository. +/// +/// The application's database context. +public class AuditLogRepository(ApplicationDBContext context) + : GenericRepository(context), IAuditLogRepository +{ +} diff --git a/Club12-Backend/Infrastructure/Repositories/BlogPostRepository.cs b/Club12-Backend/Infrastructure/Repositories/BlogPostRepository.cs new file mode 100644 index 0000000..800f326 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/BlogPostRepository.cs @@ -0,0 +1,16 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for BlogPost entities, inheriting generic CRUD and implementing IBlogPostRepository. +/// +/// The application's database context used for data access operations. +public class BlogPostRepository(ApplicationDBContext context) + : GenericRepository(context), IBlogPostRepository +{ +} \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Repositories/ClubRepository.cs b/Club12-Backend/Infrastructure/Repositories/ClubRepository.cs new file mode 100644 index 0000000..5bbeef7 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/ClubRepository.cs @@ -0,0 +1,14 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing Club entities, inheriting generic CRUD from GenericRepository. +/// +public class ClubRepository(ApplicationDBContext context) + : GenericRepository(context), IClubRepository +{ } diff --git a/Club12-Backend/Infrastructure/Repositories/DivisionRepository.cs b/Club12-Backend/Infrastructure/Repositories/DivisionRepository.cs new file mode 100644 index 0000000..6b76fec --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/DivisionRepository.cs @@ -0,0 +1,15 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing Division entities, inheriting generic CRUD and implementing IDivisionRepository. +/// +public class DivisionRepository(ApplicationDBContext context) + : GenericRepository(context), IDivisionRepository +{ +} \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Repositories/DivisionTeamRegistrationRepository.cs b/Club12-Backend/Infrastructure/Repositories/DivisionTeamRegistrationRepository.cs new file mode 100644 index 0000000..1c987c6 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/DivisionTeamRegistrationRepository.cs @@ -0,0 +1,14 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing DivisionTeamRegistration entities, inheriting generic CRUD from GenericRepository. +/// +public class DivisionTeamRegistrationRepository(ApplicationDBContext context) + : GenericRepository(context), IDivisionTeamRegistrationRepository +{ } diff --git a/Club12-Backend/Infrastructure/Repositories/GenericRepository.cs b/Club12-Backend/Infrastructure/Repositories/GenericRepository.cs new file mode 100644 index 0000000..2d8b04d --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/GenericRepository.cs @@ -0,0 +1,143 @@ +using Application.DTOs.Abstract.Request; +using Application.Interfaces.Repositories; +using Application.Utils.Extensions; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +using Microsoft.EntityFrameworkCore; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace Infrastructure.Repositories; + +/// +/// Provides a generic repository implementation for CRUD operations and querying entities in the database. +/// +/// +/// The entity type managed by the repository. Must inherit from EntityBase. +/// +public class GenericRepository(ApplicationDBContext context) + : IGenericRepository where TEntity : EntityBase +{ + /// + /// The database context used for data access. + /// + protected readonly ApplicationDBContext _context = context; + + /// + /// The DbSet{TEntity} representing the collection of entities. + /// + protected readonly DbSet _dbSet = context.Set(); + + + /// + public virtual async Task UpdateRangeAsync(IEnumerable entities) + { + _dbSet.UpdateRange(entities); + await _context.SaveChangesAsync(); + } + + /// + public virtual async Task UpdateAsync(TEntity entity) + { + _dbSet.Update(entity); + await _context.SaveChangesAsync(); + } + /// + public virtual async Task GetByIdAsync(Guid id, IEnumerable>>? includes = null) + { + IQueryable query = _dbSet; + + foreach (Expression> include in includes ?? []) + { + query = query.Include(include); + } + + return await query.FirstOrDefaultAsync(e => e.Id == id); + } + + /// + public virtual IQueryable GetQueryable() + { + return _dbSet; + } + + /// + public virtual async Task> GetAllAsync() + { + return await _dbSet.ToListAsync(); + } + + /// + public virtual async Task> FindAsync( + Expression> predicate, + IEnumerable>>? includes = null, + PaginatedFilterRequest? filter = null, + bool asSplitQuery = false) + { + IQueryable query = _dbSet.Where(predicate); + + foreach (Expression> include in includes ?? []) + { + query = query.Include(include); + } + + if (asSplitQuery) + { + query = query.AsSplitQuery(); + } + + if (filter != null) + { + query = query.SortBy(filter).Paginate(filter.PageNumber, filter.PageSize); + } + + return await query.ToListAsync(); + } + + /// + public virtual async Task AddAsync(TEntity entity) + { + await _dbSet.AddAsync(entity); + await _context.SaveChangesAsync(); + return entity; + } + + /// + public virtual async Task> AddRangeAsync(IEnumerable entities) + { + await _dbSet.AddRangeAsync(entities); + await _context.SaveChangesAsync(); + return entities; + } + + /// + public virtual void Remove(TEntity entity) + { + _dbSet.Remove(entity); + } + + /// + public virtual async Task RemoveAsync(Expression> expression) + { + return await _dbSet.Where(expression).ExecuteDeleteAsync(); + } + + /// + public virtual async Task CountAsync(Expression>? predicate = null) + { + return predicate == null ? await _dbSet.CountAsync() : await _dbSet.CountAsync(predicate); + } + + /// + public virtual async Task ExistsAsync(Expression> predicate) + { + return await _dbSet.AnyAsync(predicate); + } +} diff --git a/Club12-Backend/Infrastructure/Repositories/MatchRepository.cs b/Club12-Backend/Infrastructure/Repositories/MatchRepository.cs new file mode 100644 index 0000000..238e27f --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/MatchRepository.cs @@ -0,0 +1,45 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +using Microsoft.EntityFrameworkCore; + +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing Match entities, inheriting generic CRUD and implementing IMatchRepository. +/// +/// The application's database context used for data access. +public class MatchRepository(ApplicationDBContext context) + : GenericRepository(context), IMatchRepository +{ + /// + public async Task GetDetailByIdOrSlugAsync(string idOrSlug) + { + IQueryable query = _context.Set() + .Include(match => match.HomeTeam) + .ThenInclude(team => team!.Players) + .ThenInclude(player => player.PlayerTeamRegistrations) + .Include(match => match.VisitorTeam) + .ThenInclude(team => team!.Players) + .ThenInclude(player => player.PlayerTeamRegistrations) + .Include(match => match.Venue) + // Stage → Division gives the match's tournament, used to pick each scorer's jersey number from that tournament's roster registration. + .Include(match => match.Stage) + .ThenInclude(stage => stage.Division) + .Include(match => match.Scorers) + .ThenInclude(scorer => scorer.Player) + .ThenInclude(player => player!.PlayerTeamRegistrations) + .AsSplitQuery(); + + return Guid.TryParse(idOrSlug, out Guid matchId) + ? await query.FirstOrDefaultAsync(match => match.Id == matchId) + : await query.FirstOrDefaultAsync(match => match.Slug == idOrSlug); + } +} diff --git a/Club12-Backend/Infrastructure/Repositories/MatchSeriesRepository.cs b/Club12-Backend/Infrastructure/Repositories/MatchSeriesRepository.cs new file mode 100644 index 0000000..68a1913 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/MatchSeriesRepository.cs @@ -0,0 +1,16 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing MatchSeries entities, inheriting generic CRUD and implementing IMatchSeriesRepository. +/// +/// The application's database context used for data access. +public class MatchSeriesRepository(ApplicationDBContext context) + : GenericRepository(context), IMatchSeriesRepository +{ +} diff --git a/Club12-Backend/Infrastructure/Repositories/PlayerRepository.cs b/Club12-Backend/Infrastructure/Repositories/PlayerRepository.cs new file mode 100644 index 0000000..f20c805 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/PlayerRepository.cs @@ -0,0 +1,15 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing Player entities, inheriting generic CRUD and implementing IPlayerRepository. +/// +public class PlayerRepository(ApplicationDBContext context) + : GenericRepository(context), IPlayerRepository +{ +} \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Repositories/PlayerSanctionRepository.cs b/Club12-Backend/Infrastructure/Repositories/PlayerSanctionRepository.cs new file mode 100644 index 0000000..fda1b10 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/PlayerSanctionRepository.cs @@ -0,0 +1,15 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing PlayerSanction entities, inheriting generic CRUD and implementing IPlayerSanctionRepository. +/// +public class PlayerSanctionRepository(ApplicationDBContext context) + : GenericRepository(context), IPlayerSanctionRepository +{ +} \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Repositories/PlayerStatisticRepository.cs b/Club12-Backend/Infrastructure/Repositories/PlayerStatisticRepository.cs new file mode 100644 index 0000000..5cd82da --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/PlayerStatisticRepository.cs @@ -0,0 +1,15 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing PlayerStatistic entities, inheriting generic CRUD and implementing IPlayerStatisticRepository. +/// +public class PlayerStatisticRepository(ApplicationDBContext context) + : GenericRepository(context), IPlayerStatisticRepository +{ +} \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Repositories/PlayerTeamRegistrationRepository.cs b/Club12-Backend/Infrastructure/Repositories/PlayerTeamRegistrationRepository.cs new file mode 100644 index 0000000..fd72f06 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/PlayerTeamRegistrationRepository.cs @@ -0,0 +1,14 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing PlayerTeamRegistration entities, inheriting generic CRUD from GenericRepository. +/// +public class PlayerTeamRegistrationRepository(ApplicationDBContext context) + : GenericRepository(context), IPlayerTeamRegistrationRepository +{ } diff --git a/Club12-Backend/Infrastructure/Repositories/ScorerRepository.cs b/Club12-Backend/Infrastructure/Repositories/ScorerRepository.cs new file mode 100644 index 0000000..5037392 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/ScorerRepository.cs @@ -0,0 +1,144 @@ +using Application.DTOs.Scorer.Request; +using Application.DTOs.Scorer.Response; +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; +using Domain.Enums; + +using Infrastructure.Persistance; + +using Microsoft.EntityFrameworkCore; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for the top-scorers "goleadores" ranking, built from PlayerStatistic rows instead of the Scorer table. +/// +public class ScorerRepository(ApplicationDBContext context) + : GenericRepository(context), IScorerRepository +{ + /// + /// Builds the top-scorers ranking by summing each player's Points-type PlayerStatistic rows instead of reading the Scorer table, keeping it in sync with the per-match loading path. + /// + public async Task<(IEnumerable Items, int TotalCount)> GetPlayerScoresAsync(GetScorerFilteredRequest filter) + { + IQueryable playersQuery = _context.Set(); + + if (filter.TournamentId.HasValue) + { + playersQuery = playersQuery.Where(p => p.Team.TournamentId == filter.TournamentId.Value); + } + + if (filter.MatchId.HasValue) + { + Guid matchId = filter.MatchId.Value; + playersQuery = playersQuery.Where(p => + _context.Set().Any(m => m.Id == matchId && + (m.HomeTeamId == p.TeamId || m.VisitorTeamId == p.TeamId))); + } + + if (filter.TeamId.HasValue) + { + playersQuery = playersQuery.Where(p => p.TeamId == filter.TeamId.Value); + } + + if (filter.PlayerId.HasValue) + { + playersQuery = playersQuery.Where(p => p.Id == filter.PlayerId.Value); + } + + if (filter.DivisionId.HasValue) + { + Guid divisionId = filter.DivisionId.Value; + playersQuery = playersQuery.Where(p => + _context.Set().Any(stm => stm.TeamId == p.TeamId && stm.Stage!.DivisionId == divisionId)); + } + + if (filter.StageId.HasValue) + { + Guid stageId = filter.StageId.Value; + playersQuery = playersQuery.Where(p => + _context.Set().Any(stm => stm.TeamId == p.TeamId && stm.StageId == stageId)); + } + + // The goleadores ranking aggregates from PlayerStatistic, the same table the per-match loading path writes to, so there is no orphan Scorer write-gap and the ranking reflects real loaded points; only Points-type statistics count toward it. + IQueryable scoreStatsQuery = _context.Set() + .Where(s => s.Type == StatisticType.Points); + + if (filter.TournamentId.HasValue) + { + scoreStatsQuery = scoreStatsQuery.Where(s => s.Match!.Stage.Division.TournamentId == filter.TournamentId.Value); + } + + if (filter.DivisionId.HasValue) + { + scoreStatsQuery = scoreStatsQuery.Where(s => s.Match!.Stage.DivisionId == filter.DivisionId.Value); + } + + if (filter.StageId.HasValue) + { + scoreStatsQuery = scoreStatsQuery.Where(s => s.Match!.StageId == filter.StageId.Value); + } + + if (filter.MatchId.HasValue) + { + scoreStatsQuery = scoreStatsQuery.Where(s => s.MatchId == filter.MatchId.Value); + } + + // A season is the calendar year of a tournament's StartDate, and grouping is safe by PlayerId because Player.DocumentNumber is uniquely indexed, so one real person is exactly one Player row reused across seasons rather than duplicated per season. + if (filter.Season.HasValue) + { + int season = filter.Season.Value; + + scoreStatsQuery = scoreStatsQuery.Where(s => + _context.Set().Any(t => + t.Id == s.Match!.Stage.Division.TournamentId && t.StartDate.Year == season)); + + playersQuery = playersQuery.Where(p => scoreStatsQuery.Any(s => s.PlayerId == p.Id)); + } + + IQueryable query = playersQuery.Select(player => new ScorerByPlayerResponse + { + PlayerId = player.Id, + FullName = (player.SecondName == null || player.SecondName == "") + ? player.LastName.ToUpper() + " " + player.FirstName + : player.LastName.ToUpper() + " " + player.FirstName + " " + player.SecondName, + Points = scoreStatsQuery + .Where(s => s.PlayerId == player.Id) + .Sum(s => (int?) s.Value) ?? 0, + TeamId = player.TeamId, + TeamName = player.Team.Name, + TeamLogoUrl = player.Team.LogoUrl, + TeamShirtColor = player.Team.ShirtColor, + TeamJerseyStyle = player.Team.JerseyStyle, + TeamShirtSecondaryColor = player.Team.ShirtSecondaryColor, + TeamShirtTertiaryColor = player.Team.ShirtTertiaryColor, + // The dorsal is season-scoped through PlayerTeamRegistration rather than a property of Player itself, so a tournament-scoped ranking uses that season's registration while a season or all-time aggregate spanning several registrations falls back to the player's most recent registration with their current team, since there is no single dorsal to show. + JerseyNumber = filter.TournamentId.HasValue + ? _context.Set() + .Where(r => r.PlayerId == player.Id && r.TournamentId == filter.TournamentId.Value) + .Select(r => r.JerseyNumber) + .FirstOrDefault() + : _context.Set() + .Where(r => r.PlayerId == player.Id && r.TeamId == player.TeamId) + .OrderByDescending(r => r.DateCreated) + .Select(r => r.JerseyNumber) + .FirstOrDefault(), + }); + + int totalCount = await query.CountAsync(); + + List items = await query + .OrderByDescending(x => x.Points) + .Skip((filter.PageNumber - 1) * filter.PageSize) + .Take(filter.PageSize) + .ToListAsync(); + + return (items, totalCount); + } +} diff --git a/Club12-Backend/Infrastructure/Repositories/SeasonRepository.cs b/Club12-Backend/Infrastructure/Repositories/SeasonRepository.cs new file mode 100644 index 0000000..867b5cb --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/SeasonRepository.cs @@ -0,0 +1,16 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for Season entities, inheriting generic CRUD from GenericRepository and implementing ISeasonRepository. +/// +/// The database context used for data access operations. +public class SeasonRepository(ApplicationDBContext context) + : GenericRepository(context), ISeasonRepository +{ +} diff --git a/Club12-Backend/Infrastructure/Repositories/StageRepository.cs b/Club12-Backend/Infrastructure/Repositories/StageRepository.cs new file mode 100644 index 0000000..b0f6d7a --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/StageRepository.cs @@ -0,0 +1,15 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing Stage entities, inheriting generic CRUD from GenericRepository and implementing IStageRepository. +/// +public class StageRepository(ApplicationDBContext context) + : GenericRepository(context), IStageRepository +{ +} diff --git a/Club12-Backend/Infrastructure/Repositories/StageTeamMatchRepository.cs b/Club12-Backend/Infrastructure/Repositories/StageTeamMatchRepository.cs new file mode 100644 index 0000000..66b1255 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/StageTeamMatchRepository.cs @@ -0,0 +1,16 @@ + + +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing StageTeamMatch entities, inheriting generic CRUD from GenericRepository and implementing IStageTeamMatchRepository. +/// +public class StageTeamMatchRepository(ApplicationDBContext context) + : GenericRepository(context), IStageTeamMatchRepository +{ } diff --git a/Club12-Backend/Infrastructure/Repositories/StatisticsRepository.cs b/Club12-Backend/Infrastructure/Repositories/StatisticsRepository.cs new file mode 100644 index 0000000..00ce7b0 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/StatisticsRepository.cs @@ -0,0 +1,200 @@ +using Application.DTOs.Statistics.Response; +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; +using Domain.Enums; + +using Infrastructure.Persistance; + +using Microsoft.EntityFrameworkCore; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Infrastructure.Repositories; + +/// +/// Historical-statistics aggregations that group each person's rows across seasons by their stable PlayerId, since a person is one Player row reused across seasons rather than duplicated per season. +/// +public class StatisticsRepository(ApplicationDBContext context) : IStatisticsRepository +{ + private readonly ApplicationDBContext _context = context; + + /// + public async Task GetPlayerCardAsync(Guid playerId) + { + Player? player = await _context.Set() + .AsNoTracking() + .FirstOrDefaultAsync(p => p.Id == playerId); + + if (player is null) + { + return null; + } + + List rows = await _context.Set() + .Where(s => s.PlayerId == playerId && s.Type == StatisticType.Points) + .Select(s => new PointStatRow(s.MatchId, s.Value, s.Match!.Stage.Division.TournamentId)) + .ToListAsync(); + + Dictionary seasonByTournament = await BuildSeasonByTournamentAsync(rows.Select(r => r.TournamentId)); + + List seasons = [.. rows + .GroupBy(r => seasonByTournament.TryGetValue(r.TournamentId, out int year) ? year : (int?)null) + .Where(g => g.Key.HasValue) + .Select(g => BuildSeasonLine(g.Key!.Value, g)) + .OrderByDescending(s => s.Season)]; + + int totalPoints = rows.Sum(r => r.Value); + int gamesPlayed = rows.Select(r => r.MatchId).Distinct().Count(); + + return new PlayerStatisticCardResponse + { + PlayerId = player.Id, + FullName = player.FullName, + TotalPoints = totalPoints, + GamesPlayed = gamesPlayed, + AveragePoints = Average(totalPoints, gamesPlayed), + Seasons = seasons, + }; + } + + /// + public async Task GetPlayerHistoryAsync(Guid playerId) + { + Player? player = await _context.Set() + .AsNoTracking() + .FirstOrDefaultAsync(p => p.Id == playerId); + + if (player is null) + { + return null; + } + + // Season and team come from the season-scoped registrations, the source of truth for which team and which season, untouched by a team being reassigned to a later tournament. + List registrations = await _context.Set() + .Where(r => r.PlayerId == playerId) + .Select(r => new RegistrationRow( + r.TournamentId, + r.Tournament!.Name, + r.Tournament!.StartDate, + r.TeamId, + r.Team!.Name)) + .ToListAsync(); + + List statRows = await _context.Set() + .Where(s => s.PlayerId == playerId && s.Type == StatisticType.Points) + .Select(s => new PointStatRow(s.MatchId, s.Value, s.Match!.Stage.Division.TournamentId)) + .ToListAsync(); + + Dictionary statsByTournament = statRows + .GroupBy(r => r.TournamentId) + .ToDictionary( + g => g.Key, + g => (Points: g.Sum(x => x.Value), Games: g.Select(x => x.MatchId).Distinct().Count())); + + List sanctionRows = await _context.Set() + .Where(ps => ps.PlayerId == playerId) + .Select(ps => new SanctionRow( + ps.Id, + ps.Description, + ps.Duration, + ps.IssuedDate, + ps.MatchId, + ps.Match.Stage.Division.TournamentId)) + .ToListAsync(); + + Dictionary> sanctionsByTournament = sanctionRows + .GroupBy(s => s.TournamentId) + .ToDictionary( + g => g.Key, + g => g.Select(s => new PlayerHistorySanctionResponse + { + SanctionId = s.Id, + Description = s.Description, + Duration = s.Duration, + IssuedDate = s.IssuedDate, + MatchId = s.MatchId, + }).ToList()); + + List seasons = [.. registrations + .Select(r => + { + (int points, int games) = statsByTournament.TryGetValue(r.TournamentId, out (int Points, int Games) stat) + ? stat + : (0, 0); + + IEnumerable sanctions = + sanctionsByTournament.TryGetValue(r.TournamentId, out List? list) + ? list + : []; + + return new PlayerHistorySeasonResponse + { + Season = r.StartDate.Year, + TournamentId = r.TournamentId, + TournamentName = r.TournamentName, + TeamId = r.TeamId, + TeamName = r.TeamName, + TotalPoints = points, + GamesPlayed = games, + Sanctions = sanctions, + }; + }) + .OrderByDescending(s => s.Season) + .ThenBy(s => s.TournamentName)]; + + return new PlayerHistoryResponse + { + PlayerId = player.Id, + FullName = player.FullName, + Seasons = seasons, + }; + } + + /// + /// Resolves each tournament id to its season, the year of its StartDate, in one query computed in memory so the season definition stays provider-independent. + /// + private async Task> BuildSeasonByTournamentAsync(IEnumerable tournamentIds) + { + List ids = [.. tournamentIds.Distinct()]; + if (ids.Count == 0) + { + return []; + } + + List rows = await _context.Set() + .Where(t => ids.Contains(t.Id)) + .Select(t => new TournamentSeasonRow(t.Id, t.StartDate)) + .ToListAsync(); + + return rows.ToDictionary(r => r.Id, r => r.StartDate.Year); + } + + private static SeasonStatLineResponse BuildSeasonLine(int season, IEnumerable rows) + { + int totalPoints = rows.Sum(r => r.Value); + int gamesPlayed = rows.Select(r => r.MatchId).Distinct().Count(); + + return new SeasonStatLineResponse + { + Season = season, + TotalPoints = totalPoints, + GamesPlayed = gamesPlayed, + AveragePoints = Average(totalPoints, gamesPlayed), + }; + } + + private static double Average(int totalPoints, int gamesPlayed) => + gamesPlayed == 0 ? 0 : Math.Round((double)totalPoints / gamesPlayed, 2); + + private sealed record PointStatRow(Guid MatchId, int Value, Guid TournamentId); + + private sealed record RegistrationRow(Guid TournamentId, string TournamentName, DateTime StartDate, Guid TeamId, string TeamName); + + private sealed record SanctionRow(Guid Id, string Description, int Duration, DateTime IssuedDate, Guid MatchId, Guid TournamentId); + + private sealed record TournamentSeasonRow(Guid Id, DateTime StartDate); +} diff --git a/Club12-Backend/Infrastructure/Repositories/TeamPointDeductionRepository.cs b/Club12-Backend/Infrastructure/Repositories/TeamPointDeductionRepository.cs new file mode 100644 index 0000000..5b0b3a7 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/TeamPointDeductionRepository.cs @@ -0,0 +1,15 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for TeamPointDeduction entities, inheriting generic CRUD from GenericRepository. +/// +public class TeamPointDeductionRepository(ApplicationDBContext context) + : GenericRepository(context), ITeamPointDeductionRepository +{ +} diff --git a/Club12-Backend/Infrastructure/Repositories/TeamRepository.cs b/Club12-Backend/Infrastructure/Repositories/TeamRepository.cs new file mode 100644 index 0000000..12aa671 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/TeamRepository.cs @@ -0,0 +1,14 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing Team entities, inheriting generic CRUD from GenericRepository and implementing ITeamRepository. +/// +public class TeamRepository(ApplicationDBContext context) + : GenericRepository(context), ITeamRepository +{ } diff --git a/Club12-Backend/Infrastructure/Repositories/TeamStaffRepository.cs b/Club12-Backend/Infrastructure/Repositories/TeamStaffRepository.cs new file mode 100644 index 0000000..747d9a1 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/TeamStaffRepository.cs @@ -0,0 +1,15 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for TeamStaff entities, inheriting generic CRUD from GenericRepository. +/// +public class TeamStaffRepository(ApplicationDBContext context) + : GenericRepository(context), ITeamStaffRepository +{ +} diff --git a/Club12-Backend/Infrastructure/Repositories/TeamTournamentRegistrationRepository.cs b/Club12-Backend/Infrastructure/Repositories/TeamTournamentRegistrationRepository.cs new file mode 100644 index 0000000..4850db7 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/TeamTournamentRegistrationRepository.cs @@ -0,0 +1,14 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing TeamTournamentRegistration entities, inheriting generic CRUD from GenericRepository. +/// +public class TeamTournamentRegistrationRepository(ApplicationDBContext context) + : GenericRepository(context), ITeamTournamentRegistrationRepository +{ } diff --git a/Club12-Backend/Infrastructure/Repositories/TournamentRepository.cs b/Club12-Backend/Infrastructure/Repositories/TournamentRepository.cs new file mode 100644 index 0000000..beace93 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/TournamentRepository.cs @@ -0,0 +1,14 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for managing Tournament entities, inheriting generic CRUD from GenericRepository and implementing ITournamentRepository. +/// +public class TournamentRepository(ApplicationDBContext context) + : GenericRepository(context), ITournamentRepository +{ } \ No newline at end of file diff --git a/Club12-Backend/Infrastructure/Repositories/UnitOfWork.cs b/Club12-Backend/Infrastructure/Repositories/UnitOfWork.cs new file mode 100644 index 0000000..024a863 --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/UnitOfWork.cs @@ -0,0 +1,147 @@ +using Application.Interfaces.Repositories; + +using Infrastructure.Persistance; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; + +using System; +using System.Threading.Tasks; + +namespace Infrastructure.Repositories; + +/// +/// Implements IUnitOfWork, providing a centralized access point for all repository instances and managing transactional operations within the application's data layer. +/// +public class UnitOfWork( + ApplicationDBContext context, + IPlayerRepository playerRepository, + IClubRepository clubRepository, + ITeamRepository teamRepository, + IDivisionRepository divisionRepository, + IMatchRepository matchRepository, + IMatchSeriesRepository matchSeriesRepository, + ITournamentRepository tournamentRepository, + IBlogPostRepository blogPostRepository, + IPlayerSanctionRepository playerSanctionRepository, + IPlayerStatisticRepository playerStatisticRepository, + IVenueRepository venueRepository, + IStageRepository stageRepository, + IStageTeamMatchRepository stageTeamMatchRepository, + IPlayerTeamRegistrationRepository playerTeamRegistrationRepository, + ITeamTournamentRegistrationRepository teamTournamentRegistrationRepository, + IDivisionTeamRegistrationRepository divisionTeamRegistrationRepository + ) : IUnitOfWork +{ + /// + /// Gets the repository for blog post entities. + /// + public IBlogPostRepository BlogPostRepository { get; } = blogPostRepository; + + /// + /// Gets the repository for club entities, whose identity stays stable across seasons. + /// + public IClubRepository ClubRepository { get; } = clubRepository; + + /// + /// Gets the repository for team entities. + /// + public ITeamRepository TeamRepository { get; } = teamRepository; + + /// + /// Gets the repository for venue entities. + /// + public IVenueRepository VenueRepository { get; } = venueRepository; + + /// + /// Gets the repository for tournament entities. + /// + public ITournamentRepository TournamentRepository { get; } = tournamentRepository; + + /// + /// Gets the repository for stage entities. + /// + public IStageRepository StageRepository { get; } = stageRepository; + + + /// + /// Gets the repository for player statistic entities. + /// + public IPlayerStatisticRepository PlayerStatisticRepository { get; } = playerStatisticRepository; + + /// + /// Gets the repository for player sanction entities. + /// + public IPlayerSanctionRepository PlayerSanctionRepository { get; } = playerSanctionRepository; + + /// + /// Gets the repository for player entities. + /// + public IPlayerRepository PlayerRepository { get; } = playerRepository; + + /// + /// Gets the repository for match entities. + /// + public IMatchRepository MatchRepository { get; } = matchRepository; + + /// + /// Gets the repository for match series entities. + /// + public IMatchSeriesRepository MatchSeriesRepository { get; } = matchSeriesRepository; + + /// + /// Gets the repository for division entities. + /// + public IDivisionRepository DivisionRepository { get; } = divisionRepository; + + /// + /// Gets the repository for stage team match entities. + /// + public IStageTeamMatchRepository StageTeamMatchRepository { get; } = stageTeamMatchRepository; + + /// + /// Gets the repository for player-team registration entities. + /// + public IPlayerTeamRegistrationRepository PlayerTeamRegistrationRepository { get; } = playerTeamRegistrationRepository; + + /// + /// Gets the repository for team-tournament registration entities. + /// + public ITeamTournamentRegistrationRepository TeamTournamentRegistrationRepository { get; } = teamTournamentRegistrationRepository; + + /// + /// Gets the repository for division-team registration entities. + /// + public IDivisionTeamRegistrationRepository DivisionTeamRegistrationRepository { get; } = divisionTeamRegistrationRepository; + + /// + /// Commits all tracked changes to the database asynchronously. + /// + /// The number of state entries written to the database. + public async Task SaveChangesAsync() + { + return await context.SaveChangesAsync(); + } + + /// + public async Task ExecuteInTransactionAsync(Func operation) + { + IExecutionStrategy strategy = context.Database.CreateExecutionStrategy(); + + await strategy.ExecuteAsync(async () => + { + await using IDbContextTransaction transaction = await context.Database.BeginTransactionAsync(); + try + { + await operation(); + await transaction.CommitAsync(); + } + catch + { + await transaction.RollbackAsync(); + throw; + } + }); + } +} diff --git a/Club12-Backend/Infrastructure/Repositories/VenueRepository.cs b/Club12-Backend/Infrastructure/Repositories/VenueRepository.cs new file mode 100644 index 0000000..14762ca --- /dev/null +++ b/Club12-Backend/Infrastructure/Repositories/VenueRepository.cs @@ -0,0 +1,16 @@ +using Application.Interfaces.Repositories; + +using Domain.Entities.Models; + +using Infrastructure.Persistance; + +namespace Infrastructure.Repositories; + +/// +/// Repository implementation for Venue entities, inheriting generic CRUD from GenericRepository and implementing IVenueRepository. +/// +/// The database context used for data access operations. +public class VenueRepository(ApplicationDBContext context) + : GenericRepository(context), IVenueRepository +{ +} diff --git a/Club12-Backend/Infrastructure/Storage/SupabaseHelper.cs b/Club12-Backend/Infrastructure/Storage/SupabaseHelper.cs new file mode 100644 index 0000000..99f449f --- /dev/null +++ b/Club12-Backend/Infrastructure/Storage/SupabaseHelper.cs @@ -0,0 +1,217 @@ +using Application.Utils.Constants; +using Application.Utils.Constants.Configuration; +using Application.Utils.Helper.SupabaseHelper; + +using Microsoft.Extensions.Configuration; + +using Supabase; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Infrastructure.Storage; + +/// +/// Provides helper methods to interact with Supabase storage, including file upload and public URL generation. +/// +public class SupabaseHelper : ISupabaseRawStorage +{ + /// + /// Cache-Control max-age, in seconds, applied to uploaded images, set to one year because uploaded files are content-addressed by a fresh GUID name and never need to be invalidated. + /// + private const string ImageCacheControlMaxAgeSeconds = "31536000"; + + private readonly Client _client; + private readonly string _bucketName; + private readonly Lazy _initialization; + + public SupabaseHelper(IConfiguration configuration) + { + IConfigurationSection section = configuration.GetSection(ConfigurationKeys.Supabase.Section); + string baseUrl = section[ConfigurationKeys.Supabase.ProjectUrl]!; + string serviceRole = section[ConfigurationKeys.Supabase.ServiceRole]!; + _bucketName = section[ConfigurationKeys.Supabase.BucketName]!; + + SupabaseOptions options = new() + { + AutoRefreshToken = true, + AutoConnectRealtime = true + }; + + _client = new Client(baseUrl, serviceRole, options); + _initialization = new Lazy(_client.InitializeAsync); + } + + /// + /// Uploads an image stream to Supabase Storage. + /// + /// The image stream to upload. + /// The destination filename in the bucket. + /// A string representing the new file name. + /// Thrown when the upload fails. + public async Task UploadImageAsync(Stream fileStream, string fileName) + { + await _initialization.Value; + try + { + string newFileName = GenerateNameFile(fileName); + string fullPathInBucket = $"{typeof(T).Name}/{newFileName}"; + await _client.Storage + .From(_bucketName) + .Upload(UseStreamDotReadMethod(fileStream), fullPathInBucket, + new() + { + CacheControl = ImageCacheControlMaxAgeSeconds, + Upsert = true + } + ); + return _client.Storage.From(_bucketName).GetPublicUrl(fullPathInBucket); + } + catch (Exception ex) + { + throw new InvalidOperationException(ErrorMessages.Storage.UploadFailed(ex.Message), ex); + } + } + + /// + /// Deletes an image from the specified storage bucket. + /// + /// The name of the file to delete within the bucket. + /// A task that represents the asynchronous delete operation. + /// + /// Thrown when an error occurs while deleting the file. + /// + public async Task DeleteImageAsync(string fileName) + { + await _initialization.Value; + try + { + await _client.Storage.From(_bucketName).Remove($"{typeof(T).Name}/{fileName}"); + } + catch (Exception ex) + { + throw new InvalidOperationException(ErrorMessages.Storage.DeleteFailed(ex.Message), ex); + } + } + + /// + /// Uploads raw content to an arbitrary object path in the bucket, without the per-type folder, cache-control header, or public URL that UploadImageAsync applies. + /// + /// The exact destination path within the bucket. + /// The content stream to upload. + /// Target bucket; null uses the configured SupaBase:BucketName. + /// Thrown when the upload fails. + public async Task UploadRawAsync(string objectPath, Stream content, string? bucket = null) + { + await _initialization.Value; + try + { + await _client.Storage + .From(bucket ?? _bucketName) + .Upload(UseStreamDotReadMethod(content), objectPath, new() { Upsert = true }); + } + catch (Exception ex) + { + throw new InvalidOperationException(ErrorMessages.Storage.UploadFailed(ex.Message), ex); + } + } + + /// + /// Lists raw objects that share a given prefix within the bucket. + /// + /// The bucket-relative path prefix to list. + /// Target bucket; null uses the configured SupaBase:BucketName. + /// Thrown when listing fails. + public async Task> ListRawAsync(string prefix, string? bucket = null) + { + await _initialization.Value; + try + { + List files = await _client.Storage.From(bucket ?? _bucketName).List(prefix) + ?? []; + return files + .Select(file => new SupabaseStorageEntry( + file.Name ?? string.Empty, + file.UpdatedAt.HasValue + ? new DateTimeOffset(DateTime.SpecifyKind(file.UpdatedAt.Value, DateTimeKind.Utc)) + : null)) + .ToList(); + } + catch (Exception ex) + { + throw new InvalidOperationException(ErrorMessages.Storage.ListFailed(ex.Message), ex); + } + } + + /// + /// Removes the raw object at the given path within the bucket. + /// + /// The exact object path within the bucket. + /// Target bucket; null uses the configured SupaBase:BucketName. + /// Thrown when the removal fails. + public async Task RemoveRawAsync(string objectPath, string? bucket = null) + { + await _initialization.Value; + try + { + await _client.Storage.From(bucket ?? _bucketName).Remove(objectPath); + } + catch (Exception ex) + { + throw new InvalidOperationException(ErrorMessages.Storage.RemoveFailed(ex.Message), ex); + } + } + + /// + /// Downloads the raw content of the object at the given path within the bucket. + /// + /// The exact object path within the bucket. + /// Target bucket; null uses the configured SupaBase:BucketName. + /// Thrown when the download fails. + public async Task DownloadRawAsync(string objectPath, string? bucket = null) + { + await _initialization.Value; + try + { + return await _client.Storage.From(bucket ?? _bucketName).Download(objectPath, (EventHandler?)null); + } + catch (Exception ex) + { + throw new InvalidOperationException(ErrorMessages.Storage.DownloadFailed(ex.Message), ex); + } + } + + /// + /// Reads all bytes from a given stream using the Stream.Read method in chunks and returns the entire content as a byte array. + /// + /// The input stream to read from. + /// A byte array containing all data read from the stream. + private static byte[] UseStreamDotReadMethod(Stream stream) + { + List totalStream = []; + byte[] buffer = new byte[32]; + int read; + while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) + { + totalStream.AddRange(buffer[..read]); + } + return [.. totalStream]; + } + + /// + /// Generates a new unique file name by replacing the original name with a GUID while preserving the original file extension. + /// + /// The original file name including extension. + /// A new file name string consisting of a GUID and the original extension. + private static string GenerateNameFile(string fileName) + { + string extension = Path.GetExtension(fileName); + string newName = Guid.NewGuid().ToString(); + + return $"{newName}{extension}"; + } + +} diff --git a/Club12-Backend/Infrastructure/Storage/SupabaseMedicalRecordStorage.cs b/Club12-Backend/Infrastructure/Storage/SupabaseMedicalRecordStorage.cs new file mode 100644 index 0000000..20a95f6 --- /dev/null +++ b/Club12-Backend/Infrastructure/Storage/SupabaseMedicalRecordStorage.cs @@ -0,0 +1,50 @@ +using Application.Interfaces.Storage; +using Application.Utils.Constants.Configuration; +using Application.Utils.Helper.SupabaseHelper; + +using Microsoft.Extensions.Configuration; + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Infrastructure.Storage; + +/// +/// IMedicalRecordStorage implementation that reuses the existing Supabase client through ISupabaseRawStorage, storing every medical record in a dedicated private bucket separate from the shared public-images bucket. +/// +public sealed class SupabaseMedicalRecordStorage : IMedicalRecordStorage +{ + /// + /// Private bucket, provisioned manually per environment and never created by the client. + /// + public const string DefaultBucketName = "medical-records"; + + private readonly ISupabaseRawStorage _rawStorage; + private readonly string _bucketName; + + public SupabaseMedicalRecordStorage(ISupabaseRawStorage rawStorage, IConfiguration configuration) + { + _rawStorage = rawStorage; + IConfigurationSection section = configuration.GetSection(ConfigurationKeys.Supabase.Section); + string? configured = section[ConfigurationKeys.Supabase.MedicalRecordsBucketName]; + _bucketName = string.IsNullOrWhiteSpace(configured) ? DefaultBucketName : configured; + } + + public async Task StoreAsync( + Guid teamId, Guid playerId, string fileName, Stream content, CancellationToken ct = default) + { + string extension = Path.GetExtension(fileName); + string objectPath = $"{teamId}/{playerId}/{Guid.NewGuid()}{extension}"; + + await _rawStorage.UploadRawAsync(objectPath, content, _bucketName); + + return objectPath; + } + + public async Task DownloadAsync(string objectPath, CancellationToken ct = default) + { + return await _rawStorage.DownloadRawAsync(objectPath, _bucketName); + } +} diff --git a/Club12-Backend/Solution/Club12.sln b/Club12-Backend/Solution/Club12.sln new file mode 100644 index 0000000..fb78d29 --- /dev/null +++ b/Club12-Backend/Solution/Club12.sln @@ -0,0 +1,93 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.3.11512.155 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API", "..\API\API.csproj", "{3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application", "..\Application\Application.csproj", "{5826D3E2-35D1-3394-E8AD-3768DEE232D8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Domain", "..\Domain\Domain.csproj", "{B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "..\Infrastructure\Infrastructure.csproj", "{C89F7701-1230-045D-AC93-AE7F7C59B668}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API.Tests", "..\API.Tests\API.Tests.csproj", "{6D4052D0-1417-4235-A407-37664DE1AF59}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}.Debug|x64.ActiveCfg = Debug|Any CPU + {3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}.Debug|x64.Build.0 = Debug|Any CPU + {3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}.Debug|x86.ActiveCfg = Debug|Any CPU + {3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}.Debug|x86.Build.0 = Debug|Any CPU + {3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}.Release|Any CPU.Build.0 = Release|Any CPU + {3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}.Release|x64.ActiveCfg = Release|Any CPU + {3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}.Release|x64.Build.0 = Release|Any CPU + {3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}.Release|x86.ActiveCfg = Release|Any CPU + {3B825327-BE3C-E3E9-9DBC-4E1DA6DB1907}.Release|x86.Build.0 = Release|Any CPU + {5826D3E2-35D1-3394-E8AD-3768DEE232D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5826D3E2-35D1-3394-E8AD-3768DEE232D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5826D3E2-35D1-3394-E8AD-3768DEE232D8}.Debug|x64.ActiveCfg = Debug|Any CPU + {5826D3E2-35D1-3394-E8AD-3768DEE232D8}.Debug|x64.Build.0 = Debug|Any CPU + {5826D3E2-35D1-3394-E8AD-3768DEE232D8}.Debug|x86.ActiveCfg = Debug|Any CPU + {5826D3E2-35D1-3394-E8AD-3768DEE232D8}.Debug|x86.Build.0 = Debug|Any CPU + {5826D3E2-35D1-3394-E8AD-3768DEE232D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5826D3E2-35D1-3394-E8AD-3768DEE232D8}.Release|Any CPU.Build.0 = Release|Any CPU + {5826D3E2-35D1-3394-E8AD-3768DEE232D8}.Release|x64.ActiveCfg = Release|Any CPU + {5826D3E2-35D1-3394-E8AD-3768DEE232D8}.Release|x64.Build.0 = Release|Any CPU + {5826D3E2-35D1-3394-E8AD-3768DEE232D8}.Release|x86.ActiveCfg = Release|Any CPU + {5826D3E2-35D1-3394-E8AD-3768DEE232D8}.Release|x86.Build.0 = Release|Any CPU + {B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}.Debug|x64.ActiveCfg = Debug|Any CPU + {B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}.Debug|x64.Build.0 = Debug|Any CPU + {B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}.Debug|x86.ActiveCfg = Debug|Any CPU + {B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}.Debug|x86.Build.0 = Debug|Any CPU + {B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}.Release|Any CPU.Build.0 = Release|Any CPU + {B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}.Release|x64.ActiveCfg = Release|Any CPU + {B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}.Release|x64.Build.0 = Release|Any CPU + {B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}.Release|x86.ActiveCfg = Release|Any CPU + {B5C8CBAE-DE02-7B4C-706D-5B7BB03EEA4F}.Release|x86.Build.0 = Release|Any CPU + {C89F7701-1230-045D-AC93-AE7F7C59B668}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C89F7701-1230-045D-AC93-AE7F7C59B668}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C89F7701-1230-045D-AC93-AE7F7C59B668}.Debug|x64.ActiveCfg = Debug|Any CPU + {C89F7701-1230-045D-AC93-AE7F7C59B668}.Debug|x64.Build.0 = Debug|Any CPU + {C89F7701-1230-045D-AC93-AE7F7C59B668}.Debug|x86.ActiveCfg = Debug|Any CPU + {C89F7701-1230-045D-AC93-AE7F7C59B668}.Debug|x86.Build.0 = Debug|Any CPU + {C89F7701-1230-045D-AC93-AE7F7C59B668}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C89F7701-1230-045D-AC93-AE7F7C59B668}.Release|Any CPU.Build.0 = Release|Any CPU + {C89F7701-1230-045D-AC93-AE7F7C59B668}.Release|x64.ActiveCfg = Release|Any CPU + {C89F7701-1230-045D-AC93-AE7F7C59B668}.Release|x64.Build.0 = Release|Any CPU + {C89F7701-1230-045D-AC93-AE7F7C59B668}.Release|x86.ActiveCfg = Release|Any CPU + {C89F7701-1230-045D-AC93-AE7F7C59B668}.Release|x86.Build.0 = Release|Any CPU + {6D4052D0-1417-4235-A407-37664DE1AF59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D4052D0-1417-4235-A407-37664DE1AF59}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D4052D0-1417-4235-A407-37664DE1AF59}.Debug|x64.ActiveCfg = Debug|Any CPU + {6D4052D0-1417-4235-A407-37664DE1AF59}.Debug|x64.Build.0 = Debug|Any CPU + {6D4052D0-1417-4235-A407-37664DE1AF59}.Debug|x86.ActiveCfg = Debug|Any CPU + {6D4052D0-1417-4235-A407-37664DE1AF59}.Debug|x86.Build.0 = Debug|Any CPU + {6D4052D0-1417-4235-A407-37664DE1AF59}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D4052D0-1417-4235-A407-37664DE1AF59}.Release|Any CPU.Build.0 = Release|Any CPU + {6D4052D0-1417-4235-A407-37664DE1AF59}.Release|x64.ActiveCfg = Release|Any CPU + {6D4052D0-1417-4235-A407-37664DE1AF59}.Release|x64.Build.0 = Release|Any CPU + {6D4052D0-1417-4235-A407-37664DE1AF59}.Release|x86.ActiveCfg = Release|Any CPU + {6D4052D0-1417-4235-A407-37664DE1AF59}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0A19CD9B-2A6F-4F93-B898-0BEDB2A467C6} + EndGlobalSection +EndGlobal

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�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