first commit
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public static class AIAgents
|
||||
{
|
||||
public static readonly Guid MealAgentId = Guid.Parse("00000000-0000-0000-0000-000000000101");
|
||||
|
||||
public static readonly Guid WorkoutAgentId = Guid.Parse("00000000-0000-0000-0000-000000000102");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class AccessDevice
|
||||
{
|
||||
[Key]
|
||||
public Guid DeviceId { get; set; }
|
||||
public string DeviceName { get; set; }
|
||||
|
||||
// DEPRECATED: connection details now live on the on-prem agent (Fitness_core_BE.Models.Agent),
|
||||
// not in the cloud. Kept nullable so existing rows survive; no new code should read/write these.
|
||||
[Obsolete("Device connection details now live on the on-prem agent, not in the cloud.")]
|
||||
public string? PublicIp { get; set; }
|
||||
[Obsolete("Device connection details now live on the on-prem agent, not in the cloud.")]
|
||||
public string? Port { get; set; }
|
||||
[Obsolete("Device connection details now live on the on-prem agent, not in the cloud.")]
|
||||
public string? UserName { get; set; }
|
||||
[Obsolete("Device connection details now live on the on-prem agent, not in the cloud.")]
|
||||
public string? Password { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public Client Client { get; set; }
|
||||
|
||||
// The agent that currently owns this LAN device. Commands are dispatched only
|
||||
// to this agent, which prevents a second agent for the same gym receiving them.
|
||||
public Guid? FKAgentId { get; set; }
|
||||
[ForeignKey(nameof(FKAgentId))]
|
||||
public Agent? Agent { get; set; }
|
||||
|
||||
// Stable serial/device identifier reported by ISAPI. Used to re-claim the same
|
||||
// cloud row after reinstalling the desktop agent instead of creating duplicates.
|
||||
[MaxLength(160)]
|
||||
public string? HardwareId { get; set; }
|
||||
|
||||
// Which Place (physical zone) this terminal sits in. Null = unscoped: reachable by
|
||||
// every plan regardless of that plan's Places (keeps pre-existing devices and
|
||||
// plans without any place restriction working unchanged). Unique when set --
|
||||
// exactly one device per place for now (see Models/Place.cs).
|
||||
public Guid? FKPlaceId { get; set; }
|
||||
[ForeignKey(nameof(FKPlaceId))]
|
||||
public Place? Place { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime? LastSeenAt { get; set; }
|
||||
// Explicit reachability reported by the on-prem agent. LastSeenAt remains
|
||||
// the last successful terminal operation; health reports are tracked
|
||||
// separately so status does not depend on browser polling.
|
||||
public bool IsReachable { get; set; }
|
||||
public DateTime? LastHealthReportedAt { get; set; }
|
||||
public DateTime CreatedAt { get; set; }= DateTime.UtcNow;
|
||||
|
||||
[Obsolete("Device connection details now live on the on-prem agent, not in the cloud.")]
|
||||
public void SetPassword(string plainText)
|
||||
=> Password = EncryptionHelper.Encrypt(plainText);
|
||||
|
||||
[Obsolete("Device connection details now live on the on-prem agent, not in the cloud.")]
|
||||
public string GetPlainTextPassword()
|
||||
=> EncryptionHelper.Decrypt(Password!);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps one tenant access profile to one physical terminal user record.
|
||||
/// DeviceEmployeeNo is the terminal's person identifier, not a fingerprint template.
|
||||
/// </summary>
|
||||
public class AccessDeviceBinding
|
||||
{
|
||||
[Key]
|
||||
public Guid AccessDeviceBindingId { get; set; }
|
||||
|
||||
public long FKAccessProfileId { get; set; }
|
||||
[ForeignKey(nameof(FKAccessProfileId))]
|
||||
public AccessProfile AccessProfile { get; set; } = null!;
|
||||
|
||||
public Guid FKDeviceId { get; set; }
|
||||
[ForeignKey(nameof(FKDeviceId))]
|
||||
public AccessDevice AccessDevice { get; set; } = null!;
|
||||
|
||||
[MaxLength(32)]
|
||||
public string DeviceEmployeeNo { get; set; } = string.Empty;
|
||||
|
||||
[MaxLength(20)]
|
||||
public string ProvisionStatus { get; set; } = "Pending";
|
||||
|
||||
[MaxLength(20)]
|
||||
public string ActualAccessState { get; set; } = "Unknown";
|
||||
|
||||
public int FingerprintCount { get; set; }
|
||||
|
||||
[MaxLength(128)]
|
||||
public string? DeviceReportedName { get; set; }
|
||||
|
||||
[MaxLength(1000)]
|
||||
public string? LastError { get; set; }
|
||||
|
||||
public DateTime? LastSyncedAt { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Tenant-scoped physical-access identity. A platform user can belong to several
|
||||
/// gyms, but receives one independent access profile per gym.
|
||||
/// </summary>
|
||||
public class AccessProfile
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public long AccessProfileId { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey(nameof(FKClientId))]
|
||||
public Client Client { get; set; } = null!;
|
||||
|
||||
public Guid FKUserId { get; set; }
|
||||
[ForeignKey(nameof(FKUserId))]
|
||||
public User User { get; set; } = null!;
|
||||
|
||||
[MaxLength(20)]
|
||||
public string PersonType { get; set; } = "Customer";
|
||||
|
||||
[MaxLength(128)]
|
||||
public string DisplayName { get; set; } = string.Empty;
|
||||
|
||||
[MaxLength(20)]
|
||||
public string DesiredAccessState { get; set; } = "Enabled";
|
||||
|
||||
public int DesiredVersion { get; set; } = 1;
|
||||
|
||||
[MaxLength(100)]
|
||||
public string? LockReason { get; set; }
|
||||
|
||||
public DateTime? LockedAt { get; set; }
|
||||
|
||||
// ---- Face photo (Hikvision face library) ----
|
||||
// The image itself is never persisted here: it travels in the UpsertFacePhoto
|
||||
// DeviceCommand payload only until the terminal confirms enrolment, at which point
|
||||
// the payload is purged. What survives is the enrolment state plus the device-side
|
||||
// record id, which is what a later replace/delete needs.
|
||||
|
||||
/// <summary>True once the terminal has accepted a face for this profile.</summary>
|
||||
public bool HasFacePhoto { get; set; }
|
||||
|
||||
/// <summary>"None" | "Pending" | "Synced" | "Failed".</summary>
|
||||
[MaxLength(20)]
|
||||
public string FacePhotoStatus { get; set; } = "None";
|
||||
|
||||
/// <summary>Hikvision face-record id (FPID) returned on enrolment.</summary>
|
||||
[MaxLength(64)]
|
||||
public string? FacePhotoFPID { get; set; }
|
||||
|
||||
/// <summary>Why the terminal rejected the face (not frontal, eyes closed, ...).</summary>
|
||||
[MaxLength(500)]
|
||||
public string? FacePhotoError { get; set; }
|
||||
|
||||
public DateTime? FacePhotoUpdatedAt { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public ICollection<AccessDeviceBinding> DeviceBindings { get; set; } = new List<AccessDeviceBinding>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class ActivityLog
|
||||
{
|
||||
[Key]
|
||||
public Guid ActivityLogId { get; set; }
|
||||
public Guid FKUserId { get; set; }
|
||||
[ForeignKey("FKUserId")]
|
||||
public virtual User User { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public bool IsAdmin { get; set; }
|
||||
public string Description { get; set; }
|
||||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class Admin
|
||||
{
|
||||
[Key]
|
||||
public Guid AdminId { get; set; }
|
||||
public string AdminName { get; set; }
|
||||
public Guid FKUserId { get; set; }
|
||||
[ForeignKey("FKUserId")]
|
||||
public virtual User User { get; set; }
|
||||
public string PhoneNumber { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
public virtual ICollection<Ticket> AssignedTickets { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class Agent
|
||||
{
|
||||
[Key]
|
||||
public Guid AgentId { get; set; }
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public Client Client { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string ApiKeyHash { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime? LastSeenAt { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class Attendance
|
||||
{
|
||||
[Key]
|
||||
public Guid AttendanceId { get; set; }
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public Client Client { get; set; }
|
||||
|
||||
public Guid? FKDeviceId { get; set; }
|
||||
[ForeignKey(nameof(FKDeviceId))]
|
||||
public AccessDevice? AccessDevice { get; set; }
|
||||
|
||||
// Tenant-bucket entity: this FK holds a TenantCustomerLinkId (not the central
|
||||
// Customer.CusId directly) -- see Models/TenantCustomerLink.cs.
|
||||
public Guid? FKCustomerId { get; set; }
|
||||
[ForeignKey("FKCustomerId")]
|
||||
public TenantCustomerLink? TenantCustomerLink { get; set; }
|
||||
|
||||
public Guid? FKUserId { get; set; }
|
||||
[ForeignKey(nameof(FKUserId))]
|
||||
public User? User { get; set; }
|
||||
|
||||
public long? FKAccessProfileId { get; set; }
|
||||
[ForeignKey(nameof(FKAccessProfileId))]
|
||||
public AccessProfile? AccessProfile { get; set; }
|
||||
|
||||
public int DeviceUserId { get; set; }
|
||||
[MaxLength(32)]
|
||||
public string DeviceEmployeeNo { get; set; } = string.Empty;
|
||||
[MaxLength(64)]
|
||||
public string? SourceEventKey { get; set; }
|
||||
public DateTime EventTime { get; set; }
|
||||
public string EventType { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>One reservation against a FacilitySession — either an existing member using a
|
||||
/// CustomerPlan session allowance (FKCustomerId set, IsGuest = false) or a non-member who
|
||||
/// booked online and pays per-session (IsGuest = true, no membership at all). The guest's
|
||||
/// identity proof for check-in is the OTP issued at booking time, verified at the door
|
||||
/// instead of a fingerprint/face profile a non-member will never have.</summary>
|
||||
public class Booking
|
||||
{
|
||||
[Key]
|
||||
public Guid BookingId { get; set; }
|
||||
|
||||
public Guid FKFacilitySessionId { get; set; }
|
||||
[ForeignKey(nameof(FKFacilitySessionId))]
|
||||
public FacilitySession FacilitySession { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey(nameof(FKClientId))]
|
||||
public Client Client { get; set; }
|
||||
|
||||
public bool IsGuest { get; set; } = false;
|
||||
|
||||
// Member path. Tenant-bucket entity: holds a TenantCustomerLinkId, not the central
|
||||
// Customer.CusId directly -- see Models/TenantCustomerLink.cs.
|
||||
public Guid? FKCustomerId { get; set; }
|
||||
[ForeignKey(nameof(FKCustomerId))]
|
||||
public TenantCustomerLink? TenantCustomerLink { get; set; }
|
||||
|
||||
// A member's booking draws down this specific subscription's per-cycle session
|
||||
// allowance; null for guest bookings (they have no subscription at all).
|
||||
public Guid? FKCustomerSubscriptionId { get; set; }
|
||||
[ForeignKey(nameof(FKCustomerSubscriptionId))]
|
||||
public CustomerSubscription? CustomerSubscription { get; set; }
|
||||
|
||||
// Guest path -- no member record exists at all, so identity is captured inline.
|
||||
[MaxLength(200)]
|
||||
public string? GuestName { get; set; }
|
||||
[MaxLength(32)]
|
||||
public string? GuestPhone { get; set; }
|
||||
|
||||
/// <summary>Issued when the booking is created; the guest presents this at the door
|
||||
/// instead of a fingerprint/face profile. Members don't need one -- their existing
|
||||
/// access credential covers check-in.</summary>
|
||||
[MaxLength(10)]
|
||||
public string? OtpCode { get; set; }
|
||||
public DateTime? OtpVerifiedAt { get; set; }
|
||||
|
||||
/// <summary>"Paid" | "Unpaid" | "NotRequired" (member using a plan's included session).</summary>
|
||||
public string PaymentStatus { get; set; } = "NotRequired";
|
||||
public decimal AmountCharged { get; set; } = 0m;
|
||||
|
||||
// Set only once payment is confirmed for a pay-per-session guest booking, or
|
||||
// immediately for a member session (no payment gate) -- see PaymentStatus.
|
||||
public Guid? FKPaymentTransactionId { get; set; }
|
||||
[ForeignKey(nameof(FKPaymentTransactionId))]
|
||||
public PaymentTransaction? PaymentTransaction { get; set; }
|
||||
|
||||
/// <summary>"Confirmed" | "CheckedIn" | "Cancelled" | "NoShow".</summary>
|
||||
public string Status { get; set; } = "Confirmed";
|
||||
|
||||
public DateTime BookedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? CheckedInAt { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class Chat
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public int UserId { get; set; }
|
||||
public Guid FKSenderId { get; set; }
|
||||
[ForeignKey("FKSenderId")]
|
||||
public Guid FKReceiverId { get; set; }
|
||||
[ForeignKey("FKReceiverId")]
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public bool IsAcitve { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class ChatMessages
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public int UserId { get; set; }
|
||||
public string Message { get; set; }
|
||||
public Guid FKSenderId { get; set; }
|
||||
[ForeignKey("FKSenderId")]
|
||||
public User User { get; set; }
|
||||
public string Type { get; set; } = "userMessages";
|
||||
public Guid FKChatId { get; set; }
|
||||
[ForeignKey("FKChatId")]
|
||||
public Chat Chat { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public bool IsAcitve { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Fitness_core_BE.Models;
|
||||
using Fitness_core_BE.Models.Enums;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class Client
|
||||
{
|
||||
[Key]
|
||||
public Guid ClientId { get; set; }
|
||||
public string? ClientName { get; set; }
|
||||
public Guid FKUserId { get; set; }
|
||||
[ForeignKey("FKUserId")]
|
||||
public virtual User User { get; set; }
|
||||
|
||||
public string? ClientPhone { get; set; }
|
||||
|
||||
public string? ClientEmail { get; set; }
|
||||
|
||||
public PaymentTrack? PaymentTrack { get; set; }
|
||||
|
||||
// Payment-Service's Platform.Id for this client, minted the first time this
|
||||
// client needs to take a gateway-routed payment (see PaymentServiceClient).
|
||||
public Guid? FKPaymentPlatformId { get; set; }
|
||||
|
||||
// Payment-Service ApiKey/HmacSecret returned alongside FKPaymentPlatformId when the
|
||||
// platform was minted. Needed to sign card/payment/refund calls (RequireHmacValidation
|
||||
// on Payment-Service's side) — provisioning only ever returns these once.
|
||||
public string? ClientPaymentApiKey { get; set; }
|
||||
public string? ClientPaymentHmacSecret { get; set; }
|
||||
|
||||
// Days a member can go without payment before OverdueLockJob auto-locks their
|
||||
// fingerprint access. Client-configurable (see req 7); null defaults to
|
||||
// DefaultLockAfterDaysOverdue; an explicit 0 disables auto-lock for this client.
|
||||
public int? LockAfterDaysOverdue { get; set; }
|
||||
public const int DefaultLockAfterDaysOverdue = 2;
|
||||
|
||||
public virtual ICollection<ClientBusiness> ClientBusinesses { get; set; } = new List<ClientBusiness>();
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Fitness_core_BE.Models.Enums;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class ClientBusiness
|
||||
{
|
||||
[Key]
|
||||
public Guid Bid { get; set; }
|
||||
|
||||
public Guid? FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client Client { get; set; }
|
||||
|
||||
public BusinessType BusinessType { get; set; }
|
||||
|
||||
public string? BusinessName { get; set; }
|
||||
|
||||
public string? BusinessLogoId { get; set; }
|
||||
public string? Description { get; set; }
|
||||
|
||||
// Pysical Location
|
||||
public bool IsPysicalLocation { get; set; } = true;
|
||||
public string? Address { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? Facilities { get; set; }
|
||||
|
||||
//Coach
|
||||
public string? CoachingMode { get; set; }
|
||||
public string? Expertise { get; set; }
|
||||
public string? Gender { get; set; }
|
||||
|
||||
public string? ExperienceYears { get; set; }
|
||||
public string? SessionType { get; set; }
|
||||
|
||||
|
||||
//Product Store
|
||||
public string? StoreType { get; set; }
|
||||
public string? ProductCategory { get; set; }
|
||||
public string? DeliveryAvailable { get; set; }
|
||||
|
||||
//Geo Location
|
||||
public double? Latitude { get; set; }
|
||||
public double? Longitude { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
public DateTime createdAt { get; set; } = DateTime.UtcNow;
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Fitness_core_BE.Models;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class ClientCard
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int CardId { get; set; }
|
||||
|
||||
public int JobId { get; set; }
|
||||
public Guid FK_ClientId { get; set; }
|
||||
[ForeignKey("FK_ClientId")]
|
||||
public virtual Client Client { get; set; }
|
||||
|
||||
public string? WalletId { get; set; }
|
||||
public string? CardNumber { get; set; }
|
||||
public string? CardType { get; set; }
|
||||
public string? CardDate { get; set; }
|
||||
public bool IsActive { get; set; } = false;
|
||||
public bool IsPrimary { get; set; } = false;
|
||||
|
||||
// Payment-Service's own card identifier — the one thing we keep locally beyond
|
||||
// ApiKey/HmacSecret, because ClientInvoice.FKClientCardId needs a stable local row to
|
||||
// point at. Everything else about the card (masked PAN, brand, expiry, live status)
|
||||
// is fetched from Payment-Service on demand, never cached here.
|
||||
public string? CardToken { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
// Records what an admin actually agreed with a client for their fingerprint/access
|
||||
// device at assignment time (full purchase vs. rental + one-time setup fee).
|
||||
// DevicePricingConfig stays as the admin's reference/default rates; this is per-client.
|
||||
public class ClientDeviceBilling
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client Client { get; set; }
|
||||
|
||||
public string Mode { get; set; } = "Rental"; // "FullPurchase" | "Rental"
|
||||
public decimal AgreedAmount { get; set; } // full price if FullPurchase, monthly fee if Rental
|
||||
public decimal SetupFee { get; set; } = 0;
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime AssignedAt { get; set; } = DateTime.UtcNow;
|
||||
public Guid? AssignedByAdminId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Fitness_core_BE.Models.IOs
|
||||
{
|
||||
public class ClientGymResponse
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public double? Latitude { get; set; }
|
||||
public double? Longitude { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class ClientInvoice
|
||||
{
|
||||
[Key]
|
||||
public Guid ClientInvoiceId { get; set; }
|
||||
public Guid FKClientSubscriptionId { get; set; }
|
||||
[ForeignKey("FKClientSubscriptionId")]
|
||||
public virtual ClientSubscription ClientSubscription { get; set; }
|
||||
|
||||
public Guid FKUserId { get; set; }
|
||||
[ForeignKey("FKUserId")]
|
||||
public virtual User User { get; set; }
|
||||
|
||||
public int FKClientCardId { get; set; }
|
||||
[ForeignKey("FKClientCardId")]
|
||||
public virtual ClientCard ClientCard { get; set; }
|
||||
|
||||
public decimal Amount { get; set; }
|
||||
// "Pending" | "Partial" | "Paid"
|
||||
public string Status { get; set; } = "Pending";
|
||||
public decimal PaidAmount { get; set; } = 0;
|
||||
|
||||
public DateTime ClientInvoiceDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public virtual ICollection<ClientInvoiceItem> Items { get; set; } = new List<ClientInvoiceItem>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class ClientInvoiceItem
|
||||
{
|
||||
[Key]
|
||||
public Guid ClientInvoiceItemId { get; set; }
|
||||
public Guid FKClientInvoiceId { get; set; }
|
||||
[ForeignKey("FKClientInvoiceId")]
|
||||
public virtual ClientInvoice ClientInvoice { get; set; }
|
||||
public string? ItemName { get; set; }
|
||||
public string? ItemDescription { get; set; }
|
||||
public decimal ItemPrice { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class ClientPackage
|
||||
{
|
||||
|
||||
[Key]
|
||||
public Guid ClientPackageId { get; set; }
|
||||
public string? PackageName { get; set; }
|
||||
public string? PackageDescription { get; set; }
|
||||
public decimal PackagePrice { get; set; }
|
||||
|
||||
// Platform (HexaFitness -> gym) billing formula for this plan.
|
||||
// baseFee = MinimumMonthlyBill + max(0, min(activeMembers, MaxBillableMemberCount) - IncludedMemberCount) * PerMemberRate,
|
||||
// capped by MonthlyCap when set. PerMemberRate = 0 preserves legacy flat-PackagePrice behavior for existing rows.
|
||||
public decimal PerMemberRate { get; set; } = 0;
|
||||
public decimal MinimumMonthlyBill { get; set; } = 0;
|
||||
public decimal? MonthlyCap { get; set; }
|
||||
public decimal SetupFee { get; set; } = 0;
|
||||
|
||||
// Members already covered by MinimumMonthlyBill before PerMemberRate overage kicks in.
|
||||
public int IncludedMemberCount { get; set; } = 0;
|
||||
// Optional ceiling on the member count billed for (null = unlimited).
|
||||
public int? MaxBillableMemberCount { get; set; }
|
||||
|
||||
// Platform's cut of a client's own member-billing, depending on which gateway
|
||||
// (the client's own, or HexaFitness's) their member payments route through.
|
||||
public decimal OwnGatewayFeePercent { get; set; } = 0;
|
||||
public decimal OwnGatewayFeeFixed { get; set; } = 0;
|
||||
public decimal OurGatewayFeePercent { get; set; } = 0;
|
||||
public decimal OurGatewayFeeFixed { get; set; } = 0;
|
||||
|
||||
// Day of month (1-28) the client is paid out on, for clients on their own gateway.
|
||||
public int PayoutDayOfMonth { get; set; } = 1;
|
||||
|
||||
// The plan applied to a client when it has no active ClientSubscription assignment.
|
||||
// Only one row should have IsDefault = true.
|
||||
public bool IsDefault { get; set; } = false;
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using Fitness_core_BE.Models.Enums;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class ClientPayoutKyc
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client Client { get; set; }
|
||||
|
||||
public int? BrCertificateDocId { get; set; }
|
||||
[ForeignKey("BrCertificateDocId")]
|
||||
public virtual UserDocument? BrCertificateDoc { get; set; }
|
||||
|
||||
public int? BankProofDocId { get; set; }
|
||||
[ForeignKey("BankProofDocId")]
|
||||
public virtual UserDocument? BankProofDoc { get; set; }
|
||||
|
||||
// Legacy payout-KYC uploads. New gateway onboarding reuses the NIC front
|
||||
// and back collected during owner activation instead of uploading it again.
|
||||
public int? NicDocId { get; set; }
|
||||
[ForeignKey("NicDocId")]
|
||||
public virtual UserDocument? NicDoc { get; set; }
|
||||
|
||||
// GatewayDefinition lives in Payment-Service, so this is an external
|
||||
// catalog identifier rather than an EF foreign key.
|
||||
public Guid? SelectedGatewayId { get; set; }
|
||||
|
||||
[MaxLength(100)]
|
||||
public string? SelectedGatewayName { get; set; }
|
||||
|
||||
public KycStatus KycStatus { get; set; } = KycStatus.Pending;
|
||||
public DateTime SubmittedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? ReviewedAt { get; set; }
|
||||
|
||||
[MaxLength(500)]
|
||||
public string? RejectionReason { get; set; }
|
||||
|
||||
public Guid? ReviewedByAdminId { get; set; }
|
||||
[ForeignKey("ReviewedByAdminId")]
|
||||
public virtual Admin? ReviewedByAdmin { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class ClientSubItems
|
||||
{
|
||||
|
||||
[Key]
|
||||
public Guid ClientSubItemsId { get; set; }
|
||||
public Guid FKClientSubscriptionId { get; set; }
|
||||
[ForeignKey("FKClientSubscriptionId")]
|
||||
public virtual ClientSubscription ClientSubscription { get; set; }
|
||||
public string? SubItemName { get; set; }
|
||||
public string? SubItemDescription { get; set; }
|
||||
public decimal SubItemPrice { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class ClientSubscription
|
||||
{
|
||||
[Key]
|
||||
public Guid SubscriptionId { get; set;}
|
||||
|
||||
public Guid FKClientPackageId { get; set; }
|
||||
[ForeignKey("FKClientPackageId")]
|
||||
public virtual ClientPackage ClientPackage { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client Client { get; set; }
|
||||
public DateTime OnboardDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public DateTime? ExpiryDate { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>A gym's own Terms & Conditions, authored in the client portal. Only one row per
|
||||
/// (ClientId, IsActive=true) at a time — publishing a new version deactivates the old one so
|
||||
/// CustomerTermsAcceptance rows keep pointing at the exact version they accepted.</summary>
|
||||
public class ClientTerms
|
||||
{
|
||||
[Key]
|
||||
public Guid ClientTermsId { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client? Client { get; set; }
|
||||
|
||||
public int Version { get; set; } = 1;
|
||||
public string Content { get; set; } = string.Empty;
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class ClientWallet
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client Client { get; set; }
|
||||
|
||||
public decimal Balance { get; set; } = 0;
|
||||
public string Currency { get; set; } = "LKR";
|
||||
public DateTime? LastPayoutDate { get; set; }
|
||||
|
||||
public virtual ICollection<WalletLedgerEntry> LedgerEntries { get; set; } = new List<WalletLedgerEntry>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CusWeightLossProgress
|
||||
{
|
||||
[Key]
|
||||
public Guid WeeklyProgressId { get; set; }
|
||||
public decimal? Weight { get; set; }
|
||||
public DateTime RecordedAt { get; set; } = DateTime.UtcNow;
|
||||
public Guid FKCusId { get; set; }
|
||||
[ForeignKey("FKCusId")]
|
||||
public virtual Customer Customer { get; set; }
|
||||
public Guid CustomerTargetId { get; set; }
|
||||
[ForeignKey("CustomerTargetId")]
|
||||
public virtual CustomerTarget CustomerTarget { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Fitness_core_BE.Models;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class Customer
|
||||
{
|
||||
[Key]
|
||||
public Guid CusId { get; set; }
|
||||
|
||||
public string? CusName { get; set; }
|
||||
public string? PushTokenId { get; set; }
|
||||
// Points at a UserDocument uploaded via POST /customer/documents. No EF relationship
|
||||
// is configured (kept as a plain id) since we only ever need the id itself here — the
|
||||
// mobile app builds the display URL client-side via docService/{id}.
|
||||
public int? FKProfileImageDocId { get; set; }
|
||||
public string? Gender { get; set; }
|
||||
public DateOnly? BoD { get; set; }
|
||||
public string? PrimaryPH { get; set; }
|
||||
public bool IsPrimaryPHVerfied { get; set; } = false;
|
||||
public string? SecondaryPH { get; set; }
|
||||
public bool IsSecondaryPHVerfied { get; set; } = false;
|
||||
public string? Email { get; set; }
|
||||
public bool IsEmailVerfied { get; set; } = false;
|
||||
public string? NIC { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public Guid FKUserId { get; set; }
|
||||
[ForeignKey("FKUserId")]
|
||||
public virtual User User { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Legacy single-gym lock mirror. New physical-access decisions are stored on the
|
||||
// tenant-scoped AccessProfile so one customer can have different access state at
|
||||
// different gyms.
|
||||
public bool IsLocked { get; set; } = false;
|
||||
public string? LockReason { get; set; } // "Manual" | "Overdue"
|
||||
public DateTime? LockedAt { get; set; }
|
||||
|
||||
public virtual ICollection<CustomerWorkout> CustomerWorkouts { get; set; }
|
||||
public virtual ICollection<CustomerMeal> CustomerMeals { get; set; }
|
||||
public virtual ICollection<CustomerTarget> CustomerTargets { get; set; }
|
||||
public virtual ICollection<CustomerSubscription> CustomerSubscriptions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CustomerAccess
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public Guid FKDeviceId { get; set; }
|
||||
[ForeignKey("FKDeviceId")]
|
||||
public AccessDevice AccessDevice { get; set; }
|
||||
public Guid FKCustomerId { get; set; }
|
||||
[ForeignKey(nameof(FKCustomerId))]
|
||||
public Customer Customer { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CustomerCards
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid JobId { get; set; }
|
||||
|
||||
// Tenant-bucket entity: this FK holds a TenantCustomerLinkId (not the central
|
||||
// Customer.CusId directly) -- see Models/TenantCustomerLink.cs.
|
||||
public Guid? FK_CustomerId { get; set; }
|
||||
[ForeignKey("FK_CustomerId")]
|
||||
public virtual TenantCustomerLink? TenantCustomerLink { get; set; }
|
||||
|
||||
public Guid? FK_ClientId { get; set; }
|
||||
[ForeignKey("FK_ClientId")]
|
||||
public virtual Client? Client { get; set; }
|
||||
|
||||
public string? WalletId { get; set; }
|
||||
public string? CardId { get; set; }
|
||||
public string? CardNumber { get; set; }
|
||||
public string? CardType { get; set; }
|
||||
public string? CardDate { get; set; }
|
||||
public bool IsActive { get; set; } = false;
|
||||
public bool IsPrimary { get; set; } = false;
|
||||
public int Priority { get; set; } = 0;
|
||||
public string? NickName { get; set; }
|
||||
|
||||
/// <summary>Member's explicit opt-in to store this card for auto-deduct charges.</summary>
|
||||
public bool ConsentGiven { get; set; } = false;
|
||||
public DateTime? ConsentAt { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CustomerHealth
|
||||
{
|
||||
[Key]
|
||||
public Guid HealthId { get; set; }
|
||||
public string? BloodGroup { get; set; }
|
||||
public string? Disabilities { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public Guid FKCusId { get; set; }
|
||||
[ForeignKey("FKCusId")]
|
||||
public virtual Customer Customer { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CustomerInvoice
|
||||
{
|
||||
[Key]
|
||||
public Guid CustomerInvoiceId { get; set; }
|
||||
public Guid FKCustomerSubscriptionId { get; set; }
|
||||
[ForeignKey("FKCustomerSubscriptionId")]
|
||||
public virtual CustomerSubscription CustomerSubscription { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client Client { get; set; }
|
||||
|
||||
public Guid FKUserId { get; set; }
|
||||
[ForeignKey("FKUserId")]
|
||||
public virtual User User { get; set; }
|
||||
|
||||
public decimal Amount { get; set; }
|
||||
public string Status { get; set; } = "Pending";
|
||||
|
||||
// Approval gate for the renewal workflow: false means this invoice was auto-generated by
|
||||
// the daily renewal sweep and is awaiting staff approve/change-plan/reject. Independent of
|
||||
// Status and of CustomerSubscription.IsActive. Defaults true so every other invoice-
|
||||
// creation path (onboarding, plan change, manual billing) is unaffected.
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
public DateTime CustomerInvoiceDate { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CustomerInvoiceItem
|
||||
{
|
||||
[Key]
|
||||
public Guid CustomerInvoiceItemId { get; set; }
|
||||
public Guid FKCustomerInvoiceId { get; set; }
|
||||
[ForeignKey("FKCustomerInvoiceId")]
|
||||
public virtual CustomerInvoice CustomerInvoice { get; set; }
|
||||
public string? ItemName { get; set; }
|
||||
public string? ItemDescription { get; set; }
|
||||
public decimal ItemPrice { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CustomerMeal
|
||||
{
|
||||
[Key]
|
||||
public Guid CustomerMealId { get; set; }
|
||||
|
||||
// Tenant-bucket entity: this FK holds a TenantCustomerLinkId (not the central
|
||||
// Customer.CusId directly) -- see Models/TenantCustomerLink.cs.
|
||||
public Guid FKCustomerId { get; set; }
|
||||
[ForeignKey("FKCustomerId")]
|
||||
public virtual TenantCustomerLink? TenantCustomerLink { get; set; }
|
||||
|
||||
public Guid FKMealPlanId { get; set; }
|
||||
[ForeignKey("FKMealPlanId")]
|
||||
public virtual MealPlan? MealPlan { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime AssignedDate { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? CompletedDate { get; set; }
|
||||
public bool IsFollowing { get; set; } = false;
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
// A persisted, in-app history of pushes sent to a customer (workout/meal plan assigned,
|
||||
// a gym feed post, etc.) — backs the mobile Notification tab, independent of whether the
|
||||
// device actually received/displayed the push itself.
|
||||
public class CustomerNotification
|
||||
{
|
||||
[Key]
|
||||
public Guid CustomerNotificationId { get; set; }
|
||||
|
||||
public Guid FKCustomerId { get; set; }
|
||||
[ForeignKey("FKCustomerId")]
|
||||
public virtual Customer? Customer { get; set; }
|
||||
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string Body { get; set; } = string.Empty;
|
||||
|
||||
// "WorkoutPlanAssigned" | "MealPlanAssigned" | "FeedPost" — free text, not an enum, so
|
||||
// new notification kinds don't need a migration.
|
||||
public string NotificationType { get; set; } = string.Empty;
|
||||
|
||||
// Mirrors PushNavigationData's contract in the mobile app's pushNotification.ts, so a
|
||||
// tap on this notification in the list can navigate the same way a live push would.
|
||||
public string? RouteName { get; set; }
|
||||
public string? RouteParamsJson { get; set; }
|
||||
|
||||
public bool IsRead { get; set; } = false;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CustomerPlan
|
||||
{
|
||||
[Key]
|
||||
public Guid CustomerPlanId { get; set; }
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client Client { get; set; }
|
||||
public string? PlanName { get; set; }
|
||||
public string? PlanDescription { get; set; }
|
||||
public decimal PlanPrice { get; set; }
|
||||
|
||||
/// <summary>Whether this plan charges a one-time fee when a new subscription starts.</summary>
|
||||
public bool HasOnboardingFee { get; set; } = false;
|
||||
|
||||
/// <summary>One-time amount added to the first invoice/installment.</summary>
|
||||
public decimal OnboardingFeeAmount { get; set; } = 0m;
|
||||
|
||||
/// <summary>Used when DurationMode == "Rolling"; 0 for FixedRange plans.</summary>
|
||||
public int DurationInDays { get; set; }
|
||||
|
||||
/// <summary>"Individual" | "Group" (couple/family — one subscription covers MemberLimit members).</summary>
|
||||
public string PlanScope { get; set; } = "Individual";
|
||||
|
||||
/// <summary>1 for Individual; >= 2 for Group (couple = 2).</summary>
|
||||
public int MemberLimit { get; set; } = 1;
|
||||
|
||||
/// <summary>"Rolling" (DurationInDays from join date) | "FixedRange" (ValidFrom–ValidTo).</summary>
|
||||
public string DurationMode { get; set; } = "Rolling";
|
||||
|
||||
public DateTime? ValidFrom { get; set; }
|
||||
public DateTime? ValidTo { get; set; }
|
||||
|
||||
/// <summary>Owner decides: when true the price splits into InstallmentCount scheduled parts.</summary>
|
||||
public bool AllowSplitPayments { get; set; } = false;
|
||||
|
||||
/// <summary>Number of mostly-equal installments (1 = full payment upfront).</summary>
|
||||
public int InstallmentCount { get; set; } = 1;
|
||||
|
||||
/// <summary>Null = unlimited-access plan (existing gym behavior). A number = this plan
|
||||
/// grants that many bookable Facility sessions per billing cycle (e.g. "4 pool sessions
|
||||
/// a month") — enforced by the booking flow, not by this model.</summary>
|
||||
public int? SessionsPerCycle { get; set; }
|
||||
|
||||
/// <summary>"NoRollover" (unused sessions are forfeited when the cycle renews) |
|
||||
/// "GracePeriod" (unused sessions stay usable for RolloverGraceDays after renewal).
|
||||
/// Set per package at creation time, per owner's requirement.</summary>
|
||||
public string SessionRolloverPolicy { get; set; } = "NoRollover";
|
||||
|
||||
/// <summary>Days into the new cycle that last cycle's unused sessions remain usable.
|
||||
/// Only meaningful when SessionRolloverPolicy == "GracePeriod".</summary>
|
||||
public int RolloverGraceDays { get; set; } = 0;
|
||||
|
||||
/// <summary>"Prepaid" (pay before the cycle starts, existing behavior) | "Postpaid"
|
||||
/// (use first, invoice at cycle end — reuses Client.LockAfterDaysOverdue /
|
||||
/// OverdueLockJob to suspend access if that invoice goes unpaid past due). Asked once,
|
||||
/// per package, at creation time per owner's requirement.</summary>
|
||||
public string BillingTiming { get; set; } = "Prepaid";
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>Many-to-many join: which Place(s) a CustomerPlan grants access to. A plan
|
||||
/// with no rows here is unrestricted (reaches every active device) -- see
|
||||
/// AccessDeviceService's allowed-device resolution.</summary>
|
||||
public class CustomerPlanPlace
|
||||
{
|
||||
[Key]
|
||||
public Guid CustomerPlanPlaceId { get; set; }
|
||||
|
||||
public Guid FKCustomerPlanId { get; set; }
|
||||
[ForeignKey(nameof(FKCustomerPlanId))]
|
||||
public CustomerPlan CustomerPlan { get; set; } = null!;
|
||||
|
||||
public Guid FKPlaceId { get; set; }
|
||||
[ForeignKey(nameof(FKPlaceId))]
|
||||
public Place Place { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CustomerSubItem
|
||||
{
|
||||
[Key]
|
||||
public Guid CustomerSubItemId { get; set; }
|
||||
|
||||
public Guid FKCustomerSubscriptionId { get; set; }
|
||||
[ForeignKey("FKCustomerSubscriptionId")]
|
||||
public virtual CustomerSubscription CustomerSubscription { get; set; }
|
||||
|
||||
public string? SubItemName { get; set; }
|
||||
public string? SubItemDescription { get; set; }
|
||||
public decimal SubItemPrice { get; set; }
|
||||
|
||||
// "OneTime" (charged once, e.g. via a supplemental invoice) | "Recurring" (carried
|
||||
// forward onto the invoice each time this subscription renews/upgrades).
|
||||
public string Frequency { get; set; } = "OneTime";
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CustomerSubscription
|
||||
{
|
||||
[Key]
|
||||
public Guid SubscriptionId { get; set; }
|
||||
|
||||
public Guid FKCustomerPlanId { get; set; }
|
||||
[ForeignKey("FKCustomerPlanId")]
|
||||
public virtual CustomerPlan CustomerPlan { get; set; }
|
||||
|
||||
// Tenant-bucket entity: this FK holds a TenantCustomerLinkId (not the central
|
||||
// Customer.CusId directly) -- see Models/TenantCustomerLink.cs.
|
||||
public Guid FKCustomerId { get; set; }
|
||||
[ForeignKey("FKCustomerId")]
|
||||
public virtual TenantCustomerLink TenantCustomerLink { get; set; }
|
||||
|
||||
public DateTime OnboardDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Set once, at creation, and never touched again -- see RenewalDate for the field that
|
||||
// actually reflects the subscription's current cycle end / renewal-eligibility date.
|
||||
public DateTime? ExpiryDate { get; set; }
|
||||
|
||||
// Advances on every approved renewal cycle; every "is this due/expiring/current" query,
|
||||
// filter, sort or status display should read this instead of ExpiryDate.
|
||||
public DateTime? RenewalDate { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Set together at assignment time; "was a discount given" is DiscountValue.HasValue.
|
||||
public string? DiscountType { get; set; } // "Percentage" | "Flat"
|
||||
public decimal? DiscountValue { get; set; }
|
||||
|
||||
/// <summary>Member has consented to auto-deduct renewals from FKAutoChargeCardId.</summary>
|
||||
public bool AutoChargeEnabled { get; set; } = false;
|
||||
public Guid? FKAutoChargeCardId { get; set; }
|
||||
[ForeignKey("FKAutoChargeCardId")]
|
||||
public virtual CustomerCards? AutoChargeCard { get; set; }
|
||||
|
||||
// Session-count tracking for CustomerPlan.SessionsPerCycle plans (null for unlimited
|
||||
// gym-access plans). SessionsUsedThisCycle resets on renewal; SessionsCarriedOver holds
|
||||
// unused sessions brought forward when CustomerPlan.SessionRolloverPolicy == "GracePeriod"
|
||||
// and is cleared once RolloverGraceDays elapses. Booking creation increments
|
||||
// SessionsUsedThisCycle instead of duplicating this counter per facility.
|
||||
public int SessionsUsedThisCycle { get; set; } = 0;
|
||||
public int SessionsCarriedOver { get; set; } = 0;
|
||||
|
||||
// Set at renewal approval time when CustomerPlan.SessionRolloverPolicy == "GracePeriod"
|
||||
// (now + RolloverGraceDays); null when there's nothing carried over. Booking creation
|
||||
// treats SessionsCarriedOver as spent/expired once this passes, even though the column
|
||||
// itself isn't zeroed until the next renewal -- see BookingService.CreateMemberBooking.
|
||||
public DateTime? SessionsCarriedOverExpiresAt { get; set; }
|
||||
|
||||
public virtual ICollection<SubscriptionMember> SubscriptionMembers { get; set; }
|
||||
public virtual ICollection<SubscriptionInstallment> SubscriptionInstallments { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CustomerTarget
|
||||
{
|
||||
[Key]
|
||||
public Guid CustomerTargetId { get; set; }
|
||||
public Guid FKCusId { get; set; }
|
||||
[ForeignKey("FKCusId")]
|
||||
public virtual Customer Customer { get; set; }
|
||||
public decimal? CurrentWeight { get; set; }
|
||||
public decimal? CurrentHeight { get; set; }
|
||||
public decimal? TargetWeight { get; set; }
|
||||
public string? Goal { get; set; }
|
||||
public string? TimePeriod { get; set; }
|
||||
public string? PredictedNutritionsJson { get; set; }
|
||||
public string? PredictedWeightsJson { get; set; }
|
||||
public virtual ICollection<CusWeightLossProgress> WeeklyProgresses { get; set; } = new List<CusWeightLossProgress>();
|
||||
public bool IsChanged { get; set; } = false;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>Records that a customer accepted a specific gym's specific Terms version —
|
||||
/// e.g. at QR-join time. Kept even after the gym publishes a newer version.</summary>
|
||||
public class CustomerTermsAcceptance
|
||||
{
|
||||
[Key]
|
||||
public Guid CustomerTermsAcceptanceId { get; set; }
|
||||
|
||||
// Tenant-bucket entity: this FK holds a TenantCustomerLinkId (not the central
|
||||
// Customer.CusId directly) -- see Models/TenantCustomerLink.cs.
|
||||
public Guid FKCusId { get; set; }
|
||||
[ForeignKey("FKCusId")]
|
||||
public virtual TenantCustomerLink? TenantCustomerLink { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client? Client { get; set; }
|
||||
|
||||
public int Version { get; set; }
|
||||
public DateTime AcceptedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CustomerWorkout
|
||||
{
|
||||
[Key]
|
||||
public Guid CustomerWorkoutId { get; set; }
|
||||
|
||||
// Tenant-bucket entity: this FK holds a TenantCustomerLinkId (not the central
|
||||
// Customer.CusId directly) -- see Models/TenantCustomerLink.cs.
|
||||
public Guid FKCustomerId { get; set; }
|
||||
[ForeignKey("FKCustomerId")]
|
||||
public virtual TenantCustomerLink? TenantCustomerLink { get; set; }
|
||||
|
||||
public Guid FKWorkoutPlanId { get; set; }
|
||||
[ForeignKey("FKWorkoutPlanId")]
|
||||
public virtual WorkoutPlan? WorkoutPlan { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime AssignedDate { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? CompletedDate { get; set; }
|
||||
public bool IsFollowing { get; set; } = false;
|
||||
public string? Notes { get; set; }
|
||||
public virtual ICollection<CustomerWorkoutExerciseCompletion> ExerciseCompletions { get; set; } = new List<CustomerWorkoutExerciseCompletion>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class CustomerWorkoutExerciseCompletion
|
||||
{
|
||||
[Key]
|
||||
public Guid CustomerWorkoutExerciseCompletionId { get; set; }
|
||||
public Guid FKCustomerWorkoutId { get; set; }
|
||||
[ForeignKey("FKCustomerWorkoutId")]
|
||||
public virtual CustomerWorkout? CustomerWorkout { get; set; }
|
||||
public Guid FKWorkoutExerciseId { get; set; }
|
||||
[ForeignKey("FKWorkoutExerciseId")]
|
||||
public virtual WorkoutExercise? WorkoutExercise { get; set; }
|
||||
public DateTime CompletedDate { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class DeviceCommand
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public Client Client { get; set; }
|
||||
public Guid? FKDeviceId { get; set; }
|
||||
[ForeignKey("FKDeviceId")]
|
||||
public AccessDevice? AccessDevice { get; set; }
|
||||
|
||||
public Guid? FKAccessDeviceBindingId { get; set; }
|
||||
[ForeignKey(nameof(FKAccessDeviceBindingId))]
|
||||
public AccessDeviceBinding? AccessDeviceBinding { get; set; }
|
||||
|
||||
// e.g. "AddUser", "WhiteListMember", "BlackListMember"
|
||||
public string CommandType { get; set; }
|
||||
public string PayloadJson { get; set; }
|
||||
|
||||
// "Pending" / "InProgress" / "Completed" / "Failed"
|
||||
public string Status { get; set; } = "Pending";
|
||||
public string? ResultJson { get; set; }
|
||||
public int DesiredVersion { get; set; }
|
||||
public int AttemptCount { get; set; }
|
||||
public int MaxAttempts { get; set; } = 5;
|
||||
public DateTime? LeaseExpiresAt { get; set; }
|
||||
public DateTime? NextAttemptAt { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? ProcessedAt { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
// Single global row (admin-editable) for access-control-device fees.
|
||||
// Not per-plan: every gym pays the same device rates in the source pricing model.
|
||||
public class DevicePricingConfig
|
||||
{
|
||||
[Key]
|
||||
public Guid DevicePricingConfigId { get; set; }
|
||||
|
||||
public decimal DeviceRentalFeePerMonth { get; set; } = 0;
|
||||
public decimal DeviceInstallationFee { get; set; } = 0;
|
||||
public decimal DeviceConfigureExistingFee { get; set; } = 0;
|
||||
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class DocumentTypes
|
||||
{
|
||||
[Key]
|
||||
public int DocumentId { get; set; }
|
||||
public string DocumentType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum BeneficiaryType
|
||||
{
|
||||
Customer,
|
||||
Client
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum BusinessType
|
||||
{
|
||||
institute = 1,
|
||||
cauching = 2,
|
||||
productSeller = 3
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum CustomerOnboardingStatus
|
||||
{
|
||||
Pending = 1,
|
||||
Approved = 2,
|
||||
Rejected = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum FirstChargeStatus
|
||||
{
|
||||
Pending = 1,
|
||||
Succeeded = 2,
|
||||
Failed = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum KycStatus
|
||||
{
|
||||
Pending = 1,
|
||||
Approved = 2,
|
||||
Rejected = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum OnboardingStatus
|
||||
{
|
||||
New = 1,
|
||||
Called = 2,
|
||||
Approved = 3,
|
||||
Rejected = 4,
|
||||
Activated = 5
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum PaymentTrack
|
||||
{
|
||||
OwnGateway = 1,
|
||||
HexaFitness = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum PayoutBatchStatus
|
||||
{
|
||||
Pending = 1,
|
||||
Processed = 2,
|
||||
Failed = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum PriorityLevel
|
||||
{
|
||||
Low = 1,
|
||||
Medium = 2,
|
||||
High = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum ProgressGoalType
|
||||
{
|
||||
Loss,
|
||||
Gain,
|
||||
Maintain,
|
||||
Strength,
|
||||
Endurance,
|
||||
AthleticPerformance,
|
||||
StayFit,
|
||||
Flexibility,
|
||||
Beginner
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum Sex
|
||||
{
|
||||
Male,
|
||||
Female
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum TicketStatusEnum
|
||||
{
|
||||
Pending = 1,
|
||||
Open = 2,
|
||||
Closed = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Fitness_core_BE.Models.Enums
|
||||
{
|
||||
public enum WalletEntryType
|
||||
{
|
||||
Credit = 1,
|
||||
Debit = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>A bookable area of the club — the gym floor, the pool, a court, a class type
|
||||
/// (karate, etc). Gym-floor access itself keeps working through the existing
|
||||
/// CustomerPlan/Attendance flow unchanged; Facility/FacilitySession/Booking below are for
|
||||
/// the new capacity-limited, session-based areas (pool, courts, classes).</summary>
|
||||
public class Facility
|
||||
{
|
||||
[Key]
|
||||
public Guid FacilityId { get; set; }
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey(nameof(FKClientId))]
|
||||
public Client Client { get; set; }
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>"Gym" | "Pool" | "Court" | "Class" — informational grouping for the admin
|
||||
/// UI; booking rules themselves live on FacilitySession/CustomerPlan, not on this type.</summary>
|
||||
public string FacilityType { get; set; } = "Class";
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>Default per-session headcount limit; a specific FacilitySession can override.</summary>
|
||||
public int DefaultCapacity { get; set; } = 1;
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>One scheduled, bookable occurrence of a Facility (e.g. "Pool, Tue 6-7pm").
|
||||
/// Capacity is enforced by counting non-cancelled Bookings against this row, not by a
|
||||
/// denormalized counter, to avoid drift between the two.</summary>
|
||||
public class FacilitySession
|
||||
{
|
||||
[Key]
|
||||
public Guid FacilitySessionId { get; set; }
|
||||
|
||||
public Guid FKFacilityId { get; set; }
|
||||
[ForeignKey(nameof(FKFacilityId))]
|
||||
public Facility Facility { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey(nameof(FKClientId))]
|
||||
public Client Client { get; set; }
|
||||
|
||||
public DateTime StartTime { get; set; }
|
||||
public DateTime EndTime { get; set; }
|
||||
|
||||
/// <summary>Overrides Facility.DefaultCapacity when set; null = use the facility default.</summary>
|
||||
public int? Capacity { get; set; }
|
||||
|
||||
public bool IsCancelled { get; set; } = false;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public virtual ICollection<Booking> Bookings { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class FeedLike
|
||||
{
|
||||
[Key]
|
||||
public Guid LikeId { get; set; }
|
||||
public Guid FKUserId { get; set; }
|
||||
[ForeignKey("FKUserId")]
|
||||
public virtual User User { get; set; }
|
||||
|
||||
public Guid FKFeedPostId { get; set; }
|
||||
[ForeignKey("FKFeedPostId")]
|
||||
public virtual FeedPost FeedPost { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class FeedPost
|
||||
{
|
||||
[Key]
|
||||
public Guid FeedPostId { get; set; }
|
||||
public string? Caption { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public int? FkDocId { get; set; }
|
||||
public Guid FKUserId { get; set; }
|
||||
[ForeignKey("FKUserId")]
|
||||
public virtual User User { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
// A gym owner's custom, per-tenant staff role. Blank slate — creating a role grants
|
||||
// zero permissions; the owner assigns them explicitly via GymRolePermission afterward.
|
||||
public class GymRole
|
||||
{
|
||||
[Key]
|
||||
public Guid GymRoleId { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client Client { get; set; }
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public virtual ICollection<GymRolePermission> Permissions { get; set; } = new List<GymRolePermission>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
// Join row: a GymRole is granted access to one NavItem/permission key.
|
||||
// Composite key (FKGymRoleId, NavItemKey) is configured in TenantDbContext.OnModelCreating.
|
||||
public class GymRolePermission
|
||||
{
|
||||
public Guid FKGymRoleId { get; set; }
|
||||
[ForeignKey("FKGymRoleId")]
|
||||
public virtual GymRole GymRole { get; set; }
|
||||
|
||||
// NavItem is central-bucket (global nav catalog); this FK is a plain scalar in
|
||||
// TenantDbContext (see Ignore config there) -- resolve the label from NavItemKey
|
||||
// against CentralDbContext.NavItems when actually needed.
|
||||
public string NavItemKey { get; set; } = string.Empty;
|
||||
[ForeignKey("NavItemKey")]
|
||||
public virtual NavItem NavItem { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Fitness_core_BE.Models.IOs
|
||||
{
|
||||
public record AdminExerciseInput(string Name, string? BreathIn, string? BreathOut, string? Position, string? Reps, int? Sets, string? Duration, string? VideoUrl, string? Description);
|
||||
public record AdminWorkoutDayInput(string Day, string? Description, string? SuitableFor, string? Duration, string? WarmUp, string? Cooldown, List<AdminExerciseInput> Exercises);
|
||||
public record AdminWorkoutSuggestionRequest(string Name, string Category, string? CategoryDescription, string? Description, List<AdminWorkoutDayInput> Days);
|
||||
public record AdminFoodInput(string Name, string? Quantity);
|
||||
public record AdminMealInput(string Name, string? Description, decimal? Calories, decimal? Protein, decimal? Carbohydrates, decimal? Fats, string? ServingSize, string? PreparationNotes, List<AdminFoodInput> Foods);
|
||||
public record AdminMealSuggestionRequest(string Name, string Category, string? CategoryDescription, string? Description, List<AdminMealInput> Meals);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models.IOs
|
||||
{
|
||||
[NotMapped]
|
||||
public class ApiRequest
|
||||
{
|
||||
public string FunctionName { get; set; }
|
||||
|
||||
public object Payload { get; set; }
|
||||
|
||||
public string Reference { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Fitness_core_BE.Models.IOs
|
||||
{
|
||||
[NotMapped]
|
||||
public class ApiResponse<T>
|
||||
{
|
||||
public int StatusCode { get; set; }
|
||||
[Required]
|
||||
public string Message { get; set; }
|
||||
[Required]
|
||||
public T Data { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
namespace Fitness_core_BE.Models.IOs
|
||||
{
|
||||
public record PagedResult<T>(List<T> Items, int Total, int Page, int PageSize); //commonly tabel use
|
||||
|
||||
// Shared OTP-send response shape -- used by the client's own contact-detail-change OTP flow
|
||||
// (ClientService.SendContactOtp via AuthOtpResponseParser), not onboarding-specific despite
|
||||
// originally living in that file.
|
||||
public record SendOtpResult(string ReferenceNumber, int ExpiresInSeconds, int ResendAllowedInSeconds);
|
||||
|
||||
// ---------- Admin: client directory ----------
|
||||
public record ClientSummary(Guid ClientId, string? ClientName, string? City, string? PaymentTrack, bool IsActive, DateTime CreatedAt);
|
||||
public record ClientDirectoryQuery(string? Search, int Page = 1, int PageSize = 20);
|
||||
|
||||
// ---------- Admin: access-device pairing & status ----------
|
||||
public record PairingCodeResult(string Code, DateTime ExpiresAt);
|
||||
|
||||
// IsOnline is inferred from the client's on-prem bridge agent's last heartbeat — the cloud
|
||||
// never talks to a device directly. AccessDevice isn't tied to a specific agent, so every
|
||||
// device under a client shares the same IsOnline signal (see AdminClientDirectoryService).
|
||||
public record AdminAgentDto(Guid AgentId, string Name, bool IsActive, DateTime? LastSeenAt, bool IsOnline, DateTime CreatedAt);
|
||||
public record AdminDeviceDto(
|
||||
Guid DeviceId,
|
||||
string DeviceName,
|
||||
bool IsActive,
|
||||
DateTime? LastSeenAt,
|
||||
bool IsOnline,
|
||||
DateTime? PresenceExpiresAt,
|
||||
bool IsClaimed,
|
||||
DateTime CreatedAt);
|
||||
public record AdminClientDevicesResult(List<AdminAgentDto> Agents, List<AdminDeviceDto> Devices);
|
||||
|
||||
// ---------- Client's own customers / staff ----------
|
||||
public record CreateCustomerWithPlansRequest(
|
||||
string Fullname, string UserName, string Email, string MobileNumber, string? Nic, string Gender, string? Birthday,
|
||||
List<Guid>? WorkoutPlanIds, List<Guid>? MealPlanIds, Guid? CustomerPlanId,
|
||||
DateTime? OnboardDate, bool ChargePayment = true,
|
||||
// Set true only on the resubmit after staff has confirmed "yes, link this existing
|
||||
// platform customer to my gym" -- see ClientService.CreateCustomerWithPlans.
|
||||
bool ConfirmLink = false,
|
||||
string? DiscountType = null, decimal? DiscountValue = null);
|
||||
|
||||
// MobileNumber (not PhoneNumber) -- must match the frontend's MemberInput.mobileNumber
|
||||
// JSON key, same convention CreateCustomerWithPlansRequest above already uses. A prior
|
||||
// mismatch here (PhoneNumber) silently bound to null on every edit, since ASP.NET Core's
|
||||
// JSON binder does not fuzzy-match differently-named keys -- every customer phone edit was
|
||||
// wiping the number instead of changing it.
|
||||
public record UpdateCustomerByClientRequest(
|
||||
string Fullname, string UserName, string Email, string? Nic, string? Gender, string? Birthday,
|
||||
string? MobileNumber);
|
||||
|
||||
// "Parent" path: starts a brand new subscription for a customer who was created without
|
||||
// a plan, with them as the primary member.
|
||||
public record AssignCustomerPlanRequest(
|
||||
Guid CustomerPlanId, DateTime? ExpiryDate, DateTime? OnboardDate, bool ChargePayment = true,
|
||||
string? DiscountType = null, decimal? DiscountValue = null);
|
||||
|
||||
// Switches an existing Individual-scope subscription to a different Individual-scope plan.
|
||||
public record ChangeMembershipPlanRequest(Guid CustomerPlanId);
|
||||
|
||||
// ---------- Client's own gym profile (post-activation) ----------
|
||||
public record EquipmentCountDto(string Name, int Count);
|
||||
public record UpdateGymProfileRequest(
|
||||
string? BusinessName, string? Address, string? City, string? Description,
|
||||
bool? HasAc, List<EquipmentCountDto>? Equipment);
|
||||
|
||||
// ---------- Gym-specific Terms & Conditions authoring ----------
|
||||
public record PublishGymTermsRequest(string Content);
|
||||
|
||||
// ---------- Client's own contact detail change (OTP-verified) ----------
|
||||
public record SendContactOtpRequest(string Type, string Identifier);
|
||||
public record VerifyContactOtpRequest(string Type, string Identifier, string ReferenceNumber, string OtpCode);
|
||||
|
||||
// ---------- Member medical details unlock ----------
|
||||
public record MedicalUnlockRequest(string Password);
|
||||
|
||||
// GymRoleId optional -- must belong to the caller's own client (validated in StaffService).
|
||||
// Left null, the new staff member has no dashboard access until a role is assigned later.
|
||||
public record CreateStaffRequest(string UserName, string Email, string PhoneNumber, string? StaffPosition, string? Department, DateTime? HireDate, Guid? GymRoleId = null);
|
||||
public record UpdateStaffRequest(string? UserName, string? Email, string? PhoneNumber, string? StaffPosition, string? Department);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Fitness_core_BE.Models.IOs
|
||||
{
|
||||
public class SmsRequest
|
||||
{
|
||||
public string api_key { get; set; } = string.Empty;
|
||||
public string phone_number { get; set; } = string.Empty;
|
||||
public string msg_text { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class OtpResponse
|
||||
{
|
||||
public int StatusCode { get; set; }
|
||||
public string Message { get; set; } = string.Empty;
|
||||
public string? ApiResponse { get; set; }
|
||||
public string? Error { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
// One row per (client, sweep) a background job processed. Central, not tenant-scoped --
|
||||
// job history isn't gym-operational data, and needs to be visible from one place
|
||||
// regardless of which tenant schema it's actually about.
|
||||
public class JobRunLog
|
||||
{
|
||||
[Key]
|
||||
public Guid JobRunLogId { get; set; }
|
||||
|
||||
public string JobName { get; set; } = string.Empty;
|
||||
public Guid FKClientId { get; set; }
|
||||
public DateTime RunAtUtc { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// "Success" | "PartialFailure" | "Failed"
|
||||
public string Status { get; set; } = string.Empty;
|
||||
public int SubscriptionsRenewed { get; set; }
|
||||
public string? ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
// Manual/ad-hoc ledger line (e.g. rent, utilities, misc income) — a simple ledger for
|
||||
// things not already captured elsewhere. The full P&L view merges these with derived
|
||||
// entries from PaymentTransaction (member payments), ClientInvoice (platform fees) and
|
||||
// StaffPayment (payroll), rather than duplicating those into this table.
|
||||
public class LedgerEntry
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client Client { get; set; }
|
||||
|
||||
public string EntryType { get; set; } = "Expense"; // "Income" | "Expense"
|
||||
public string Category { get; set; } = "Other";
|
||||
public string? Description { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public DateTime EntryDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class Meal
|
||||
{
|
||||
[Key]
|
||||
public Guid MealId { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public decimal? Calories { get; set; }
|
||||
public decimal? Protein { get; set; }
|
||||
public decimal? Carbohydrates { get; set; }
|
||||
public decimal? Fats { get; set; }
|
||||
public string? ServingSize { get; set; }
|
||||
public string? MealItems { get; set; } // JSON: {"Chicken": {"qountity" : 10pc, "Kal" : "40kal"}, "Rice": "200g"}
|
||||
public string? PreparationNotes { get; set; }
|
||||
|
||||
public Guid FKMealPlanId { get; set; }
|
||||
[ForeignKey("FKMealPlanId")]
|
||||
public virtual MealPlan? MealPlan { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class MealCategory
|
||||
{
|
||||
[Key]
|
||||
public Guid MealCategoryId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
public virtual ICollection<MealPlan> MealPlans { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class MealPlan
|
||||
{
|
||||
[Key]
|
||||
public Guid MealPlanId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsTemplate { get; set; } = false;
|
||||
|
||||
public Guid FKMealCategoryId { get; set; }
|
||||
[ForeignKey("FKMealCategoryId")]
|
||||
public virtual MealCategory MealCategory { get; set; }
|
||||
|
||||
public Guid? FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client? Client { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public virtual ICollection<Meal> MealItems { get; set; } = new List<Meal>();
|
||||
public virtual ICollection<CustomerMeal> CustomerMeals { get; set; } = new List<CustomerMeal>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
// Global reference table (not per-tenant) driving the gym-owner dashboard's sidebar.
|
||||
// Key doubles as the permission key used by GymRolePermission and RequiresPermissionAttribute —
|
||||
// this is a coarse, one-permission-per-nav-section system, so a separate PermissionKey
|
||||
// column would just be a redundant mirror of Key.
|
||||
public class NavItem
|
||||
{
|
||||
[Key]
|
||||
public string Key { get; set; } = string.Empty;
|
||||
|
||||
public string Label { get; set; } = string.Empty;
|
||||
public string? ShortLabel { get; set; }
|
||||
public string Href { get; set; } = string.Empty;
|
||||
public string Icon { get; set; } = string.Empty;
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
// Rows like "settings" are never grantable to staff, regardless of role — the owner
|
||||
// always sees them, staff never does.
|
||||
public bool IsOwnerOnly { get; set; } = false;
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
[Index(nameof(Code))]
|
||||
public class PairingCode
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
public string Code { get; set; }
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public Client Client { get; set; }
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
public bool IsUsed { get; set; } = false;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Fitness_core_BE.Models.Enums;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class PaymentTransaction
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid AttemptId { get; set; }
|
||||
|
||||
public DateTime? PayDate { get; set; }
|
||||
public string PayStatus { get; set; }
|
||||
public decimal PayAmount { get; set; }
|
||||
public string? PayNote { get; set; }
|
||||
|
||||
public Guid? FK_CardId { get; set; }
|
||||
[ForeignKey("FK_CardId")]
|
||||
public virtual CustomerCards? CustomerCard { get; set; }
|
||||
|
||||
public Guid? FK_CustomerInvoiceId { get; set; }
|
||||
[ForeignKey("FK_CustomerInvoiceId")]
|
||||
public virtual CustomerInvoice? CustomerInvoice { get; set; }
|
||||
|
||||
public Guid? FK_ClientInvoiceId { get; set; }
|
||||
[ForeignKey("FK_ClientInvoiceId")]
|
||||
public virtual ClientInvoice? ClientInvoice { get; set; }
|
||||
|
||||
public Guid? FK_ClientId { get; set; }
|
||||
[ForeignKey("FK_ClientId")]
|
||||
public virtual Client? Client { get; set; }
|
||||
|
||||
public Guid? FK_CustomerId { get; set; }
|
||||
[ForeignKey("FK_CustomerId")]
|
||||
public virtual Customer? Customer { get; set; }
|
||||
|
||||
public BeneficiaryType? BeneficiaryType { get; set; }
|
||||
public string? RefType { get; set; } = "installment";
|
||||
// How a manually-recorded (counter) payment was actually taken -- "Cash" | "Bank".
|
||||
// Left null for gateway/auto-charge attempts, where the method is implied elsewhere.
|
||||
public string? PaymentMethod { get; set; }
|
||||
public string? BankRefNo { get; set; }
|
||||
public string? PaymentReferenceId { get; set; }
|
||||
public bool? IsActive { get; set; } = true;
|
||||
public bool? IsApproved { get; set; } = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Fitness_core_BE.Models.Enums;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class PayoutBatch
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client Client { get; set; }
|
||||
|
||||
public DateOnly ScheduledDate { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public PayoutBatchStatus Status { get; set; } = PayoutBatchStatus.Pending;
|
||||
public DateTime? ProcessedAt { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>A physical access zone within the club (e.g. "Pool Area", "Indoor Gym"),
|
||||
/// each fitted with its own fingerprint/face terminal. CustomerPlan grants access to
|
||||
/// one or more Places via CustomerPlanPlace; AccessDevice.FKPlaceId says which Place a
|
||||
/// terminal sits in. Deliberately separate from Facility, which models capacity-limited
|
||||
/// bookable sessions (pool/court/class bookings) -- Places are about which physical
|
||||
/// machine a member's access profile should reach, not about booking a time slot.</summary>
|
||||
public class Place
|
||||
{
|
||||
[Key]
|
||||
public Guid PlaceId { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey(nameof(FKClientId))]
|
||||
public Client Client { get; set; }
|
||||
|
||||
[MaxLength(128)]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[MaxLength(500)]
|
||||
public string? Description { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Fitness_core_BE.Models.Receipt
|
||||
{
|
||||
/// <summary>Flat data used to render a "GYM OS" receipt PDF and its email/SMS notifications.</summary>
|
||||
public class ReceiptModel
|
||||
{
|
||||
public Guid ClientId { get; set; }
|
||||
public string ReceiptNo { get; set; } = "";
|
||||
public DateTime Date { get; set; } = DateTime.UtcNow;
|
||||
public string GymName { get; set; } = "GYM OS";
|
||||
public string MemberName { get; set; } = "Member";
|
||||
public string? MemberPhone { get; set; }
|
||||
public string? MemberEmail { get; set; }
|
||||
public string PlanName { get; set; } = "Membership";
|
||||
public decimal AmountPaid { get; set; }
|
||||
public decimal InvoiceAmount { get; set; }
|
||||
public decimal Balance { get; set; }
|
||||
public string Currency { get; set; } = "LKR";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class Staff
|
||||
{
|
||||
[Key]
|
||||
public Guid StaffId { get; set; }
|
||||
|
||||
public Guid FKUserId { get; set; }
|
||||
[ForeignKey("FKUserId")]
|
||||
public virtual User User { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client Client { get; set; }
|
||||
|
||||
// Null = no role assigned yet = zero permissions (see PermissionService).
|
||||
public Guid? FKGymRoleId { get; set; }
|
||||
[ForeignKey("FKGymRoleId")]
|
||||
public virtual GymRole? GymRole { get; set; }
|
||||
|
||||
public string? StaffPosition { get; set; }
|
||||
public string? Department { get; set; }
|
||||
public DateTime HireDate { get; set; } = DateTime.UtcNow;
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class StaffPayRate
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid FKStaffId { get; set; }
|
||||
[ForeignKey("FKStaffId")]
|
||||
public virtual Staff Staff { get; set; }
|
||||
|
||||
public string RateType { get; set; } = "Monthly"; // "Monthly" | "Hourly"
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
public DateTime EffectiveFrom { get; set; } = DateTime.UtcNow;
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class StaffPayment
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid FKStaffId { get; set; }
|
||||
[ForeignKey("FKStaffId")]
|
||||
public virtual Staff Staff { get; set; }
|
||||
|
||||
public decimal Amount { get; set; }
|
||||
public string? Note { get; set; }
|
||||
public DateTime PaidAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// The gym-owner (Client.FKUserId) or staff user who recorded this payment.
|
||||
public Guid FKPaidByUserId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// One scheduled part of a subscription's price (mostly-equal split, e.g. a
|
||||
/// Rs. 6000 six-month plan → 6 monthly rows of Rs. 1000; the last row absorbs
|
||||
/// rounding). Payments are allocated to installments in InstallmentNo order.
|
||||
/// InstallmentCount = 1 plans get a single row due at onboarding.
|
||||
/// </summary>
|
||||
public class SubscriptionInstallment
|
||||
{
|
||||
[Key]
|
||||
public Guid SubscriptionInstallmentId { get; set; }
|
||||
|
||||
public Guid FKSubscriptionId { get; set; }
|
||||
[ForeignKey("FKSubscriptionId")]
|
||||
public virtual CustomerSubscription Subscription { get; set; }
|
||||
|
||||
/// <summary>1-based position in the schedule.</summary>
|
||||
public int InstallmentNo { get; set; }
|
||||
|
||||
public DateTime DueDate { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public decimal PaidAmount { get; set; } = 0m;
|
||||
|
||||
/// <summary>"Pending" | "Partial" | "Paid".</summary>
|
||||
public string Status { get; set; } = "Pending";
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Links a customer to a subscription. Every subscription has one IsPrimary row
|
||||
/// (mirroring CustomerSubscription.FKCustomerId); group plans add more rows up to
|
||||
/// the plan's MemberLimit. The primary row is never removed.
|
||||
/// </summary>
|
||||
public class SubscriptionMember
|
||||
{
|
||||
[Key]
|
||||
public Guid SubscriptionMemberId { get; set; }
|
||||
|
||||
public Guid FKSubscriptionId { get; set; }
|
||||
[ForeignKey("FKSubscriptionId")]
|
||||
public virtual CustomerSubscription Subscription { get; set; }
|
||||
|
||||
// Tenant-bucket entity: this FK holds a TenantCustomerLinkId (not the central
|
||||
// Customer.CusId directly) -- see Models/TenantCustomerLink.cs.
|
||||
public Guid FKCustomerId { get; set; }
|
||||
[ForeignKey("FKCustomerId")]
|
||||
public virtual TenantCustomerLink TenantCustomerLink { get; set; }
|
||||
|
||||
public bool IsPrimary { get; set; }
|
||||
public DateTime JoinedAt { get; set; } = DateTime.UtcNow;
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the member is currently *using* this plan. Unassigning clears this and
|
||||
/// leaves IsActive alone, so the member keeps their row in the gym's member list
|
||||
/// (and their access/device enrollment) and the same button can re-assign them.
|
||||
/// IsActive stays reserved for a real removal, which does hide the row.
|
||||
/// </summary>
|
||||
public bool IsPlanAssigned { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
// Tenant-DB only, one row per customer this gym has ever enrolled. Every tenant-local
|
||||
// table that used to FK straight to the central Customer.CusId keeps doing so, unchanged
|
||||
// -- TenantCustomerLinkId IS the central Customer.CusId (same GUID value), not a new
|
||||
// surrogate key. That's deliberate: a fresh surrogate key would mean "customerId" holds
|
||||
// a different value depending on whether it's about to query a central or a tenant table,
|
||||
// which is exactly the kind of silent, hard-to-spot bug this split needs to avoid. EF/MySQL
|
||||
// still can't enforce this as a real cross-schema FK constraint -- it's just data -- but the
|
||||
// ID space stays uniform everywhere, and this row is also the local anchor tenant tables
|
||||
// join against for cached display fields without a central round-trip.
|
||||
public class TenantCustomerLink
|
||||
{
|
||||
[Key]
|
||||
public Guid TenantCustomerLinkId { get; set; } // == central Customer.CusId
|
||||
|
||||
// = the shared login User id (same GUID in Auth's Users table), cached.
|
||||
public Guid CentralUserId { get; set; }
|
||||
|
||||
public string? DisplayName { get; set; }
|
||||
public string? PrimaryPhoneCached { get; set; }
|
||||
public string? EmailCached { get; set; }
|
||||
|
||||
// What a client-initiated delete flips. Central Customer.IsActive is never touched
|
||||
// by a client-initiated delete -- see CustomerRepository.DeactivateCustomerForClient.
|
||||
public bool IsActiveAtThisClient { get; set; } = true;
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Fitness_core_BE.Models.Enums;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class Ticket
|
||||
{
|
||||
[Key]
|
||||
public Guid TicketId { get; set; }
|
||||
public string MainReason { get; set; }
|
||||
|
||||
public Guid? FKTicketCategoryId { get; set; }
|
||||
[ForeignKey("FKTicketCategoryId")]
|
||||
public virtual TicketCategory TicketCategory { get; set; }
|
||||
public Guid? FKAssignerId { get; set; }
|
||||
[ForeignKey("FKAssignerId")]
|
||||
public virtual Admin Admin { get; set; }
|
||||
public Guid FKUserId { get; set; }
|
||||
[ForeignKey("FKUserId")]
|
||||
public virtual User User { get; set; }
|
||||
public TicketStatusEnum TicketStatus { get; set; } = TicketStatusEnum.Pending;
|
||||
public int? UserRating { get; set; } = null;
|
||||
public string? UserFeedback { get; set; } = null;
|
||||
public DateTime? FeedbackAt { get; set; } = null;
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
public virtual ICollection<TicketMessage> Messages { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Fitness_core_BE.Models.Enums;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class TicketCategory
|
||||
{
|
||||
[Key]
|
||||
public Guid TicketCategoryId { get; set; }
|
||||
public string TicketCategoryName { get; set; }
|
||||
public PriorityLevel PriorityLevel { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
public virtual ICollection<Ticket> Tickets { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class TicketMessage
|
||||
{
|
||||
[Key]
|
||||
public Guid MessageId { get; set; }
|
||||
public bool IsSentByAdmin { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string? AttachmentURL { get; set; }
|
||||
public int? FKDocumentId { get; set; }
|
||||
[ForeignKey("FKDocumentId")]
|
||||
public virtual UserDocument UserDocument { get; set; }
|
||||
public Guid FKTicketId { get; set; }
|
||||
[ForeignKey("FKTicketId")]
|
||||
public virtual Ticket Ticket { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Fitness_core_BE.Models.Enums;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class TicketStatus
|
||||
{
|
||||
[Key]
|
||||
public Guid TicketStatusId { get; set; }
|
||||
public string StatusName { get; set; }
|
||||
public TicketStatusEnum StatusCode { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
public virtual ICollection<Ticket> Tickets { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>A prepaid time balance on an RFID card, deducted by actual use via a tap-in/
|
||||
/// tap-out swipe pattern (a 1-hour visit deducts 60 minutes, a 15-minute visit deducts 15 --
|
||||
/// see ActiveSessionStartedAt) — separate from Booking's per-session OTP flow. Can belong to
|
||||
/// a member (FKCustomerId set, e.g. an add-on to their plan) or be sold standalone to a
|
||||
/// walk-in against just the RFID tag.</summary>
|
||||
public class TimeBankCard
|
||||
{
|
||||
[Key]
|
||||
public Guid TimeBankCardId { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey(nameof(FKClientId))]
|
||||
public Client Client { get; set; }
|
||||
|
||||
// Tenant-bucket entity: holds a TenantCustomerLinkId, not the central Customer.CusId
|
||||
// directly -- see Models/TenantCustomerLink.cs. Null for a card sold to a walk-in with
|
||||
// no membership at all.
|
||||
public Guid? FKCustomerId { get; set; }
|
||||
[ForeignKey(nameof(FKCustomerId))]
|
||||
public TenantCustomerLink? TenantCustomerLink { get; set; }
|
||||
|
||||
[MaxLength(64)]
|
||||
public string RfidTagId { get; set; } = string.Empty;
|
||||
|
||||
public int TotalMinutesPurchased { get; set; }
|
||||
public int MinutesRemaining { get; set; }
|
||||
|
||||
public decimal AmountPaid { get; set; } = 0m;
|
||||
public Guid? FKPaymentTransactionId { get; set; }
|
||||
[ForeignKey(nameof(FKPaymentTransactionId))]
|
||||
public PaymentTransaction? PaymentTransaction { get; set; }
|
||||
|
||||
public DateTime PurchasedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>Null = balance never expires (spend it whenever); business can still set one.</summary>
|
||||
public DateTime? ExpiresAt { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Tap-in/tap-out state: null = not currently on the floor anywhere. A swipe with this
|
||||
// null starts a session (tap-in, no deduction yet); a swipe with this set ends it
|
||||
// (tap-out) and deducts the elapsed minutes. One open session per card at a time --
|
||||
// a second tap-in without a matching tap-out first is rejected by the service, not
|
||||
// silently overwritten, so a lost/forgotten tap-out surfaces instead of losing time.
|
||||
public DateTime? ActiveSessionStartedAt { get; set; }
|
||||
public Guid? ActiveSessionFacilityId { get; set; }
|
||||
[ForeignKey(nameof(ActiveSessionFacilityId))]
|
||||
public Facility? ActiveSessionFacility { get; set; }
|
||||
|
||||
public virtual ICollection<TimeBankCardUsageLog> UsageLogs { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
/// <summary>One deduction event against a TimeBankCard's balance — an append-only ledger so
|
||||
/// MinutesRemaining is always auditable/reconstructable, the same reasoning as LedgerEntry
|
||||
/// for money.</summary>
|
||||
public class TimeBankCardUsageLog
|
||||
{
|
||||
[Key]
|
||||
public Guid TimeBankCardUsageLogId { get; set; }
|
||||
|
||||
public Guid FKTimeBankCardId { get; set; }
|
||||
[ForeignKey(nameof(FKTimeBankCardId))]
|
||||
public TimeBankCard TimeBankCard { get; set; }
|
||||
|
||||
public Guid FKClientId { get; set; }
|
||||
[ForeignKey(nameof(FKClientId))]
|
||||
public Client Client { get; set; }
|
||||
|
||||
// Which facility this swipe/use was against -- not a FacilitySession, since a time-bank
|
||||
// card is spent on open-ended usage (e.g. "swim for however long"), not a fixed
|
||||
// pre-booked slot the way Booking is.
|
||||
public Guid FKFacilityId { get; set; }
|
||||
[ForeignKey(nameof(FKFacilityId))]
|
||||
public Facility Facility { get; set; }
|
||||
|
||||
public int MinutesDeducted { get; set; }
|
||||
public int MinutesRemainingAfter { get; set; }
|
||||
|
||||
public DateTime UsedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
// A fingerprint enrolled directly on the physical access-control terminal (by gym
|
||||
// staff, outside our system) that has no matching CustomerAccess yet. Surfaced by
|
||||
// attendance ingestion (AgentService.IngestAttendanceAsync) whenever an incoming
|
||||
// employeeNo/DeviceUserId has no CustomerAccess mapping for that device, so the
|
||||
// client portal can offer it in a dropdown to link to a system Customer.
|
||||
public class UnlinkedDeviceEnrollment
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid FKDeviceId { get; set; }
|
||||
[ForeignKey("FKDeviceId")]
|
||||
public virtual AccessDevice AccessDevice { get; set; }
|
||||
|
||||
public int DeviceUserId { get; set; }
|
||||
[MaxLength(32)]
|
||||
public string DeviceEmployeeNo { get; set; } = string.Empty;
|
||||
public string? DeviceReportedName { get; set; }
|
||||
[MaxLength(20)]
|
||||
public string? DeviceUserType { get; set; }
|
||||
public int FingerprintCount { get; set; }
|
||||
|
||||
public DateTime DiscoveredAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime LastSeenAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? LinkedAt { get; set; }
|
||||
|
||||
public long? FKAccessProfileId { get; set; }
|
||||
[ForeignKey(nameof(FKAccessProfileId))]
|
||||
public virtual AccessProfile? AccessProfile { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class User
|
||||
{
|
||||
[Key]
|
||||
public Guid UserId { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? PhoneNumber { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
public virtual ICollection<Ticket> Tickets { get; set; }
|
||||
public virtual ICollection<UserDocument> Documents { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class UserDocument
|
||||
{
|
||||
[Key]
|
||||
public int DocumentId { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public int FkDocumentTypeId { get; set; }
|
||||
[ForeignKey("FkDocumentTypeId")]
|
||||
public DocumentTypes DocumentType { get; set; }
|
||||
public string DocumentUrl { get; set; }
|
||||
public string DocumentName { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime Created_At { get; set; } = DateTime.Now;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Fitness_core_BE.Models.Enums;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class WalletLedgerEntry
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid FKWalletId { get; set; }
|
||||
[ForeignKey("FKWalletId")]
|
||||
public virtual ClientWallet Wallet { get; set; }
|
||||
|
||||
public WalletEntryType EntryType { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
public Guid? RelatedCustomerInvoiceId { get; set; }
|
||||
[ForeignKey("RelatedCustomerInvoiceId")]
|
||||
public virtual CustomerInvoice? RelatedCustomerInvoice { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class WorkoutCategory
|
||||
{
|
||||
[Key]
|
||||
public Guid WorkoutCategoryId { get; set; }
|
||||
public string WorkoutCategoryName { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
public virtual ICollection<WorkoutPlan> WorkoutPlans { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class WorkoutExercise
|
||||
{
|
||||
[Key]
|
||||
public Guid WorkoutExerciseId { get; set; }
|
||||
public Guid FKWorkoutItemId { get; set; }
|
||||
[ForeignKey("FKWorkoutItemId")]
|
||||
public virtual WorkoutItem? WorkoutItem { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? MuscleGroup { get; set; }
|
||||
public int? Sets { get; set; }
|
||||
public string? Reps { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? BreathIn { get; set; }
|
||||
public string? BreathOut { get; set; }
|
||||
public string? VideoUrl { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class WorkoutItem
|
||||
{
|
||||
[Key]
|
||||
public Guid WorkoutItemId { get; set; }
|
||||
public string ExerciseDay { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public string? Exercises { get; set; } // JSON: {"Push-ups": "3 sets x 10 reps", "Squats": "4 sets x 15 reps"}
|
||||
public string? HowToDo { get; set; } // Structured JSON
|
||||
public string? DoURL { get; set; }
|
||||
public Guid FKWorkoutPlanId { get; set; }
|
||||
[ForeignKey("FKWorkoutPlanId")]
|
||||
public virtual WorkoutPlan? WorkoutPlan { get; set; }
|
||||
public string? Duration { get; set; }
|
||||
public string? WormUp { get; set; }
|
||||
public string? Cooldown { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public virtual ICollection<WorkoutExercise> WorkoutExercises { get; set; } = new List<WorkoutExercise>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Fitness_core_BE.Models
|
||||
{
|
||||
public class WorkoutPlan
|
||||
{
|
||||
[Key]
|
||||
public Guid WorkoutPlanId { get; set; }
|
||||
public string WorkoutPlanName { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsTemplate { get; set; } = false;
|
||||
|
||||
public Guid FKWorkoutCategoryId { get; set; }
|
||||
[ForeignKey("FKWorkoutCategoryId")]
|
||||
public virtual WorkoutCategory WorkoutCategory { get; set; }
|
||||
|
||||
public Guid? FKClientId { get; set; }
|
||||
[ForeignKey("FKClientId")]
|
||||
public virtual Client? Client { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public virtual ICollection<WorkoutItem> WorkoutItems { get; set; } = new List<WorkoutItem>();
|
||||
public virtual ICollection<CustomerWorkout> CustomerWorkouts { get; set; } = new List<CustomerWorkout>();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user