User Tanımına Departman ve Job Position eklendi

This commit is contained in:
Sedat ÖZTÜRK 2026-05-04 17:35:18 +03:00
parent 183674afb9
commit 31f632d16a
24 changed files with 586 additions and 370 deletions

View file

@ -0,0 +1,13 @@
using System;
namespace Sozsoft.Platform.Identity.Dto;
public class AssignedDepartmentViewModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public bool IsAssigned { get; set; }
}

View file

@ -0,0 +1,15 @@
using System;
namespace Sozsoft.Platform.Identity.Dto;
public class AssignedJobPoisitionViewModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid DepartmentId { get; set; }
public bool IsAssigned { get; set; }
}

View file

@ -42,6 +42,10 @@ public class UserInfoViewModel: ExtensibleObject
public AssignedWorkHourViewModel[] WorkHours { get; set; }
public AssignedDepartmentViewModel[] Departments { get; set; }
public AssignedJobPoisitionViewModel[] JobPositions { get; set; }
public bool LockUser { get; set; }
public DateTimeOffset? LastPasswordChangeTime { get; set; }
@ -54,6 +58,6 @@ public class UserInfoViewModel: ExtensibleObject
public DateTimeOffset? CreationTime { get; set; }
public DateTimeOffset? LastModificationTime { get; set; }
public string WorkHour { get; set; }
public string DepartmentId { get; set; }
public string JobPositionId { get; set; }
public Guid DepartmentId { get; set; }
public Guid JobPositionId { get; set; }
}

View file

@ -11,6 +11,8 @@ public class CreateUpdateUserInput
public string PhoneNumber { get; set; }
public string Password { get; set; }
public string WorkHour { get; set; }
public string DepartmentId { get; set; }
public string JobPositionId { get; set; }
public bool? IsActive { get; set; }
public bool? LockoutEnabled { get; set; }
public string[] RoleNames { get; set; }

View file

@ -213,9 +213,7 @@ public static class LookupQueryValues
$"\"Id\" AS \"Key\", " +
$"\"Name\" AS \"Name\" " +
$"FROM \"{TableNameResolver.GetFullTableName(nameof(TableNameEnum.Department))}\" " +
$"WHERE " +
$"(\"BranchId\" = @param0 OR @param0 IS NULL) " +
$"AND \"IsDeleted\" = 'false' " +
$"WHERE \"IsDeleted\" = 'false' " +
$"ORDER BY \"Name\";";
public static string JobPositionValues =
@ -224,8 +222,7 @@ public static class LookupQueryValues
$"\"Name\" AS \"Name\" " +
$"FROM \"{TableNameResolver.GetFullTableName(nameof(TableNameEnum.JobPosition))}\" " +
$"WHERE " +
$"(\"BranchId\" = @param0 OR @param0 IS NULL) " +
$"AND (\"DepartmentId\" = @param1 OR @param1 IS NULL) " +
$"(\"DepartmentId\" = @param0 OR @param0 IS NULL) " +
$"AND \"IsDeleted\" = 'false' " +
$"ORDER BY \"Name\";";
}

View file

@ -30,6 +30,8 @@ public class PlatformIdentityAppService : ApplicationService
public IGuidGenerator guidGenerator { get; }
public IdentityUserManager UserManager { get; set; }
public IRepository<WorkHour, Guid> workHourRepository { get; }
public IRepository<Department, Guid> departmentRepository { get; }
public IRepository<JobPosition, Guid> jobPositionRepository { get; }
public PlatformIdentityAppService(
IIdentityUserAppService identityUserAppService,
@ -41,6 +43,8 @@ public class PlatformIdentityAppService : ApplicationService
IRepository<BranchUsers, Guid> branchUsersRepository,
IRepository<IdentityClaimType, Guid> claimTypesRepository,
IRepository<WorkHour, Guid> workHourRepository,
IRepository<Department, Guid> departmentRepository,
IRepository<JobPosition, Guid> jobPositionRepository,
IGuidGenerator guidGenerator
)
{
@ -49,6 +53,8 @@ public class PlatformIdentityAppService : ApplicationService
this.identitySessionRepository = identitySessionRepository;
this.openIddictTokenManager = openIddictTokenManager;
this.workHourRepository = workHourRepository;
this.departmentRepository = departmentRepository;
this.jobPositionRepository = jobPositionRepository;
this.permissionRepository = permissionRepository;
this.branchRepository = branchRepository;
this.branchUsersRepository = branchUsersRepository;
@ -107,6 +113,23 @@ public class PlatformIdentityAppService : ApplicationService
IsAssigned = workHourList.Contains(workHour)
}).ToArray();
var departmentList = await departmentRepository.GetListAsync();
var departments = (await departmentRepository.GetListAsync()).Select(department => new AssignedDepartmentViewModel
{
Id = department.Id,
Name = department.Name,
IsAssigned = departmentList.Contains(department)
}).ToArray();
var jobPositionList = await jobPositionRepository.GetListAsync();
var jobPositions = (await jobPositionRepository.GetListAsync()).Select(jobPosition => new AssignedJobPoisitionViewModel
{
Id = jobPosition.Id,
Name = jobPosition.Name,
DepartmentId = jobPosition.DepartmentId,
IsAssigned = jobPositionList.Contains(jobPosition)
}).ToArray();
return new UserInfoViewModel()
{
Id = user.Id,
@ -115,6 +138,8 @@ public class PlatformIdentityAppService : ApplicationService
Surname = user.Surname,
Roles = roles,
Branches = branches,
Departments = departments,
JobPositions = jobPositions,
Claims = claims,
WorkHours = workHours,
Email = user.Email,
@ -143,6 +168,37 @@ public class PlatformIdentityAppService : ApplicationService
};
}
public async Task UpdatePermissionAsync(UserInfoViewModel UserInfo)
{
var roles = UserInfo.Roles.Where(r => r.IsAssigned).Select(r => r.Name).ToArray();
var user = await UserManager.GetByIdAsync(UserInfo.Id);
await UserManager.SetRolesAsync(user, roles);
//Braches bu kısımda güncelleniyor.
var existingBranches = await branchUsersRepository.GetListAsync(x => x.UserId == user.Id);
foreach (var item in existingBranches)
{
await branchUsersRepository.DeleteAsync(item);
}
// 2. Yeni atamaları ekle
var assignedBranchIds = UserInfo.Branches
.Where(b => b.IsAssigned)
.Select(b => b.Id)
.ToList();
foreach (var branchId in assignedBranchIds)
{
var branchUser = new BranchUsers
{
UserId = user.Id,
BranchId = branchId
};
await branchUsersRepository.InsertAsync(branchUser);
}
}
public async Task UpdateLockoutAsync(UserInfoViewModel UserInfo)
{
var user = await UserManager.GetByIdAsync(UserInfo.Id);
@ -181,10 +237,7 @@ public class PlatformIdentityAppService : ApplicationService
public async Task UpdateUserAsync(UserInfoViewModel UserInfo)
{
var roles = UserInfo.Roles.Where(r => r.IsAssigned).Select(r => r.Name).ToArray();
var user = await UserManager.GetByIdAsync(UserInfo.Id);
await UserManager.SetRolesAsync(user, roles);
user.Name = UserInfo.Name;
user.Surname = UserInfo.Surname;
@ -196,29 +249,6 @@ public class PlatformIdentityAppService : ApplicationService
user.SetJobPositionId(UserInfo.JobPositionId);
await UserManager.UpdateAsync(user);
//Braches bu kısımda güncelleniyor.
var existingBranches = await branchUsersRepository.GetListAsync(x => x.UserId == user.Id);
foreach (var item in existingBranches)
{
await branchUsersRepository.DeleteAsync(item);
}
// 2. Yeni atamaları ekle
var assignedBranchIds = UserInfo.Branches
.Where(b => b.IsAssigned)
.Select(b => b.Id)
.ToList();
foreach (var branchId in assignedBranchIds)
{
var branchUser = new BranchUsers
{
UserId = user.Id,
BranchId = branchId
};
await branchUsersRepository.InsertAsync(branchUser);
}
}
public async Task<List<PermissionDefinitionRecord>> GetPermissionList()
@ -243,11 +273,6 @@ public class PlatformIdentityAppService : ApplicationService
user.Claims.Remove(claim);
}
/// <summary>
/// Kullanıcıyı anlık olarak oturumdan atar:
/// 1. AbpSessions kaydını siler → concurrent lisans slotunu serbest bırakır.
/// 2. OpenIddict tokenlarını revoke eder → access token anında geçersiz olur.
/// </summary>
public async Task KickUserAsync(Guid userId)
{
using (CurrentTenant.Change(CurrentTenant.Id))

View file

@ -47,6 +47,8 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic
RoleNames = [], //input.Data.RoleNames,
};
user.SetProperty(PlatformConsts.AbpIdentity.User.WorkHour, input.Data.WorkHour);
user.SetProperty(PlatformConsts.AbpIdentity.User.DepartmentId, input.Data.DepartmentId);
user.SetProperty(PlatformConsts.AbpIdentity.User.JobPositionId, input.Data.JobPositionId);
await identityUserAppService.CreateAsync(user);
}
@ -79,6 +81,14 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic
{
user.SetProperty(PlatformConsts.AbpIdentity.User.WorkHour, input.Data.WorkHour);
}
if (input.Data.DepartmentId != null)
{
user.SetProperty(PlatformConsts.AbpIdentity.User.DepartmentId, input.Data.DepartmentId);
}
if (input.Data.JobPositionId != null)
{
user.SetProperty(PlatformConsts.AbpIdentity.User.JobPositionId, input.Data.JobPositionId);
}
await identityUserAppService.UpdateAsync(id, user);
}

View file

@ -2712,6 +2712,24 @@
"en": "User Information",
"tr": "Kullanıcı Bilgileri"
},
{
"resourceName": "Platform",
"key": "Abp.Identity.User.SavePermission",
"en": "Permissions saved.",
"tr": "Yetkiler kaydedildi."
},
{
"resourceName": "Platform",
"key": "Abp.Identity.User.SaveLockout",
"en": "Lockout settings saved.",
"tr": "Kilit ayarları kaydedildi."
},
{
"resourceName": "Platform",
"key": "Abp.Identity.User.Permissions",
"en": "User Permissions",
"tr": "Kullanıcı Yetkileri"
},
{
"resourceName": "Platform",
"key": "Abp.Identity.User.UserInformation.RoleManagement",

View file

@ -802,9 +802,11 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
new EditingFormItemDto { Order=2, DataField="Name", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=3, DataField="Surname", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=4, DataField="PhoneNumber", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=5, DataField="WorkHour", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxSelectBox },
new EditingFormItemDto { Order=6, DataField="Password", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=7, DataField="IsActive", ColSpan=1, EditorType2=EditorTypes.dxCheckBox },
new EditingFormItemDto { Order=5, DataField="WorkHour", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxSelectBox, EditorOptions = EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order=6, DataField="DepartmentId", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxSelectBox, EditorOptions = EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order=7, DataField="JobPositionId", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxSelectBox, EditorOptions = EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order=8, DataField="Password", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=9, DataField="IsActive", ColSpan=1, EditorType2=EditorTypes.dxCheckBox },
]}
}),
InsertServiceAddress = "list-form-dynamic-api/user-insert",
@ -1002,6 +1004,65 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Guid,
FieldName = "DepartmentId",
CaptionName = "App.Listform.ListformField.DepartmentId",
Width = 150,
ListOrderNo = 7,
LookupJson = JsonSerializer.Serialize(new LookupDto {
DataSourceType = UiLookupDataSourceTypeEnum.Query,
DisplayExpr = "Name",
ValueExpr = "Key",
LookupQuery = LookupQueryValues.DepartmentValues,
CascadeEmptyFields = "JobPositionId"
}),
Visible = true,
IsActive = true,
AllowSearch = true,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Guid,
FieldName = "JobPositionId",
CaptionName = "App.Listform.ListformField.JobPositionId",
Width = 150,
ListOrderNo = 8,
LookupJson = JsonSerializer.Serialize(new LookupDto {
DataSourceType = UiLookupDataSourceTypeEnum.Query,
DisplayExpr = "Name",
ValueExpr = "Key",
LookupQuery = LookupQueryValues.JobPositionValues,
CascadeRelationField = "DepartmentId",
CascadeFilterOperator="=",
CascadeParentFields = "DepartmentId",
}),
Visible = true,
IsActive = true,
AllowSearch = true,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
ListFormCode = listForm.ListFormCode,
@ -1012,7 +1073,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "IsActive",
CaptionName = "App.Listform.ListformField.IsActive",
Width = 100,
ListOrderNo = 7,
ListOrderNo = 9,
Visible = true,
IsActive = true,
@ -1030,7 +1091,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "IsVerified",
CaptionName = "App.Listform.ListformField.IsVerified",
Width = 100,
ListOrderNo = 8,
ListOrderNo = 10,
Visible = true,
IsActive = true,
@ -1048,7 +1109,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "EmailConfirmed",
CaptionName = "App.Listform.ListformField.EmailConfirmed",
Width = 100,
ListOrderNo = 9,
ListOrderNo = 11,
Visible = true,
IsActive = true,
@ -1066,7 +1127,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "TwoFactorEnabled",
CaptionName = "App.Listform.ListformField.TwoFactorEnabled",
Width = 100,
ListOrderNo = 10,
ListOrderNo = 12,
Visible = true,
IsActive = true,
@ -1084,7 +1145,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "Password",
CaptionName = "App.Listform.ListformField.Password",
Width = 100,
ListOrderNo = 11,
ListOrderNo = 13,
Visible = false,
IsActive = false,
@ -2355,9 +2416,8 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
new() {
Order = 1, ColCount = 1, ColSpan = 1, ItemType = "group", Items =
[
new EditingFormItemDto { Order = 1, DataField = "BranchId", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order = 2, DataField = "Name", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 3, DataField = "ParentId", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton }
new EditingFormItemDto { Order = 1, DataField = "Name", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 2, DataField = "ParentId", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton }
]
}
}),
@ -2416,32 +2476,6 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
DisplayExpr = "Name",
ValueExpr = "Key",
LookupQuery = LookupQueryValues.DepartmentValues,
CascadeRelationField = "BranchId",
CascadeFilterOperator="=",
CascadeParentFields = "BranchId",
}),
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson
},
new()
{
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Guid,
FieldName = "BranchId",
CaptionName = "App.Listform.ListformField.BranchId",
Width = 200,
ListOrderNo = 4,
Visible = true,
IsActive = true,
AllowSearch = true,
LookupJson = JsonSerializer.Serialize(new LookupDto {
DataSourceType = UiLookupDataSourceTypeEnum.Query,
DisplayExpr = "Name",
ValueExpr = "Key",
LookupQuery = LookupQueryValues.BranchValues,
CascadeEmptyFields = "ParentId"
}),
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
@ -2498,10 +2532,9 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
new() {
Order = 1, ColCount = 1, ColSpan = 1, ItemType = "group", Items =
[
new EditingFormItemDto { Order = 1, DataField = "BranchId", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order = 2, DataField = "DepartmentId", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order = 3, DataField = "Name", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 4, DataField = "ParentId", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order = 1, DataField = "DepartmentId", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order = 2, DataField = "Name", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 3, DataField = "ParentId", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
]
}
}),
@ -2560,9 +2593,9 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
DisplayExpr = "Name",
ValueExpr = "Key",
LookupQuery = LookupQueryValues.JobPositionValues,
CascadeRelationField = "BranchId",
CascadeRelationField = "DepartmentId",
CascadeFilterOperator="=",
CascadeParentFields = "BranchId,DepartmentId",
CascadeParentFields = "DepartmentId",
}),
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
@ -2585,9 +2618,6 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
DisplayExpr = "Name",
ValueExpr = "Key",
LookupQuery = LookupQueryValues.DepartmentValues,
CascadeRelationField = "BranchId",
CascadeFilterOperator="=",
CascadeParentFields = "BranchId",
CascadeEmptyFields = "ParentId"
}),
ValidationRuleJson = DefaultValidationRuleRequiredJson,
@ -2595,29 +2625,6 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson
},
new()
{
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Guid,
FieldName = "BranchId",
CaptionName = "App.Listform.ListformField.BranchId",
Width = 0,
ListOrderNo = 5,
Visible = true,
IsActive = true,
AllowSearch = true,
LookupJson = JsonSerializer.Serialize(new LookupDto {
DataSourceType = UiLookupDataSourceTypeEnum.Query,
DisplayExpr = "Name",
ValueExpr = "Key",
LookupQuery = LookupQueryValues.BranchValues,
CascadeEmptyFields = "DepartmentId,ParentId"
}),
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson
},
]);
#endregion
}

View file

@ -139,6 +139,8 @@ public static class PlatformConsts
public const string AdminPhoneNumberDefaultValue = "05449476346";
public const string AdminRocketUsernameDefaultValue = "sedat.ozturk";
public const string AdminWorkHourDefaultValue = "00:00-23:59";
public const string AdminDepartmentIdDefaultValue = "Yönetim";
public const string AdminJobPositionIdDefaultValue = "Genel Müdür";
}
public static class OrganizationUnits

View file

@ -76,6 +76,20 @@ public static class PlatformModuleExtensionConfigurator
property.DisplayName = new LocalizableString(typeof(PlatformResource), PlatformConsts.AbpIdentity.User.RocketUsername);
});
user.AddOrUpdateProperty<Guid>(
PlatformConsts.AbpIdentity.User.DepartmentId,
property =>
{
property.DisplayName = new LocalizableString(typeof(PlatformResource), PlatformConsts.AbpIdentity.User.DepartmentId);
});
user.AddOrUpdateProperty<Guid>(
PlatformConsts.AbpIdentity.User.JobPositionId,
property =>
{
property.DisplayName = new LocalizableString(typeof(PlatformResource), PlatformConsts.AbpIdentity.User.JobPositionId);
});
user.AddOrUpdateProperty<string>(
PlatformConsts.AbpIdentity.User.WorkHour,
property =>

View file

@ -13,8 +13,6 @@ public class Department : FullAuditedEntity<Guid>, IMultiTenant
public Guid? ParentId { get; set; }
public Guid? BranchId { get; set; }
Guid? IMultiTenant.TenantId => TenantId;
public ICollection<JobPosition> JobPositions { get; set; }

View file

@ -10,8 +10,6 @@ public class JobPosition : FullAuditedEntity<Guid>, IMultiTenant
public string Name { get; set; }
public Guid? BranchId { get; set; }
public Guid DepartmentId { get; set; }
public Department Department { get; set; }

View file

@ -52,23 +52,23 @@ public static class AbpIdentityUserExtensions
}
//DepartmentId
public static void SetDepartmentId(this IdentityUser user, string departmentId)
public static void SetDepartmentId(this IdentityUser user, Guid departmentId)
{
user.SetProperty(PlatformConsts.AbpIdentity.User.DepartmentId, departmentId);
}
public static string GetDepartmentId(this IdentityUser user)
public static Guid GetDepartmentId(this IdentityUser user)
{
return user.GetProperty<string>(PlatformConsts.AbpIdentity.User.DepartmentId);
return user.GetProperty<Guid>(PlatformConsts.AbpIdentity.User.DepartmentId);
}
//JobPositionId
public static void SetJobPositionId(this IdentityUser user, string jobPositionId)
public static void SetJobPositionId(this IdentityUser user, Guid jobPositionId)
{
user.SetProperty(PlatformConsts.AbpIdentity.User.JobPositionId, jobPositionId);
}
public static string GetJobPositionId(this IdentityUser user)
public static Guid GetJobPositionId(this IdentityUser user)
{
return user.GetProperty<string>(PlatformConsts.AbpIdentity.User.JobPositionId);
return user.GetProperty<Guid>(PlatformConsts.AbpIdentity.User.JobPositionId);
}
}

View file

@ -56,7 +56,7 @@ public static class PlatformEfCoreEntityExtensionMappings
);
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>(
.MapEfCoreProperty<IdentityUser, Guid>(
PlatformConsts.AbpIdentity.User.DepartmentId,
(entityBuilder, propertyBuilder) =>
{
@ -65,7 +65,7 @@ public static class PlatformEfCoreEntityExtensionMappings
);
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>(
.MapEfCoreProperty<IdentityUser, Guid>(
PlatformConsts.AbpIdentity.User.JobPositionId,
(entityBuilder, propertyBuilder) =>
{

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Sozsoft.Platform.Migrations
{
[DbContext(typeof(PlatformDbContext))]
[Migration("20260504085315_Initial")]
[Migration("20260504141857_Initial")]
partial class Initial
{
/// <inheritdoc />
@ -1649,9 +1649,6 @@ namespace Sozsoft.Platform.Migrations
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("BranchId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
@ -2105,9 +2102,6 @@ namespace Sozsoft.Platform.Migrations
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("BranchId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
@ -5078,8 +5072,8 @@ namespace Sozsoft.Platform.Migrations
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("DepartmentId")
.HasColumnType("nvarchar(max)");
b.Property<Guid>("DepartmentId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Email")
.IsRequired()
@ -5122,8 +5116,8 @@ namespace Sozsoft.Platform.Migrations
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<string>("JobPositionId")
.HasColumnType("nvarchar(max)");
b.Property<Guid>("JobPositionId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")

View file

@ -417,9 +417,9 @@ namespace Sozsoft.Platform.Migrations
ShouldChangePasswordOnNextLogin = table.Column<bool>(type: "bit", nullable: false),
EntityVersion = table.Column<int>(type: "int", nullable: false),
LastPasswordChangeTime = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
DepartmentId = table.Column<string>(type: "nvarchar(max)", nullable: true),
DepartmentId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
IsVerified = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
JobPositionId = table.Column<string>(type: "nvarchar(max)", nullable: true),
JobPositionId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
LoginEndDate = table.Column<DateTime>(type: "datetime2", nullable: true),
RocketUsername = table.Column<string>(type: "nvarchar(max)", nullable: true),
WorkHour = table.Column<string>(type: "nvarchar(max)", nullable: true),
@ -446,7 +446,6 @@ namespace Sozsoft.Platform.Migrations
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Name = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
ParentId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
BranchId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
@ -1793,7 +1792,6 @@ namespace Sozsoft.Platform.Migrations
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Name = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
BranchId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DepartmentId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
ParentId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),

View file

@ -1646,9 +1646,6 @@ namespace Sozsoft.Platform.Migrations
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("BranchId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
@ -2102,9 +2099,6 @@ namespace Sozsoft.Platform.Migrations
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("BranchId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
@ -5075,8 +5069,8 @@ namespace Sozsoft.Platform.Migrations
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("DepartmentId")
.HasColumnType("nvarchar(max)");
b.Property<Guid>("DepartmentId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Email")
.IsRequired()
@ -5119,8 +5113,8 @@ namespace Sozsoft.Platform.Migrations
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<string>("JobPositionId")
.HasColumnType("nvarchar(max)");
b.Property<Guid>("JobPositionId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")

View file

@ -1246,57 +1246,47 @@
],
"Departments": [
{
"Name": "Yönetim",
"BranchCode": "DEMO"
"Name": "Yönetim"
},
{
"Name": "Üretim",
"BranchCode": "DEMO",
"ParentName": "Yönetim"
},
{
"Name": "Satış",
"BranchCode": "DEMO",
"ParentName": "Yönetim"
},
{
"Name": "Finans",
"BranchCode": "DEMO",
"ParentName": "Yönetim"
},
{
"Name": "Muhasebe",
"BranchCode": "DEMO",
"ParentName": "Finans"
},
{
"Name": "Bilgi İşlem",
"BranchCode": "DEMO",
"ParentName": "Yönetim"
}
],
"JobPositions": [
{
"DepartmentName": "Yönetim",
"Name": "Genel Müdür",
"BranchCode": "DEMO"
"Name": "Genel Müdür"
},
{
"DepartmentName": "Yönetim",
"Name": "Genel Müdür Yardımcısı",
"BranchCode": "DEMO",
"ParentName": "Genel Müdür"
},
{
"DepartmentName": "Muhasebe",
"Name": "Muhasebe Müdürü",
"BranchCode": "DEMO",
"ParentName": "Genel Müdür"
},
{
"DepartmentName": "Muhasebe",
"Name": "Muhasebe Şefi",
"BranchCode": "DEMO",
"ParentName": "Muhasebe Müdürü"
}
]

View file

@ -14,6 +14,7 @@ using Volo.Abp.Identity;
using Volo.Abp.Timing;
using System.Collections.Generic;
using Volo.Abp.MultiTenancy;
using Sozsoft.Platform.Extensions;
namespace Sozsoft.Platform.Data.Seeds;
@ -748,7 +749,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
await _departmentRepository.InsertAsync(new Department
{
Name = item.Name,
BranchId = branch.Id,
ParentId = parent?.Id
}, autoSave: true);
}
@ -759,7 +759,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
if (exists)
continue;
var branch = await _branchRepository.FirstOrDefaultAsync(x => x.Code == item.BranchCode);
var department = await _departmentRepository.FirstOrDefaultAsync(x => x.Name == item.DepartmentName);
var parent = await _jobPositionRepository.FirstOrDefaultAsync(x => x.Name == item.ParentName);
@ -767,9 +766,27 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
{
Name = item.Name,
DepartmentId = department.Id,
BranchId = branch.Id,
ParentId = parent?.Id
}, autoSave: true);
}
//admin kullanının departmen ve pozisyonunu default olarak belirliyoruz
var adminUser = await _repositoryUser.FindByNormalizedEmailAsync(PlatformConsts.AbpIdentity.User.AdminEmailDefaultValue);
if (adminUser != null)
{
var defaultDepartment = await _departmentRepository.FirstOrDefaultAsync(x => x.Name == PlatformConsts.AbpIdentity.User.AdminDepartmentIdDefaultValue);
if (defaultDepartment != null)
{
adminUser.SetDepartmentId(defaultDepartment.Id);
}
var defaultJobPosition = await _jobPositionRepository.FirstOrDefaultAsync(x => x.Name == PlatformConsts.AbpIdentity.User.AdminJobPositionIdDefaultValue);
if (defaultJobPosition != null)
{
adminUser.SetJobPositionId(defaultJobPosition.Id);
}
await _repositoryUser.UpdateAsync(adminUser);
}
}
}

View file

@ -125,6 +125,8 @@ export interface UserInfoViewModel extends ExtensibleObject {
branches: AssignedRoleViewModel[]
claims: AssignedClaimViewModel[]
workHours: AssignedWorkHourViewModel[]
departments: AssignedDepartmentViewModel[]
jobPositions: AssignedJobPositionViewModel[]
lockUser: boolean
lastPasswordChangeTime?: Date | string
@ -136,6 +138,8 @@ export interface UserInfoViewModel extends ExtensibleObject {
creationTime: Date | string
lastModificationTime: Date | string
workHour?: string
departmentId?: string
jobPositionId?: string
}
export interface AssignedRoleViewModel {
@ -161,6 +165,19 @@ export interface AssignedWorkHourViewModel {
isAssigned: boolean
}
export interface AssignedDepartmentViewModel {
id: string
name?: string
isAssigned: boolean
}
export interface AssignedJobPositionViewModel {
id: string
name?: string
departmentId?: string
isAssigned: boolean
}
export interface UserClaimModel {
id?: string
userId?: string

View file

@ -43,6 +43,13 @@ export const putUserLookout = (input: UserInfoViewModel) =>
data: input,
})
export const putUserPermission = (input: UserInfoViewModel) =>
apiService.fetchData({
method: 'PUT',
url: `/api/app/platform-identity/permission`,
data: input,
})
export const getPermissions = (providerName: string, providerKey?: string) =>
apiService.fetchData<GetPermissionListResultDto>({
method: 'GET',

View file

@ -2,3 +2,9 @@ export type SelectBoxOption = {
value?: string
label?: string
}
export type SelectBoxOptionWithGroup = {
value?: string
label?: string
group?: string
}

View file

@ -27,16 +27,17 @@ import {
postClaimUser,
putUserDetail,
putUserLookout,
putUserPermission,
} from '@/services/identity.service'
import { useLocalization } from '@/utils/hooks/useLocalization'
import dayjs from 'dayjs'
import { Field, FieldArray, FieldProps, Form, Formik, FormikHelpers } from 'formik'
import { useEffect, useState } from 'react'
import { Helmet } from 'react-helmet'
import { FaLockOpen, FaUser, FaFileAlt, FaTrashAlt } from 'react-icons/fa'
import { FaLockOpen, FaUser, FaFileAlt, FaTrashAlt, FaCheckCircle } from 'react-icons/fa'
import { useParams } from 'react-router-dom'
import * as Yup from 'yup'
import { SelectBoxOption } from '@/types/shared'
import { SelectBoxOption, SelectBoxOptionWithGroup } from '@/types/shared'
import { ConfirmDialog, Container } from '@/components/shared'
import { AssignedClaimViewModel, UserInfoViewModel } from '@/proxy/admin/models'
import { APP_NAME } from '@/constants/app.constant'
@ -115,6 +116,9 @@ function UserDetails() {
<TabNav value="user" icon={<FaUser />}>
{translate('::Abp.Identity.User.UserInformation')}
</TabNav>
<TabNav value="permission" icon={<FaCheckCircle />}>
{translate('::Abp.Identity.User.Permissions')}
</TabNav>
<TabNav value="lockout" icon={<FaLockOpen />}>
{translate('::Abp.Identity.User.LockoutManagement')}
</TabNav>
@ -144,14 +148,27 @@ function UserDetails() {
}}
>
{({ isSubmitting, values }) => {
const roles = values.roles
const branches = values.branches
const departments = values.departments
const jobPositions = values.jobPositions
const departmentOptions: SelectBoxOption[] = departments.map((department) => ({
value: department.id,
label: department.name,
}))
const jobPositionOptions: SelectBoxOptionWithGroup[] = jobPositions.map(
(jobPosition) => ({
value: jobPosition.id,
label: jobPosition.name,
group: jobPosition.departmentId,
}),
)
return (
<Form>
<div className="w-1/2">
<FormContainer size="md">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 w-full">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2 w-full">
{/* Personal Information */}
<div>
<FormItem
@ -179,6 +196,208 @@ function UserDetails() {
</FormItem>
</div>
<div>
<FormItem
label={translate(
'::Abp.Identity.User.UserInformation.DepartmentId',
)}
>
<Field type="text" name="departmentId">
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
options={departmentOptions}
isClearable={true}
value={departmentOptions.filter(
(option) => option.value === values.departmentId,
)}
onChange={(option) => {
form.setFieldValue(field.name, option?.value)
form.setFieldValue('jobPositionId', null)
}}
/>
)}
</Field>
</FormItem>
</div>
<div>
<FormItem
label={translate(
'::Abp.Identity.User.UserInformation.JobPositionId',
)}
>
<Field type="text" name="jobPositionId">
{({ field, form }: FieldProps<SelectBoxOptionWithGroup>) => (
<Select
field={field}
form={form}
options={jobPositionOptions.filter(
(option) => option.group === values.departmentId,
)}
isClearable={true}
value={jobPositionOptions.filter(
(option) => option.value === values.jobPositionId,
)}
onChange={(option) =>
form.setFieldValue(field.name, option?.value)
}
/>
)}
</Field>
</FormItem>
</div>
<div>
<h6 className="mb-4">
{translate(
'::Abp.Identity.User.UserInformation.ContactInformation',
)}
</h6>
<FormItem label={translate('::Abp.Account.EmailAddress')}>
<Field
type="text"
disabled
name="email"
placeholder="Email Address"
component={Input}
/>
</FormItem>
<FormItem
label={translate('::Abp.Identity.User.UserInformation.PhoneNumber')}
>
<Field
type="text"
name="phoneNumber"
placeholder={translate(
'::Abp.Identity.User.UserInformation.PhoneNumber',
)}
component={Input}
/>
</FormItem>
<FormItem label={translate('::RocketUsername')}>
<Field
type="text"
name="rocketUsername"
placeholder={translate('::RocketUsername')}
component={Input}
/>
</FormItem>
</div>
<div>
<h6 className="mb-4">
{translate('::Abp.Identity.User.UserInformation.AccountTimestamps')}
</h6>
<FormItem
label={translate(
'::Abp.Identity.User.UserInformation.PasswordChangeTime',
)}
>
<Field name="lastPasswordChangeTime">
{({ field, form }: FieldProps) => (
<DateTimepicker
inputFormat="DD/MM/YYYY HH:mm"
field={field}
form={form}
value={field.value ? dayjs(field.value).toDate() : null}
placeholder="Select Date"
onChange={(date: any) => {
form.setFieldValue(
field.name,
date ? dayjs(date).format('YYYY-MM-DDTHH:mm:ss') : null,
)
}}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::Abp.Identity.User.UserInformation.CreateTime')}
>
<Field name="creationTime">
{({ field, form }: FieldProps) => (
<Input
field={field}
form={form}
value={
field.value
? dayjs(field.value).format('DD/MM/YYYY HH:mm')
: undefined
}
disabled
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::Abp.Identity.User.UserInformation.UpdateTime')}
>
<Field name="lastModificationTime">
{({ field, form }: FieldProps) => (
<Input
field={field}
form={form}
value={
field.value
? dayjs(field.value).format('DD/MM/YYYY HH:mm')
: undefined
}
disabled
/>
)}
</Field>
</FormItem>
</div>
</div>
</FormContainer>
</div>
<div className="w-1/2">
<Button variant="solid" block loading={isSubmitting} type="submit">
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</div>
</Form>
)
}}
</Formik>
</div>
</TabContent>
<TabContent value="permission">
<div className="mt-5">
<Formik
initialValues={userDetails}
onSubmit={async (values, { setSubmitting }) => {
setSubmitting(true)
await putUserPermission({ ...values })
toast.push(
<Notification type="success" duration={2000}>
{translate('::Abp.Identity.User.SavePermission')}
</Notification>,
{
placement: 'top-end',
},
)
getUser()
setSubmitting(false)
}}
>
{({ isSubmitting, values }) => {
const roles = values.roles
const branches = values.branches
return (
<Form>
<div className="w-1/2">
<FormContainer size="md">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 w-full">
{/* Şube Management */}
<div>
<h6 className="mb-4">
@ -242,142 +461,12 @@ function UserDetails() {
</FieldArray>
</div>
</div>
<div>
<FormItem
label={translate(
'::Abp.Identity.User.UserInformation.DepartmentId',
)}
>
<Field
type="text"
name="departmentId"
placeholder="Department"
component={Input}
/>
</FormItem>
</div>
<div>
<FormItem
label={translate(
'::Abp.Identity.User.UserInformation.JobPositionId',
)}
>
<Field
type="text"
name="jobPositionId"
placeholder="Job Position"
component={Input}
/>
</FormItem>
</div>
{/* Contact Information */}
<div>
<h6 className="mb-4">
{translate(
'::Abp.Identity.User.UserInformation.ContactInformation',
)}
</h6>
<FormItem label={translate('::Abp.Account.EmailAddress')}>
<Field
type="text"
disabled
name="email"
placeholder="Email Address"
component={Input}
/>
</FormItem>
<FormItem
label={translate('::Abp.Identity.User.UserInformation.PhoneNumber')}
>
<Field
type="text"
name="phoneNumber"
placeholder={translate(
'::Abp.Identity.User.UserInformation.PhoneNumber',
)}
component={Input}
/>
</FormItem>
<FormItem label={translate('::RocketUsername')}>
<Field
type="text"
name="rocketUsername"
placeholder={translate('::RocketUsername')}
component={Input}
/>
</FormItem>
</div>
{/* Account Timestamps */}
<div>
<h6 className="mb-4">
{translate('::Abp.Identity.User.UserInformation.AccountTimestamps')}
</h6>
<FormItem
label={translate(
'::Abp.Identity.User.UserInformation.PasswordChangeTime',
)}
>
<Field name="lastPasswordChangeTime">
{({ field, form }: FieldProps) => (
<DateTimepicker
inputFormat="DD/MM/YYYY HH:mm"
field={field}
form={form}
value={field.value ? dayjs(field.value).toDate() : null}
placeholder="Select Date"
onChange={(date: any) => {
form.setFieldValue(
field.name,
date ? dayjs(date).format('YYYY-MM-DDTHH:mm:ss') : null,
)
}}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::Abp.Identity.User.UserInformation.CreateTime')}
>
<Field name="creationTime">
{({ field, form }: FieldProps) => (
<Input
field={field}
form={form}
value={
field.value ? dayjs(field.value).format('DD/MM/YYYY HH:mm') : undefined
}
disabled
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::Abp.Identity.User.UserInformation.UpdateTime')}
>
<Field name="lastModificationTime">
{({ field, form }: FieldProps) => (
<Input
field={field}
form={form}
value={
field.value ? dayjs(field.value).format('DD/MM/YYYY HH:mm') : undefined
}
disabled
/>
)}
</Field>
</FormItem>
</div>
</div>
</FormContainer>
</div>
<div>
<Button variant="solid" loading={isSubmitting} type="submit">
<div className="w-1/2 mt-4">
<Button variant="solid" block loading={isSubmitting} type="submit">
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</div>
@ -387,6 +476,7 @@ function UserDetails() {
</Formik>
</div>
</TabContent>
<TabContent value="lockout">
<div className="mt-5">
<Formik
@ -398,7 +488,7 @@ function UserDetails() {
toast.push(
<Notification type="success" duration={2000}>
{'Lockout bilgileri kaydedildi.'}
{translate('::Abp.Identity.User.SaveLockout')}
</Notification>,
{
placement: 'top-end',
@ -422,7 +512,7 @@ function UserDetails() {
<Form>
<div className="w-1/2">
<FormContainer size="md">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 w-full">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2 w-full">
{/* Account Status */}
<div>
<h6 className="mb-4">
@ -665,8 +755,8 @@ function UserDetails() {
</FormContainer>
</div>
<div>
<Button variant="solid" loading={isSubmitting} type="submit">
<div className="w-1/2">
<Button variant="solid" block loading={isSubmitting} type="submit">
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</div>