You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
11 KiB
307 lines
11 KiB
//
|
|
// PlayerRegistration.swift
|
|
// Padel Tournament
|
|
//
|
|
// Created by razmig on 10/03/2024.
|
|
//
|
|
|
|
import Foundation
|
|
import LeStorage
|
|
|
|
@Observable
|
|
final public class PlayerRegistration: BasePlayerRegistration, SideStorable {
|
|
|
|
public func localizedSourceLabel() -> String {
|
|
switch source {
|
|
case .frenchFederation:
|
|
return "base fédérale"
|
|
case .beachPadel:
|
|
return "beach-padel"
|
|
case nil:
|
|
if registeredOnline {
|
|
return "à vérifier vous-même"
|
|
} else {
|
|
return "créé par vous-même"
|
|
}
|
|
}
|
|
}
|
|
|
|
public var tournamentStore: TournamentStore? {
|
|
guard let storeId else {
|
|
fatalError("missing store id for \(String(describing: type(of: self)))")
|
|
}
|
|
return TournamentLibrary.shared.store(tournamentId: storeId)
|
|
// if let store = self.store as? TournamentStore {
|
|
// return store
|
|
// }
|
|
}
|
|
|
|
public var computedAge: Int? {
|
|
if let birthdate {
|
|
let components = birthdate.components(separatedBy: "/")
|
|
if let age = components.last, let ageInt = Int(age) {
|
|
let year = Calendar.current.getSportAge()
|
|
|
|
if age.count == 2 { //si l'année est sur 2 chiffres dans le fichier
|
|
if ageInt < 23 {
|
|
return year - 2000 - ageInt
|
|
} else {
|
|
return year - 2000 + 100 - ageInt
|
|
}
|
|
} else { //si l'année est représenté sur 4 chiffres
|
|
return year - ageInt
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
public func pasteData(_ exportFormat: ExportFormat = .rawText) -> String {
|
|
switch exportFormat {
|
|
case .rawText:
|
|
return [firstName.capitalized, lastName.capitalized, licenceId?.computedLicense].compactMap({ $0 }).joined(separator: exportFormat.separator())
|
|
case .csv:
|
|
return [lastName.uppercased() + " " + firstName.capitalized].joined(separator: exportFormat.separator())
|
|
}
|
|
}
|
|
|
|
public func isPlaying() -> Bool {
|
|
team()?.isPlaying() == true
|
|
}
|
|
|
|
public func contains(_ searchField: String) -> Bool {
|
|
let nameComponents = searchField.canonicalVersion.split(separator: " ")
|
|
|
|
if nameComponents.count > 1 {
|
|
let pairs = nameComponents.pairs()
|
|
return pairs.contains(where: {
|
|
(firstName.canonicalVersion.localizedCaseInsensitiveContains(String($0)) &&
|
|
lastName.canonicalVersion.localizedCaseInsensitiveContains(String($1))) ||
|
|
(firstName.canonicalVersion.localizedCaseInsensitiveContains(String($1)) &&
|
|
lastName.canonicalVersion.localizedCaseInsensitiveContains(String($0)))
|
|
})
|
|
} else {
|
|
return nameComponents.contains { component in
|
|
firstName.canonicalVersion.localizedCaseInsensitiveContains(component) ||
|
|
lastName.canonicalVersion.localizedCaseInsensitiveContains(component)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func isSameAs(_ player: PlayerRegistration) -> Bool {
|
|
if let licenceId, let playerLicenceId = player.licenceId {
|
|
let result = licenceId.strippedLicense == playerLicenceId.strippedLicense
|
|
if result {
|
|
return result
|
|
}
|
|
}
|
|
return firstName.trimmedMultiline.canonicalVersion.localizedCaseInsensitiveCompare(player.firstName.trimmedMultiline.canonicalVersion) == .orderedSame &&
|
|
lastName.trimmedMultiline.canonicalVersion.localizedCaseInsensitiveCompare(player.lastName.trimmedMultiline.canonicalVersion) == .orderedSame
|
|
}
|
|
|
|
public func tournament() -> Tournament? {
|
|
guard let tournament = team()?.tournament else { return nil }
|
|
return Store.main.findById(tournament)
|
|
}
|
|
|
|
public func team() -> TeamRegistration? {
|
|
guard let teamRegistration else { return nil }
|
|
return self.tournamentStore?.teamRegistrations.findById(teamRegistration)
|
|
}
|
|
|
|
public func isHere() -> Bool {
|
|
hasArrived
|
|
}
|
|
|
|
public func hasPaid() -> Bool {
|
|
paymentType != nil
|
|
}
|
|
|
|
public func playerLabel(_ displayStyle: DisplayStyle = .wide) -> String {
|
|
switch displayStyle {
|
|
case .title:
|
|
return firstName.trimmed.capitalized + " " + lastName.trimmed.capitalized
|
|
case .wide:
|
|
return lastName.trimmed.capitalized + " " + firstName.trimmed.capitalized
|
|
case .short:
|
|
let names = lastName.components(separatedBy: .whitespaces)
|
|
if lastName.components(separatedBy: .whitespaces).count > 1 {
|
|
if let firstLongWord = names.first(where: { $0.count > 3 }) {
|
|
return firstLongWord.trimmed.capitalized.trunc(length: 10) + " " + firstName.trimmed.prefix(1).capitalized + "."
|
|
}
|
|
}
|
|
return lastName.trimmed.capitalized.trunc(length: 10) + " " + firstName.trimmed.prefix(1).capitalized + "."
|
|
}
|
|
}
|
|
|
|
public func isImported() -> Bool {
|
|
source == .beachPadel
|
|
}
|
|
|
|
public func unrankedOrUnknown() -> Bool {
|
|
source == nil
|
|
}
|
|
|
|
public func isValidLicenseNumber(year: Int) -> Bool {
|
|
guard let licenceId else { return false }
|
|
guard licenceId.isLicenseNumber else { return false }
|
|
guard licenceId.suffix(6) == "(\(year))" else { return false }
|
|
return true
|
|
}
|
|
|
|
@objc
|
|
public var canonicalName: String {
|
|
playerLabel().folding(options: .diacriticInsensitive, locale: .current).lowercased()
|
|
}
|
|
|
|
public func rankLabel(_ displayStyle: DisplayStyle = .wide) -> String {
|
|
if let rank, rank > 0 {
|
|
if rank != computedRank {
|
|
return computedRank.formatted() + " (" + rank.formatted() + ")"
|
|
} else {
|
|
return rank.formatted()
|
|
}
|
|
} else {
|
|
return "non classé" + (isMalePlayer() ? "" : "e")
|
|
}
|
|
}
|
|
|
|
public func setComputedRank(in tournament: Tournament) {
|
|
let maleUnranked = tournament.unrankValue(for: isMalePlayer()) ?? 90_415
|
|
let femaleUnranked = tournament.unrankValue(for: false) ?? 0
|
|
let currentRank = rank ?? maleUnranked
|
|
switch tournament.tournamentCategory {
|
|
case .men:
|
|
let addon = tournament.addon(for: currentRank, manMax: maleUnranked, womanMax: femaleUnranked)
|
|
computedRank = isMalePlayer() ? currentRank : currentRank + addon
|
|
default:
|
|
computedRank = currentRank
|
|
}
|
|
}
|
|
|
|
public func setClubMember(for tournament: Tournament) {
|
|
guard let clubCode, clubCode.isEmpty == false, let code = tournament.eventObject()?.clubObject()?.code, code.isEmpty == false else {
|
|
return
|
|
}
|
|
self.clubMember = clubCode.replaceCharactersFromSet(characterSet: .whitespaces).caseInsensitiveCompare(code.replaceCharactersFromSet(characterSet: .whitespaces)) == .orderedSame
|
|
}
|
|
|
|
public func isMalePlayer() -> Bool {
|
|
sex == .male
|
|
}
|
|
|
|
public func validateLicenceId(_ year: Int) {
|
|
if let currentLicenceId = licenceId {
|
|
if currentLicenceId.trimmed.hasSuffix("(\(year-1))") {
|
|
self.licenceId = currentLicenceId.replacingOccurrences(of: "\(year-1)", with: "\(year)")
|
|
} else if let computedLicense = currentLicenceId.strippedLicense?.computedLicense {
|
|
self.licenceId = computedLicense + " (\(year))"
|
|
}
|
|
}
|
|
}
|
|
|
|
public func hasPaidOnline() -> Bool {
|
|
registrationStatus == .confirmed && paymentId != nil && paymentType == .creditCard
|
|
}
|
|
|
|
public func hasConfirmed() -> Bool {
|
|
registrationStatus == .confirmed
|
|
}
|
|
|
|
public func confirmRegistration() {
|
|
registrationStatus = .confirmed
|
|
}
|
|
|
|
public func paidAmount(_ tournament: Tournament, accountForGiftOrForfeit: Bool = false) -> Double {
|
|
if accountForGiftOrForfeit == false, paymentType == .gift {
|
|
return 0.0
|
|
}
|
|
if accountForGiftOrForfeit == false, paymentType == .forfeit {
|
|
return 0.0
|
|
}
|
|
if hasPaid(), let entryFee = tournament.entryFee {
|
|
if clubMember, let clubMemberFeeDeduction = tournament.clubMemberFeeDeduction {
|
|
return entryFee - clubMemberFeeDeduction
|
|
} else {
|
|
return entryFee
|
|
}
|
|
} else {
|
|
return 0.0
|
|
}
|
|
}
|
|
|
|
public func remainingAmount(_ tournament: Tournament) -> Double {
|
|
if let entryFee = tournament.entryFee {
|
|
if clubMember, let clubMemberFeeDeduction = tournament.clubMemberFeeDeduction {
|
|
return entryFee - clubMemberFeeDeduction - paidAmount(tournament, accountForGiftOrForfeit: true)
|
|
} else {
|
|
return entryFee - paidAmount(tournament, accountForGiftOrForfeit: true)
|
|
}
|
|
} else {
|
|
return 0.0
|
|
}
|
|
}
|
|
|
|
public func totalIncome(_ tournament: Tournament) -> Double {
|
|
if let entryFee = tournament.entryFee {
|
|
if clubMember, let clubMemberFeeDeduction = tournament.clubMemberFeeDeduction {
|
|
return entryFee - clubMemberFeeDeduction
|
|
} else {
|
|
return entryFee
|
|
}
|
|
} else {
|
|
return 0.0
|
|
}
|
|
}
|
|
|
|
public enum PlayerDataSource: Int, Codable {
|
|
case frenchFederation = 0
|
|
case beachPadel = 1
|
|
}
|
|
|
|
public enum RegistrationStatus: Int, Codable, CaseIterable, Identifiable {
|
|
case waiting = 0
|
|
case pending = 1
|
|
case confirmed = 2
|
|
case canceled = 3
|
|
|
|
public var id: Int { self.rawValue }
|
|
|
|
public func localizedRegistrationStatus() -> String {
|
|
switch self {
|
|
case .waiting:
|
|
return "En attente"
|
|
case .pending:
|
|
return "En cours"
|
|
case .confirmed:
|
|
return "Confirmé"
|
|
case .canceled:
|
|
return "Annulé"
|
|
}
|
|
}
|
|
}
|
|
|
|
func insertOnServer() {
|
|
self.tournamentStore?.playerRegistrations.writeChangeAndInsertOnServer(instance: self)
|
|
}
|
|
|
|
}
|
|
|
|
enum PlayerDataSource: Int, Codable {
|
|
case frenchFederation = 0
|
|
case beachPadel = 1
|
|
}
|
|
|
|
public enum PlayerSexType: Int, Hashable, CaseIterable, Identifiable, Codable {
|
|
init?(rawValue: Int?) {
|
|
guard let value = rawValue else { return nil }
|
|
self.init(rawValue: value)
|
|
}
|
|
|
|
public var id: Self {
|
|
self
|
|
}
|
|
|
|
case female = 0
|
|
case male = 1
|
|
}
|
|
|