diff --git a/AMREZ.EOP.Application/UseCases/Authentications/RegisterUseCase.cs b/AMREZ.EOP.Application/UseCases/Authentications/RegisterUseCase.cs index 034f6c6..622c63e 100644 --- a/AMREZ.EOP.Application/UseCases/Authentications/RegisterUseCase.cs +++ b/AMREZ.EOP.Application/UseCases/Authentications/RegisterUseCase.cs @@ -16,6 +16,7 @@ public sealed class RegisterUseCase : IRegisterUseCase private readonly ITenantResolver _tenantResolver; private readonly IUnitOfWork _uow; private readonly IUserRepository _users; + private readonly ITenantRepository _tenants; private readonly IPasswordHasher _hasher; private readonly IHttpContextAccessor _http; @@ -23,10 +24,16 @@ public sealed class RegisterUseCase : IRegisterUseCase ITenantResolver resolver, IUnitOfWork uow, IUserRepository users, + ITenantRepository tenants, IPasswordHasher hasher, IHttpContextAccessor http) { - _tenantResolver = resolver; _uow = uow; _users = users; _hasher = hasher; _http = http; + _tenantResolver = resolver; + _uow = uow; + _users = users; + _tenants = tenants; + _hasher = hasher; + _http = http; } public async Task ExecuteAsync(RegisterRequest request, CancellationToken ct = default) @@ -48,8 +55,11 @@ public sealed class RegisterUseCase : IRegisterUseCase var hash = _hasher.Hash(request.Password); + var tn = await _tenants.GetAsync(request.Tenant); + var user = new User { + TenantId = tn.TenantId, PasswordHash = hash, IsActive = true, }; diff --git a/AMREZ.EOP.Domain/Entities/Tenancy/TenantConfig.cs b/AMREZ.EOP.Domain/Entities/Tenancy/TenantConfig.cs index 06d2c9c..330884a 100644 --- a/AMREZ.EOP.Domain/Entities/Tenancy/TenantConfig.cs +++ b/AMREZ.EOP.Domain/Entities/Tenancy/TenantConfig.cs @@ -4,6 +4,7 @@ namespace AMREZ.EOP.Domain.Entities.Tenancy; public sealed class TenantConfig { + public Guid TenantId { get; set; } = Guid.NewGuid(); public string TenantKey { get; set; } = default!; public string? Schema { get; set; } public string? ConnectionString { get; set; } diff --git a/AMREZ.EOP.Infrastructures/Migrations/20250930101327_Add_Tenant_Guid.Designer.cs b/AMREZ.EOP.Infrastructures/Migrations/20250930101327_Add_Tenant_Guid.Designer.cs new file mode 100644 index 0000000..430a85d --- /dev/null +++ b/AMREZ.EOP.Infrastructures/Migrations/20250930101327_Add_Tenant_Guid.Designer.cs @@ -0,0 +1,1539 @@ +// +using System; +using AMREZ.EOP.Infrastructures.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace AMREZ.EOP.Infrastructures.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20250930101327_Add_Tenant_Guid")] + partial class Add_Tenant_Guid + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("TenantId", "Code") + .IsUnique(); + + b.ToTable("permissions", null, t => + { + t.HasCheckConstraint("ck_permissions_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("TenantId", "Code") + .IsUnique(); + + b.ToTable("roles", null, t => + { + t.HasCheckConstraint("ck_roles_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.RolePermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("PermissionId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.HasKey("Id"); + + b.HasIndex("PermissionId"); + + b.HasIndex("RoleId"); + + b.HasIndex("TenantId"); + + b.HasIndex("TenantId", "RoleId", "PermissionId") + .IsUnique(); + + b.ToTable("role_permissions", null, t => + { + t.HasCheckConstraint("ck_role_permissions_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("LockoutEndUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("MfaEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.ToTable("users", null, t => + { + t.HasCheckConstraint("ck_users_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.UserExternalAccount", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("LinkedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Provider") + .HasColumnType("integer"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserId"); + + b.HasIndex("TenantId", "Provider", "Subject") + .IsUnique(); + + b.ToTable("user_external_accounts", null, t => + { + t.HasCheckConstraint("ck_user_external_accounts_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.UserIdentity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("Identifier") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("IsPrimary") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("VerifiedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserId"); + + b.HasIndex("TenantId", "Type", "Identifier") + .IsUnique(); + + b.HasIndex("TenantId", "UserId", "Type", "IsPrimary") + .HasDatabaseName("ix_user_identity_primary_per_type"); + + b.ToTable("user_identities", null, t => + { + t.HasCheckConstraint("ck_user_identities_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.UserMfaFactor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CredentialId") + .HasColumnType("text"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("Label") + .HasColumnType("text"); + + b.Property("LastUsedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("PhoneE164") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("Secret") + .HasColumnType("text"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserId"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("user_mfa_factors", null, t => + { + t.HasCheckConstraint("ck_user_mfa_factors_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.UserPasswordHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChangedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserId"); + + b.HasIndex("TenantId", "UserId", "ChangedAt"); + + b.ToTable("user_password_histories", null, t => + { + t.HasCheckConstraint("ck_user_password_histories_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.UserRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserId"); + + b.HasIndex("TenantId", "UserId", "RoleId") + .IsUnique(); + + b.ToTable("user_roles", null, t => + { + t.HasCheckConstraint("ck_user_roles_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.UserSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IpAddress") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("IssuedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("RefreshTokenHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.Property("UserAgent") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserId"); + + b.HasIndex("TenantId", "DeviceId"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("user_sessions", null, t => + { + t.HasCheckConstraint("ck_user_sessions_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.Department", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ParentDepartmentId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.HasKey("Id"); + + b.HasIndex("ParentDepartmentId"); + + b.HasIndex("TenantId"); + + b.HasIndex("TenantId", "Code") + .IsUnique(); + + b.ToTable("departments", null, t => + { + t.HasCheckConstraint("ck_departments_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.EmergencyContact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("IsPrimary") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("Relationship") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.Property("UserProfileId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserProfileId"); + + b.HasIndex("TenantId", "UserProfileId", "IsPrimary"); + + b.ToTable("emergency_contacts", null, t => + { + t.HasCheckConstraint("ck_emergency_contacts_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.EmployeeAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("City") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("IsPrimary") + .HasColumnType("boolean"); + + b.Property("Line1") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Line2") + .HasColumnType("text"); + + b.Property("PostalCode") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("State") + .HasColumnType("text"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.Property("UserProfileId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserProfileId"); + + b.HasIndex("TenantId", "UserProfileId", "IsPrimary"); + + b.ToTable("employee_addresses", null, t => + { + t.HasCheckConstraint("ck_employee_addresses_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.EmployeeBankAccount", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountHolder") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("AccountNumber") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("BankName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Branch") + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("IsPrimary") + .HasColumnType("boolean"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.Property("UserProfileId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserProfileId"); + + b.HasIndex("TenantId", "UserProfileId", "IsPrimary"); + + b.ToTable("employee_bank_accounts", null, t => + { + t.HasCheckConstraint("ck_employee_bank_accounts_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.Employment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("DepartmentId") + .HasColumnType("uuid"); + + b.Property("EmploymentType") + .HasColumnType("integer"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("ManagerUserId") + .HasColumnType("uuid"); + + b.Property("PositionId") + .HasColumnType("uuid"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.Property("UserProfileId") + .HasColumnType("uuid"); + + b.Property("WorkEmail") + .HasColumnType("text"); + + b.Property("WorkPhone") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DepartmentId"); + + b.HasIndex("PositionId"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserProfileId"); + + b.HasIndex("TenantId", "UserProfileId", "StartDate"); + + b.ToTable("employments", null, t => + { + t.HasCheckConstraint("ck_employments_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.Position", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("TenantId", "Code") + .IsUnique(); + + b.ToTable("positions", null, t => + { + t.HasCheckConstraint("ck_positions_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.UserProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("DateOfBirth") + .HasColumnType("timestamp with time zone"); + + b.Property("DepartmentId") + .HasColumnType("uuid"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Gender") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_deleted"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("MiddleName") + .HasColumnType("text"); + + b.Property("Nickname") + .HasColumnType("text"); + + b.Property("PositionId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tenant_id") + .HasDefaultValueSql("nullif(current_setting('app.tenant_id', true),'')::uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpdatedBy") + .HasColumnType("text") + .HasColumnName("updated_by"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DepartmentId"); + + b.HasIndex("PositionId"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("TenantId", "UserId") + .IsUnique(); + + b.ToTable("user_profiles", null, t => + { + t.HasCheckConstraint("ck_user_profiles_tenant_not_null", "tenant_id is not null"); + }); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig", b => + { + b.Property("TenantKey") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ConnectionString") + .HasColumnType("text"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Mode") + .HasColumnType("integer"); + + b.Property("Schema") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.Property("UpdatedAtUtc") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at_utc") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("TenantKey"); + + b.HasIndex("IsActive"); + + b.ToTable("tenants", "meta"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Tenancy.TenantDomain", b => + { + b.Property("Domain") + .HasMaxLength(253) + .HasColumnType("character varying(253)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("IsPlatformBaseDomain") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("TenantKey") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UpdatedAtUtc") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at_utc") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Domain"); + + b.HasIndex("IsActive"); + + b.HasIndex("IsPlatformBaseDomain"); + + b.HasIndex("TenantKey"); + + b.ToTable("tenant_domains", "meta"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.RolePermission", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.Authentications.Permission", "Permission") + .WithMany("RolePermissions") + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AMREZ.EOP.Domain.Entities.Authentications.Role", "Role") + .WithMany("RolePermissions") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.UserExternalAccount", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.Authentications.User", "User") + .WithMany("ExternalAccounts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.UserIdentity", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.Authentications.User", "User") + .WithMany("Identities") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.UserMfaFactor", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.Authentications.User", "User") + .WithMany("MfaFactors") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.UserPasswordHistory", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.Authentications.User", "User") + .WithMany("PasswordHistories") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.UserRole", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.Authentications.Role", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AMREZ.EOP.Domain.Entities.Authentications.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.UserSession", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.Authentications.User", "User") + .WithMany("Sessions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.Department", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.HumanResources.Department", "Parent") + .WithMany("Children") + .HasForeignKey("ParentDepartmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.EmergencyContact", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.HumanResources.UserProfile", "UserProfile") + .WithMany("EmergencyContacts") + .HasForeignKey("UserProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("UserProfile"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.EmployeeAddress", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.HumanResources.UserProfile", "UserProfile") + .WithMany("Addresses") + .HasForeignKey("UserProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("UserProfile"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.EmployeeBankAccount", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.HumanResources.UserProfile", "UserProfile") + .WithMany("BankAccounts") + .HasForeignKey("UserProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("UserProfile"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.Employment", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.HumanResources.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("AMREZ.EOP.Domain.Entities.HumanResources.Position", "Position") + .WithMany() + .HasForeignKey("PositionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("AMREZ.EOP.Domain.Entities.HumanResources.UserProfile", "UserProfile") + .WithMany("Employments") + .HasForeignKey("UserProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Department"); + + b.Navigation("Position"); + + b.Navigation("UserProfile"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.UserProfile", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.HumanResources.Department", null) + .WithMany("Profiles") + .HasForeignKey("DepartmentId"); + + b.HasOne("AMREZ.EOP.Domain.Entities.HumanResources.Position", null) + .WithMany("Profiles") + .HasForeignKey("PositionId"); + + b.HasOne("AMREZ.EOP.Domain.Entities.Authentications.User", "User") + .WithOne() + .HasForeignKey("AMREZ.EOP.Domain.Entities.HumanResources.UserProfile", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Tenancy.TenantDomain", b => + { + b.HasOne("AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig", null) + .WithMany() + .HasForeignKey("TenantKey") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.Permission", b => + { + b.Navigation("RolePermissions"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.Role", b => + { + b.Navigation("RolePermissions"); + + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.Authentications.User", b => + { + b.Navigation("ExternalAccounts"); + + b.Navigation("Identities"); + + b.Navigation("MfaFactors"); + + b.Navigation("PasswordHistories"); + + b.Navigation("Sessions"); + + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.Department", b => + { + b.Navigation("Children"); + + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.Position", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("AMREZ.EOP.Domain.Entities.HumanResources.UserProfile", b => + { + b.Navigation("Addresses"); + + b.Navigation("BankAccounts"); + + b.Navigation("EmergencyContacts"); + + b.Navigation("Employments"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AMREZ.EOP.Infrastructures/Migrations/20250930101327_Add_Tenant_Guid.cs b/AMREZ.EOP.Infrastructures/Migrations/20250930101327_Add_Tenant_Guid.cs new file mode 100644 index 0000000..998c697 --- /dev/null +++ b/AMREZ.EOP.Infrastructures/Migrations/20250930101327_Add_Tenant_Guid.cs @@ -0,0 +1,32 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AMREZ.EOP.Infrastructures.Migrations +{ + /// + public partial class Add_Tenant_Guid : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "TenantId", + schema: "meta", + table: "tenants", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "TenantId", + schema: "meta", + table: "tenants"); + } + } +} diff --git a/AMREZ.EOP.Infrastructures/Migrations/AppDbContextModelSnapshot.cs b/AMREZ.EOP.Infrastructures/Migrations/AppDbContextModelSnapshot.cs index b2251a8..6f4e0c3 100644 --- a/AMREZ.EOP.Infrastructures/Migrations/AppDbContextModelSnapshot.cs +++ b/AMREZ.EOP.Infrastructures/Migrations/AppDbContextModelSnapshot.cs @@ -1240,6 +1240,9 @@ namespace AMREZ.EOP.Infrastructures.Migrations .HasMaxLength(128) .HasColumnType("character varying(128)"); + b.Property("TenantId") + .HasColumnType("uuid"); + b.Property("UpdatedAtUtc") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone")