From 2a31b27f9b6877c691dff83f66ae86efbe3a2b71 Mon Sep 17 00:00:00 2001 From: mahan Date: Mon, 15 Dec 2025 10:43:17 +0330 Subject: [PATCH 01/27] feat: simplify StartWork and StopWork methods by removing userId parameter --- .../ChangeStatusSectionCommandHandler.cs | 14 +++++++------- .../ProjectAgg/Entities/TaskSection.cs | 16 +++------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Commands/ChangeStatusSection/ChangeStatusSectionCommandHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Commands/ChangeStatusSection/ChangeStatusSectionCommandHandler.cs index 9e37cdc8..4214ec2d 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Commands/ChangeStatusSection/ChangeStatusSectionCommandHandler.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Commands/ChangeStatusSection/ChangeStatusSectionCommandHandler.cs @@ -45,12 +45,12 @@ public class ChangeStatusSectionCommandHandler : IBaseCommandHandler> { - { TaskSectionStatus.ReadyToStart, new List { TaskSectionStatus.InProgress } }, - { TaskSectionStatus.InProgress, new List { TaskSectionStatus.Incomplete, TaskSectionStatus.Completed } }, - { TaskSectionStatus.Incomplete, new List { TaskSectionStatus.InProgress, TaskSectionStatus.Completed } }, - { TaskSectionStatus.Completed, new List { TaskSectionStatus.InProgress, TaskSectionStatus.Incomplete } }, // Can return to InProgress or Incomplete - { TaskSectionStatus.NotAssigned, new List { TaskSectionStatus.InProgress, TaskSectionStatus.ReadyToStart } } + { TaskSectionStatus.ReadyToStart, [TaskSectionStatus.InProgress] }, + { TaskSectionStatus.InProgress, [TaskSectionStatus.Incomplete, TaskSectionStatus.Completed] }, + { TaskSectionStatus.Incomplete, [TaskSectionStatus.InProgress, TaskSectionStatus.Completed] }, + { TaskSectionStatus.Completed, [TaskSectionStatus.InProgress, TaskSectionStatus.Incomplete] }, // Can return to InProgress or Incomplete + { TaskSectionStatus.NotAssigned, [TaskSectionStatus.InProgress, TaskSectionStatus.ReadyToStart] } }; if (!validTransitions.TryGetValue(currentStatus, out var allowedTargets)) diff --git a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/TaskSection.cs b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/TaskSection.cs index b2a6fd91..c663ed4b 100644 --- a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/TaskSection.cs +++ b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/TaskSection.cs @@ -104,13 +104,8 @@ public class TaskSection : EntityBase UpdateStatus(TaskSectionStatus.NotAssigned); } - public void StartWork(long userId, string? notes = null) + public void StartWork(string? notes = null) { - if (CurrentAssignedUserId != userId) - { - throw new BadRequestException("کاربر مجاز به شروع این بخش نیست"); - } - // if (Status == TaskSectionStatus.Completed) // { // throw new BadRequestException("این بخش قبلاً تکمیل شده است"); @@ -121,14 +116,14 @@ public class TaskSection : EntityBase throw new BadRequestException("یک فعالیت در حال انجام وجود دارد"); } - var activity = new TaskSectionActivity(Id, userId, notes); + var activity = new TaskSectionActivity(Id, CurrentAssignedUserId, notes); _activities.Add(activity); UpdateStatus(TaskSectionStatus.InProgress); } - public void StopWork(long userId, TaskSectionStatus taskSectionStatus, string? endNotes = null) + public void StopWork(TaskSectionStatus taskSectionStatus, string? endNotes = null) { var activeActivity = _activities.FirstOrDefault(a => a.IsActive); if (activeActivity == null) @@ -136,11 +131,6 @@ public class TaskSection : EntityBase throw new BadRequestException("هیچ فعالیت فعالی یافت نشد"); } - if (activeActivity.UserId != userId) - { - throw new BadRequestException("کاربر مجاز به توقف این فعالیت نیست"); - } - UpdateStatus(taskSectionStatus); activeActivity.StopWork(endNotes); } From e67aca37f92cdb84bc33a71c705ae2b23555e2c8 Mon Sep 17 00:00:00 2001 From: SamSys Date: Mon, 15 Dec 2025 12:15:00 +0330 Subject: [PATCH 02/27] Insurance HasCheckout change --- .../Repository/CheckoutRepository.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/CompanyManagment.EFCore/Repository/CheckoutRepository.cs b/CompanyManagment.EFCore/Repository/CheckoutRepository.cs index cb843e5a..a78ff18e 100644 --- a/CompanyManagment.EFCore/Repository/CheckoutRepository.cs +++ b/CompanyManagment.EFCore/Repository/CheckoutRepository.cs @@ -64,7 +64,7 @@ public class CheckoutRepository : RepositoryBase, ICheckoutRepos } /// - /// چیک میکند که آیا پرسنل در سال و ماه درخواستی در این کارگاه فیش حقوقی دارد یا خیر + /// چک میکند که آیا پرسنل در سال و ماه درخواستی در این کارگاه فیش حقوقی دارد یا خیر /// /// /// @@ -74,12 +74,21 @@ public class CheckoutRepository : RepositoryBase, ICheckoutRepos public (bool hasChekout, double FamilyAlloance, double OverTimePay, double RotatingShift, double Nightwork, double Fridaywork, double YraesPay) HasCheckout(long workshopId, long employeId, string year, string month) { var farisMonthName = Tools.ToFarsiMonthByNumber(month); - + var res = _context.CheckoutSet.FirstOrDefault(x => x.WorkshopId == workshopId && x.EmployeeId == employeId && x.Year == year && x.Month == farisMonthName && x.IsActiveString == "true"); if (res == null) - return (false, 0, 0,0,0,0,0); + { + var checkLeftDate = ($"{year}/{month}/01").ToGeorgianDateTime(); + var hasLeftwork = _context.LeftWorkList.Any(x => + x.EmployeeId == employeId && x.WorkshopId == workshopId && x.LeftWorkDate == checkLeftDate); + if(hasLeftwork) + return (true, 0, 0, 0, 0, 0, 0); + + return (false, 0, 0, 0, 0, 0, 0); + } + return (true, res.FamilyAllowance, res.OvertimePay, res.ShiftPay, res.NightworkPay, res.FridayPay,res.YearsPay); } From 902ef3475741995907f05e61cb6da1feba55daca Mon Sep 17 00:00:00 2001 From: SamSys Date: Mon, 15 Dec 2025 12:42:19 +0330 Subject: [PATCH 03/27] Remove Pm Old Context --- .../PmDbBootstrapper.cs | 18 --- .../PmDomains/PmPermissionAgg/PmPermission.cs | 20 --- .../PmDomains/PmRoleAgg/IPmRoleRepository.cs | 15 --- .../PmDomains/PmRoleAgg/PmRole.cs | 46 ------- .../PmDomains/PmRoleUserAgg/PmRoleUser.cs | 19 --- .../PmDomains/PmUserAgg/IPmUserRepository.cs | 22 --- .../PmDomains/PmUserAgg/PmUser.cs | 127 ------------------ .../Mappings/PmMappings/PmRoleMapping.cs | 24 ---- .../Mappings/PmMappings/PmUserMapping.cs | 30 ----- .../PmDbConetxt/PmDbContext.cs | 32 ----- .../PmRepositories/PmRoleRepository.cs | 49 ------- .../PmRepositories/PmUserRepository.cs | 48 ------- 12 files changed, 450 deletions(-) delete mode 100644 AccountManagement.Configuration/PmDbBootstrapper.cs delete mode 100644 AccountManagement.Domain/PmDomains/PmPermissionAgg/PmPermission.cs delete mode 100644 AccountManagement.Domain/PmDomains/PmRoleAgg/IPmRoleRepository.cs delete mode 100644 AccountManagement.Domain/PmDomains/PmRoleAgg/PmRole.cs delete mode 100644 AccountManagement.Domain/PmDomains/PmRoleUserAgg/PmRoleUser.cs delete mode 100644 AccountManagement.Domain/PmDomains/PmUserAgg/IPmUserRepository.cs delete mode 100644 AccountManagement.Domain/PmDomains/PmUserAgg/PmUser.cs delete mode 100644 AccountMangement.Infrastructure.EFCore/Mappings/PmMappings/PmRoleMapping.cs delete mode 100644 AccountMangement.Infrastructure.EFCore/Mappings/PmMappings/PmUserMapping.cs delete mode 100644 AccountMangement.Infrastructure.EFCore/PmDbConetxt/PmDbContext.cs delete mode 100644 AccountMangement.Infrastructure.EFCore/Repository/PmRepositories/PmRoleRepository.cs delete mode 100644 AccountMangement.Infrastructure.EFCore/Repository/PmRepositories/PmUserRepository.cs diff --git a/AccountManagement.Configuration/PmDbBootstrapper.cs b/AccountManagement.Configuration/PmDbBootstrapper.cs deleted file mode 100644 index 84d93fa8..00000000 --- a/AccountManagement.Configuration/PmDbBootstrapper.cs +++ /dev/null @@ -1,18 +0,0 @@ -using AccountManagement.Domain.PmDomains.PmRoleAgg; -using AccountManagement.Domain.PmDomains.PmUserAgg; -using AccountMangement.Infrastructure.EFCore.PmDbConetxt; -using AccountMangement.Infrastructure.EFCore.Repository.PmRepositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; - -namespace AccountManagement.Configuration; - -public class PmDbBootstrapper -{ - public static void Configure(IServiceCollection services, string connectionString) - { - services.AddTransient(); - services.AddTransient(); - services.AddDbContext(x => x.UseSqlServer(connectionString)); - } -} \ No newline at end of file diff --git a/AccountManagement.Domain/PmDomains/PmPermissionAgg/PmPermission.cs b/AccountManagement.Domain/PmDomains/PmPermissionAgg/PmPermission.cs deleted file mode 100644 index 691a9954..00000000 --- a/AccountManagement.Domain/PmDomains/PmPermissionAgg/PmPermission.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using AccountManagement.Domain.PmDomains.PmRoleAgg; - -namespace AccountManagement.Domain.PmDomains.PmPermissionAgg; - -public class PmPermission -{ - public long Id { get; private set; } - public int Code { get; private set; } - public PmRole Role { get; private set; } - - public PmPermission(int code) - { - Code = code; - } -} \ No newline at end of file diff --git a/AccountManagement.Domain/PmDomains/PmRoleAgg/IPmRoleRepository.cs b/AccountManagement.Domain/PmDomains/PmRoleAgg/IPmRoleRepository.cs deleted file mode 100644 index 49cb7f4e..00000000 --- a/AccountManagement.Domain/PmDomains/PmRoleAgg/IPmRoleRepository.cs +++ /dev/null @@ -1,15 +0,0 @@ -using _0_Framework.Domain; -using System.Collections.Generic; -using System.Threading.Tasks; -using AccountManagement.Application.Contracts.ProgramManager; - -namespace AccountManagement.Domain.PmDomains.PmRoleAgg; - -public interface IPmRoleRepository :IRepository -{ - Task> GetPmRoleList(long? gozareshgirRoleId); - - Task GetPmRoleToEdit(long gozareshgirRoleId); - -} - diff --git a/AccountManagement.Domain/PmDomains/PmRoleAgg/PmRole.cs b/AccountManagement.Domain/PmDomains/PmRoleAgg/PmRole.cs deleted file mode 100644 index ddb305ea..00000000 --- a/AccountManagement.Domain/PmDomains/PmRoleAgg/PmRole.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; -using _0_Framework.Domain; -using AccountManagement.Domain.PmDomains.PmPermissionAgg; - -namespace AccountManagement.Domain.PmDomains.PmRoleAgg; - -public class PmRole : EntityBase -{ - /// - /// نام نقش - /// - public string RoleName { get; private set; } - - - /// - /// لیست پرمیشن کد ها - /// - public List PmPermission { get; private set; } - - /// - /// ای دی نقش در گزارشگیر - /// - public long? GozareshgirRoleId { get; private set; } - - - protected PmRole() - { - } - - public PmRole(string roleName,long? gozareshgirRolId, List permissions) - { - RoleName = roleName; - PmPermission = permissions; - GozareshgirRoleId = gozareshgirRolId; - - } - - - public void Edit(string roleName, List permissions) - { - RoleName = roleName; - PmPermission = permissions; - } - - -} \ No newline at end of file diff --git a/AccountManagement.Domain/PmDomains/PmRoleUserAgg/PmRoleUser.cs b/AccountManagement.Domain/PmDomains/PmRoleUserAgg/PmRoleUser.cs deleted file mode 100644 index a7cf6355..00000000 --- a/AccountManagement.Domain/PmDomains/PmRoleUserAgg/PmRoleUser.cs +++ /dev/null @@ -1,19 +0,0 @@ -using AccountManagement.Domain.PmDomains.PmUserAgg; - -namespace AccountManagement.Domain.PmDomains.PmRoleUserAgg; - -public class PmRoleUser -{ - public PmRoleUser(long roleId) - { - RoleId = roleId; - } - - public long Id { get; private set; } - public long RoleId { get; private set; } - - - public PmUser User { get; set; } - - -} \ No newline at end of file diff --git a/AccountManagement.Domain/PmDomains/PmUserAgg/IPmUserRepository.cs b/AccountManagement.Domain/PmDomains/PmUserAgg/IPmUserRepository.cs deleted file mode 100644 index 52c28fc1..00000000 --- a/AccountManagement.Domain/PmDomains/PmUserAgg/IPmUserRepository.cs +++ /dev/null @@ -1,22 +0,0 @@ -using _0_Framework.Domain; -using AccountManagement.Application.Contracts.ProgramManager; -using System.Threading.Tasks; -using Shared.Contracts.PmUser.Queries; - -namespace AccountManagement.Domain.PmDomains.PmUserAgg; - -public interface IPmUserRepository : IRepository -{ - /// - /// دریافت کاربر پروگرام منیجر جهتد ویرایش - /// - /// - /// - Task GetByPmUsertoEditbyAccountId(long accountId); - /// - /// دریافت کرابر پروگرام منیجر با اکانت آی دی در گزارشگیر - /// - /// - /// - Task GetPmUserByAccountId(long accountId); -} \ No newline at end of file diff --git a/AccountManagement.Domain/PmDomains/PmUserAgg/PmUser.cs b/AccountManagement.Domain/PmDomains/PmUserAgg/PmUser.cs deleted file mode 100644 index 6b0c531a..00000000 --- a/AccountManagement.Domain/PmDomains/PmUserAgg/PmUser.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Collections.Generic; -using _0_Framework.Domain; -using AccountManagement.Domain.PmDomains.PmRoleUserAgg; - - -namespace AccountManagement.Domain.PmDomains.PmUserAgg; - -/// -/// کاربر -/// -public class PmUser : EntityBase -{ - /// - /// ایجاد - /// - /// - /// - /// - /// - /// - /// - /// - public PmUser(string fullName, string userName, string password, string mobile, string email, long? accountId, List roles) - { - FullName = fullName; - UserName = userName; - Password = password; - Mobile = mobile; - Email = email; - IsActive = true; - AccountId = accountId; - RoleUser = roles; - } - - protected PmUser() - { - - } - /// - /// نام و نام خانوادگی - /// - public string FullName { get; private set; } - - /// - /// نام کاربری - /// - public string UserName { get; private set; } - - /// - /// گذرواژه - /// - public string Password { get; private set; } - - /// - /// مسیر عکس پروفایل - /// - public string ProfilePhotoPath { get; private set; } - - /// - /// شماره موبایل - /// - public string Mobile { get; set; } - - /// - /// ایمیل - /// - public string Email { get; private set; } - - /// - /// فعال/غیر فعال بودن یوزر - /// - public bool IsActive { get; private set; } - - - /// - /// کد یکبارمصرف ورود - /// - public string VerifyCode { get; private set; } - - /// - /// آی دی کاربر در گزارشگیر - /// - public long? AccountId { get; private set; } - - - /// - /// لیست پرمیشن کد ها - /// - public List RoleUser { get; private set; } - - - /// - /// آپدیت کاربر - /// - /// - /// - /// - /// - /// - public void Edit(string fullName, string userName, string mobile, List roles, bool isActive) - { - FullName = fullName; - UserName = userName; - Mobile = mobile; - RoleUser = roles; - IsActive = isActive; - } - - /// - /// غیرفعال سازی - /// - public void DeActive() - { - IsActive = false; - } - - /// - /// فعال سازی - /// - public void ReActive() - { - IsActive = true; - } - - -} \ No newline at end of file diff --git a/AccountMangement.Infrastructure.EFCore/Mappings/PmMappings/PmRoleMapping.cs b/AccountMangement.Infrastructure.EFCore/Mappings/PmMappings/PmRoleMapping.cs deleted file mode 100644 index 00324b3d..00000000 --- a/AccountMangement.Infrastructure.EFCore/Mappings/PmMappings/PmRoleMapping.cs +++ /dev/null @@ -1,24 +0,0 @@ -using AccountManagement.Domain.PmDomains.PmRoleAgg; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace AccountMangement.Infrastructure.EFCore.Mappings.PmMappings; - -public class PmRoleMapping : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("PmRoles", t => t.ExcludeFromMigrations()); - builder.HasKey(x => x.id); - - builder.Property(x => x.RoleName).HasMaxLength(100).IsRequired(); - - builder.OwnsMany(x => x.PmPermission, navigationBuilder => - { - navigationBuilder.HasKey(x => x.Id); - navigationBuilder.ToTable("PmRolePermissions", t => t.ExcludeFromMigrations()); - - navigationBuilder.WithOwner(x => x.Role); - }); - } -} \ No newline at end of file diff --git a/AccountMangement.Infrastructure.EFCore/Mappings/PmMappings/PmUserMapping.cs b/AccountMangement.Infrastructure.EFCore/Mappings/PmMappings/PmUserMapping.cs deleted file mode 100644 index 9ee5e126..00000000 --- a/AccountMangement.Infrastructure.EFCore/Mappings/PmMappings/PmUserMapping.cs +++ /dev/null @@ -1,30 +0,0 @@ -using AccountManagement.Domain.PmDomains.PmUserAgg; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace AccountMangement.Infrastructure.EFCore.Mappings.PmMappings; - -public class PmUserMapping :IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("Users"); - builder.HasKey(x => x.id); - - builder.Property(x => x.FullName).HasMaxLength(100).IsRequired(); - builder.Property(x => x.UserName).HasMaxLength(100).IsRequired(); - builder.Property(x => x.Password).HasMaxLength(1000).IsRequired(); - builder.Property(x => x.ProfilePhotoPath).HasMaxLength(500).IsRequired(false); - builder.Property(x => x.Mobile).HasMaxLength(20).IsRequired(); - builder.Property(x => x.Email).HasMaxLength(150).IsRequired(false); ; - builder.Property(x => x.VerifyCode).HasMaxLength(10).IsRequired(false); - builder.OwnsMany(x => x.RoleUser, navigationBuilder => - { - navigationBuilder.HasKey(x => x.Id); - navigationBuilder.ToTable("RoleUsers"); - navigationBuilder.WithOwner(x => x.User); - }); - - - } -} \ No newline at end of file diff --git a/AccountMangement.Infrastructure.EFCore/PmDbConetxt/PmDbContext.cs b/AccountMangement.Infrastructure.EFCore/PmDbConetxt/PmDbContext.cs deleted file mode 100644 index a668490e..00000000 --- a/AccountMangement.Infrastructure.EFCore/PmDbConetxt/PmDbContext.cs +++ /dev/null @@ -1,32 +0,0 @@ -using AccountManagement.Domain.PmDomains.PmRoleAgg; -using AccountManagement.Domain.PmDomains.PmUserAgg; -using AccountMangement.Infrastructure.EFCore.Mappings; -using AccountMangement.Infrastructure.EFCore.Mappings.PmMappings; -using Microsoft.EntityFrameworkCore; - -namespace AccountMangement.Infrastructure.EFCore.PmDbConetxt; - -public class PmDbContext : DbContext -{ - public PmDbContext(DbContextOptions options) : base(options) - { - - } - public DbSet Users { get; set; } = null!; - public DbSet PmRoles { get; set; } = null!; - - - public PmDbContext() - { - - } - - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - var assembly = typeof(PmUserMapping).Assembly; - modelBuilder.ApplyConfigurationsFromAssembly(assembly); - //SubAccountPermissionSeeder.Seed(modelBuilder); - base.OnModelCreating(modelBuilder); - } -} \ No newline at end of file diff --git a/AccountMangement.Infrastructure.EFCore/Repository/PmRepositories/PmRoleRepository.cs b/AccountMangement.Infrastructure.EFCore/Repository/PmRepositories/PmRoleRepository.cs deleted file mode 100644 index e3f93e53..00000000 --- a/AccountMangement.Infrastructure.EFCore/Repository/PmRepositories/PmRoleRepository.cs +++ /dev/null @@ -1,49 +0,0 @@ -using _0_Framework.InfraStructure; -using AccountManagement.Application.Contracts.ProgramManager; -using AccountManagement.Domain.PmDomains.PmRoleAgg; -using AccountMangement.Infrastructure.EFCore.PmDbConetxt; -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using static Microsoft.EntityFrameworkCore.DbLoggerCategory; - -namespace AccountMangement.Infrastructure.EFCore.Repository.PmRepositories; - -public class PmRoleRepository : RepositoryBase, IPmRoleRepository -{ - private readonly PmDbContext _pmDbContext; - public PmRoleRepository(PmDbContext context) : base(context) - { - _pmDbContext = context; - } - - public async Task> GetPmRoleList(long? gozareshgirRoleId) - { - var query = _pmDbContext.PmRoles.AsQueryable(); - if (gozareshgirRoleId != null && gozareshgirRoleId > 0) - query = query.Where(x => x.GozareshgirRoleId == gozareshgirRoleId); - var res = await query - .Select(p => new GetPmRolesDto() - { - Id = p.id, - RoleName = p.RoleName, - GozareshgirRoleId = p.GozareshgirRoleId, - Permissions = p.PmPermission.Select(x => x.Code).ToList() - - }) - .ToListAsync(); - - return res; - } - - - - public async Task GetPmRoleToEdit(long gozareshgirRoleId) - { - return await _pmDbContext.PmRoles.FirstOrDefaultAsync(x => x.GozareshgirRoleId == gozareshgirRoleId); - - } - -} \ No newline at end of file diff --git a/AccountMangement.Infrastructure.EFCore/Repository/PmRepositories/PmUserRepository.cs b/AccountMangement.Infrastructure.EFCore/Repository/PmRepositories/PmUserRepository.cs deleted file mode 100644 index e4915a65..00000000 --- a/AccountMangement.Infrastructure.EFCore/Repository/PmRepositories/PmUserRepository.cs +++ /dev/null @@ -1,48 +0,0 @@ -using _0_Framework.InfraStructure; -using AccountManagement.Application.Contracts.ProgramManager; -using AccountManagement.Domain.PmDomains.PmUserAgg; -using AccountMangement.Infrastructure.EFCore.PmDbConetxt; -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Shared.Contracts.PmUser.Queries; - -namespace AccountMangement.Infrastructure.EFCore.Repository.PmRepositories; - -public class PmUserRepository :RepositoryBase, IPmUserRepository -{ - private readonly PmDbContext _pmDbContext; - public PmUserRepository(PmDbContext context, PmDbContext pmDbContext) : base(context) - { - _pmDbContext = pmDbContext; - } - public async Task GetByPmUsertoEditbyAccountId(long accountId) - { - return await _pmDbContext.Users.FirstOrDefaultAsync(x => x.AccountId == accountId); - } - - public async Task GetPmUserByAccountId(long accountId) - { - var query = await _pmDbContext.Users.FirstOrDefaultAsync(x => x.AccountId == accountId); - if (query == null) - return new GetPmUserDto(); - List roles = query.RoleUser.Select(x => x.RoleId).ToList(); - return new GetPmUserDto() - { - FullName = query.FullName, - UserName = query.UserName, - ProfilePhotoPath = query.ProfilePhotoPath, - Mobile = query.Mobile, - IsActive = query.IsActive, - AccountId = query.AccountId, - Roles = roles, - RoleListDto = await _pmDbContext.PmRoles.Where(x => roles.Contains(x.id)).Select(x => new RoleListDto() - { - RoleName = x.RoleName, - RoleId = x.id, - Permissions = x.PmPermission.Select(x => x.Code).ToList() - }).ToListAsync(), - }; - } -} \ No newline at end of file From a3d286c040a552c5c8cb4673783f069d6bb15740 Mon Sep 17 00:00:00 2001 From: SamSys Date: Mon, 15 Dec 2025 13:05:05 +0330 Subject: [PATCH 04/27] remove PmDbContext and depndencies --- .../Account/EditAccount.cs | 4 +-- .../Account/EditClientAccount.cs | 8 +---- .../Account/IAccountApplication.cs | 7 ----- .../Account/RegisterAccount.cs | 8 +---- .../Account/WorkshopSelectList.cs | 8 +---- .../ProgramManager/GetRoleDto.cs | 28 ------------------ .../AccountApplication.cs | 29 ++++--------------- .../CameraAccountApplication.cs | 2 -- .../MediaApplication.cs | 3 +- .../RoleApplication.cs | 13 ++------- .../SubAccountApplication.cs | 3 -- .../TaskSubjectApplication.cs | 4 +-- .../Pages/Accounts/Account/Index.cshtml.cs | 2 +- ServiceHost/Program.cs | 4 +-- 14 files changed, 16 insertions(+), 107 deletions(-) delete mode 100644 AccountManagement.Application.Contracts/ProgramManager/GetRoleDto.cs diff --git a/AccountManagement.Application.Contracts/Account/EditAccount.cs b/AccountManagement.Application.Contracts/Account/EditAccount.cs index 41afe474..877edafc 100644 --- a/AccountManagement.Application.Contracts/Account/EditAccount.cs +++ b/AccountManagement.Application.Contracts/Account/EditAccount.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace AccountManagement.Application.Contracts.Account; +namespace AccountManagement.Application.Contracts.Account; public class EditAccount : CreateAccount { diff --git a/AccountManagement.Application.Contracts/Account/EditClientAccount.cs b/AccountManagement.Application.Contracts/Account/EditClientAccount.cs index eef61835..9d18014f 100644 --- a/AccountManagement.Application.Contracts/Account/EditClientAccount.cs +++ b/AccountManagement.Application.Contracts/Account/EditClientAccount.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AccountManagement.Application.Contracts.Account; +namespace AccountManagement.Application.Contracts.Account; public class EditClientAccount : RegisterAccount { diff --git a/AccountManagement.Application.Contracts/Account/IAccountApplication.cs b/AccountManagement.Application.Contracts/Account/IAccountApplication.cs index 10b107ad..6cb88864 100644 --- a/AccountManagement.Application.Contracts/Account/IAccountApplication.cs +++ b/AccountManagement.Application.Contracts/Account/IAccountApplication.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Threading.Tasks; -using AccountManagement.Application.Contracts.ProgramManager; using Shared.Contracts.PmUser.Queries; namespace AccountManagement.Application.Contracts.Account; @@ -75,12 +74,6 @@ public interface IAccountApplication void CameraLogin(CameraLoginRequest request); - /// - /// دریافت کاربر پروگرام منیجر با اکانت آی دی - /// - /// - /// - Task GetPmUserByAccountId(long accountId); } public class CameraLoginRequest diff --git a/AccountManagement.Application.Contracts/Account/RegisterAccount.cs b/AccountManagement.Application.Contracts/Account/RegisterAccount.cs index 78b9fcf0..d299dd88 100644 --- a/AccountManagement.Application.Contracts/Account/RegisterAccount.cs +++ b/AccountManagement.Application.Contracts/Account/RegisterAccount.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using _0_Framework.Application; +using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Http; namespace AccountManagement.Application.Contracts.Account; diff --git a/AccountManagement.Application.Contracts/Account/WorkshopSelectList.cs b/AccountManagement.Application.Contracts/Account/WorkshopSelectList.cs index 8e17212f..48d952b8 100644 --- a/AccountManagement.Application.Contracts/Account/WorkshopSelectList.cs +++ b/AccountManagement.Application.Contracts/Account/WorkshopSelectList.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AccountManagement.Application.Contracts.Account; +namespace AccountManagement.Application.Contracts.Account; public class WorkshopSelectList { diff --git a/AccountManagement.Application.Contracts/ProgramManager/GetRoleDto.cs b/AccountManagement.Application.Contracts/ProgramManager/GetRoleDto.cs deleted file mode 100644 index a3256abd..00000000 --- a/AccountManagement.Application.Contracts/ProgramManager/GetRoleDto.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; - -namespace AccountManagement.Application.Contracts.ProgramManager; - -public record GetPmRolesDto -{ - /// - /// آی دی نقش - /// - public long Id { get; set; } - - /// - /// نام نقش - /// - public string RoleName { get; set; } - - /// - /// آی دی نقش در گزارشگیر - /// - public long? GozareshgirRoleId { get; set; } - - /// - /// لیست کدهای دسترسی - /// - public List Permissions { get; set; } - - -} \ No newline at end of file diff --git a/AccountManagement.Application/AccountApplication.cs b/AccountManagement.Application/AccountApplication.cs index 0736293d..2b111195 100644 --- a/AccountManagement.Application/AccountApplication.cs +++ b/AccountManagement.Application/AccountApplication.cs @@ -2,13 +2,9 @@ using _0_Framework.Application.Sms; using _0_Framework.Exceptions; using AccountManagement.Application.Contracts.Account; -using AccountManagement.Application.Contracts.ProgramManagerApiResult; using AccountManagement.Domain.AccountAgg; using AccountManagement.Domain.AccountLeftWorkAgg; using AccountManagement.Domain.CameraAccountAgg; -using AccountManagement.Domain.InternalApiCaller; -using AccountManagement.Domain.PmDomains.PmRoleUserAgg; -using AccountManagement.Domain.PmDomains.PmUserAgg; using AccountManagement.Domain.PositionAgg; using AccountManagement.Domain.RoleAgg; using AccountManagement.Domain.SubAccountAgg; @@ -17,25 +13,13 @@ using AccountManagement.Domain.SubAccountRoleAgg; using Company.Domain._common; using Company.Domain.WorkshopAgg; using Company.Domain.WorkshopSubAccountAgg; -using CompanyManagment.App.Contracts.Workshop; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.JsonPatch.Operations; using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.EntityFrameworkCore; -using Newtonsoft.Json; using System; -using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Net.Http; -using System.Runtime.InteropServices; -using System.Security.Claims; -using System.Text; -using System.Threading; using System.Threading.Tasks; -using AccountManagement.Application.Contracts.ProgramManager; + using Shared.Contracts.PmUser.Commands; -using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database; using Shared.Contracts.PmUser.Queries; //using AccountManagement.Domain.RoleAgg; @@ -58,13 +42,13 @@ public class AccountApplication : IAccountApplication private readonly ISubAccountRoleRepository _subAccountRoleRepository; private readonly IWorkshopSubAccountRepository _workshopSubAccountRepository; private readonly ISubAccountPermissionSubtitle1Repository _accountPermissionSubtitle1Repository; - private readonly IPmUserRepository _pmUserRepository; + private readonly IUnitOfWork _unitOfWork; private readonly IPmUserQueryService _pmUserQueryService; private readonly IPmUserCommandService _pmUserCommandService; public AccountApplication(IAccountRepository accountRepository, IPasswordHasher passwordHasher, - IFileUploader fileUploader, IAuthHelper authHelper, IRoleRepository roleRepository, IWorker worker, ISmsService smsService, ICameraAccountRepository cameraAccountRepository, IPositionRepository positionRepository, IAccountLeftworkRepository accountLeftworkRepository, IWorkshopRepository workshopRepository, ISubAccountRepository subAccountRepository, ISubAccountRoleRepository subAccountRoleRepository, IWorkshopSubAccountRepository workshopSubAccountRepository, ISubAccountPermissionSubtitle1Repository accountPermissionSubtitle1Repository, IUnitOfWork unitOfWork, IPmUserRepository pmUserRepository, IPmUserQueryService pmUserQueryService, IPmUserCommandService pmUserCommandService) + IFileUploader fileUploader, IAuthHelper authHelper, IRoleRepository roleRepository, IWorker worker, ISmsService smsService, ICameraAccountRepository cameraAccountRepository, IPositionRepository positionRepository, IAccountLeftworkRepository accountLeftworkRepository, IWorkshopRepository workshopRepository, ISubAccountRepository subAccountRepository, ISubAccountRoleRepository subAccountRoleRepository, IWorkshopSubAccountRepository workshopSubAccountRepository, ISubAccountPermissionSubtitle1Repository accountPermissionSubtitle1Repository, IUnitOfWork unitOfWork, IPmUserQueryService pmUserQueryService, IPmUserCommandService pmUserCommandService) { _authHelper = authHelper; _roleRepository = roleRepository; @@ -78,7 +62,7 @@ public class AccountApplication : IAccountApplication _workshopSubAccountRepository = workshopSubAccountRepository; _accountPermissionSubtitle1Repository = accountPermissionSubtitle1Repository; _unitOfWork = unitOfWork; - _pmUserRepository = pmUserRepository; + _pmUserQueryService = pmUserQueryService; _pmUserCommandService = pmUserCommandService; _fileUploader = fileUploader; @@ -1015,8 +999,5 @@ public class AccountApplication : IAccountApplication _authHelper.CameraSignIn(authViewModel); } - public async Task GetPmUserByAccountId(long accountId) - { - return await _pmUserRepository.GetPmUserByAccountId(accountId); - } + } \ No newline at end of file diff --git a/AccountManagement.Application/CameraAccountApplication.cs b/AccountManagement.Application/CameraAccountApplication.cs index bf0c02c2..d5a0ae24 100644 --- a/AccountManagement.Application/CameraAccountApplication.cs +++ b/AccountManagement.Application/CameraAccountApplication.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using _0_Framework.Application; using AccountManagement.Application.Contracts.Account; using AccountManagement.Application.Contracts.CameraAccount; diff --git a/AccountManagement.Application/MediaApplication.cs b/AccountManagement.Application/MediaApplication.cs index 354027da..48056abb 100644 --- a/AccountManagement.Application/MediaApplication.cs +++ b/AccountManagement.Application/MediaApplication.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using _0_Framework.Application; diff --git a/AccountManagement.Application/RoleApplication.cs b/AccountManagement.Application/RoleApplication.cs index eb25b1dd..b4d7a7db 100644 --- a/AccountManagement.Application/RoleApplication.cs +++ b/AccountManagement.Application/RoleApplication.cs @@ -4,13 +4,7 @@ using AccountManagement.Domain.RoleAgg; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using AccountManagement.Application.Contracts.ProgramManagerApiResult; -using AccountManagement.Domain.InternalApiCaller; using Company.Domain._common; -using AccountManagement.Application.Contracts.Ticket; -using AccountManagement.Domain.PmDomains.PmPermissionAgg; -using AccountManagement.Domain.PmDomains.PmRoleAgg; -using AccountManagement.Domain.PmDomains.PmUserAgg; using Microsoft.AspNetCore.Mvc.Rendering; using Shared.Contracts.PmRole.Commands; using GetPmRolesDto = Shared.Contracts.PmRole.Queries.GetPmRolesDto; @@ -22,18 +16,15 @@ namespace AccountManagement.Application; public class RoleApplication : IRoleApplication { private readonly IRoleRepository _roleRepository; - private readonly IPmRoleRepository _pmRoleRepository; - private readonly IPmUserRepository _pmUserRepository; + private readonly IPmRoleQueryService _pmRoleQueryService; private readonly IPmRoleCommandService _pmRoleCommandService; private readonly IUnitOfWork _unitOfWork; - public RoleApplication(IRoleRepository roleRepository, IUnitOfWork unitOfWork, IPmRoleRepository pmRoleRepository, IPmUserRepository pmUserRepository, IPmRoleQueryService pmRoleQueryService, IPmRoleCommandService pmRoleCommandService) + public RoleApplication(IRoleRepository roleRepository, IUnitOfWork unitOfWork, IPmRoleQueryService pmRoleQueryService, IPmRoleCommandService pmRoleCommandService) { _roleRepository = roleRepository; _unitOfWork = unitOfWork; - _pmRoleRepository = pmRoleRepository; - _pmUserRepository = pmUserRepository; _pmRoleQueryService = pmRoleQueryService; _pmRoleCommandService = pmRoleCommandService; } diff --git a/AccountManagement.Application/SubAccountApplication.cs b/AccountManagement.Application/SubAccountApplication.cs index 8d2f592f..cd79c847 100644 --- a/AccountManagement.Application/SubAccountApplication.cs +++ b/AccountManagement.Application/SubAccountApplication.cs @@ -5,14 +5,11 @@ using AccountManagement.Domain.AccountAgg; using AccountManagement.Domain.CameraAccountAgg; using AccountManagement.Domain.SubAccountAgg; using AccountManagement.Domain.SubAccountRoleAgg; -using Company.Domain.WorkshopAccountAgg; using Company.Domain.WorkshopSubAccountAgg; -using CompanyManagment.App.Contracts.Workshop; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database; namespace AccountManagement.Application diff --git a/AccountManagement.Application/TaskSubjectApplication.cs b/AccountManagement.Application/TaskSubjectApplication.cs index 42383942..c27fa674 100644 --- a/AccountManagement.Application/TaskSubjectApplication.cs +++ b/AccountManagement.Application/TaskSubjectApplication.cs @@ -1,9 +1,7 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using _0_Framework.Application; using AccountManagement.Application.Contracts.TaskSubject; using AccountManagement.Domain.TaskSubjectAgg; -using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database; namespace AccountManagement.Application; diff --git a/ServiceHost/Areas/Admin/Pages/Accounts/Account/Index.cshtml.cs b/ServiceHost/Areas/Admin/Pages/Accounts/Account/Index.cshtml.cs index e4597d4f..88899470 100644 --- a/ServiceHost/Areas/Admin/Pages/Accounts/Account/Index.cshtml.cs +++ b/ServiceHost/Areas/Admin/Pages/Accounts/Account/Index.cshtml.cs @@ -121,7 +121,7 @@ public class IndexModel : PageModel //); - var result = _accountApplication.GetPmUserByAccountId(account.Id).GetAwaiter().GetResult(); + var result = _accountApplication.getu(account.Id).GetAwaiter().GetResult(); // مثل قبل: diff --git a/ServiceHost/Program.cs b/ServiceHost/Program.cs index 4378ec2c..42b1b3bd 100644 --- a/ServiceHost/Program.cs +++ b/ServiceHost/Program.cs @@ -48,7 +48,7 @@ builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpClient("holidayApi", c => c.BaseAddress = new System.Uri("https://api.github.com")); var connectionString = builder.Configuration.GetConnectionString("MesbahDb"); var connectionStringTestDb = builder.Configuration.GetConnectionString("TestDb"); -var connectionStringProgramManager = builder.Configuration.GetConnectionString("ProgramManagerDb"); + builder.Services.AddProgramManagerApplication(); @@ -69,7 +69,7 @@ builder.Services.AddSingleton(mongoDatabase); builder.Services.AddSingleton, CustomJsonResultExecutor>(); PersonalBootstrapper.Configure(builder.Services, connectionString); TestDbBootStrapper.Configure(builder.Services, connectionStringTestDb); -PmDbBootstrapper.Configure(builder.Services, connectionStringProgramManager); + AccountManagementBootstrapper.Configure(builder.Services, connectionString); WorkFlowBootstrapper.Configure(builder.Services, connectionString); QueryBootstrapper.Configure(builder.Services); From a4f3feba1c5bdc32fdd1975fd539ea427693b5bd Mon Sep 17 00:00:00 2001 From: SamSys Date: Mon, 15 Dec 2025 13:26:09 +0330 Subject: [PATCH 05/27] update account edit method --- .../Account/IAccountApplication.cs | 2 ++ AccountManagement.Application/AccountApplication.cs | 5 ++++- .../Areas/Admin/Pages/Accounts/Account/Index.cshtml.cs | 7 +++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/AccountManagement.Application.Contracts/Account/IAccountApplication.cs b/AccountManagement.Application.Contracts/Account/IAccountApplication.cs index 6cb88864..b1da0153 100644 --- a/AccountManagement.Application.Contracts/Account/IAccountApplication.cs +++ b/AccountManagement.Application.Contracts/Account/IAccountApplication.cs @@ -74,6 +74,8 @@ public interface IAccountApplication void CameraLogin(CameraLoginRequest request); + Task GetPmUserAsync(long accountId); + } public class CameraLoginRequest diff --git a/AccountManagement.Application/AccountApplication.cs b/AccountManagement.Application/AccountApplication.cs index 2b111195..545c991e 100644 --- a/AccountManagement.Application/AccountApplication.cs +++ b/AccountManagement.Application/AccountApplication.cs @@ -999,5 +999,8 @@ public class AccountApplication : IAccountApplication _authHelper.CameraSignIn(authViewModel); } - + public async Task GetPmUserAsync(long accountId) + { + return await _pmUserQueryService.GetPmUserDataByAccountId(accountId); + } } \ No newline at end of file diff --git a/ServiceHost/Areas/Admin/Pages/Accounts/Account/Index.cshtml.cs b/ServiceHost/Areas/Admin/Pages/Accounts/Account/Index.cshtml.cs index 88899470..902acf03 100644 --- a/ServiceHost/Areas/Admin/Pages/Accounts/Account/Index.cshtml.cs +++ b/ServiceHost/Areas/Admin/Pages/Accounts/Account/Index.cshtml.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.SignalR; using Newtonsoft.Json; using ServiceHost.Hubs; +using Shared.Contracts.PmUser.Queries; namespace ServiceHost.Areas.Admin.Pages.Accounts.Account; @@ -30,6 +31,7 @@ public class IndexModel : PageModel public List Roless; public AccountSearchModel SearchModel; + public IndexModel(IAccountApplication accountApplication, IRoleApplication roleApplication, IAuthHelper authHelper, ISmsService smsService, IHubContext hubContext) @@ -121,11 +123,11 @@ public class IndexModel : PageModel //); - var result = _accountApplication.getu(account.Id).GetAwaiter().GetResult(); + var result = _accountApplication.GetPmUserAsync(account.Id).GetAwaiter().GetResult(); // مثل قبل: - if (result != null) + if (result.AccountId > 0) { account.IsProgramManagerUser = (result.AccountId== account.Id && result.IsActive); account.UserRoles = result.Roles; @@ -136,6 +138,7 @@ public class IndexModel : PageModel } var pmRolseSelectList = _roleApplication.GetPmRoleList(null).GetAwaiter().GetResult(); + //سلکت لیست تمام رول های پروگرام منیجر account.RoleList = pmRolseSelectList; //var response = InternalApiCaller.GetAsync( From 000af89fd7a9a3a48bfba6f6e77c829ff3f0f990 Mon Sep 17 00:00:00 2001 From: mahan Date: Mon, 15 Dec 2025 13:49:41 +0330 Subject: [PATCH 06/27] feat: add permission checking and task assignment handling in ProjectBoardHub --- 0_Framework/Application/AuthHelper.cs | 5 ++ 0_Framework/Application/IAuthHelper.cs | 1 + DadmehrGostar.sln | 3 + .../Constants/ProgramManagerPermissionCode.cs | 26 +++++++ .../Repositories/ITaskSectionRepository.cs | 2 + .../Hubs/ProgramManager/ProjectBoardHub.cs | 67 ++++++++++++++++--- .../SignalRBoardNotificationPublisher.cs | 15 ++++- ServiceHost/Properties/launchSettings.json | 2 +- 8 files changed, 109 insertions(+), 12 deletions(-) create mode 100644 ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Constants/ProgramManagerPermissionCode.cs diff --git a/0_Framework/Application/AuthHelper.cs b/0_Framework/Application/AuthHelper.cs index 41e6efa2..2af2188f 100644 --- a/0_Framework/Application/AuthHelper.cs +++ b/0_Framework/Application/AuthHelper.cs @@ -56,6 +56,11 @@ public class AuthHelper : IAuthHelper return Tools.DeserializeFromBsonList(permissions); //Mahan } + public bool HasPermission(int permission) + { + return GetPermissions().Any(x => x == permission); + } + public long CurrentAccountId() { return IsAuthenticated() diff --git a/0_Framework/Application/IAuthHelper.cs b/0_Framework/Application/IAuthHelper.cs index ab9cf572..d43ac069 100644 --- a/0_Framework/Application/IAuthHelper.cs +++ b/0_Framework/Application/IAuthHelper.cs @@ -12,6 +12,7 @@ public interface IAuthHelper string CurrentAccountRole(); AuthViewModel CurrentAccountInfo(); List GetPermissions(); + bool HasPermission(int permission); long CurrentAccountId(); string CurrentAccountMobile(); diff --git a/DadmehrGostar.sln b/DadmehrGostar.sln index f31d5bcb..342cf0ef 100644 --- a/DadmehrGostar.sln +++ b/DadmehrGostar.sln @@ -106,6 +106,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GozareshgirProgramManager.I EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared.Contracts", "Shared.Contracts\Shared.Contracts.csproj", "{08B234B6-783B-44E9-9961-4F97EAD16308}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{F61F77F5-9B14-49CC-870B-1C61D2636586}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -275,6 +277,7 @@ Global {B57EB542-C028-4A77-9386-9DFF1E60FDCB} = {9D85672B-D48E-40B5-9804-0CE220E0E64C} {D2B4F1D7-6336-4B30-910C-219F4119303F} = {D74D1E3B-3BE3-47EE-9914-785A8AD536E5} {408281FE-615F-4CBE-BD95-2E86F5ACC6C3} = {C0AE9368-D4E7-450B-9713-929D319DE690} + {08B234B6-783B-44E9-9961-4F97EAD16308} = {F61F77F5-9B14-49CC-870B-1C61D2636586} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E6CFB3A7-A7C8-4E82-8F06-F750408F0BA9} diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Constants/ProgramManagerPermissionCode.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Constants/ProgramManagerPermissionCode.cs new file mode 100644 index 00000000..ea171972 --- /dev/null +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Constants/ProgramManagerPermissionCode.cs @@ -0,0 +1,26 @@ +namespace GozareshgirProgramManager.Application._Common.Constants; + +public static class ProgramManagerPermissionCode +{ + public const int Code = 99; + + /// + ///بخش اجرا + /// + public static class Board + { + public const int Code = 991; +/// +/// تب همه +/// + public static class All + { + public const int Code = 99101; + /// + /// دیدن همه تسک ها + /// + public const int ViewAll = 9910101; + } + + } +} diff --git a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Repositories/ITaskSectionRepository.cs b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Repositories/ITaskSectionRepository.cs index bedf59bf..d9c11d75 100644 --- a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Repositories/ITaskSectionRepository.cs +++ b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Repositories/ITaskSectionRepository.cs @@ -9,4 +9,6 @@ public interface ITaskSectionRepository: IRepository Task GetByIdWithFullDataAsync(Guid id, CancellationToken cancellationToken = default); + + Task> GetAssignedToUserAsync(long userId); } \ No newline at end of file diff --git a/ServiceHost/Hubs/ProgramManager/ProjectBoardHub.cs b/ServiceHost/Hubs/ProgramManager/ProjectBoardHub.cs index 5c262095..c7870a62 100644 --- a/ServiceHost/Hubs/ProgramManager/ProjectBoardHub.cs +++ b/ServiceHost/Hubs/ProgramManager/ProjectBoardHub.cs @@ -1,19 +1,70 @@ +using _0_Framework.Application; +using GozareshgirProgramManager.Application._Common.Constants; using Microsoft.AspNetCore.SignalR; +using GozareshgirProgramManager.Domain.ProjectAgg.Repositories; +using System.Collections.Generic; +using System.Linq; +using System; namespace ServiceHost.Hubs.ProgramManager; -public class ProjectBoardHub:Hub +public class ProjectBoardHub : Hub { + private readonly IAuthHelper _authHelper; + private readonly ITaskSectionRepository _taskSectionRepository; + + public ProjectBoardHub(IAuthHelper authHelper, ITaskSectionRepository taskSectionRepository) + { + _authHelper = authHelper; + _taskSectionRepository = taskSectionRepository; + } + public override async Task OnConnectedAsync() { - var user = Context.User?.FindFirst("pm.userId")?.Value; - - if (user != null && user !="0") + // Rule 4: Determine all group memberships server-side. + if (!_authHelper.IsAuthenticated()) { - await Groups.AddToGroupAsync( - Context.ConnectionId, - $"pm.user-{user}" - ); + await base.OnConnectedAsync(); + return; + } + + var connectionId = Context.ConnectionId; + + // Rule 2: Add to all permission-based groups based on user's claims. + var permissionGroups = _authHelper.GetPermissions() ?? new List(); + + // Rule 3: Add to task-specific groups for all tasks assigned to the user (by accountId claim). + var userId =Convert.ToInt32(Context.User?.FindFirst("pm.userId")?.Value); + + var taskGroups = new List(); + if (userId > 0) + { + var assignedTasks = await _taskSectionRepository.GetAssignedToUserAsync(userId); + if (assignedTasks is { Count: > 0 }) + { + taskGroups = assignedTasks + .Select(t => $"pm.task:{t.Id}") + .ToList(); + } + } + + // Build the full, de-duplicated set of groups to join. + var groupsToJoin = new HashSet(StringComparer.OrdinalIgnoreCase); + + // Permission-based groups + foreach (var perm in permissionGroups) + groupsToJoin.Add($"pm.perm:{perm}"); + + // Task-based groups + foreach (var tg in taskGroups) + groupsToJoin.Add(tg); + + // Rule 5: Avoid duplicate joins; join all needed groups concurrently. + if (groupsToJoin.Count > 0) + { + var joinTasks = groupsToJoin + .Select(group => Groups.AddToGroupAsync(connectionId, group)); + await Task.WhenAll(joinTasks); } await base.OnConnectedAsync(); diff --git a/ServiceHost/Notifications/ProgramManager/SignalRBoardNotificationPublisher.cs b/ServiceHost/Notifications/ProgramManager/SignalRBoardNotificationPublisher.cs index 130d8f09..87a0c539 100644 --- a/ServiceHost/Notifications/ProgramManager/SignalRBoardNotificationPublisher.cs +++ b/ServiceHost/Notifications/ProgramManager/SignalRBoardNotificationPublisher.cs @@ -1,3 +1,4 @@ +using GozareshgirProgramManager.Application._Common.Constants; using GozareshgirProgramManager.Application.Interfaces; using GozareshgirProgramManager.Domain.ProjectAgg.Enums; using Microsoft.AspNetCore.SignalR; @@ -19,12 +20,20 @@ public class SignalRBoardNotificationPublisher:IBoardNotificationPublisher { var payload = new { - UserId = userId, + UserId= userId, OldStatus = oldStatus, NewStatus = newStatus, SectionId = sectionId }; - _hubContext.Clients.Group($"pm.user-{userId}").SendAsync("ReceiveProjectStatusChanged",payload); - return Task.CompletedTask; + + // گروه task-specific (برای همه assigneeهای واقعی) + var taskGroup = $"pm.task:{sectionId}"; + + // گروه permission-based (مثلاً برای Admin ها) + var permissionGroup = $"pm.perm:{ProgramManagerPermissionCode.Board.All.ViewAll}"; + + // ارسال به هر دو گروه؛ SignalR خودش duplicate connection رو هندل می‌کنه + return _hubContext.Clients.Groups(taskGroup, permissionGroup) + .SendAsync("ReceiveProjectStatusChanged", payload); } } \ No newline at end of file diff --git a/ServiceHost/Properties/launchSettings.json b/ServiceHost/Properties/launchSettings.json index fd650415..d7381591 100644 --- a/ServiceHost/Properties/launchSettings.json +++ b/ServiceHost/Properties/launchSettings.json @@ -19,7 +19,7 @@ "sqlDebugging": true, "dotnetRunMessages": "true", "nativeDebugging": true, - "applicationUrl": "https://localhost:5004;http://localhost:5003", + "applicationUrl": "https://localhost:5004;http://localhost:5003;https://192.168.0.117:5006", "jsWebView2Debugging": false, "hotReloadEnabled": true }, From 1c1c8816a51104a2fd242201ebb8132acf47e675 Mon Sep 17 00:00:00 2001 From: SamSys Date: Mon, 15 Dec 2025 14:01:42 +0330 Subject: [PATCH 07/27] pm GetHolidaysInDates completed - remove OldholidayItems --- .../Services/HolidayQueryService.cs | 28 +++++++++++++++---- .../CreateOrEditCheckoutCommandHandler.cs | 6 +--- .../HolidayAgg/Holiday.cs | 25 ----------------- .../HolidayItemAgg/HolidayItem.cs | 28 ------------------- .../Mappings/HolidayItemMapping.cs | 20 ------------- .../Persistence/Mappings/HolidayMapping.cs | 20 ------------- Shared.Contracts/Holidays/HolidayDto.cs | 6 ++-- 7 files changed, 27 insertions(+), 106 deletions(-) delete mode 100644 ProgramManager/src/Domain/GozareshgirProgramManager.Domain/HolidayAgg/Holiday.cs delete mode 100644 ProgramManager/src/Domain/GozareshgirProgramManager.Domain/HolidayItemAgg/HolidayItem.cs delete mode 100644 ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/HolidayItemMapping.cs delete mode 100644 ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/HolidayMapping.cs diff --git a/CompanyManagment.EFCore/Services/HolidayQueryService.cs b/CompanyManagment.EFCore/Services/HolidayQueryService.cs index 4b8aa51c..e8c2e4f3 100644 --- a/CompanyManagment.EFCore/Services/HolidayQueryService.cs +++ b/CompanyManagment.EFCore/Services/HolidayQueryService.cs @@ -1,14 +1,32 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using PersianTools.Core; using Shared.Contracts.Holidays; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; namespace CompanyManagment.EFCore.Services; public class HolidayQueryService : IHolidayQueryService { - public Task> GetHolidaysInDates(DateTime startDate, DateTime endDate) + private readonly CompanyContext _context; + + public HolidayQueryService(CompanyContext context) { - throw new NotImplementedException(); + _context = context; + } + + public async Task> GetHolidaysInDates(DateTime startDate, DateTime endDate) + { + return await _context.HolidayItems.Where(x => x.Holidaydate >= startDate && x.Holidaydate <= endDate).Select(x=> new HolidayDto() + { + Holidaydate = x.Holidaydate, + HolidayId = x.id, + HolidayYear = x.HolidayYear + }) + .ToListAsync(); + } } \ No newline at end of file diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Checkouts/Commands/CreateCheckout/CreateOrEditCheckoutCommandHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Checkouts/Commands/CreateCheckout/CreateOrEditCheckoutCommandHandler.cs index 3536ad0e..9b3fdcb8 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Checkouts/Commands/CreateCheckout/CreateOrEditCheckoutCommandHandler.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Checkouts/Commands/CreateCheckout/CreateOrEditCheckoutCommandHandler.cs @@ -185,7 +185,7 @@ public class CreateOrEditCheckoutCommandHandler : IBaseCommandHandler -{ - public Holiday(string year) - { - Year = year; - - } - - public string Year { get; private set; } - public List HolidayItems { get; set; } - - public Holiday() - { - HolidayItems = new List(); - } - public void Edit(string year) - { - Year = year; - } -} \ No newline at end of file diff --git a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/HolidayItemAgg/HolidayItem.cs b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/HolidayItemAgg/HolidayItem.cs deleted file mode 100644 index 3a66bd58..00000000 --- a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/HolidayItemAgg/HolidayItem.cs +++ /dev/null @@ -1,28 +0,0 @@ -using GozareshgirProgramManager.Domain._Common; -using GozareshgirProgramManager.Domain.HolidayAgg; - -namespace GozareshgirProgramManager.Domain.HolidayItemAgg; - -public class HolidayItem : EntityBase -{ - public HolidayItem(DateTime holidaydate, long holidayId, string holidayYear) - { - Holidaydate = holidaydate; - HolidayId = holidayId; - HolidayYear = holidayYear; - } - - public DateTime Holidaydate { get; private set; } - public long HolidayId { get; private set; } - public string HolidayYear { get; private set; } - - public Holiday Holidayss { get; set; } - - public void Edit(DateTime holidaydate, long holidayId, string holidayYear) - { - Holidaydate = holidaydate; - HolidayId = holidayId; - HolidayYear = holidayYear; - - } -} \ No newline at end of file diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/HolidayItemMapping.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/HolidayItemMapping.cs deleted file mode 100644 index 42eff124..00000000 --- a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/HolidayItemMapping.cs +++ /dev/null @@ -1,20 +0,0 @@ -using GozareshgirProgramManager.Domain.HolidayItemAgg; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace GozareshgirProgramManager.Infrastructure.Persistence.Mappings; - -public class HolidayItemMapping : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("Holidayitems"); - builder.HasKey(x => x.Id); - - builder.Property(x => x.HolidayYear).HasMaxLength(4); - - builder.HasOne(x => x.Holidayss) - .WithMany(x => x.HolidayItems) - .HasForeignKey(x => x.HolidayId); - } -} \ No newline at end of file diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/HolidayMapping.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/HolidayMapping.cs deleted file mode 100644 index 3038d89a..00000000 --- a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/HolidayMapping.cs +++ /dev/null @@ -1,20 +0,0 @@ -using GozareshgirProgramManager.Domain.HolidayAgg; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace GozareshgirProgramManager.Infrastructure.Persistence.Mappings; - -public class HolidayMapping : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("Holidays"); - builder.HasKey(x => x.Id); - - builder.Property(x => x.Year).HasMaxLength(4); - - builder.HasMany(x => x.HolidayItems) - .WithOne(x => x.Holidayss) - .HasForeignKey(x => x.HolidayId); - } -} \ No newline at end of file diff --git a/Shared.Contracts/Holidays/HolidayDto.cs b/Shared.Contracts/Holidays/HolidayDto.cs index 7318b83e..107977ae 100644 --- a/Shared.Contracts/Holidays/HolidayDto.cs +++ b/Shared.Contracts/Holidays/HolidayDto.cs @@ -2,7 +2,7 @@ namespace Shared.Contracts.Holidays; public record HolidayDto { - public DateTime Holidaydate { get; private set; } - public long HolidayId { get; private set; } - public string HolidayYear { get; private set; } + public DateTime Holidaydate { get; set; } + public long HolidayId { get; set; } + public string HolidayYear { get; set; } } \ No newline at end of file From d9da2e97ab300415fb8039e22210fed7db6bb0dc Mon Sep 17 00:00:00 2001 From: mahan Date: Mon, 15 Dec 2025 14:06:07 +0330 Subject: [PATCH 08/27] feat: add method to retrieve task sections assigned to a user and enhance permission code structure --- .../Constants/ProgramManagerPermissionCode.cs | 46 +++++++++++++++++-- .../Repositories/TaskSectionRepository.cs | 7 +++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Constants/ProgramManagerPermissionCode.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Constants/ProgramManagerPermissionCode.cs index ea171972..f5578ea2 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Constants/ProgramManagerPermissionCode.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Constants/ProgramManagerPermissionCode.cs @@ -1,7 +1,10 @@ +using System.Reflection; + namespace GozareshgirProgramManager.Application._Common.Constants; public static class ProgramManagerPermissionCode { + public const int Code = 99; /// @@ -10,17 +13,50 @@ public static class ProgramManagerPermissionCode public static class Board { public const int Code = 991; -/// -/// تب همه -/// + + /// + /// تب همه + /// public static class All { public const int Code = 99101; + /// /// دیدن همه تسک ها /// public const int ViewAll = 9910101; } - } -} + + public static List GetAllCodes() + { + var result = new List(); + + void Collect(Type type) + { + // Collect const int fields directly declared on this type + var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly); + foreach (var f in fields) + { + if (f.FieldType == typeof(int) && f.IsLiteral && !f.IsInitOnly) + { + var raw = f.GetRawConstantValue(); + if (raw is int value) + { + result.Add(value); + } + } + } + + // Recurse into nested types + var nestedTypes = type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic); + foreach (var nt in nestedTypes) + { + Collect(nt); + } + } + + Collect(typeof(ProgramManagerPermissionCode)); + return result; + } +} \ No newline at end of file diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Repositories/TaskSectionRepository.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Repositories/TaskSectionRepository.cs index 80564915..41551be5 100644 --- a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Repositories/TaskSectionRepository.cs +++ b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Repositories/TaskSectionRepository.cs @@ -28,4 +28,11 @@ public class TaskSectionRepository:RepositoryBase,ITaskSection .Include(x => x.AdditionalTimes) .FirstOrDefaultAsync(x => x.Id == id, cancellationToken); } + + public async Task> GetAssignedToUserAsync(long userId) + { + return await _context.TaskSections + .Where(x => x.CurrentAssignedUserId == userId) + .ToListAsync(); + } } \ No newline at end of file From 01d33ff340cba3cf933d2cc478773efd0e0eefa5 Mon Sep 17 00:00:00 2001 From: SamSys Date: Mon, 15 Dec 2025 18:49:13 +0330 Subject: [PATCH 09/27] add InstitutionContract Confirm Reminder Sms completed --- 0_Framework/Application/Sms/ISmsService.cs | 2 +- .../BackgroundInstitutionContract.Task.csproj | 1 + .../Jobs/JobSchedulerRegistrator.cs | 16 + .../Program.cs | 10 + .../IInstitutionContractRepository.cs | 5 + .../InstitutionCreationVerificationSmsDto.cs | 12 + .../InstitutionContractRepository.cs | 471 +++++++++++------- .../Services/SmsService.cs | 10 +- .../Pages/Company/SmsResult/Index.cshtml | 1 + 9 files changed, 354 insertions(+), 174 deletions(-) create mode 100644 CompanyManagment.App.Contracts/InstitutionContract/InstitutionCreationVerificationSmsDto.cs diff --git a/0_Framework/Application/Sms/ISmsService.cs b/0_Framework/Application/Sms/ISmsService.cs index f0056ba1..94e31299 100644 --- a/0_Framework/Application/Sms/ISmsService.cs +++ b/0_Framework/Application/Sms/ISmsService.cs @@ -27,7 +27,7 @@ public interface ISmsService Task GetCreditAmount(); - public Task SendInstitutionCreationVerificationLink(string number, string fullName, Guid institutionId, long contractingPartyId, long institutionContractId); + public Task SendInstitutionCreationVerificationLink(string number, string fullName, Guid institutionId, long contractingPartyId, long institutionContractId, string typeOfSms = null); public Task SendInstitutionVerificationCode(string number, string code, string contractingPartyFullName, long contractingPartyId, long institutionContractId); diff --git a/BackgroundInstitutionContract/BackgroundInstitutionContract.Task/BackgroundInstitutionContract.Task.csproj b/BackgroundInstitutionContract/BackgroundInstitutionContract.Task/BackgroundInstitutionContract.Task.csproj index d003ac92..3032b3a1 100644 --- a/BackgroundInstitutionContract/BackgroundInstitutionContract.Task/BackgroundInstitutionContract.Task.csproj +++ b/BackgroundInstitutionContract/BackgroundInstitutionContract.Task/BackgroundInstitutionContract.Task.csproj @@ -12,6 +12,7 @@ + diff --git a/BackgroundInstitutionContract/BackgroundInstitutionContract.Task/Jobs/JobSchedulerRegistrator.cs b/BackgroundInstitutionContract/BackgroundInstitutionContract.Task/Jobs/JobSchedulerRegistrator.cs index 1425a1a7..b97ff4f8 100644 --- a/BackgroundInstitutionContract/BackgroundInstitutionContract.Task/Jobs/JobSchedulerRegistrator.cs +++ b/BackgroundInstitutionContract/BackgroundInstitutionContract.Task/Jobs/JobSchedulerRegistrator.cs @@ -47,6 +47,11 @@ public class JobSchedulerRegistrator () => SendBlockSms(), "*/1 * * * *" // هر 1 دقیقه یکبار چک کن ); + RecurringJob.AddOrUpdate( + "InstitutionContract.SendInstitutionContractConfirmSms", + () => SendInstitutionContractConfirmSms(), + "*/1 * * * *" // هر 1 دقیقه یکبار چک کن + ); } @@ -146,4 +151,15 @@ public class JobSchedulerRegistrator await _institutionContractRepository.SendBlockSmsForBackgroundTask(); } + + /// + /// ارسال پیامک یادآور تایید قراداد مالی + /// + /// + [DisableConcurrentExecution(timeoutInSeconds: 100)] + public async System.Threading.Tasks.Task SendInstitutionContractConfirmSms() + { + await _institutionContractRepository.SendInstitutionContractConfirmSmsTask(); + } + } \ No newline at end of file diff --git a/BackgroundInstitutionContract/BackgroundInstitutionContract.Task/Program.cs b/BackgroundInstitutionContract/BackgroundInstitutionContract.Task/Program.cs index dda9d58e..3cb83458 100644 --- a/BackgroundInstitutionContract/BackgroundInstitutionContract.Task/Program.cs +++ b/BackgroundInstitutionContract/BackgroundInstitutionContract.Task/Program.cs @@ -7,11 +7,16 @@ using BackgroundInstitutionContract.Task; using BackgroundInstitutionContract.Task.Jobs; using CompanyManagment.App.Contracts.Hubs; using CompanyManagment.EFCore.Services; +using GozareshgirProgramManager.Application._Bootstrapper; +using GozareshgirProgramManager.Application.Modules.Users.Commands.CreateUser; +using GozareshgirProgramManager.Infrastructure; +using GozareshgirProgramManager.Infrastructure.Persistence.Seed; using Hangfire; using Microsoft.AspNetCore.Identity; using MongoDB.Driver; using PersonalContractingParty.Config; using Query.Bootstrapper; +using Shared.Contracts.PmUser.Queries; using WorkFlow.Infrastructure.Config; var builder = WebApplication.CreateBuilder(args); @@ -38,10 +43,15 @@ builder.Services.AddSingleton(mongoDatabase); #endregion +builder.Services.AddProgramManagerApplication(); +builder.Services.AddProgramManagerInfrastructure(builder.Configuration); + + PersonalBootstrapper.Configure(builder.Services, connectionString); TestDbBootStrapper.Configure(builder.Services, connectionStringTestDb); AccountManagementBootstrapper.Configure(builder.Services, connectionString); WorkFlowBootstrapper.Configure(builder.Services, connectionString); + QueryBootstrapper.Configure(builder.Services); JobsBootstrapper.Configure(builder.Services); builder.Services.AddHttpClient(); diff --git a/Company.Domain/InstitutionContractAgg/IInstitutionContractRepository.cs b/Company.Domain/InstitutionContractAgg/IInstitutionContractRepository.cs index 1d33e34f..124bc1bb 100644 --- a/Company.Domain/InstitutionContractAgg/IInstitutionContractRepository.cs +++ b/Company.Domain/InstitutionContractAgg/IInstitutionContractRepository.cs @@ -140,6 +140,11 @@ public interface IInstitutionContractRepository : IRepository Task SendReminderSmsToContractingParties(List smsListData, string typeOfSms, string sendMessStart, string sendMessEnd); + /// + /// ارسال پیامک یادآور تایید قراداد مالی + /// + /// + Task SendInstitutionContractConfirmSmsTask(); #endregion #region CreateMontlyTransaction diff --git a/CompanyManagment.App.Contracts/InstitutionContract/InstitutionCreationVerificationSmsDto.cs b/CompanyManagment.App.Contracts/InstitutionContract/InstitutionCreationVerificationSmsDto.cs new file mode 100644 index 00000000..b0151e87 --- /dev/null +++ b/CompanyManagment.App.Contracts/InstitutionContract/InstitutionCreationVerificationSmsDto.cs @@ -0,0 +1,12 @@ +using System; + +namespace CompanyManagment.App.Contracts.InstitutionContract; + +public record InstitutionCreationVerificationSmsDto +{ + public string Number{ get; set; } + public string FullName { get; set; } + public Guid InstitutionId { get; set; } + public long ContractingPartyId { get; set; } + public long InstitutionContractId { get; set; } +}; \ No newline at end of file diff --git a/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs b/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs index 1a22db1f..99a08cab 100644 --- a/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs +++ b/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs @@ -1,15 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Diagnostics.Metrics; -using System.Drawing; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using _0_Framework.Application; +using _0_Framework.Application; using _0_Framework.Application.Enums; using _0_Framework.Application.Sms; using _0_Framework.Exceptions; @@ -38,6 +27,7 @@ using CompanyManagment.App.Contracts.Law; using CompanyManagment.App.Contracts.TemporaryClientRegistration; using CompanyManagment.App.Contracts.Workshop; using CompanyManagment.App.Contracts.WorkshopPlan; +using CompanyManagment.EFCore.Migrations; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; @@ -47,10 +37,23 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using MongoDB.Driver; using OfficeOpenXml.Packaging.Ionic.Zip; using PersianTools.Core; -using static System.Runtime.InteropServices.JavaScript.JSType; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.Metrics; +using System.Drawing; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database; -using SmsResult = CompanyManagment.EFCore.Migrations.SmsResult; +using static System.Runtime.InteropServices.JavaScript.JSType; +using ContractingPartyAccount = Company.Domain.ContractingPartyAccountAgg.ContractingPartyAccount; +using FinancialStatment = Company.Domain.FinancialStatmentAgg.FinancialStatment; using String = System.String; +using Workshop = Company.Domain.WorkshopAgg.Workshop; namespace CompanyManagment.EFCore.Repository; @@ -102,69 +105,69 @@ public class InstitutionContractRepository : RepositoryBase new EditInstitutionContract() - { - Id = x.id, - ContractNo = x.ContractNo, - ContractStartGr = x.ContractStartGr, - ContractStartFa = x.ContractStartFa, - ContractEndGr = x.ContractEndGr, - ContractEndFa = x.ContractEndFa, - RepresentativeName = x.RepresentativeName, - ContractingPartyName = x.ContractingPartyName, - RepresentativeId = x.RepresentativeId, - ContractingPartyId = x.ContractingPartyId, - ContractDateFa = x.ContractDateFa, - State = x.State, - City = x.City, - Address = x.Address, - Description = x.Description, - WorkshopManualCount = x.WorkshopManualCount, - EmployeeManualCount = x.EmployeeManualCount, - ContractAmountString = x.ContractAmount.ToMoney(), - ContractAmount = x.ContractAmount, - DailyCompenseationString = x.DailyCompenseation.ToMoney(), - ObligationString = x.Obligation.ToMoney(), - TotalAmountString = x.TotalAmount.ToMoney(), - ExtensionNo = x.ExtensionNo, - OfficialCompany = x.OfficialCompany, - TypeOfContract = x.TypeOfContract, - Signature = x.Signature, - HasValueAddedTax = x.HasValueAddedTax, - ValueAddedTax = x.ValueAddedTax, - }) + { + Id = x.id, + ContractNo = x.ContractNo, + ContractStartGr = x.ContractStartGr, + ContractStartFa = x.ContractStartFa, + ContractEndGr = x.ContractEndGr, + ContractEndFa = x.ContractEndFa, + RepresentativeName = x.RepresentativeName, + ContractingPartyName = x.ContractingPartyName, + RepresentativeId = x.RepresentativeId, + ContractingPartyId = x.ContractingPartyId, + ContractDateFa = x.ContractDateFa, + State = x.State, + City = x.City, + Address = x.Address, + Description = x.Description, + WorkshopManualCount = x.WorkshopManualCount, + EmployeeManualCount = x.EmployeeManualCount, + ContractAmountString = x.ContractAmount.ToMoney(), + ContractAmount = x.ContractAmount, + DailyCompenseationString = x.DailyCompenseation.ToMoney(), + ObligationString = x.Obligation.ToMoney(), + TotalAmountString = x.TotalAmount.ToMoney(), + ExtensionNo = x.ExtensionNo, + OfficialCompany = x.OfficialCompany, + TypeOfContract = x.TypeOfContract, + Signature = x.Signature, + HasValueAddedTax = x.HasValueAddedTax, + ValueAddedTax = x.ValueAddedTax, + }) .FirstOrDefault(x => x.Id == id); } public EditInstitutionContract GetFirstContract(long contractingPartyId, string typeOfContract) { return _context.InstitutionContractSet.Select(x => new EditInstitutionContract() - { - Id = x.id, - ContractNo = x.ContractNo, - ContractStartGr = x.ContractStartGr, - ContractStartFa = x.ContractStartFa, - ContractEndGr = x.ContractEndGr, - ContractEndFa = x.ContractEndFa, - RepresentativeName = x.RepresentativeName, - ContractingPartyName = x.ContractingPartyName, - RepresentativeId = x.RepresentativeId, - ContractingPartyId = x.ContractingPartyId, - ContractDateFa = x.ContractDateFa, - State = x.State, - City = x.City, - Address = x.Address, - Description = x.Description, - WorkshopManualCount = x.WorkshopManualCount, - EmployeeManualCount = x.EmployeeManualCount, - ContractAmountString = x.ContractAmount.ToMoney(), - DailyCompenseationString = x.DailyCompenseation.ToMoney(), - ObligationString = x.Obligation.ToMoney(), - TotalAmountString = x.TotalAmount.ToMoney(), - ExtensionNo = x.ExtensionNo, - OfficialCompany = x.OfficialCompany, - TypeOfContract = x.TypeOfContract, - Signature = x.Signature - }) + { + Id = x.id, + ContractNo = x.ContractNo, + ContractStartGr = x.ContractStartGr, + ContractStartFa = x.ContractStartFa, + ContractEndGr = x.ContractEndGr, + ContractEndFa = x.ContractEndFa, + RepresentativeName = x.RepresentativeName, + ContractingPartyName = x.ContractingPartyName, + RepresentativeId = x.RepresentativeId, + ContractingPartyId = x.ContractingPartyId, + ContractDateFa = x.ContractDateFa, + State = x.State, + City = x.City, + Address = x.Address, + Description = x.Description, + WorkshopManualCount = x.WorkshopManualCount, + EmployeeManualCount = x.EmployeeManualCount, + ContractAmountString = x.ContractAmount.ToMoney(), + DailyCompenseationString = x.DailyCompenseation.ToMoney(), + ObligationString = x.Obligation.ToMoney(), + TotalAmountString = x.TotalAmount.ToMoney(), + ExtensionNo = x.ExtensionNo, + OfficialCompany = x.OfficialCompany, + TypeOfContract = x.TypeOfContract, + Signature = x.Signature + }) .Where(x => x.ContractingPartyId == contractingPartyId && x.TypeOfContract == typeOfContract) .OrderBy(x => x.ExtensionNo).FirstOrDefault(); } @@ -582,40 +585,40 @@ public class InstitutionContractRepository : RepositoryBase new InstitutionContractViewModel() - { - Id = x.Id, - ContractNo = x.ContractNo, - ContractStartGr = x.ContractStartGr, - ContractStartFa = x.ContractStartFa, - ContractEndGr = x.ContractEndGr, - ContractEndFa = x.ContractEndFa, - RepresentativeId = x.RepresentativeId, - RepresentativeName = x.RepresentativeName, - ContractingPartyName = x.ContractingPartyName, - ContractingPartyId = x.ContractingPartyId, - ContractAmount = x.ContractAmount, - TotalAmount = x.TotalAmount, - SearchAmount = x.SearchAmount, - IsActiveString = x.IsActiveString, - OfficialCompany = x.OfficialCompany, - TypeOfContract = x.TypeOfContract, - Signature = x.Signature, - ExpireColor = x.ExpireColor, - IsExpier = x.IsExpier, - BalanceDouble = x.BalanceDouble, - BalanceStr = x.BalanceStr, - EmployerViewModels = x.EmployerViewModels, - EmployerNo = x.EmployerNo, - EmployerName = x.EmployerViewModels.Select(n => n.FullName).FirstOrDefault(), - WorkshopViewModels = x.WorkshopViewModels, - WorkshopCount = x.WorkshopCount, - IsContractingPartyBlock = x.IsContractingPartyBlock, - BlockTimes = x.BlockTimes, - EmployeeCount = + { + Id = x.Id, + ContractNo = x.ContractNo, + ContractStartGr = x.ContractStartGr, + ContractStartFa = x.ContractStartFa, + ContractEndGr = x.ContractEndGr, + ContractEndFa = x.ContractEndFa, + RepresentativeId = x.RepresentativeId, + RepresentativeName = x.RepresentativeName, + ContractingPartyName = x.ContractingPartyName, + ContractingPartyId = x.ContractingPartyId, + ContractAmount = x.ContractAmount, + TotalAmount = x.TotalAmount, + SearchAmount = x.SearchAmount, + IsActiveString = x.IsActiveString, + OfficialCompany = x.OfficialCompany, + TypeOfContract = x.TypeOfContract, + Signature = x.Signature, + ExpireColor = x.ExpireColor, + IsExpier = x.IsExpier, + BalanceDouble = x.BalanceDouble, + BalanceStr = x.BalanceStr, + EmployerViewModels = x.EmployerViewModels, + EmployerNo = x.EmployerNo, + EmployerName = x.EmployerViewModels.Select(n => n.FullName).FirstOrDefault(), + WorkshopViewModels = x.WorkshopViewModels, + WorkshopCount = x.WorkshopCount, + IsContractingPartyBlock = x.IsContractingPartyBlock, + BlockTimes = x.BlockTimes, + EmployeeCount = ((x.WorkshopViewModels.Sum(w => w.LeftWorkIds.Count)) + (x.WorkshopViewModels.Sum(w => w.InsuranceLeftWorkIds.Count(c => !w.LeftWorkIds.Contains(c))))).ToString(), - ArchiveCode = x.WorkshopViewModels.Count > 0 ? ArchiveCodeFinder(x.WorkshopViewModels) : 0, - }).OrderBy(x => x.WorkshopCount != "0" && string.IsNullOrWhiteSpace(x.ExpireColor)) + ArchiveCode = x.WorkshopViewModels.Count > 0 ? ArchiveCodeFinder(x.WorkshopViewModels) : 0, + }).OrderBy(x => x.WorkshopCount != "0" && string.IsNullOrWhiteSpace(x.ExpireColor)) .ThenBy(x => x.WorkshopCount == "0" && string.IsNullOrWhiteSpace(x.ExpireColor)) .ThenBy(x => x.IsExpier == "true") .ThenBy(x => x.ExpireColor == "purple") @@ -640,17 +643,17 @@ public class InstitutionContractRepository : RepositoryBase x.id == id); - + var prevInstitutionContracts = _context.InstitutionContractSet .Where(x => x.ContractingPartyId == institutionContarct.ContractingPartyId) .OrderByDescending(x => x.ContractEndGr).Skip(1).FirstOrDefault(); - + var transaction = _context.Database.BeginTransaction(); - + var contactInfo = _context.InstitutionContractContactInfos .Where(x => x.InstitutionContractId == id) .ToList(); - + if (contactInfo.Count > 0) { foreach (var item in contactInfo) @@ -673,20 +676,20 @@ public class InstitutionContractRepository : RepositoryBase x.FinancialTransactionList) - .FirstOrDefault(x=>x.ContractingPartyId == institutionContarct.ContractingPartyId); + .FirstOrDefault(x => x.ContractingPartyId == institutionContarct.ContractingPartyId); if (financialStatement != null) { - - var sumDebtor = financialStatement.FinancialTransactionList - .Sum(x => x.Deptor); + + var sumDebtor = financialStatement.FinancialTransactionList + .Sum(x => x.Deptor); var sumCreditor = financialStatement.FinancialTransactionList .Sum(x => x.Creditor); var balance = sumDebtor - sumCreditor; - if (balance>0 && prevInstitutionContracts.ContractEndGr 0 && prevInstitutionContracts.ContractEndGr < now) + { + prevInstitutionContracts.DeActiveBlue(); + _context.SaveChanges(); + } } } @@ -3573,19 +3576,19 @@ public class InstitutionContractRepository : RepositoryBase(); var institutionContracts = await _context.InstitutionContractSet.Select(x => new InstitutionContractViewModel - { - Id = x.id, - ContractingPartyId = x.ContractingPartyId, - ContractingPartyName = x.ContractingPartyName, - ContractStartGr = x.ContractStartGr, - ContractStartFa = x.ContractStartFa, - ContractEndGr = x.ContractEndGr, - ContractEndFa = x.ContractEndFa, - IsActiveString = x.IsActiveString, - ContractAmountDouble = x.ContractAmount, - OfficialCompany = x.OfficialCompany - }).Where(x => x.ContractStartGr < checkDate && x.ContractEndGr >= checkDate && - x.ContractAmountDouble > 0).GroupBy(x => x.ContractingPartyId).Select(x => x.First()) + { + Id = x.id, + ContractingPartyId = x.ContractingPartyId, + ContractingPartyName = x.ContractingPartyName, + ContractStartGr = x.ContractStartGr, + ContractStartFa = x.ContractStartFa, + ContractEndGr = x.ContractEndGr, + ContractEndFa = x.ContractEndFa, + IsActiveString = x.IsActiveString, + ContractAmountDouble = x.ContractAmount, + OfficialCompany = x.OfficialCompany + }).Where(x => x.ContractStartGr < checkDate && x.ContractEndGr >= checkDate && + x.ContractAmountDouble > 0).GroupBy(x => x.ContractingPartyId).Select(x => x.First()) .ToListAsync(); @@ -3679,9 +3682,12 @@ public class InstitutionContractRepository : RepositoryBase @@ -4258,6 +4307,88 @@ public class InstitutionContractRepository : RepositoryBase x.ContractingPartyId == contractingPartyId); } + #endregion + + + #region InstitutionContractConfirm + /// + /// ارسال پیامک یادآور تایید قراداد مالی + /// + /// + public async Task SendInstitutionContractConfirmSmsTask() + { + var now = DateTime.Now; + + + // تبدیل تاریخ میلادی به شمسی + var persianNow = now.ToFarsi(); + var persianEndOfMonth = int.Parse(persianNow.FindeEndOfMonth().Substring(8, 2)); + var dayOfMonth = int.Parse(persianNow.Substring(8, 2)); + var hour = now.Hour; + var minute = now.Minute; + var checkAnyToExecute = false; + + //اگر آخرین روز ماه باشد + //اگر روز مثلا عدد روز 31 بود ولی آخرین روز ماه 30 بود + if (dayOfMonth == persianEndOfMonth) + { + checkAnyToExecute = await _context.SmsSettings + .AnyAsync(x => + x.DayOfMonth >= dayOfMonth && /// اگر بزرگتر یا مساوی رو جاری بود + x.TimeOfDay.Hours == hour && + x.TimeOfDay.Minutes == minute && + x.TypeOfSmsSetting == TypeOfSmsSetting.InstitutionContractConfirm && + x.IsActive + ); + } + else + { + checkAnyToExecute = await _context.SmsSettings + .AnyAsync(x => + x.DayOfMonth == dayOfMonth && + x.TimeOfDay.Hours == hour && + x.TimeOfDay.Minutes == minute && + x.TypeOfSmsSetting == TypeOfSmsSetting.InstitutionContractConfirm && + x.IsActive + ); + } + + + if (checkAnyToExecute) + { + //اجرای تسک + + //دریافت لیست قراداد های تایید نشده + var fromAmonthAgo = now.AddDays(-30); + var pendingContracts = await _context.InstitutionContractSet + .Where(x => x.CreationDate >= fromAmonthAgo && x.CreationDate.Date != now.Date && x.VerificationStatus == InstitutionContractVerificationStatus.PendingForVerify) + .Join(_context.PersonalContractingParties, + contract => contract.ContractingPartyId, + contractingParty => contractingParty.id, + (contract, contractingParty) => new { contract, contractingParty }).Select(x => new InstitutionCreationVerificationSmsDto + { + Number = x.contractingParty.Phone, + FullName = x.contractingParty.IsLegal == "حقیقی" ? $"{x.contractingParty.FName} {x.contractingParty.LName}" : $"{x.contractingParty.LName}", + ContractingPartyId = x.contract.ContractingPartyId, + InstitutionContractId = x.contract.id, + InstitutionId = x.contract.PublicId, + }).ToListAsync(); + + string typeOfSms = "یادآور تایید قرارداد مالی"; + foreach (var item in pendingContracts) + { + var sendResult = await _smsService.SendInstitutionCreationVerificationLink(item.Number, item.FullName, + item.InstitutionId, item.ContractingPartyId, item.InstitutionContractId, typeOfSms); + } + + Console.WriteLine("executed at : " + persianNow + " - " + hour + ":" + minute); + + } + + + } + + #endregion #endregion @@ -4301,7 +4432,7 @@ public class InstitutionContractRepository : RepositoryBase new InstitutionContractInstallmentViewModel - { AmountDouble = ins.Amount, InstallmentDateGr = ins.InstallmentDateGr }) + { AmountDouble = ins.Amount, InstallmentDateGr = ins.InstallmentDateGr }) .OrderBy(ins => ins.InstallmentDateGr).Skip(1).ToList(), }).Where(x => x.ContractStartGr < endOfMonthGr && x.ContractEndGr >= endOfMonthGr && x.ContractAmountDouble > 0) @@ -4345,7 +4476,7 @@ public class InstitutionContractRepository : RepositoryBase new InstitutionContractInstallmentViewModel - { AmountDouble = ins.Amount, InstallmentDateGr = ins.InstallmentDateGr }) + { AmountDouble = ins.Amount, InstallmentDateGr = ins.InstallmentDateGr }) .OrderBy(ins => ins.InstallmentDateGr).Skip(1).ToList(), }).ToListAsync(); diff --git a/CompanyManagment.EFCore/Services/SmsService.cs b/CompanyManagment.EFCore/Services/SmsService.cs index 9a29b90b..55be5ff9 100644 --- a/CompanyManagment.EFCore/Services/SmsService.cs +++ b/CompanyManagment.EFCore/Services/SmsService.cs @@ -350,9 +350,12 @@ public class SmsService : ISmsService } - public async Task SendInstitutionCreationVerificationLink(string number, string fullName, Guid institutionId, long contractingPartyId, long institutionContractId) + public async Task SendInstitutionCreationVerificationLink(string number, string fullName, Guid institutionId, long contractingPartyId, long institutionContractId, string typeOfSms) { - var full = fullName; + typeOfSms = string.IsNullOrWhiteSpace(typeOfSms) ? "لینک تاییدیه ایجاد قرارداد مالی" : typeOfSms; + + + var full = fullName; var fullName1 = fullName; if (fullName.Length >= 25) { @@ -377,8 +380,9 @@ public class SmsService : ISmsService new("CODE1",firstPart), new("CODE2",secondPart) }); + - var smsResult = new SmsResult(verificationSendResult.Data.MessageId, verificationSendResult.Message, "لینک تاییدیه ایجاد قرارداد مالی", + var smsResult = new SmsResult(verificationSendResult.Data.MessageId, verificationSendResult.Message, typeOfSms, fullName, number, contractingPartyId, institutionContractId); await _smsResultRepository.CreateAsync(smsResult); await _smsResultRepository.SaveChangesAsync(); diff --git a/ServiceHost/Areas/Admin/Pages/Company/SmsResult/Index.cshtml b/ServiceHost/Areas/Admin/Pages/Company/SmsResult/Index.cshtml index 51e82b29..caf255e3 100644 --- a/ServiceHost/Areas/Admin/Pages/Company/SmsResult/Index.cshtml +++ b/ServiceHost/Areas/Admin/Pages/Company/SmsResult/Index.cshtml @@ -358,6 +358,7 @@ + From ef865d9c68676e8e7e76d2244008d1329dc922be Mon Sep 17 00:00:00 2001 From: mahan Date: Mon, 15 Dec 2025 20:56:32 +0330 Subject: [PATCH 10/27] Add Skill navigation to Phase/ProjectSection, refactor logic - Add Skill navigation properties to PhaseSection and ProjectSection, with EF Core mappings and migration for foreign keys and indexes. - Refactor SetSkillFlags in GetProjectsListQueryHandler for clarity and efficiency; use eager loading for Skill. - Add HasRemainingTime() to TaskSection and enforce time check in ChangeStatusSectionCommandHandler. - Optimize EmployeeDocumentsRepository queries; add EmployeeId to WorkshopWithEmployeeDocumentsViewModel. - Improve CustomExceptionHandler to handle FluentValidation exceptions and return proper status codes. - Add FluentValidation package reference and perform minor code cleanups. --- 0_Framework/0_Framework.csproj | 1 + .../Handler/CustomExceptionHandler.cs | 19 +- .../WorkshopWithEmployeeDocumentsViewModel.cs | 1 + .../Repository/EmployeeDocumentsRepository.cs | 124 ++- .../ChangeStatusSectionCommandHandler.cs | 7 +- .../GetProjectsListQueryHandler.cs | 127 ++- .../ProjectAgg/Entities/PhaseSection.cs | 2 + .../ProjectAgg/Entities/ProjectSection.cs | 2 + .../ProjectAgg/Entities/TaskSection.cs | 7 + ...on and projectsection to skill.Designer.cs | 857 ++++++++++++++++++ ...ase section and projectsection to skill.cs | 63 ++ .../Migrations/AppDbContextModelSnapshot.cs | 86 +- .../Mappings/PhaseSectionMapping.cs | 6 + .../Mappings/ProjectSectionMapping.cs | 6 + 14 files changed, 1137 insertions(+), 171 deletions(-) create mode 100644 ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251215155149_add relation to phase section and projectsection to skill.Designer.cs create mode 100644 ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251215155149_add relation to phase section and projectsection to skill.cs diff --git a/0_Framework/0_Framework.csproj b/0_Framework/0_Framework.csproj index ad747dc2..66522232 100644 --- a/0_Framework/0_Framework.csproj +++ b/0_Framework/0_Framework.csproj @@ -6,6 +6,7 @@ + diff --git a/0_Framework/Exceptions/Handler/CustomExceptionHandler.cs b/0_Framework/Exceptions/Handler/CustomExceptionHandler.cs index e6fd9da1..3af1276d 100644 --- a/0_Framework/Exceptions/Handler/CustomExceptionHandler.cs +++ b/0_Framework/Exceptions/Handler/CustomExceptionHandler.cs @@ -1,7 +1,10 @@ -using System; +#nullable enable +using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using FluentValidation; using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -27,6 +30,14 @@ public class CustomExceptionHandler : IExceptionHandler (string Detail, string Title, int StatusCode, Dictionary? Extra) details = exception switch { + ValidationException validationException => + ( + validationException.Errors.FirstOrDefault()?.ErrorMessage ?? "One or more validation errors occurred.", + "Validation Error", + context.Response.StatusCode = StatusCodes.Status400BadRequest, + null + ), + InternalServerException => ( exception.Message, @@ -34,6 +45,7 @@ public class CustomExceptionHandler : IExceptionHandler context.Response.StatusCode = StatusCodes.Status500InternalServerError, null ), + BadRequestException bre => ( exception.Message, @@ -41,6 +53,7 @@ public class CustomExceptionHandler : IExceptionHandler context.Response.StatusCode = StatusCodes.Status400BadRequest, bre.Extra ), + NotFoundException => ( exception.Message, @@ -48,6 +61,7 @@ public class CustomExceptionHandler : IExceptionHandler context.Response.StatusCode = StatusCodes.Status404NotFound, null ), + UnAuthorizeException => ( exception.Message, @@ -55,6 +69,7 @@ public class CustomExceptionHandler : IExceptionHandler context.Response.StatusCode = StatusCodes.Status401Unauthorized, null ), + _ => ( exception.Message, @@ -73,8 +88,6 @@ public class CustomExceptionHandler : IExceptionHandler Extensions = details.Extra ?? new Dictionary() }; - - problemDetails.Extensions.Add("traceId", context.TraceIdentifier); await context.Response.WriteAsJsonAsync(problemDetails, cancellationToken: cancellationToken); diff --git a/CompanyManagment.App.Contracts/EmployeeDocuments/WorkshopWithEmployeeDocumentsViewModel.cs b/CompanyManagment.App.Contracts/EmployeeDocuments/WorkshopWithEmployeeDocumentsViewModel.cs index bfc63e07..df9aedd1 100644 --- a/CompanyManagment.App.Contracts/EmployeeDocuments/WorkshopWithEmployeeDocumentsViewModel.cs +++ b/CompanyManagment.App.Contracts/EmployeeDocuments/WorkshopWithEmployeeDocumentsViewModel.cs @@ -13,5 +13,6 @@ namespace CompanyManagment.App.Contracts.EmployeeDocuments public string EmployerName { get; set; } public List SubmittedItems { get; set; } public int EmployeesWithoutDocumentCount { get; set; } + public long EmployeeId { get; set; } } } diff --git a/CompanyManagment.EFCore/Repository/EmployeeDocumentsRepository.cs b/CompanyManagment.EFCore/Repository/EmployeeDocumentsRepository.cs index 7635c7e9..1cffdc1d 100644 --- a/CompanyManagment.EFCore/Repository/EmployeeDocumentsRepository.cs +++ b/CompanyManagment.EFCore/Repository/EmployeeDocumentsRepository.cs @@ -1194,41 +1194,99 @@ public class EmployeeDocumentsRepository : RepositoryBase(); } + // Step 1: Get employee client temps in memory + var employeeClientTempData = await _companyContext.EmployeeClientTemps + .Where(x => workshops.Contains(x.WorkshopId)) + .Select(x => new { x.WorkshopId, x.EmployeeId }) + .ToListAsync(); + // Step 2: Get employee documents with simplified filter + var employeeDocuments = await _companyContext.EmployeeDocuments + .Where(x => workshops.Contains(x.WorkshopId) && !x.IsConfirmed && !x.IsSentToChecker) + .Select(x => new + { + x.id, + x.WorkshopId, + x.EmployeeId, + x.IsConfirmed, + x.IsSentToChecker, + WorkshopName = x.Workshop.WorkshopName + }) + .ToListAsync(); - var employeeClientTemp = _companyContext.EmployeeClientTemps.Where(x => workshops.Contains(x.WorkshopId)); + var filteredDocuments = employeeDocuments + .Where(x => employeeClientTempData.Any(temp => + temp.WorkshopId == x.WorkshopId && temp.EmployeeId == x.EmployeeId)) + .ToList(); + var groupedByWorkshop = filteredDocuments + .GroupBy(x => x.WorkshopId) + .Select(g => new + { + WorkshopId = g.Key, + WorkshopName = g.First().WorkshopName, + Count = g.Count() + }) + .ToList(); - var query = _companyContext.EmployeeDocuments - .Where(x => workshops.Contains(x.WorkshopId) && - employeeClientTemp.Any(temp => x.EmployeeId == temp.EmployeeId && temp.WorkshopId == x.WorkshopId) && x.IsConfirmed == false &&x.IsSentToChecker == false) - .Include(x => x.Workshop).Include(x => x.EmployeeDocumentItemCollection) - .GroupBy(x => x.WorkshopId).Select(x => new WorkshopWithEmployeeDocumentsViewModel() - { - WorkshopId = x.Key, - WorkshopFullName = x.FirstOrDefault().Workshop.WorkshopFullName, - EmployeesWithoutDocumentCount = x.Count() - }); + // Step 5: Get workshop employers for the filtered workshops + var workshopIds = groupedByWorkshop.Select(x => x.WorkshopId).ToList(); + var workshopEmployers = await _companyContext.WorkshopEmployers + .Where(x => workshopIds.Contains(x.WorkshopId)) + .Include(x => x.Employer) + .GroupBy(x => x.WorkshopId) + .Select(g => g.FirstOrDefault()) + .ToListAsync(); + + // Step 6: Build result + var res = groupedByWorkshop + .Select(x => new WorkshopWithEmployeeDocumentsViewModel() + { + WorkshopId = x.WorkshopId, + WorkshopFullName = x.WorkshopName, + EmployeesWithoutDocumentCount = x.Count, + EmployerName = workshopEmployers + .FirstOrDefault(y => y.WorkshopId == x.WorkshopId)? + .Employer?.FullName + }) + .Where(x => x.EmployeesWithoutDocumentCount > 0) + .OrderByDescending(x => x.EmployeesWithoutDocumentCount) + .ToList(); + + return res; + + //var employeeClientTemp = _companyContext.EmployeeClientTemps.Where(x => workshops.Contains(x.WorkshopId)); + + // var query = _companyContext.EmployeeDocuments + // .Where(x => workshops.Contains(x.WorkshopId) && + // employeeClientTemp.Any(temp => x.EmployeeId == temp.EmployeeId && temp.WorkshopId == x.WorkshopId) && x.IsConfirmed == false &&x.IsSentToChecker == false) + // .Include(x => x.Workshop).Include(x => x.EmployeeDocumentItemCollection) + // .GroupBy(x => x.WorkshopId).Select(x => new WorkshopWithEmployeeDocumentsViewModel() + // { + // WorkshopId = x.Key, + // WorkshopFullName = x.FirstOrDefault().Workshop.WorkshopFullName, + // EmployeesWithoutDocumentCount = x.Count() + // }); - var workshopEmployers = await _companyContext.WorkshopEmployers.Include(x => x.Employer) - .Where(x => query.Any(y => y.WorkshopId == x.WorkshopId)) - .GroupBy(x => x.WorkshopId).Select(x => x.FirstOrDefault()).ToListAsync(); + // var workshopEmployers = await _companyContext.WorkshopEmployers.Include(x => x.Employer) + // .Where(x => query.Any(y => y.WorkshopId == x.WorkshopId)) + // .GroupBy(x => x.WorkshopId).Select(x => x.FirstOrDefault()).ToListAsync(); - var result = await query.ToListAsync(); + // var result = await query.ToListAsync(); - result.ForEach(x => - { - var employer = workshopEmployers.FirstOrDefault(y => y.WorkshopId == x.WorkshopId)?.Employer; - x.EmployerName = employer?.FullName; + // result.ForEach(x => + // { + // var employer = workshopEmployers.FirstOrDefault(y => y.WorkshopId == x.WorkshopId)?.Employer; + // x.EmployerName = employer?.FullName; - //x.SubmittedItems.ForEach(y=>y.PicturePath= medias.FirstOrDefault(z=>z.id == y.MediaId)?.Path ?? ""); - }); + // //x.SubmittedItems.ForEach(y=>y.PicturePath= medias.FirstOrDefault(z=>z.id == y.MediaId)?.Path ?? ""); + // }); - return result.Where(x => x.EmployeesWithoutDocumentCount > 0).OrderByDescending(x => x.EmployeesWithoutDocumentCount).ToList(); + // return result.Where(x => x.EmployeesWithoutDocumentCount > 0).OrderByDescending(x => x.EmployeesWithoutDocumentCount).ToList(); } public async Task> GetCreatedEmployeesDocumentByWorkshopIdForAdmin(long workshopId) @@ -1347,33 +1405,37 @@ public class EmployeeDocumentsRepository : RepositoryBase workshops.Contains(x.WorkshopId)) - .Include(x => x.Employer).Select(x => x.Employer.ContractingPartyId).Distinct(); + .Include(x => x.Employer).Select(x => x.Employer.ContractingPartyId).Distinct().ToList(); var accountIds = await _companyContext.ContractingPartyAccounts .Where(x => contractingPartyIds.Contains(x.PersonalContractingPartyId)).Select(x => x.AccountId) .ToListAsync(); - var query = _companyContext.EmployeeDocuments - .Where(x => workshops.Contains(x.WorkshopId) && - (allActiveEmployees.Any(y => y.WorkshopId == x.WorkshopId && y.EmployeeId == x.EmployeeId))) + var query =await _companyContext.EmployeeDocuments + .Where(x => workshops.Contains(x.WorkshopId)) .Include(x => x.Workshop).Include(x => x.EmployeeDocumentItemCollection) .Where(x => x.IsSentToChecker == false && x.HasRejectedItems && x.EmployeeDocumentItemCollection.Any(i => i.DocumentStatus == DocumentStatus.Rejected && accountIds.Contains(i.UploaderId))) .GroupBy(x => x.WorkshopId).Select(x => new WorkshopWithEmployeeDocumentsViewModel() { WorkshopId = x.Key, WorkshopFullName = x.FirstOrDefault().Workshop.WorkshopName, - EmployeesWithoutDocumentCount = x.Count() - }); + EmployeesWithoutDocumentCount = x.Count(), + EmployeeId = x.First().EmployeeId + }).ToListAsync(); + query = query.Where(x => + (allActiveEmployees.Any(y => y.WorkshopId == x.WorkshopId && y.EmployeeId == x.EmployeeId))).ToList(); + + var resWorkshopIds = query.Select(x => x.WorkshopId).ToList(); var workshopEmployers = await _companyContext.WorkshopEmployers.Include(x => x.Employer) - .Where(x => query.Any(y => y.WorkshopId == x.WorkshopId)) + .Where(x => resWorkshopIds.Contains(x.WorkshopId)) .GroupBy(x => x.WorkshopId).Select(x => x.FirstOrDefault()).ToListAsync(); - var result = await query.ToListAsync(); + var result = query; result.ForEach(x => diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Commands/ChangeStatusSection/ChangeStatusSectionCommandHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Commands/ChangeStatusSection/ChangeStatusSectionCommandHandler.cs index 4214ec2d..9e6803f5 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Commands/ChangeStatusSection/ChangeStatusSectionCommandHandler.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Commands/ChangeStatusSection/ChangeStatusSectionCommandHandler.cs @@ -49,8 +49,11 @@ public class ChangeStatusSectionCommandHandler : IBaseCommandHandler projects, CancellationToken cancellationToken) { + if (!projects.Any()) + return; + var projectIds = projects.Select(x => x.Id).ToList(); - - // تنها تسک‌ها sections دارند، بنابراین برای سطوح مختلف باید متفاوت عمل کنیم - List taskIds; - - switch (projects.FirstOrDefault()?.Level) + var hierarchyLevel = projects.First().Level; + + switch (hierarchyLevel) { case ProjectHierarchyLevel.Project: - // برای پروژه‌ها، باید تمام تسک‌های زیرمجموعه را پیدا کنیم - var phaseIds = await _context.ProjectPhases - .Where(ph => projectIds.Contains(ph.ProjectId)) - .Select(ph => ph.Id) - .ToListAsync(cancellationToken); - - taskIds = await _context.ProjectTasks - .Where(t => phaseIds.Contains(t.PhaseId)) - .Select(t => t.Id) - .ToListAsync(cancellationToken); + await SetSkillFlagsForProjects(projects, projectIds, cancellationToken); break; case ProjectHierarchyLevel.Phase: - // برای فازها، تمام تسک‌های آن فازها را پیدا کنیم - taskIds = await _context.ProjectTasks - .Where(t => projectIds.Contains(t.PhaseId)) - .Select(t => t.Id) - .ToListAsync(cancellationToken); + await SetSkillFlagsForPhases(projects, projectIds, cancellationToken); break; case ProjectHierarchyLevel.Task: - // برای تسک‌ها، خود آنها taskIds هستند - taskIds = projectIds; + await SetSkillFlagsForTasks(projects, projectIds, cancellationToken); break; - - default: - return; } + } - if (!taskIds.Any()) - return; - - var sections = await _context.TaskSections + private async Task SetSkillFlagsForProjects(List projects, List projectIds, CancellationToken cancellationToken) + { + var projectSections = await _context.ProjectSections .Include(x => x.Skill) - .Where(x => taskIds.Contains(x.TaskId)) + .Where(s => projectIds.Contains(s.ProjectId)) .ToListAsync(cancellationToken); + if (!projectSections.Any()) + return; + foreach (var project in projects) { - List relevantTaskIds; - - switch (project.Level) - { - case ProjectHierarchyLevel.Project: - // برای پروژه، تمام تسک‌های زیرمجموعه - var projectPhaseIds = await _context.ProjectPhases - .Where(ph => ph.ProjectId == project.Id) - .Select(ph => ph.Id) - .ToListAsync(cancellationToken); - - relevantTaskIds = await _context.ProjectTasks - .Where(t => projectPhaseIds.Contains(t.PhaseId)) - .Select(t => t.Id) - .ToListAsync(cancellationToken); - break; - - case ProjectHierarchyLevel.Phase: - // برای فاز، تمام تسک‌های آن فاز - relevantTaskIds = await _context.ProjectTasks - .Where(t => t.PhaseId == project.Id) - .Select(t => t.Id) - .ToListAsync(cancellationToken); - break; - - case ProjectHierarchyLevel.Task: - // برای تسک، خود آن - relevantTaskIds = new List { project.Id }; - break; - - default: - continue; - } - - var projectSections = sections.Where(x => relevantTaskIds.Contains(x.TaskId)).ToList(); - project.HasBackend = projectSections.Any(x => x.Skill.Name == "Backend"); - project.HasFront = projectSections.Any(x => x.Skill.Name == "Frontend"); - project.HasDesign = projectSections.Any(x => x.Skill.Name == "UI/UX Design"); + var sections = projectSections.Where(s => s.ProjectId == project.Id).ToList(); + project.HasBackend = sections.Any(x => x.Skill?.Name == "Backend"); + project.HasFront = sections.Any(x => x.Skill?.Name == "Frontend"); + project.HasDesign = sections.Any(x => x.Skill?.Name == "UI/UX Design"); + } + } + + private async Task SetSkillFlagsForPhases(List projects, List phaseIds, CancellationToken cancellationToken) + { + var phaseSections = await _context.PhaseSections + .Include(x => x.Skill) + .Where(s => phaseIds.Contains(s.PhaseId)) + .ToListAsync(cancellationToken); + + if (!phaseSections.Any()) + return; + + foreach (var phase in projects) + { + var sections = phaseSections.Where(s => s.PhaseId == phase.Id).ToList(); + phase.HasBackend = sections.Any(x => x.Skill?.Name == "Backend"); + phase.HasFront = sections.Any(x => x.Skill?.Name == "Frontend"); + phase.HasDesign = sections.Any(x => x.Skill?.Name == "UI/UX Design"); + } + } + + private async Task SetSkillFlagsForTasks(List projects, List taskIds, CancellationToken cancellationToken) + { + var taskSections = await _context.TaskSections + .Include(x => x.Skill) + .Where(s => taskIds.Contains(s.TaskId)) + .ToListAsync(cancellationToken); + + if (!taskSections.Any()) + return; + + foreach (var task in projects) + { + var sections = taskSections.Where(s => s.TaskId == task.Id).ToList(); + task.HasBackend = sections.Any(x => x.Skill?.Name == "Backend"); + task.HasFront = sections.Any(x => x.Skill?.Name == "Frontend"); + task.HasDesign = sections.Any(x => x.Skill?.Name == "UI/UX Design"); } } diff --git a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/PhaseSection.cs b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/PhaseSection.cs index 33bdfcdf..05c7bb83 100644 --- a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/PhaseSection.cs +++ b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/PhaseSection.cs @@ -1,4 +1,5 @@ using GozareshgirProgramManager.Domain._Common; +using GozareshgirProgramManager.Domain.SkillAgg.Entities; namespace GozareshgirProgramManager.Domain.ProjectAgg.Entities; @@ -22,6 +23,7 @@ public class PhaseSection : EntityBase // Navigation property public ProjectPhase Phase { get; private set; } = null!; + public Skill? Skill { get; set; } public void UpdateUser(long userId) { diff --git a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/ProjectSection.cs b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/ProjectSection.cs index 67820d49..d41c5eb0 100644 --- a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/ProjectSection.cs +++ b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/ProjectSection.cs @@ -1,4 +1,5 @@ using GozareshgirProgramManager.Domain._Common; +using GozareshgirProgramManager.Domain.SkillAgg.Entities; namespace GozareshgirProgramManager.Domain.ProjectAgg.Entities; @@ -21,6 +22,7 @@ public class ProjectSection : EntityBase public Guid SkillId { get; private set; } public Project Project { get; private set; } = null!; + public Skill? Skill { get; set; } public void UpdateUser(long userId) { diff --git a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/TaskSection.cs b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/TaskSection.cs index c663ed4b..7fd3f3e8 100644 --- a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/TaskSection.cs +++ b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/ProjectAgg/Entities/TaskSection.cs @@ -210,4 +210,11 @@ public class TaskSection : EntityBase { _additionalTimes.Clear(); } + + public bool HasRemainingTime() + { + var totalSpent = GetTotalTimeSpent(); + var finalEstimate = FinalEstimatedHours; + return totalSpent < finalEstimate; + } } \ No newline at end of file diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251215155149_add relation to phase section and projectsection to skill.Designer.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251215155149_add relation to phase section and projectsection to skill.Designer.cs new file mode 100644 index 00000000..2fbb0fa8 --- /dev/null +++ b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251215155149_add relation to phase section and projectsection to skill.Designer.cs @@ -0,0 +1,857 @@ +// +using System; +using GozareshgirProgramManager.Infrastructure.Persistence.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace GozareshgirProgramManager.Infrastructure.Migrations +{ + [DbContext(typeof(ProgramManagerDbContext))] + [Migration("20251215155149_add relation to phase section and projectsection to skill")] + partial class addrelationtophasesectionandprojectsectiontoskill + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.CheckoutAgg.Entities.Checkout", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CheckoutEndDate") + .HasColumnType("datetime2"); + + b.Property("CheckoutStartDate") + .HasColumnType("datetime2"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("DeductionFromSalary") + .HasColumnType("float"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("MandatoryHours") + .HasColumnType("int"); + + b.Property("Month") + .HasColumnType("int"); + + b.Property("MonthlySalaryDefined") + .HasColumnType("float"); + + b.Property("MonthlySalaryPay") + .HasColumnType("float"); + + b.Property("RemainingHours") + .HasColumnType("int"); + + b.Property("TotalDaysWorked") + .HasColumnType("int"); + + b.Property("TotalHoursWorked") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Checkouts", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.CustomerAgg.Customer", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.ToTable("Customers", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.PhaseSection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("PhaseId") + .HasColumnType("uniqueidentifier"); + + b.Property("SkillId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("PhaseId"); + + b.HasIndex("SkillId"); + + b.ToTable("PhaseSections"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.Project", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("HasAssignmentOverride") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("PlannedEndDate") + .HasColumnType("datetime2"); + + b.Property("PlannedStartDate") + .HasColumnType("datetime2"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectPhase", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("HasAssignmentOverride") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("OrderIndex") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("ProjectPhases", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectSection", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("SkillId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.HasIndex("SkillId"); + + b.ToTable("ProjectSections"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectTask", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AllocatedTime") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DueDate") + .HasColumnType("datetime2"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("HasAssignmentOverride") + .HasColumnType("bit"); + + b.Property("HasTimeOverride") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("OrderIndex") + .HasColumnType("int"); + + b.Property("PhaseId") + .HasColumnType("uniqueidentifier"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("PhaseId"); + + b.ToTable("ProjectTasks", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSection", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("CurrentAssignedUserId") + .HasColumnType("bigint"); + + b.Property("InitialDescription") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("InitialEstimatedHours") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("OriginalAssignedUserId") + .HasColumnType("bigint"); + + b.Property("SkillId") + .HasColumnType("uniqueidentifier"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("TaskId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SkillId"); + + b.HasIndex("TaskId"); + + b.ToTable("TaskSections", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSectionActivity", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("EndNotes") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("SectionId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("SectionId"); + + b.ToTable("TaskSectionActivities", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSectionAdditionalTime", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AddedAt") + .HasColumnType("datetime2"); + + b.Property("AddedByUserId") + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Hours") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("TaskSectionId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TaskSectionId"); + + b.ToTable("TaskSectionAdditionalTimes", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.RoleAgg.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("GozareshgirRoleId") + .HasColumnType("bigint"); + + b.Property("RoleName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.ToTable("PmRoles", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.SalaryPaymentSettingAgg.Entities.SalaryPaymentSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("EndSettingDate") + .HasColumnType("datetime2"); + + b.Property("HolidayWorking") + .HasColumnType("bit"); + + b.Property("MonthlySalary") + .HasColumnType("float"); + + b.Property("StartSettingDate") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("SalaryPaymentSetting", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.SkillAgg.Entities.Skill", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.ToTable("Skills", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Mobile") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("ProfilePhotoPath") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("VerifyCode") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.HasKey("Id"); + + b.ToTable("Users", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.UserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("RevokedAt") + .HasColumnType("datetime2"); + + b.Property("Token") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserAgent") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ExpiresAt"); + + b.HasIndex("Token") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("UserRefreshTokens", (string)null); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.PhaseSection", b => + { + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectPhase", "Phase") + .WithMany("PhaseSections") + .HasForeignKey("PhaseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GozareshgirProgramManager.Domain.SkillAgg.Entities.Skill", "Skill") + .WithMany() + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Phase"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectPhase", b => + { + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.Project", "Project") + .WithMany("Phases") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectSection", b => + { + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.Project", "Project") + .WithMany("ProjectSections") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GozareshgirProgramManager.Domain.SkillAgg.Entities.Skill", "Skill") + .WithMany() + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Project"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectTask", b => + { + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectPhase", "Phase") + .WithMany("Tasks") + .HasForeignKey("PhaseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Phase"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSection", b => + { + b.HasOne("GozareshgirProgramManager.Domain.SkillAgg.Entities.Skill", "Skill") + .WithMany("Sections") + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectTask", "Task") + .WithMany("Sections") + .HasForeignKey("TaskId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Skill"); + + b.Navigation("Task"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSectionActivity", b => + { + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSection", "Section") + .WithMany("Activities") + .HasForeignKey("SectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Section"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSectionAdditionalTime", b => + { + b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSection", null) + .WithMany("AdditionalTimes") + .HasForeignKey("TaskSectionId"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.RoleAgg.Entities.Role", b => + { + b.OwnsMany("GozareshgirProgramManager.Domain.PermissionAgg.Entities.Permission", "Permissions", b1 => + { + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b1.Property("Id")); + + b1.Property("Code") + .HasColumnType("int"); + + b1.Property("RoleId") + .HasColumnType("bigint"); + + b1.HasKey("Id"); + + b1.HasIndex("RoleId"); + + b1.ToTable("PmRolePermissions", (string)null); + + b1.WithOwner("Role") + .HasForeignKey("RoleId"); + + b1.Navigation("Role"); + }); + + b.Navigation("Permissions"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.SalaryPaymentSettingAgg.Entities.SalaryPaymentSetting", b => + { + b.OwnsMany("GozareshgirProgramManager.Domain.SalaryPaymentSettingAgg.Entities.WorkingHours", "WorkingHoursList", b1 => + { + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b1.Property("Id")); + + b1.Property("EndShiftOne") + .HasColumnType("time(0)"); + + b1.Property("EndShiftTwo") + .HasColumnType("time(0)"); + + b1.Property("HasRestTime") + .HasColumnType("bit"); + + b1.Property("HasShiftOne") + .HasColumnType("bit"); + + b1.Property("HasShiftTow") + .HasColumnType("bit"); + + b1.Property("IsActiveDay") + .HasColumnType("bit"); + + b1.Property("PersianDayOfWeek") + .HasColumnType("int"); + + b1.Property("RestTime") + .HasColumnType("time(0)"); + + b1.Property("SalaryPaymentSettingId") + .HasColumnType("bigint"); + + b1.Property("ShiftDurationInMinutes") + .HasColumnType("int"); + + b1.Property("StartShiftOne") + .HasColumnType("time(0)"); + + b1.Property("StartShiftTwo") + .HasColumnType("time(0)"); + + b1.HasKey("Id"); + + b1.HasIndex("SalaryPaymentSettingId"); + + b1.ToTable("WorkingHours", (string)null); + + b1.WithOwner("SalaryPaymentSetting") + .HasForeignKey("SalaryPaymentSettingId"); + + b1.Navigation("SalaryPaymentSetting"); + }); + + b.Navigation("WorkingHoursList"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.User", b => + { + b.OwnsMany("GozareshgirProgramManager.Domain.RoleUserAgg.RoleUser", "RoleUser", b1 => + { + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b1.Property("Id")); + + b1.Property("RoleId") + .HasColumnType("bigint"); + + b1.Property("UserId") + .HasColumnType("bigint"); + + b1.HasKey("Id"); + + b1.HasIndex("UserId"); + + b1.ToTable("RoleUsers", (string)null); + + b1.WithOwner("User") + .HasForeignKey("UserId"); + + b1.Navigation("User"); + }); + + b.Navigation("RoleUser"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.UserRefreshToken", b => + { + b.HasOne("GozareshgirProgramManager.Domain.UserAgg.Entities.User", "User") + .WithMany("RefreshTokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.Project", b => + { + b.Navigation("Phases"); + + b.Navigation("ProjectSections"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectPhase", b => + { + b.Navigation("PhaseSections"); + + b.Navigation("Tasks"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectTask", b => + { + b.Navigation("Sections"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.TaskSection", b => + { + b.Navigation("Activities"); + + b.Navigation("AdditionalTimes"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.SkillAgg.Entities.Skill", b => + { + b.Navigation("Sections"); + }); + + modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.User", b => + { + b.Navigation("RefreshTokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251215155149_add relation to phase section and projectsection to skill.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251215155149_add relation to phase section and projectsection to skill.cs new file mode 100644 index 00000000..3f63a30b --- /dev/null +++ b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/20251215155149_add relation to phase section and projectsection to skill.cs @@ -0,0 +1,63 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace GozareshgirProgramManager.Infrastructure.Migrations +{ + /// + public partial class addrelationtophasesectionandprojectsectiontoskill : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + migrationBuilder.CreateIndex( + name: "IX_ProjectSections_SkillId", + table: "ProjectSections", + column: "SkillId"); + + migrationBuilder.CreateIndex( + name: "IX_PhaseSections_SkillId", + table: "PhaseSections", + column: "SkillId"); + + migrationBuilder.AddForeignKey( + name: "FK_PhaseSections_Skills_SkillId", + table: "PhaseSections", + column: "SkillId", + principalTable: "Skills", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ProjectSections_Skills_SkillId", + table: "ProjectSections", + column: "SkillId", + principalTable: "Skills", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_PhaseSections_Skills_SkillId", + table: "PhaseSections"); + + migrationBuilder.DropForeignKey( + name: "FK_ProjectSections_Skills_SkillId", + table: "ProjectSections"); + + migrationBuilder.DropIndex( + name: "IX_ProjectSections_SkillId", + table: "ProjectSections"); + + migrationBuilder.DropIndex( + name: "IX_PhaseSections_SkillId", + table: "PhaseSections"); + + } + } +} diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/AppDbContextModelSnapshot.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/AppDbContextModelSnapshot.cs index 8e4c3871..9ea2308c 100644 --- a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/AppDbContextModelSnapshot.cs +++ b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Migrations/AppDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("ProductVersion", "10.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -102,56 +102,6 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations b.ToTable("Customers", (string)null); }); - modelBuilder.Entity("GozareshgirProgramManager.Domain.HolidayAgg.Holiday", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CreationDate") - .HasColumnType("datetime2"); - - b.Property("Year") - .IsRequired() - .HasMaxLength(4) - .HasColumnType("nvarchar(4)"); - - b.HasKey("Id"); - - b.ToTable("Holidays", (string)null); - }); - - modelBuilder.Entity("GozareshgirProgramManager.Domain.HolidayItemAgg.HolidayItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CreationDate") - .HasColumnType("datetime2"); - - b.Property("HolidayId") - .HasColumnType("bigint"); - - b.Property("HolidayYear") - .IsRequired() - .HasMaxLength(4) - .HasColumnType("nvarchar(4)"); - - b.Property("Holidaydate") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.HasIndex("HolidayId"); - - b.ToTable("Holidayitems", (string)null); - }); - modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.PhaseSection", b => { b.Property("Id") @@ -174,6 +124,8 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations b.HasIndex("PhaseId"); + b.HasIndex("SkillId"); + b.ToTable("PhaseSections"); }); @@ -284,6 +236,8 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations b.HasIndex("ProjectId"); + b.HasIndex("SkillId"); + b.ToTable("ProjectSections"); }); @@ -630,17 +584,6 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations b.ToTable("UserRefreshTokens", (string)null); }); - modelBuilder.Entity("GozareshgirProgramManager.Domain.HolidayItemAgg.HolidayItem", b => - { - b.HasOne("GozareshgirProgramManager.Domain.HolidayAgg.Holiday", "Holidayss") - .WithMany("HolidayItems") - .HasForeignKey("HolidayId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Holidayss"); - }); - modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.PhaseSection", b => { b.HasOne("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectPhase", "Phase") @@ -649,7 +592,14 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("GozareshgirProgramManager.Domain.SkillAgg.Entities.Skill", "Skill") + .WithMany() + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Restrict); + b.Navigation("Phase"); + + b.Navigation("Skill"); }); modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectPhase", b => @@ -671,7 +621,14 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("GozareshgirProgramManager.Domain.SkillAgg.Entities.Skill", "Skill") + .WithMany() + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Restrict); + b.Navigation("Project"); + + b.Navigation("Skill"); }); modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.ProjectTask", b => @@ -856,11 +813,6 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations b.Navigation("User"); }); - modelBuilder.Entity("GozareshgirProgramManager.Domain.HolidayAgg.Holiday", b => - { - b.Navigation("HolidayItems"); - }); - modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.Project", b => { b.Navigation("Phases"); diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/PhaseSectionMapping.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/PhaseSectionMapping.cs index c1db7d49..1b4a829f 100644 --- a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/PhaseSectionMapping.cs +++ b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/PhaseSectionMapping.cs @@ -17,5 +17,11 @@ public class PhaseSectionMapping:IEntityTypeConfiguration .WithMany(p => p.PhaseSections) .HasForeignKey(ps => ps.PhaseId) .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(ps => ps.Skill) + .WithMany() + .HasForeignKey(ps => ps.SkillId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Restrict); } } \ No newline at end of file diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/ProjectSectionMapping.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/ProjectSectionMapping.cs index ebc8df44..454ab86d 100644 --- a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/ProjectSectionMapping.cs +++ b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Persistence/Mappings/ProjectSectionMapping.cs @@ -16,5 +16,11 @@ public class ProjectSectionMapping:IEntityTypeConfiguration .WithMany(x => x.ProjectSections) .HasForeignKey(x => x.ProjectId) .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(x => x.Skill) + .WithMany() + .HasForeignKey(x => x.SkillId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Restrict); } } \ No newline at end of file From 35b7a3a3dd66520bf3fd94cc87cc4d4203e96bce Mon Sep 17 00:00:00 2001 From: mahan Date: Tue, 16 Dec 2025 09:32:42 +0330 Subject: [PATCH 11/27] remove documentation file generation from project settings --- ServiceHost/ServiceHost.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/ServiceHost/ServiceHost.csproj b/ServiceHost/ServiceHost.csproj index 6b5cbcc6..d6666050 100644 --- a/ServiceHost/ServiceHost.csproj +++ b/ServiceHost/ServiceHost.csproj @@ -3,7 +3,6 @@ net10.0 enable - true From b64d0e5ffd2824bd65f3bfd0db50e8c1d6d07c99 Mon Sep 17 00:00:00 2001 From: mahan Date: Tue, 16 Dec 2025 10:39:51 +0330 Subject: [PATCH 12/27] feat: add method to update employee full name in FaceEmbeddingService and integrate with RollCallEmployeeApplication --- .../FaceEmbedding/IFaceEmbeddingService.cs | 1 + .../InfraStructure/FaceEmbeddingService.cs | 644 ++++++++++-------- .../IRollCallEmployeeApplication.cs | 1 + .../RollCallEmployeeApplication.cs | 17 +- ServiceHost/ServiceHost.csproj | 3 +- 5 files changed, 370 insertions(+), 296 deletions(-) diff --git a/0_Framework/Application/FaceEmbedding/IFaceEmbeddingService.cs b/0_Framework/Application/FaceEmbedding/IFaceEmbeddingService.cs index b5aab83b..78dbf735 100644 --- a/0_Framework/Application/FaceEmbedding/IFaceEmbeddingService.cs +++ b/0_Framework/Application/FaceEmbedding/IFaceEmbeddingService.cs @@ -11,6 +11,7 @@ public interface IFaceEmbeddingService Task RefineEmbeddingAsync(long employeeId, long workshopId, float[] embedding, float confidence, Dictionary metadata = null); Task DeleteEmbeddingAsync(long employeeId, long workshopId); Task> GetEmbeddingAsync(long employeeId, long workshopId); + Task UpdateEmbeddingFullNameAsync(long employeeId, long workshopId, string newFullName); } public class FaceEmbeddingResponse diff --git a/0_Framework/InfraStructure/FaceEmbeddingService.cs b/0_Framework/InfraStructure/FaceEmbeddingService.cs index 0b709ed5..148adc80 100644 --- a/0_Framework/InfraStructure/FaceEmbeddingService.cs +++ b/0_Framework/InfraStructure/FaceEmbeddingService.cs @@ -18,329 +18,387 @@ namespace _0_Framework.Infrastructure; /// public class FaceEmbeddingService : IFaceEmbeddingService { - private readonly IHttpClientFactory _httpClientFactory; - private readonly ILogger _logger; - private readonly IFaceEmbeddingNotificationService _notificationService; - private readonly string _apiBaseUrl; + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger _logger; + private readonly IFaceEmbeddingNotificationService _notificationService; + private readonly string _apiBaseUrl; - public FaceEmbeddingService(IHttpClientFactory httpClientFactory, ILogger logger, - IFaceEmbeddingNotificationService notificationService = null) - { - _httpClientFactory = httpClientFactory; - _logger = logger; - _notificationService = notificationService; - _apiBaseUrl = "http://localhost:8000"; - } + public FaceEmbeddingService(IHttpClientFactory httpClientFactory, ILogger logger, + IFaceEmbeddingNotificationService notificationService = null) + { + _httpClientFactory = httpClientFactory; + _logger = logger; + _notificationService = notificationService; + _apiBaseUrl = "http://localhost:8000"; + } - public async Task GenerateEmbeddingsAsync(long employeeId, long workshopId, - string employeeFullName, string picture1Path, string picture2Path) - { - try - { - var httpClient = _httpClientFactory.CreateClient(); - httpClient.BaseAddress = new Uri(_apiBaseUrl); - httpClient.Timeout = TimeSpan.FromSeconds(30); + public async Task GenerateEmbeddingsAsync(long employeeId, long workshopId, + string employeeFullName, string picture1Path, string picture2Path) + { + try + { + var httpClient = _httpClientFactory.CreateClient(); + httpClient.BaseAddress = new Uri(_apiBaseUrl); + httpClient.Timeout = TimeSpan.FromSeconds(30); - using var content = new MultipartFormDataContent(); + using var content = new MultipartFormDataContent(); - // Add form fields - content.Add(new StringContent(employeeId.ToString()), "employee_id"); - content.Add(new StringContent(workshopId.ToString()), "workshop_id"); - content.Add(new StringContent(employeeFullName ?? ""), "employee_full_name"); + // Add form fields + content.Add(new StringContent(employeeId.ToString()), "employee_id"); + content.Add(new StringContent(workshopId.ToString()), "workshop_id"); + content.Add(new StringContent(employeeFullName ?? ""), "employee_full_name"); - // Add picture files - if (File.Exists(picture1Path)) - { - var picture1Bytes = await File.ReadAllBytesAsync(picture1Path); - var picture1Content = new ByteArrayContent(picture1Bytes); - picture1Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg"); - content.Add(picture1Content, "picture1", "1.jpg"); - } - else - { - _logger.LogWarning("Picture1 not found at path: {Path}", picture1Path); - return new OperationResult { IsSuccedded = false, Message = "تصویر اول یافت نشد" }; - } + // Add picture files + if (File.Exists(picture1Path)) + { + var picture1Bytes = await File.ReadAllBytesAsync(picture1Path); + var picture1Content = new ByteArrayContent(picture1Bytes); + picture1Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg"); + content.Add(picture1Content, "picture1", "1.jpg"); + } + else + { + _logger.LogWarning("Picture1 not found at path: {Path}", picture1Path); + return new OperationResult { IsSuccedded = false, Message = "تصویر اول یافت نشد" }; + } - if (File.Exists(picture2Path)) - { - var picture2Bytes = await File.ReadAllBytesAsync(picture2Path); - var picture2Content = new ByteArrayContent(picture2Bytes); - picture2Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg"); - content.Add(picture2Content, "picture2", "2.jpg"); - } - else - { - _logger.LogWarning("Picture2 not found at path: {Path}", picture2Path); - return new OperationResult { IsSuccedded = false, Message = "تصویر دوم یافت نشد" }; - } + if (File.Exists(picture2Path)) + { + var picture2Bytes = await File.ReadAllBytesAsync(picture2Path); + var picture2Content = new ByteArrayContent(picture2Bytes); + picture2Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg"); + content.Add(picture2Content, "picture2", "2.jpg"); + } + else + { + _logger.LogWarning("Picture2 not found at path: {Path}", picture2Path); + return new OperationResult { IsSuccedded = false, Message = "تصویر دوم یافت نشد" }; + } - // Send request to Python API - var response = await httpClient.PostAsync("embeddings", content); + // Send request to Python API + var response = await httpClient.PostAsync("embeddings", content); - if (response.IsSuccessStatusCode) - { - var responseContent = await response.Content.ReadAsStringAsync(); - _logger.LogInformation("Embeddings generated successfully for Employee {EmployeeId}, Workshop {WorkshopId}", - employeeId, workshopId); + if (response.IsSuccessStatusCode) + { + var responseContent = await response.Content.ReadAsStringAsync(); + _logger.LogInformation( + "Embeddings generated successfully for Employee {EmployeeId}, Workshop {WorkshopId}", + employeeId, workshopId); - // ارسال اطلاع‌رسانی به سایر سیستم‌ها - if (_notificationService != null) - { - await _notificationService.NotifyEmbeddingCreatedAsync(workshopId, employeeId, employeeFullName); - } + // ارسال اطلاع‌رسانی به سایر سیستم‌ها + if (_notificationService != null) + { + await _notificationService.NotifyEmbeddingCreatedAsync(workshopId, employeeId, employeeFullName); + } - return new OperationResult - { - IsSuccedded = true, - Message = "Embedding با موفقیت ایجاد شد" - }; - } - else - { - var errorContent = await response.Content.ReadAsStringAsync(); - _logger.LogError("Failed to generate embeddings. Status: {StatusCode}, Error: {Error}", - response.StatusCode, errorContent); + return new OperationResult + { + IsSuccedded = true, + Message = "Embedding با موفقیت ایجاد شد" + }; + } + else + { + var errorContent = await response.Content.ReadAsStringAsync(); + _logger.LogError("Failed to generate embeddings. Status: {StatusCode}, Error: {Error}", + response.StatusCode, errorContent); - return new OperationResult - { - IsSuccedded = false, - Message = $"خطا در تولید Embedding: {response.StatusCode}" - }; - } - } - catch (HttpRequestException ex) - { - _logger.LogError(ex, "HTTP error while calling embeddings API for Employee {EmployeeId}", employeeId); - return new OperationResult - { - IsSuccedded = false, - Message = "خطا در ارتباط با سرور Embedding" - }; - } - catch (Exception ex) - { - _logger.LogError(ex, "Error while calling embeddings API for Employee {EmployeeId}", employeeId); - return new OperationResult - { - IsSuccedded = false, - Message = "خطای غیرمنتظره در تولید Embedding" - }; - } - } + return new OperationResult + { + IsSuccedded = false, + Message = $"خطا در تولید Embedding: {response.StatusCode}" + }; + } + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "HTTP error while calling embeddings API for Employee {EmployeeId}", employeeId); + return new OperationResult + { + IsSuccedded = false, + Message = "خطا در ارتباط با سرور Embedding" + }; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while calling embeddings API for Employee {EmployeeId}", employeeId); + return new OperationResult + { + IsSuccedded = false, + Message = "خطای غیرمنتظره در تولید Embedding" + }; + } + } - public async Task GenerateEmbeddingsFromStreamAsync(long employeeId, long workshopId, - string employeeFullName, Stream picture1Stream, Stream picture2Stream) - { - try - { - var httpClient = _httpClientFactory.CreateClient(); - httpClient.BaseAddress = new Uri(_apiBaseUrl); - httpClient.Timeout = TimeSpan.FromSeconds(30); + public async Task GenerateEmbeddingsFromStreamAsync(long employeeId, long workshopId, + string employeeFullName, Stream picture1Stream, Stream picture2Stream) + { + try + { + var httpClient = _httpClientFactory.CreateClient(); + httpClient.BaseAddress = new Uri(_apiBaseUrl); + httpClient.Timeout = TimeSpan.FromSeconds(30); - using var content = new MultipartFormDataContent(); + using var content = new MultipartFormDataContent(); - // Add form fields - content.Add(new StringContent(employeeId.ToString()), "employee_id"); - content.Add(new StringContent(workshopId.ToString()), "workshop_id"); - content.Add(new StringContent(employeeFullName ?? ""), "employee_full_name"); + // Add form fields + content.Add(new StringContent(employeeId.ToString()), "employee_id"); + content.Add(new StringContent(workshopId.ToString()), "workshop_id"); + content.Add(new StringContent(employeeFullName ?? ""), "employee_full_name"); - // Add picture streams - var picture1Content = new StreamContent(picture1Stream); - picture1Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg"); - content.Add(picture1Content, "picture1", "1.jpg"); + // Add picture streams + var picture1Content = new StreamContent(picture1Stream); + picture1Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg"); + content.Add(picture1Content, "picture1", "1.jpg"); - var picture2Content = new StreamContent(picture2Stream); - picture2Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg"); - content.Add(picture2Content, "picture2", "2.jpg"); + var picture2Content = new StreamContent(picture2Stream); + picture2Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg"); + content.Add(picture2Content, "picture2", "2.jpg"); - // Send request to Python API - var response = await httpClient.PostAsync("embeddings", content); + // Send request to Python API + var response = await httpClient.PostAsync("embeddings", content); - if (response.IsSuccessStatusCode) - { - _logger.LogInformation("Embeddings generated successfully from streams for Employee {EmployeeId}", employeeId); - - // ارسال اطلاع‌رسانی به سایر سیستم‌ها - if (_notificationService != null) - { - await _notificationService.NotifyEmbeddingCreatedAsync(workshopId, employeeId, employeeFullName); - } - - return new OperationResult { IsSuccedded = true, Message = "Embedding با موفقیت ایجاد شد" }; - } - else - { - var errorContent = await response.Content.ReadAsStringAsync(); - _logger.LogError("Failed to generate embeddings from streams. Status: {StatusCode}, Error: {Error}", - response.StatusCode, errorContent); + if (response.IsSuccessStatusCode) + { + _logger.LogInformation("Embeddings generated successfully from streams for Employee {EmployeeId}", + employeeId); - return new OperationResult - { - IsSuccedded = false, - Message = $"خطا در تولید Embedding: {response.StatusCode}" - }; - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Error while generating embeddings from streams for Employee {EmployeeId}", employeeId); - return new OperationResult - { - IsSuccedded = false, - Message = "خطا در تولید Embedding" - }; - } - } + // ارسال اطلاع‌رسانی به سایر سیستم‌ها + if (_notificationService != null) + { + await _notificationService.NotifyEmbeddingCreatedAsync(workshopId, employeeId, employeeFullName); + } - public async Task RefineEmbeddingAsync(long employeeId, long workshopId, float[] embedding, - float confidence, Dictionary metadata = null) - { - try - { - var httpClient = _httpClientFactory.CreateClient(); - httpClient.BaseAddress = new Uri(_apiBaseUrl); - httpClient.Timeout = TimeSpan.FromSeconds(30); + return new OperationResult { IsSuccedded = true, Message = "Embedding با موفقیت ایجاد شد" }; + } + else + { + var errorContent = await response.Content.ReadAsStringAsync(); + _logger.LogError("Failed to generate embeddings from streams. Status: {StatusCode}, Error: {Error}", + response.StatusCode, errorContent); - var requestBody = new - { - employeeId, - workshopId, - embedding, - confidence, - metadata = metadata ?? new Dictionary() - }; + return new OperationResult + { + IsSuccedded = false, + Message = $"خطا در تولید Embedding: {response.StatusCode}" + }; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while generating embeddings from streams for Employee {EmployeeId}", + employeeId); + return new OperationResult + { + IsSuccedded = false, + Message = "خطا در تولید Embedding" + }; + } + } - var response = await httpClient.PostAsJsonAsync("embeddings/refine", requestBody); + public async Task RefineEmbeddingAsync(long employeeId, long workshopId, float[] embedding, + float confidence, Dictionary metadata = null) + { + try + { + var httpClient = _httpClientFactory.CreateClient(); + httpClient.BaseAddress = new Uri(_apiBaseUrl); + httpClient.Timeout = TimeSpan.FromSeconds(30); - if (response.IsSuccessStatusCode) - { - _logger.LogInformation("Embedding refined successfully for Employee {EmployeeId}", employeeId); - - // ارسال اطلاع‌رسانی به سایر سیستم‌ها - if (_notificationService != null) - { - await _notificationService.NotifyEmbeddingRefinedAsync(workshopId, employeeId); - } - - return new OperationResult { IsSuccedded = true, Message = "Embedding بهبود یافت" }; - } - else - { - var errorContent = await response.Content.ReadAsStringAsync(); - _logger.LogError("Failed to refine embedding. Status: {StatusCode}, Error: {Error}", - response.StatusCode, errorContent); + var requestBody = new + { + employeeId, + workshopId, + embedding, + confidence, + metadata = metadata ?? new Dictionary() + }; - return new OperationResult - { - IsSuccedded = false, - Message = $"خطا در بهبود Embedding: {response.StatusCode}" - }; - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Error while refining embedding for Employee {EmployeeId}", employeeId); - return new OperationResult - { - IsSuccedded = false, - Message = "خطا در بهبود Embedding" - }; - } - } + var response = await httpClient.PostAsJsonAsync("embeddings/refine", requestBody); - public async Task DeleteEmbeddingAsync(long employeeId, long workshopId) - { - try - { - var httpClient = _httpClientFactory.CreateClient(); - httpClient.BaseAddress = new Uri(_apiBaseUrl); - httpClient.Timeout = TimeSpan.FromSeconds(30); + if (response.IsSuccessStatusCode) + { + _logger.LogInformation("Embedding refined successfully for Employee {EmployeeId}", employeeId); - var response = await httpClient.DeleteAsync($"embeddings/{workshopId}/{employeeId}"); + // ارسال اطلاع‌رسانی به سایر سیستم‌ها + if (_notificationService != null) + { + await _notificationService.NotifyEmbeddingRefinedAsync(workshopId, employeeId); + } - if (response.IsSuccessStatusCode) - { - _logger.LogInformation("Embedding deleted successfully for Employee {EmployeeId}", employeeId); - - // ارسال اطلاع‌رسانی به سایر سیستم‌ها - if (_notificationService != null) - { - await _notificationService.NotifyEmbeddingDeletedAsync(workshopId, employeeId); - } - - return new OperationResult { IsSuccedded = true, Message = "Embedding حذف شد" }; - } - else - { - var errorContent = await response.Content.ReadAsStringAsync(); - _logger.LogError("Failed to delete embedding. Status: {StatusCode}, Error: {Error}", - response.StatusCode, errorContent); + return new OperationResult { IsSuccedded = true, Message = "Embedding بهبود یافت" }; + } + else + { + var errorContent = await response.Content.ReadAsStringAsync(); + _logger.LogError("Failed to refine embedding. Status: {StatusCode}, Error: {Error}", + response.StatusCode, errorContent); - return new OperationResult - { - IsSuccedded = false, - Message = $"خطا در حذف Embedding: {response.StatusCode}" - }; - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Error while deleting embedding for Employee {EmployeeId}", employeeId); - return new OperationResult - { - IsSuccedded = false, - Message = "خطا در حذف Embedding" - }; - } - } + return new OperationResult + { + IsSuccedded = false, + Message = $"خطا در بهبود Embedding: {response.StatusCode}" + }; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while refining embedding for Employee {EmployeeId}", employeeId); + return new OperationResult + { + IsSuccedded = false, + Message = "خطا در بهبود Embedding" + }; + } + } - public async Task> GetEmbeddingAsync(long employeeId, long workshopId) - { - try - { - var httpClient = _httpClientFactory.CreateClient(); - httpClient.BaseAddress = new Uri(_apiBaseUrl); - httpClient.Timeout = TimeSpan.FromSeconds(30); + public async Task DeleteEmbeddingAsync(long employeeId, long workshopId) + { + try + { + var httpClient = _httpClientFactory.CreateClient(); + httpClient.BaseAddress = new Uri(_apiBaseUrl); + httpClient.Timeout = TimeSpan.FromSeconds(30); - var response = await httpClient.GetAsync($"embeddings/{workshopId}/{employeeId}"); + var response = await httpClient.DeleteAsync($"embeddings/{workshopId}/{employeeId}"); - if (response.IsSuccessStatusCode) - { - var content = await response.Content.ReadAsStringAsync(); - var embeddingData = JsonSerializer.Deserialize(content, - new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + if (response.IsSuccessStatusCode) + { + _logger.LogInformation("Embedding deleted successfully for Employee {EmployeeId}", employeeId); - _logger.LogInformation("Embedding retrieved successfully for Employee {EmployeeId}", employeeId); + // ارسال اطلاع‌رسانی به سایر سیستم‌ها + if (_notificationService != null) + { + await _notificationService.NotifyEmbeddingDeletedAsync(workshopId, employeeId); + } - return new OperationResult - { - IsSuccedded = true, - Message = "Embedding دریافت شد", - Data = embeddingData - }; - } - else - { - var errorContent = await response.Content.ReadAsStringAsync(); - _logger.LogError("Failed to get embedding. Status: {StatusCode}, Error: {Error}", - response.StatusCode, errorContent); - - return new OperationResult - { - IsSuccedded = false, - Message = $"خطا در دریافت Embedding: {response.StatusCode}" - }; - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Error while getting embedding for Employee {EmployeeId}", employeeId); - return new OperationResult - { - IsSuccedded = false, - Message = "خطا در دریافت Embedding" - }; - } - } -} + return new OperationResult { IsSuccedded = true, Message = "Embedding حذف شد" }; + } + else + { + var errorContent = await response.Content.ReadAsStringAsync(); + _logger.LogError("Failed to delete embedding. Status: {StatusCode}, Error: {Error}", + response.StatusCode, errorContent); + + return new OperationResult + { + IsSuccedded = false, + Message = $"خطا در حذف Embedding: {response.StatusCode}" + }; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while deleting embedding for Employee {EmployeeId}", employeeId); + return new OperationResult + { + IsSuccedded = false, + Message = "خطا در حذف Embedding" + }; + } + } + + public async Task> GetEmbeddingAsync(long employeeId, long workshopId) + { + try + { + var httpClient = _httpClientFactory.CreateClient(); + httpClient.BaseAddress = new Uri(_apiBaseUrl); + httpClient.Timeout = TimeSpan.FromSeconds(30); + + var response = await httpClient.GetAsync($"embeddings/{workshopId}/{employeeId}"); + + if (response.IsSuccessStatusCode) + { + var content = await response.Content.ReadAsStringAsync(); + var embeddingData = JsonSerializer.Deserialize(content, + new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + _logger.LogInformation("Embedding retrieved successfully for Employee {EmployeeId}", employeeId); + + return new OperationResult + { + IsSuccedded = true, + Message = "Embedding دریافت شد", + Data = embeddingData + }; + } + else + { + var errorContent = await response.Content.ReadAsStringAsync(); + _logger.LogError("Failed to get embedding. Status: {StatusCode}, Error: {Error}", + response.StatusCode, errorContent); + + return new OperationResult + { + IsSuccedded = false, + Message = $"خطا در دریافت Embedding: {response.StatusCode}" + }; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while getting embedding for Employee {EmployeeId}", employeeId); + return new OperationResult + { + IsSuccedded = false, + Message = "خطا در دریافت Embedding" + }; + } + } + + public async Task UpdateEmbeddingFullNameAsync(long employeeId, long workshopId, + string newFullName) + { + try + { + var httpClient = _httpClientFactory.CreateClient(); + httpClient.BaseAddress = new Uri(_apiBaseUrl); + httpClient.Timeout = TimeSpan.FromSeconds(30); + + var requestBody = new + { + employee_id = employeeId, + workshop_id = workshopId, + employee_full_name = newFullName + }; + + var response = await httpClient.PutAsJsonAsync("embeddings/update-name", requestBody); + + if (response.IsSuccessStatusCode) + { + _logger.LogInformation("Employee Name Changed For {EmployeeId} In workshop ={WorkshopId}", employeeId, + workshopId); + + // ارسال اطلاع‌رسانی به سایر سیستم‌ها + if (_notificationService != null) + { + //await _notificationService.NotifyEmbeddingRefinedAsync(workshopId, employeeId); + } + + return new OperationResult { IsSuccedded = true, Message = "عملیات با موفقیت انجام شد" }; + } + else + { + var errorContent = await response.Content.ReadAsStringAsync(); + _logger.LogError("Failed to refine embedding. Status: {StatusCode}, Error: {Error}", + response.StatusCode, errorContent); + + return new OperationResult + { + IsSuccedded = false, + Message = $"خطا در بهبود Embedding: {response.StatusCode}" + }; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while Changing EmployeeFullName for Employee {EmployeeId}", employeeId); + return new OperationResult + { + IsSuccedded = false, + Message = "خطا در بهبود Embedding" + }; + } + } +} \ No newline at end of file diff --git a/CompanyManagment.App.Contracts/RollCallEmployee/IRollCallEmployeeApplication.cs b/CompanyManagment.App.Contracts/RollCallEmployee/IRollCallEmployeeApplication.cs index b736b0bc..8691b2f1 100644 --- a/CompanyManagment.App.Contracts/RollCallEmployee/IRollCallEmployeeApplication.cs +++ b/CompanyManagment.App.Contracts/RollCallEmployee/IRollCallEmployeeApplication.cs @@ -1,6 +1,7 @@ using _0_Framework.Application; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace CompanyManagment.App.Contracts.RollCallEmployee; diff --git a/CompanyManagment.Application/RollCallEmployeeApplication.cs b/CompanyManagment.Application/RollCallEmployeeApplication.cs index 9f4630cd..134807e2 100644 --- a/CompanyManagment.Application/RollCallEmployeeApplication.cs +++ b/CompanyManagment.Application/RollCallEmployeeApplication.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Transactions; +using _0_Framework.Application.FaceEmbedding; using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database; namespace CompanyManagment.Application; @@ -23,7 +24,9 @@ public class RollCallEmployeeApplication : IRollCallEmployeeApplication private readonly ILeftWorkRepository _leftWorkRepository; private readonly IRollCallEmployeeStatusRepository _rollCallEmployeeStatusRepository; private readonly IWebHostEnvironment _webHostEnvironment; - public RollCallEmployeeApplication(IRollCallEmployeeRepository rollCallEmployeeRepository, IEmployeeRepository employeeRepository, IRollCallEmployeeStatusApplication rollCallEmployeeStatusApplication, ILeftWorkRepository leftWorkRepository, IRollCallEmployeeStatusRepository rollCallEmployeeStatusRepository, IWebHostEnvironment webHostEnvironment) + private readonly IFaceEmbeddingService _faceEmbeddingService; + + public RollCallEmployeeApplication(IRollCallEmployeeRepository rollCallEmployeeRepository, IEmployeeRepository employeeRepository, IRollCallEmployeeStatusApplication rollCallEmployeeStatusApplication, ILeftWorkRepository leftWorkRepository, IRollCallEmployeeStatusRepository rollCallEmployeeStatusRepository, IWebHostEnvironment webHostEnvironment, IFaceEmbeddingService faceEmbeddingService) { _rollCallEmployeeRepository = rollCallEmployeeRepository; _employeeRepository = employeeRepository; @@ -31,6 +34,7 @@ public class RollCallEmployeeApplication : IRollCallEmployeeApplication _leftWorkRepository = leftWorkRepository; _rollCallEmployeeStatusRepository = rollCallEmployeeStatusRepository; _webHostEnvironment = webHostEnvironment; + _faceEmbeddingService = faceEmbeddingService; } public OperationResult Create(CreateRollCallEmployee command) @@ -213,9 +217,18 @@ public class RollCallEmployeeApplication : IRollCallEmployeeApplication if (entity.IsActiveString != "true") return result.Failed("امکان تغییر نام برای کارمند غیر فعال وجود ندارد"); - + var transaction = _rollCallEmployeeRepository.BeginTransactionAsync().GetAwaiter().GetResult(); + entity.ChangeName(fName, lName); _rollCallEmployeeRepository.SaveChanges(); + + var embeddingRes =_faceEmbeddingService + .UpdateEmbeddingFullNameAsync(entity.EmployeeId, entity.WorkshopId, fullName) + .GetAwaiter().GetResult(); + if (!embeddingRes.IsSuccedded) + return result.Failed("خطا در به روز رسانی نام در سیستم تشخیص چهره: " + embeddingRes.Message); + + transaction.Commit(); return result.Succcedded(); } #endregion diff --git a/ServiceHost/ServiceHost.csproj b/ServiceHost/ServiceHost.csproj index d6666050..744e0213 100644 --- a/ServiceHost/ServiceHost.csproj +++ b/ServiceHost/ServiceHost.csproj @@ -3,7 +3,8 @@ net10.0 enable - + true + $(NoWarn);1591 From f7351454f38220e84280c030514cbbb8828f012f Mon Sep 17 00:00:00 2001 From: mahan Date: Tue, 16 Dec 2025 11:05:22 +0330 Subject: [PATCH 13/27] feat: implement method to update face embedding names for employees with changed names --- .../Pages/Company/AndroidApk/Index.cshtml.cs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/ServiceHost/Areas/AdminNew/Pages/Company/AndroidApk/Index.cshtml.cs b/ServiceHost/Areas/AdminNew/Pages/Company/AndroidApk/Index.cshtml.cs index 0939b03c..9d04e631 100644 --- a/ServiceHost/Areas/AdminNew/Pages/Company/AndroidApk/Index.cshtml.cs +++ b/ServiceHost/Areas/AdminNew/Pages/Company/AndroidApk/Index.cshtml.cs @@ -29,6 +29,7 @@ using Parbad.AspNetCore; using Parbad.Gateway.Sepehr; using System.ComponentModel.DataAnnotations; using _0_Framework.Application.Enums; +using _0_Framework.Application.FaceEmbedding; using CompanyManagement.Infrastructure.Excel.WorkshopsRollCall; using static ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk.IndexModel2; @@ -45,6 +46,7 @@ namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk private readonly ITemporaryClientRegistrationApplication _clientRegistrationApplication; private readonly IHttpClientFactory _httpClientFactory; private readonly IOnlinePayment _onlinePayment; + private readonly IFaceEmbeddingService _faceEmbeddingService; [BindProperty] public IFormFile File { get; set; } @@ -65,7 +67,7 @@ namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk public IndexModel(IAndroidApkVersionApplication application, IRollCallDomainService rollCallDomainService, CompanyContext context, AccountContext accountContext, IHttpClientFactory httpClientFactory, IOptions appSetting, - ITemporaryClientRegistrationApplication clientRegistrationApplication, IOnlinePayment onlinePayment) + ITemporaryClientRegistrationApplication clientRegistrationApplication, IOnlinePayment onlinePayment, IFaceEmbeddingService faceEmbeddingService) { _application = application; _rollCallDomainService = rollCallDomainService; @@ -74,6 +76,7 @@ namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk _httpClientFactory = httpClientFactory; _clientRegistrationApplication = clientRegistrationApplication; _onlinePayment = onlinePayment; + _faceEmbeddingService = faceEmbeddingService; _paymentGateway = new SepehrPaymentGateway(httpClientFactory); } @@ -136,7 +139,8 @@ namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk public async Task OnPostShiftDateNew() { - await UpdateInstitutionContract(); + //await UpdateInstitutionContract(); + await UpdateFaceEmbeddingNames(); ViewData["message"] = "تومام یک"; return Page(); } @@ -256,6 +260,18 @@ namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk } } + private async System.Threading.Tasks.Task UpdateFaceEmbeddingNames() + { + var rollCallEmployees = await _context.RollCallEmployees + .Where(x => x.HasChangedName) + .ToListAsync(); + foreach (var rollCallEmployee in rollCallEmployees) + { + await _faceEmbeddingService.UpdateEmbeddingFullNameAsync(rollCallEmployee.EmployeeId, + rollCallEmployee.WorkshopId, rollCallEmployee.EmployeeFullName); + } + } + public async Task OnPostPaymentGateWay(CancellationToken cancellationToken) { var command = new CreatePaymentGatewayRequest() From 2972807c9f51ebb4f297787dee0875bce30c0293 Mon Sep 17 00:00:00 2001 From: mahan Date: Tue, 16 Dec 2025 12:56:49 +0330 Subject: [PATCH 14/27] feat: add permission handling methods and project board detail query --- .../ProjectBoardDetailQueryHandler.cs | 74 +++++++++++++++++++ .../ProjectBoardList/ProjectBoardListQuery.cs | 4 +- .../ProjectBoardListQueryHandler.cs | 20 ++++- .../_Common/Interfaces/IAuthHelper.cs | 4 + .../GozareshgirProgramManager.Domain.csproj | 1 + .../_Common/Tools.cs | 38 +++++----- .../Services/Authentication/AuthHelper.cs | 16 ++++ 7 files changed, 133 insertions(+), 24 deletions(-) create mode 100644 ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardDetail/ProjectBoardDetailQueryHandler.cs diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardDetail/ProjectBoardDetailQueryHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardDetail/ProjectBoardDetailQueryHandler.cs new file mode 100644 index 00000000..f10ee486 --- /dev/null +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardDetail/ProjectBoardDetailQueryHandler.cs @@ -0,0 +1,74 @@ +using GozareshgirProgramManager.Application._Common.Interfaces; +using GozareshgirProgramManager.Application._Common.Models; +using GozareshgirProgramManager.Domain._Common; +using Microsoft.EntityFrameworkCore; + +namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectBoardDetail; + +public record ProjectBoardDetailQuery(Guid SectionId) : IBaseQuery; + +public record ProjectBoardDetailResponse(List Users, string TotalTime); + +public record ProjectBoardDetailUserResponse +{ + public List Histories { get; set; } = new(); + public string UserFullName { get; set; } + public long UserId { get; set; } +} + +public class ProjectBoardDetailUserHistoryResponse +{ + public string Date { get; set; } + public string startTime { get; set; } + public string EndTime { get; set; } + public string TotalTime { get; set; } +} + +public class ProjectBoardDetailQueryHandler : IBaseQueryHandler +{ + private readonly IProgramManagerDbContext _programManagerDbContext; + + public ProjectBoardDetailQueryHandler(IProgramManagerDbContext programManagerDbContext) + { + _programManagerDbContext = programManagerDbContext; + } + + public async Task> Handle(ProjectBoardDetailQuery request, + CancellationToken cancellationToken) + { + var section = await _programManagerDbContext.TaskSections + .Include(x => x.Activities) + .FirstOrDefaultAsync(x => x.Id == request.SectionId, cancellationToken: cancellationToken); + + if (section == null) + return OperationResult.NotFound("بخش مورد نظر یافت نشد"); + + var userIds = section.Activities.Select(x => x.UserId).Distinct().ToList(); + + var usersDict = await _programManagerDbContext.Users + .Where(x => userIds.Contains(x.Id)) + .ToDictionaryAsync(x => x.Id, x => x.FullName, cancellationToken); + + var totalTimeSpan = section.Activities + .Select(x => x.GetTimeSpent()) + .Aggregate(TimeSpan.Zero, (sum, next) => sum.Add(next)); + + var users = section.Activities.GroupBy(x => x.UserId).Select(x => + { + return new ProjectBoardDetailUserResponse() + { + UserId = x.Key, + UserFullName = usersDict[x.Key], + Histories = x.Select(h => new ProjectBoardDetailUserHistoryResponse() + { + Date = h.StartDate.ToFarsi(), + startTime = h.StartDate.ToString("HH:mm"), + EndTime = h.EndDate?.ToString("HH:mm") ?? "-", + TotalTime = h.GetTimeSpent().ToString(@"hh\:mm") + }).ToList() + }; + }).ToList(); + var response = new ProjectBoardDetailResponse(users, $"{totalTimeSpan.TotalHours}:{totalTimeSpan.Minutes:D2}"); + return OperationResult.Success(response); + } +} \ No newline at end of file diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQuery.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQuery.cs index db180808..2d42152a 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQuery.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQuery.cs @@ -6,5 +6,5 @@ namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.Project public record ProjectBoardListQuery: IBaseQuery> { -} - + public TaskSectionStatus? Status { get; set; } +} \ No newline at end of file diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs index 0edfdc0b..4ee9564e 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs @@ -1,6 +1,9 @@ +using GozareshgirProgramManager.Application._Common.Constants; using GozareshgirProgramManager.Application._Common.Interfaces; using GozareshgirProgramManager.Application._Common.Models; +using GozareshgirProgramManager.Domain.ProjectAgg.Enums; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query.Internal; namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectBoardList; @@ -19,15 +22,24 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler x.CurrentAssignedUserId == currentUserId) + var queryable = _programManagerDbContext.TaskSections.AsNoTracking() .Where(x => x.InitialEstimatedHours > TimeSpan.Zero) .Include(x => x.Task) .ThenInclude(x => x.Phase) .ThenInclude(x => x.Project) .Include(x => x.Activities) - .Include(x => x.AdditionalTimes) - .ToListAsync(cancellationToken); + .Include(x => x.AdditionalTimes).AsQueryable(); + + if (!_authHelper.HasPermission(ProgramManagerPermissionCode.Board.All.ViewAll)) + { + queryable = queryable.Where(x => x.CurrentAssignedUserId == currentUserId); + } + if (request.Status != null) + { + queryable = queryable.Where(x => x.Status == request.Status); + } + + var data = await queryable.ToListAsync(cancellationToken); var activityUserIds = data.SelectMany(x => x.Activities).Select(a => a.UserId).Distinct().ToList(); var users = await _programManagerDbContext.Users.AsNoTracking() diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Interfaces/IAuthHelper.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Interfaces/IAuthHelper.cs index a41dad2b..314c5984 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Interfaces/IAuthHelper.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Interfaces/IAuthHelper.cs @@ -71,6 +71,10 @@ public interface IAuthHelper /// دریافت نام کامل کاربر جاری از Claims /// string? GetCurrentFullName(); + bool HasPermission(int permission); + List GetPermissions(); + + } diff --git a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/GozareshgirProgramManager.Domain.csproj b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/GozareshgirProgramManager.Domain.csproj index 9ae2365c..9b9a0605 100644 --- a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/GozareshgirProgramManager.Domain.csproj +++ b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/GozareshgirProgramManager.Domain.csproj @@ -8,6 +8,7 @@ + diff --git a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/_Common/Tools.cs b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/_Common/Tools.cs index 0f1ed2da..a7ff25de 100644 --- a/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/_Common/Tools.cs +++ b/ProgramManager/src/Domain/GozareshgirProgramManager.Domain/_Common/Tools.cs @@ -1,5 +1,7 @@ using System.Globalization; using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Bson; using PersianTools.Core; @@ -1704,25 +1706,25 @@ public static class Tools // return Convert.ToBase64String(bsonData); //} ////بیسان هایی که بصورت لیست بودند استخراج میشود - //public static List DeserializeFromBsonList(string base64Data) - //{ - // byte[] data = Convert.FromBase64String(base64Data); + public static List DeserializeFromBsonList(string base64Data) + { + byte[] data = Convert.FromBase64String(base64Data); - // using MemoryStream memoryStream = new MemoryStream(data); - // using BsonDataReader reader = new BsonDataReader(memoryStream); - // reader.ReadRootValueAsArray = true; - // JsonSerializer serializer = new JsonSerializer(); - // return serializer.Deserialize>(reader); - //} - ////بیسان هایی که بصورت تکی بودند استخراج میشود - //public static T DeserializeFromBson(string base64Data) - //{ - // byte[] bsonData = Convert.FromBase64String(base64Data); - // using MemoryStream memoryStream = new MemoryStream(bsonData); - // using BsonDataReader bsonReader = new BsonDataReader(memoryStream); - // JsonSerializer serializer = new JsonSerializer(); - // return serializer.Deserialize(bsonReader); - //} + using MemoryStream memoryStream = new MemoryStream(data); + using BsonDataReader reader = new BsonDataReader(memoryStream); + reader.ReadRootValueAsArray = true; + JsonSerializer serializer = new JsonSerializer(); + return serializer.Deserialize>(reader); + } + //بیسان هایی که بصورت تکی بودند استخراج میشود + public static T DeserializeFromBson(string base64Data) + { + byte[] bsonData = Convert.FromBase64String(base64Data); + using MemoryStream memoryStream = new MemoryStream(bsonData); + using BsonDataReader bsonReader = new BsonDataReader(memoryStream); + JsonSerializer serializer = new JsonSerializer(); + return serializer.Deserialize(bsonReader); + } //public static TimeOnly CalculateOffset(ICollection shiftDetailsRegularShifts) //{ diff --git a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Services/Authentication/AuthHelper.cs b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Services/Authentication/AuthHelper.cs index ec2ccd4f..aeb76f2e 100644 --- a/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Services/Authentication/AuthHelper.cs +++ b/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Services/Authentication/AuthHelper.cs @@ -1,5 +1,6 @@ using System.Security.Claims; using GozareshgirProgramManager.Application._Common.Interfaces; +using GozareshgirProgramManager.Domain._Common; using Microsoft.AspNetCore.Http; namespace GozareshgirProgramManager.Infrastructure.Services.Authentication; @@ -106,6 +107,21 @@ public class AuthHelper : IAuthHelper return _httpContextAccessor.HttpContext?.User?.FindFirst("FullName")?.Value; } + public bool HasPermission(int permission) + { + return GetPermissions().Any(x => x == permission); + } + + public List GetPermissions() + { + if (!IsAuthenticated()) + return new List(); + + var permissions = _httpContextAccessor.HttpContext.User.Claims.FirstOrDefault(x => x.Type == "permissions") + ?.Value; + return Tools.DeserializeFromBsonList(permissions); //Mahan + } + // /// // /// دریافت AccountId کاربر جاری از Claims // /// From 9e5a494881ef2df477861d1c7224f3d06e057705 Mon Sep 17 00:00:00 2001 From: mahan Date: Tue, 16 Dec 2025 14:18:00 +0330 Subject: [PATCH 15/27] feat: enhance project board query with skill and user details --- .../SetTimeProjectCommandValidator.cs | 18 +++++++++++++++--- .../ProjectBoardListQueryHandler.cs | 15 ++++++++++++--- .../ProjectBoardListResponse.cs | 4 ++++ ServiceHost/appsettings.Development.json | 2 +- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Commands/SetTimeProject/SetTimeProjectCommandValidator.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Commands/SetTimeProject/SetTimeProjectCommandValidator.cs index 7fb182b7..c47ef658 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Commands/SetTimeProject/SetTimeProjectCommandValidator.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Commands/SetTimeProject/SetTimeProjectCommandValidator.cs @@ -1,4 +1,5 @@ using FluentValidation; +using FluentValidation.Validators; using GozareshgirProgramManager.Application.Modules.Projects.DTOs; namespace GozareshgirProgramManager.Application.Modules.Projects.Commands.SetTimeProject; @@ -15,6 +16,9 @@ public class SetTimeProjectCommandValidator:AbstractValidator x.SectionItems) .SetValidator(command => new SetTimeProjectSectionItemValidator()); + RuleFor(x => x.SectionItems) + .Must(sectionItems => sectionItems.Any(si => si.InitData?.Hours > 0)) + .WithMessage("حداقل یکی از بخش‌ها باید مقدار ساعت معتبری داشته باشد."); } } public class SetTimeProjectSectionItemValidator:AbstractValidator @@ -30,13 +34,14 @@ public class SetTimeProjectSectionItemValidator:AbstractValidatorx.AdditionalTime) - .SetValidator(new TimeDataValidator()); + .SetValidator(new AdditionalTimeDataValidator()); + } } -public class TimeDataValidator : AbstractValidator +public class AdditionalTimeDataValidator: AbstractValidator { - public TimeDataValidator() + public AdditionalTimeDataValidator() { RuleFor(x => x.Hours) .GreaterThanOrEqualTo(0) @@ -45,6 +50,13 @@ public class TimeDataValidator : AbstractValidator RuleFor(x=>x.Description) .MaximumLength(500) .WithMessage("توضیحات نمی‌تواند بیشتر از 500 کاراکتر باشد."); + } +} + +public class TimeDataValidator : AbstractValidator +{ + public TimeDataValidator() + { } } \ No newline at end of file diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs index 4ee9564e..740758be 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs @@ -28,7 +28,8 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler x.Phase) .ThenInclude(x => x.Project) .Include(x => x.Activities) - .Include(x => x.AdditionalTimes).AsQueryable(); + .Include(x => x.AdditionalTimes).AsQueryable(). + Include(x => x.Skill).AsQueryable(); if (!_authHelper.HasPermission(ProgramManagerPermissionCode.Board.All.ViewAll)) { @@ -42,10 +43,14 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler x.Activities).Select(a => a.UserId).Distinct().ToList(); + var assignedUser = data.Select(x => x.CurrentAssignedUserId) + .Concat(data.Select(x => x.OriginalAssignedUserId)).ToList(); + var allUserIds = activityUserIds.Concat(assignedUser).Distinct().ToList(); var users = await _programManagerDbContext.Users.AsNoTracking() - .Where(x => activityUserIds.Contains(x.Id)) + .Where(x => allUserIds.Contains(x.Id)) .Select(x => new { x.Id, x.FullName }) .ToDictionaryAsync(x => x.Id, x => x.FullName, cancellationToken); + var result = data.Select(x => { @@ -101,7 +106,11 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler a.TotalSeconds), Histories = mergedHistories - } + }, + OriginalUser = users.GetValueOrDefault(x.OriginalAssignedUserId, "ناشناس"), + AssignedUser = x.CurrentAssignedUserId == x.OriginalAssignedUserId ? null + : users.GetValueOrDefault(x.CurrentAssignedUserId, "ناشناس"), + SkillName = x.Skill?.Name??"-", }; }).ToList(); diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListResponse.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListResponse.cs index 83421e89..ab38750e 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListResponse.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListResponse.cs @@ -10,6 +10,10 @@ public class ProjectBoardListResponse public string TaskName { get; set; } public ProjectProgressDto Progress { get; set; } public TaskSectionStatus SectionStatus { get; set; } + public string? AssignedUser { get; set; } + public string OriginalUser { get; set; } + public string SkillName { get; set; } + } public class ProjectProgressDto { diff --git a/ServiceHost/appsettings.Development.json b/ServiceHost/appsettings.Development.json index 684aa6aa..d892b51d 100644 --- a/ServiceHost/appsettings.Development.json +++ b/ServiceHost/appsettings.Development.json @@ -28,7 +28,7 @@ //program_manager_db "ProgramManagerDb": "Data Source=.;Initial Catalog=program_manager_db;Integrated Security=True;TrustServerCertificate=true;", //"ProgramManagerDb": "Data Source=185.208.175.186;Initial Catalog=program_manager_db;Persist Security Info=False;User ID=ir_db;Password=R2rNp[170]18[3019]#@ATt;TrustServerCertificate=true;" - //"ProgramManagerDbServer": "Data Source=171.22.24.15;Initial Catalog=program_manager_db;Persist Security Info=False;User ID=ir_db;Password=R2rNp[170]18[3019]#@ATt;TrustServerCertificate=true;" + "ProgramManagerDbServer": "Data Source=171.22.24.15;Initial Catalog=program_manager_db;Persist Security Info=False;User ID=ir_db;Password=R2rNp[170]18[3019]#@ATt;TrustServerCertificate=true;" //mahan Docker //"MesbahDb": "Data Source=localhost,5069;Initial Catalog=mesbah_db;User ID=sa;Password=YourPassword123;TrustServerCertificate=True;" }, From ebdc9b1e555c10081a5fba6159094939c5165460 Mon Sep 17 00:00:00 2001 From: SamSys Date: Tue, 16 Dec 2025 16:26:15 +0330 Subject: [PATCH 16/27] ProgramManager PermissionCodes set --- .../Constants/ProgramManagerPermissionCode.cs | 221 +++++++++++- .../Pages/Accounts/Account/CreateRole.cshtml | 315 ++++++++++++++---- .../Pages/Accounts/Account/EditRole.cshtml | 301 +++++++++++++---- .../Areas/Admin/Pages/Shared/_Menu.cshtml | 2 +- .../Areas/AdminNew/Pages/Shared/_Menu.cshtml | 2 +- ServiceHost/Properties/launchSettings.json | 2 +- 6 files changed, 694 insertions(+), 149 deletions(-) diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Constants/ProgramManagerPermissionCode.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Constants/ProgramManagerPermissionCode.cs index f5578ea2..3449f4f0 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Constants/ProgramManagerPermissionCode.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/_Common/Constants/ProgramManagerPermissionCode.cs @@ -4,30 +4,233 @@ namespace GozareshgirProgramManager.Application._Common.Constants; public static class ProgramManagerPermissionCode { - + /// + /// دسترسی به بخش مدیریت پروژه + /// public const int Code = 99; + #region DeveloperUsers[تب کاربران برنامه نویسی] /// - ///بخش اجرا + /// تب کاربران برنامه نویسی + /// + public static class DeveloperUsers + { + public const int Code = 9900; + + /// + /// تب تعیین حقوق + /// + public static class SalaryDetermination + { + public const int Code = 990001; + + /// + /// تعیین زمان + /// + public const int WorkingHoursDetermination = 990001; + } + + /// + /// تب وضعیت کارکرد + /// + public static class WorkingStatus + { + public const int Code = 990002; + } + + /// + /// تب فیش حقوقی + /// + public static class Checkout + { + public const int Code = 990003; + + /// + /// ایجاد فیش + /// + public const int Create = 99000301; + + /// + /// حذف تکی فیش + /// + public const int SingleDeletion = 99000302; + + /// + /// حذف گروهی فیش ها + /// + public const int GroupDeletion = 99000303; + + /// + /// محاسبه مجدد گروهی فیش ها + /// + public const int GroupReCompute = 99000304; + + /// + /// محاسبه مجدد تکی فیش + /// + public const int SingleReCompute = 99000305; + + } + + } + + #endregion + + #region ProgramManager[تب مدیریت پروژه] + + /// + /// تب مدیریت پروژه + /// + public static class ProgramManager + { + public const int Code = 9901; + + /// + /// ایجاد پروژه + /// + public static class CreateProject + { + public const int Code = 9901; + } + + /// + /// همه پروژه ها + /// + public static class AllProjects + { + public const int Code = 990102; + + } + + /// + /// تعیین کاربر + /// + public static class UserDetermination + { + public const int Code = 990106; + } + + /// + /// تعیین زمان پروژه + /// + public static class ProjectTimeSetting + { + public const int Code = 990107; + } + + /// + /// ویرایش پروژه + /// + public static class EditProject + { + public const int Code = 990108; + } + + /// + /// حذف پروژه + /// + public static class DeleteProject + { + public const int Code = 990109; + } + + /// + /// ایجاد بخش اصلی + /// + public static class CreateMainSection + { + public const int Code = 990110; + } + + /// + /// ایجاد بخش فرعی + /// + public static class CreateSubSection + { + public const int Code = 990111; + } + } + + #endregion + + #region Board[تب اجرا] + + /// + ///تب اجرا /// public static class Board { - public const int Code = 991; + public const int Code = 9902; /// /// تب همه /// public static class All { - public const int Code = 99101; + public const int Code = 990201; + } - /// - /// دیدن همه تسک ها - /// - public const int ViewAll = 9910101; + /// + /// آماده اجرا + /// + public static class ReadyToRun + { + public const int Code = 990202; + } + + /// + /// در حال اجرا + /// + public static class Running + { + public const int Code = 990203; + } + + + /// + /// نیمه کاره + /// + public static class Unfinished + { + public const int Code = 990204; + } + + /// + /// اتمام اجرا + /// + public static class Finished + { + public const int Code = 990204; + } + + /// + /// دیدن پروژه همه کاربران + /// + public static class ViewAllUsersProjects + { + public const int Code = 990206; + } + + /// + /// ارجاع به دیگران + /// + public static class ReferralToOthers + { + public const int Code = 990207; + } + + /// + /// چت + /// + public static class Chat + { + public const int Code = 990208; } } - + + #endregion + + public static List GetAllCodes() { var result = new List(); diff --git a/ServiceHost/Areas/Admin/Pages/Accounts/Account/CreateRole.cshtml b/ServiceHost/Areas/Admin/Pages/Accounts/Account/CreateRole.cshtml index 398e2b56..e276aecf 100644 --- a/ServiceHost/Areas/Admin/Pages/Accounts/Account/CreateRole.cshtml +++ b/ServiceHost/Areas/Admin/Pages/Accounts/Account/CreateRole.cshtml @@ -1014,7 +1014,7 @@ -
+ @*
@@ -1024,111 +1024,281 @@ -
+
*@ - +
- - -
+ + + + +
- - + + + +
- + + + +
+ +
+
+ + +
+ + + + +
+ + +
+ + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + + + +
+ + + +
+ +
- - + + +
- - -
- -
-
- -
-
- -
-
- -
+ +
- +
- + -
- -
-
- -
-
- -
-
- -
+
- +
- + -
- -
-
- -
-
- -
-
- -
+
-
- + + +
+ + + + +
+ +
+ + + + +
+ + +
+ + + + +
+ + +
+ + + + +
+ + +
+ + + + +
+ + + +
+ + + + +
+ + + +
+ + + + +
+
+ + +
- + + + + +
+ + + +
+ + +
+ + + + +
+ + +
+ + + + +
+ + +
+ + + + +
+ + + +
+ + + + +
+ + +
+ + + + +
+ + +
+ + + + +
+ + +
+ + + + +
+
-
- -
-
- -
-
- -
-
- -
-
@@ -1324,6 +1494,13 @@ name="PmPermissions[${pmIndex}]" value="${chk.checked ? chk.value : 0}"> `); + if(chk.checked && chk.value == 99){ + $("#hiddenInput").append(` + `); + normalIndex++; + } pmIndex++; } else { $("#hiddenInput").append(` diff --git a/ServiceHost/Areas/Admin/Pages/Accounts/Account/EditRole.cshtml b/ServiceHost/Areas/Admin/Pages/Accounts/Account/EditRole.cshtml index da948964..b1e53af0 100644 --- a/ServiceHost/Areas/Admin/Pages/Accounts/Account/EditRole.cshtml +++ b/ServiceHost/Areas/Admin/Pages/Accounts/Account/EditRole.cshtml @@ -1017,124 +1017,282 @@ - +
- + - - - -
- - -
- - - - + +
- + + +
- + + + +
+ +
+
+ + +
+ + +
- +
- + +
- +
- +
- +
- -
-
- -
- - - -
- +
- -
-
- -
-
- -
-
- -
- - - -
- -
-
- -
-
- -
-
- +
- + + + +
- + + +
+ + -
-
+ + +
- + + +
+
- + + + +
+
- + + + + +
+ + +
+ + + + +
+ +
+ + + + +
+ + +
+ + + + +
+ + +
+ + + + +
+ + +
+ + + + +
+ + + +
+ + + + +
+ + + +
+ + + +
+ + +
+ + + + + +
+ + + +
+ + +
+ + + + +
+ + +
+ + + + +
+ + +
+ + + + +
+ + + +
+ + + + +
+ + +
+ + + + +
+ + +
+ + + + +
+ + +
+ + + + +
+
+ +
+
@@ -1378,6 +1536,13 @@ name="PmPermissions[${pmIndex}]" value="${chk.checked ? chk.value : 0}"> `); + if(chk.checked && chk.value == 99){ + $("#hiddenInput").append(` + `); + normalIndex++; + } pmIndex++; } else { $("#hiddenInput").append(` diff --git a/ServiceHost/Areas/Admin/Pages/Shared/_Menu.cshtml b/ServiceHost/Areas/Admin/Pages/Shared/_Menu.cshtml index b1f15662..90b8007a 100644 --- a/ServiceHost/Areas/Admin/Pages/Shared/_Menu.cshtml +++ b/ServiceHost/Areas/Admin/Pages/Shared/_Menu.cshtml @@ -518,7 +518,7 @@ diff --git a/ServiceHost/Areas/AdminNew/Pages/Shared/_Menu.cshtml b/ServiceHost/Areas/AdminNew/Pages/Shared/_Menu.cshtml index 2aa0ccd4..7e246091 100644 --- a/ServiceHost/Areas/AdminNew/Pages/Shared/_Menu.cshtml +++ b/ServiceHost/Areas/AdminNew/Pages/Shared/_Menu.cshtml @@ -671,7 +671,7 @@ diff --git a/ServiceHost/Properties/launchSettings.json b/ServiceHost/Properties/launchSettings.json index d7381591..788962e4 100644 --- a/ServiceHost/Properties/launchSettings.json +++ b/ServiceHost/Properties/launchSettings.json @@ -19,7 +19,7 @@ "sqlDebugging": true, "dotnetRunMessages": "true", "nativeDebugging": true, - "applicationUrl": "https://localhost:5004;http://localhost:5003;https://192.168.0.117:5006", + "applicationUrl": "https://localhost:5004;http://localhost:5003;", "jsWebView2Debugging": false, "hotReloadEnabled": true }, From 2ef1ea3d1a6b0715ee7f244730cf32d022c330c4 Mon Sep 17 00:00:00 2001 From: SamSys Date: Tue, 16 Dec 2025 17:02:40 +0330 Subject: [PATCH 17/27] add new permission for workshop --- .../Pages/Accounts/Account/CreateRole.cshtml | 19 ++++++++++++++++--- .../Pages/Accounts/Account/EditRole.cshtml | 18 +++++++++++++++--- .../Company/Workshops/CreateWorkshop.cshtml | 2 +- .../Company/Workshops/EditWorkshop.cshtml | 2 +- .../SignalRBoardNotificationPublisher.cs | 2 +- 5 files changed, 34 insertions(+), 9 deletions(-) diff --git a/ServiceHost/Areas/Admin/Pages/Accounts/Account/CreateRole.cshtml b/ServiceHost/Areas/Admin/Pages/Accounts/Account/CreateRole.cshtml index e276aecf..5172224a 100644 --- a/ServiceHost/Areas/Admin/Pages/Accounts/Account/CreateRole.cshtml +++ b/ServiceHost/Areas/Admin/Pages/Accounts/Account/CreateRole.cshtml @@ -1,6 +1,10 @@ @model AccountManagement.Application.Contracts.Role.CreateRole @{