From 3d86ba401f446cf2ba75420b1a2463ac5ca45e60 Mon Sep 17 00:00:00 2001 From: mahan Date: Thu, 23 Oct 2025 16:44:19 +0330 Subject: [PATCH 1/5] fix: update IsOldContract logic to handle null and empty workshop groups --- .../Repository/InstitutionContractRepository.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs b/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs index 4452dd3d..5a0c90ac 100644 --- a/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs +++ b/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs @@ -1318,8 +1318,9 @@ public class InstitutionContractRepository : RepositoryBasey.Services.ContractInPerson)??true, - IsOldContract = x.contract.WorkshopGroup?.CurrentWorkshops - .Any(y=>y.Price == 0)??true + IsOldContract = x.contract.WorkshopGroup?.CurrentWorkshops == null + || x.contract.WorkshopGroup.CurrentWorkshops.Count == 0 + || x.contract.WorkshopGroup.CurrentWorkshops.Any(y => y.Price == 0) }; }).ToList() }; From 19b390b17d0ef22c6f74c14c3f99fdb0c568a10e Mon Sep 17 00:00:00 2001 From: mahan Date: Sat, 25 Oct 2025 12:08:27 +0330 Subject: [PATCH 2/5] refactor: update employee sorting logic by color for improved clarity --- CompanyManagment.Application/CustomizeCheckoutApplication.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CompanyManagment.Application/CustomizeCheckoutApplication.cs b/CompanyManagment.Application/CustomizeCheckoutApplication.cs index b82e3be1..8c41e745 100644 --- a/CompanyManagment.Application/CustomizeCheckoutApplication.cs +++ b/CompanyManagment.Application/CustomizeCheckoutApplication.cs @@ -373,7 +373,9 @@ namespace CompanyManagment.Application Color = color, PersonnelCode = personnelCodes.FirstOrDefault(y => x.Id == y.EmployeeId)?.PersonnelCode.ToString() ?? "-" }; - }).OrderByDescending(x => x.IsEligible).ThenByDescending(x => x.Reason).ToList(); + }).OrderBy(x => x.Color switch { "orange" => 0, "white" => 1, "red" => 2, "green" => 3, "black" => 4, _ => 5 }) + .ToList(); + return op.Succcedded(employees); } } From 25c7b67eb5ee11052a05ff888b1504c1d3b2fdd0 Mon Sep 17 00:00:00 2001 From: mahan Date: Sat, 25 Oct 2025 14:18:04 +0330 Subject: [PATCH 3/5] feat: extend SMS service to include contracting party and institution contract IDs in verification link and code methods --- 0_Framework/Application/Sms/ISmsService.cs | 5 ++-- Company.Domain/SmsResultAgg/SmsResult.cs | 3 +- .../InstitutionContractApplication.cs | 4 +-- .../InstitutionContractRepository.cs | 2 +- .../Services}/SmsService.cs | 28 +++++++++++++------ 5 files changed, 28 insertions(+), 14 deletions(-) rename {0_Framework/Application/Sms => CompanyManagment.EFCore/Services}/SmsService.cs (89%) diff --git a/0_Framework/Application/Sms/ISmsService.cs b/0_Framework/Application/Sms/ISmsService.cs index 743b18e4..d3915923 100644 --- a/0_Framework/Application/Sms/ISmsService.cs +++ b/0_Framework/Application/Sms/ISmsService.cs @@ -27,9 +27,10 @@ public interface ISmsService Task GetCreditAmount(); - public Task SendInstitutionVerificationLink(string number,string fullName, Guid institutionId); + public Task SendInstitutionVerificationLink(string number, string fullName, Guid institutionId, long contractingPartyId, long institutionContractId); - public Task SendInstitutionVerificationCode(string number, string code); + public Task SendInstitutionVerificationCode(string number, string code, string contractingPartyFullName, + long contractingPartyId, long institutionContractId); #endregion diff --git a/Company.Domain/SmsResultAgg/SmsResult.cs b/Company.Domain/SmsResultAgg/SmsResult.cs index 7a460662..5270f4de 100644 --- a/Company.Domain/SmsResultAgg/SmsResult.cs +++ b/Company.Domain/SmsResultAgg/SmsResult.cs @@ -5,7 +5,8 @@ namespace Company.Domain.SmsResultAgg; public class SmsResult: EntityBase { - public SmsResult(int messageId, string status,string typeOfSms, string contractingPartyName,string mobile, long contractingPatyId, long institutionContractId) + public SmsResult(int messageId, string status,string typeOfSms, + string contractingPartyName,string mobile, long contractingPatyId, long institutionContractId) { MessageId = messageId; Status = status; diff --git a/CompanyManagment.Application/InstitutionContractApplication.cs b/CompanyManagment.Application/InstitutionContractApplication.cs index f6599b33..4ac9321b 100644 --- a/CompanyManagment.Application/InstitutionContractApplication.cs +++ b/CompanyManagment.Application/InstitutionContractApplication.cs @@ -1141,7 +1141,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication await _smsService.SendInstitutionVerificationLink(contractingParty.Phone, contractingPartyFullName, - entity.PublicId); + entity.PublicId, contractingParty.id,entity.id ); await _institutionContractRepository.SaveChangesAsync(); @@ -1373,7 +1373,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication throw new NotFoundException("طرف قرارداد یافت نشد"); var contractingPartyFullName = contractingParty.FName + " " + contractingParty.LName; await _smsService.SendInstitutionVerificationLink(contractingParty.Phone, contractingPartyFullName, - institutionContract.PublicId); + institutionContract.PublicId, contractingParty.id, institutionContract.id); return new OperationResult().Succcedded(); } diff --git a/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs b/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs index 5a0c90ac..8486eb7e 100644 --- a/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs +++ b/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs @@ -2339,7 +2339,7 @@ public class InstitutionContractRepository : RepositoryBase SendInstitutionVerificationLink(string number,string fullName, Guid institutionId) + public async Task SendInstitutionVerificationLink(string number, string fullName, Guid institutionId, long contractingPartyId, long institutionContractId) { var guidStr=institutionId.ToString(); var firstPart = guidStr.Substring(0, 15); @@ -341,15 +343,25 @@ public class SmsService : ISmsService new("CODE1",firstPart), new("CODE2",secondPart) }); + var smsResult = new SmsResult(verificationSendResult.Data.MessageId, verificationSendResult.Message, "لینک تاییدیه قرارداد مالی", + fullName, number, contractingPartyId, institutionContractId); + await _smsResultRepository.CreateAsync(smsResult); + await _smsResultRepository.SaveChangesAsync(); return verificationSendResult.Status == 0; } - public async Task SendInstitutionVerificationCode(string number, string code) + public async Task SendInstitutionVerificationCode(string number, string code, string contractingPartyFullName, + long contractingPartyId, long institutionContractId) { var verificationSendResult =await SmsIr.VerifySendAsync(number, 965348, new VerifySendParameter[] { new("VERIFYCODE", code) }); + + var smsResult = new SmsResult(verificationSendResult.Data.MessageId, verificationSendResult.Message, "کد تاییدیه قرارداد مالی", + contractingPartyFullName, number, contractingPartyId, institutionContractId); + await _smsResultRepository.CreateAsync(smsResult); + await _smsResultRepository.SaveChangesAsync(); return verificationSendResult.Status == 0; } From 4c6de6a76f99b97c7b229ed3e9f6daeddb2639af Mon Sep 17 00:00:00 2001 From: mahan Date: Sat, 25 Oct 2025 17:25:08 +0330 Subject: [PATCH 4/5] refactor: improve sorting logic in Fine, Loan, and Reward repositories --- CompanyManagment.EFCore/Repository/FineRepository.cs | 2 +- CompanyManagment.EFCore/Repository/LoanRepository.cs | 2 +- CompanyManagment.EFCore/Repository/RewardRepository.cs | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CompanyManagment.EFCore/Repository/FineRepository.cs b/CompanyManagment.EFCore/Repository/FineRepository.cs index 07e1a8b8..0955122f 100644 --- a/CompanyManagment.EFCore/Repository/FineRepository.cs +++ b/CompanyManagment.EFCore/Repository/FineRepository.cs @@ -124,7 +124,7 @@ public class FineRepository : RepositoryBase, IFineRepository } query = query.Where(x => x.EmployeeId == searchModel.EmployeeId); - var list = query.ToList().Select(x => new FineViewModel() + var list = query.OrderByDescending(x=>x.FineDate).ToList().Select(x => new FineViewModel() { Amount = x.Amount.ToMoney(), AmountDouble = x.Amount, diff --git a/CompanyManagment.EFCore/Repository/LoanRepository.cs b/CompanyManagment.EFCore/Repository/LoanRepository.cs index 366fdde6..05c681e7 100644 --- a/CompanyManagment.EFCore/Repository/LoanRepository.cs +++ b/CompanyManagment.EFCore/Repository/LoanRepository.cs @@ -97,7 +97,7 @@ public class LoanRepository : RepositoryBase, ILoanRepository query = query.Where(x => x.StartInstallmentPayment >= startDate && x.StartInstallmentPayment <= endDate); } query = query.Where(x => x.EmployeeId == searchModel.EmployeeId); - var list = query.ToList().Select(x => new LoanViewModel() + var list = query.OrderByDescending(x=>x.StartInstallmentPayment).ToList().Select(x => new LoanViewModel() { Amount = x.Amount.ToMoney(), AmountDouble = x.Amount, diff --git a/CompanyManagment.EFCore/Repository/RewardRepository.cs b/CompanyManagment.EFCore/Repository/RewardRepository.cs index cfecb44f..f40853b1 100644 --- a/CompanyManagment.EFCore/Repository/RewardRepository.cs +++ b/CompanyManagment.EFCore/Repository/RewardRepository.cs @@ -6,6 +6,7 @@ using _0_Framework.InfraStructure; using Company.Domain.RewardAgg; using CompanyManagment.App.Contracts.Reward; using Microsoft.EntityFrameworkCore; +using SharpCompress.Compressors.Xz; namespace CompanyManagment.EFCore.Repository; @@ -119,12 +120,12 @@ public class RewardRepository : RepositoryBase, IRewardRepository query = query.Where(x => x.EmployeeId == searchModel.EmployeeId); - var list = query.ToList().Select(x => new RewardViewModel + var list = query.ToList().OrderByDescending(x=>x.GrantDate).Select(x => new RewardViewModel { WorkshopId = x.WorkshopId, Amount = x.Amount.ToMoney(), AmountDouble = x.Amount, - Description = x.Description, + Description = x.Description ?? "", EmployeeFullName = employees.FirstOrDefault(e => e.id == x.EmployeeId).FullName, EmployeeId = x.EmployeeId, GrantDateFa = x.GrantDate.ToFarsi(), From c5a1e5c27410517bc91ef5d4b428247dfe805608 Mon Sep 17 00:00:00 2001 From: mahan Date: Sun, 26 Oct 2025 11:23:51 +0330 Subject: [PATCH 5/5] feat: implement RollCallController and enhance workshop roll call reporting functionality --- .../GetWorkshopWithRollCallHandler.cs | 106 +++++++++++------- .../WorkshopWithRollCallServiceQueryModel.cs | 11 +- .../Admin/Controllers/RollCallController.cs | 36 ++++++ 3 files changed, 103 insertions(+), 50 deletions(-) create mode 100644 ServiceHost/Areas/Admin/Controllers/RollCallController.cs diff --git a/Query/AdminReports/Handlers/GetWorkshopWithRollCallHandler.cs b/Query/AdminReports/Handlers/GetWorkshopWithRollCallHandler.cs index e6f9df89..394d1e9e 100644 --- a/Query/AdminReports/Handlers/GetWorkshopWithRollCallHandler.cs +++ b/Query/AdminReports/Handlers/GetWorkshopWithRollCallHandler.cs @@ -9,20 +9,20 @@ using WorkFlow.Application.Contracts.WorkFlow; namespace Query.AdminReports.Handlers { - public interface IGetWorkshopWithRollCallHandler - { - List Handle(WorkshopWithRollCallServiceQueryParameters parameters); - } + public interface IGetWorkshopWithRollCallHandler + { + List Handle(WorkshopWithRollCallServiceQueryParameters parameters); + } - public class GetWorkshopWithRollCallHandler: IGetWorkshopWithRollCallHandler - { + public class GetWorkshopWithRollCallHandler : IGetWorkshopWithRollCallHandler + { private readonly CompanyContext _companyContext; private readonly IWorkFlowApplication workFlowApplication; public GetWorkshopWithRollCallHandler(CompanyContext companyContext, IWorkFlowApplication workFlowApplication) { - _companyContext = companyContext; - this.workFlowApplication = workFlowApplication; + _companyContext = companyContext; + this.workFlowApplication = workFlowApplication; } public List Handle(WorkshopWithRollCallServiceQueryParameters parameters) @@ -30,7 +30,6 @@ namespace Query.AdminReports.Handlers var now = DateTime.Now.Date; var lastWeek = now.AddDays(-7); - //workshop filter by parameters var rollCallServiceQuery = _companyContext.RollCallServices.AsSplitQuery(); @@ -46,35 +45,49 @@ namespace Query.AdminReports.Handlers rollCallServiceQuery = rollCallServiceQuery.Where(x => x.ServiceType == parameters.RollCallServiceType); if (parameters.FilterMode == FilterMode.Active) - rollCallServiceQuery = rollCallServiceQuery.Where(x => x.StartService.Date <= now && x.EndService.Date >= now); - else if (parameters.FilterMode == FilterMode.DeActive) - rollCallServiceQuery = rollCallServiceQuery.Where(x => x.EndService.Date < now || x.StartService.Date > now); + rollCallServiceQuery = + rollCallServiceQuery.Where(x => x.StartService.Date <= now && x.EndService.Date >= now); - var workshopsWithService = rollCallServiceQuery.Join(allWorkshops, x => x.WorkshopId, y => y.id, (rcs, workshop) => - new WorkshopWithRollCallServiceQueryModel() - { - WorkshopId = workshop.id, - RollCallServiceType = rcs.ServiceType, - WorkshopName = workshop.WorkshopFullName, - MaxPersonValid = rcs.MaxPersonValid, - IsActive = rcs.StartService <= DateTime.Now && rcs.EndService >= DateTime.Now, - ServiceStart = rcs.StartService, - ServiceEnd = rcs.EndService - }); + else if (parameters.FilterMode == FilterMode.DeActive) + rollCallServiceQuery = + rollCallServiceQuery.Where(x => x.EndService.Date < now || x.StartService.Date > now); + + var workshopsWithService = rollCallServiceQuery.Join(allWorkshops, x => x.WorkshopId, y => y.id, + (rcs, workshop) => + new WorkshopWithRollCallServiceQueryModel() + { + WorkshopId = workshop.id, + RollCallServiceType = rcs.ServiceType, + WorkshopName = workshop.WorkshopFullName, + MaxPersonValid = rcs.MaxPersonValid, + IsActive = rcs.StartService <= DateTime.Now && rcs.EndService >= DateTime.Now, + ServiceStart = rcs.StartService, + ServiceEnd = rcs.EndService + }); //workshop population - var workshopLeftWorks = _companyContext.Workshops.AsSplitQuery().Where(x => workshopsWithService.Any(y => y.WorkshopId == x.id)).Include(x => x.LeftWorks).Include(x => x.LeftWorkInsurances) - .Select(x => new - { - WorkshopId = x.id, - LeftWorks = x.LeftWorks.Where(y => y.StartWorkDate <= lastWeek && y.LeftWorkDate > now).Select(y => y.EmployeeId), - LeftWorkInsurances = x.LeftWorkInsurances.Where(y => y.StartWorkDate <= lastWeek && (y.LeftWorkDate > now || y.LeftWorkDate == null)).Select(y => y.EmployeeId) - }).ToList(); + var workshopLeftWorks = _companyContext.Workshops.AsSplitQuery() + .Where(x => workshopsWithService.Any(y => y.WorkshopId == x.id)).Include(x => x.LeftWorks) + .Include(x => x.LeftWorkInsurances) + .Select(x => new + { + WorkshopId = x.id, + LeftWorks = x.LeftWorks.Where(y => y.StartWorkDate <= lastWeek && y.LeftWorkDate > now) + .Select(y => y.EmployeeId), + LeftWorkInsurances = x.LeftWorkInsurances + .Where(y => y.StartWorkDate <= lastWeek && (y.LeftWorkDate > now || y.LeftWorkDate == null)) + .Select(y => y.EmployeeId) + }).ToList(); var workshopsWorkingEmployeesList = workshopLeftWorks - .Select(x => new { x.WorkshopId, TotalWorkingEmployeesCount = x.LeftWorks.Concat(x.LeftWorkInsurances).Distinct().Count(), EmployeeIds = x.LeftWorks.Concat(x.LeftWorkInsurances).Distinct() }).ToList(); - + .Select(x => + new + { + x.WorkshopId, + TotalWorkingEmployeesCount = x.LeftWorks.Concat(x.LeftWorkInsurances).Distinct().Count(), + EmployeeIds = x.LeftWorks.Concat(x.LeftWorkInsurances).Distinct() + }).ToList(); var activeEmployees = _companyContext.RollCallEmployees.AsSplitQuery() @@ -83,15 +96,25 @@ namespace Query.AdminReports.Handlers workshopsWithService.Any(y => y.WorkshopId == x.WorkshopId)) .Select(x => new { x.WorkshopId, x.EmployeeId }); - var lastWeekRollCalls = _companyContext.RollCalls.AsSplitQuery().Where(x => x.StartDate.HasValue && x.StartDate.Value >= lastWeek - && workshopsWithService.Any(y => y.WorkshopId == x.WorkshopId)).GroupBy(x => x.EmployeeId).Select(x => x.Key); + var lastWeekRollCalls = _companyContext.RollCalls.AsSplitQuery() + .Where(x => x.StartDate.HasValue && + x.StartDate.Value >= lastWeek + && workshopsWithService + .Any(y => y.WorkshopId == x.WorkshopId)) + .GroupBy(x => x.EmployeeId) + .Select(x => x.Key); - var leaves = _companyContext.LeaveList.AsSplitQuery().Where(x => x.StartLeave <= lastWeek && x.EndLeave >= now && (x.LeaveType == "استعلاجی" || x.PaidLeaveType == "روزانه") - && workshopsWithService.Any(y => y.WorkshopId == x.WorkshopId)).Select(x => x.EmployeeId); + var leaves = _companyContext.LeaveList.AsSplitQuery().Where(x => + x.StartLeave <= lastWeek && x.EndLeave >= now && + (x.LeaveType == "استعلاجی" || x.PaidLeaveType == "روزانه") + && workshopsWithService.Any(y => y.WorkshopId == x.WorkshopId)) + .Select(x => x.EmployeeId); - var activeEmployeesList = activeEmployees.ToList().Where(x => workshopsWorkingEmployeesList.Any(w => w.WorkshopId == x.WorkshopId && w.EmployeeIds.Contains(x.EmployeeId))); + var activeEmployeesList = activeEmployees.ToList().Where(x => + workshopsWorkingEmployeesList.Any(w => + w.WorkshopId == x.WorkshopId && w.EmployeeIds.Contains(x.EmployeeId))); var lastWeekRollCallsList = lastWeekRollCalls.ToList(); var leavesList = leaves.ToList(); var workshopsWithServiceList = workshopsWithService.ToList(); @@ -107,12 +130,13 @@ namespace Query.AdminReports.Handlers MaxPersonValid = x.MaxPersonValid, WorkshopName = x.WorkshopName, ActiveEmployeesCount = activeEmployeesList.Count(y => y.WorkshopId == x.WorkshopId), - ActiveEmployeesWithRollCallInLastWeekCount = activeEmployeesList.Count(y => y.WorkshopId == x.WorkshopId && + ActiveEmployeesWithRollCallInLastWeekCount = activeEmployeesList.Count(y => + y.WorkshopId == x.WorkshopId && lastWeekRollCallsList.Contains(y.EmployeeId) && !leavesList.Contains(y.EmployeeId)), - TotalEmployeesCount = workshopsWorkingEmployeesList.FirstOrDefault(y => y.WorkshopId == x.WorkshopId)?.TotalWorkingEmployeesCount ?? 0, + TotalEmployeesCount = workshopsWorkingEmployeesList + .FirstOrDefault(y => y.WorkshopId == x.WorkshopId)?.TotalWorkingEmployeesCount ?? 0, //UndoneWorkFlowsCount = workFlowApplication.GetAllWorkFlowCount(x.WorkshopId).Result }).OrderByDescending(x => x.IsActive).ToList(); - } } -} +} \ No newline at end of file diff --git a/Query/AdminReports/Models/WorkshopWithRollCallServiceQueryModel.cs b/Query/AdminReports/Models/WorkshopWithRollCallServiceQueryModel.cs index 90bdeb2d..60619858 100644 --- a/Query/AdminReports/Models/WorkshopWithRollCallServiceQueryModel.cs +++ b/Query/AdminReports/Models/WorkshopWithRollCallServiceQueryModel.cs @@ -13,15 +13,8 @@ public int ActiveEmployeesWithRollCallInLastWeekCount { get; set; } public int UndoneWorkFlowsCount { get; set; } - - - public float ActiveEmployeesWithRollCallPercentage - { - get - { - return ((float)ActiveEmployeesWithRollCallInLastWeekCount / ActiveEmployeesCount) * 100; - } - } + + public float ActiveEmployeesWithRollCallPercentage => ActiveEmployeesCount == 0 ? 0f : ((float)ActiveEmployeesWithRollCallInLastWeekCount / ActiveEmployeesCount) * 100f; public int MaxPersonValid { get; set; } public bool IsActive { get; set; } diff --git a/ServiceHost/Areas/Admin/Controllers/RollCallController.cs b/ServiceHost/Areas/Admin/Controllers/RollCallController.cs new file mode 100644 index 00000000..51c29916 --- /dev/null +++ b/ServiceHost/Areas/Admin/Controllers/RollCallController.cs @@ -0,0 +1,36 @@ +using _0_Framework.Application; +using Microsoft.AspNetCore.Mvc; +using Query.AdminReports.Handlers; +using Query.AdminReports.Models; +using ServiceHost.BaseControllers; +using WorkFlow.Application.Contracts.WorkFlow; + +namespace ServiceHost.Areas.Admin.Controllers; + +public class RollCallController:AdminBaseController +{ + private readonly IGetWorkshopWithRollCallHandler _workshopWithRollCallHandler; + private readonly IWorkFlowApplication _workFlowApplication; + private readonly long _currentAccountId; + + public RollCallController(IGetWorkshopWithRollCallHandler workshopWithRollCallHandler,IAuthHelper _authHelper, IWorkFlowApplication workFlowApplication) + { + _workshopWithRollCallHandler = workshopWithRollCallHandler; + _workFlowApplication = workFlowApplication; + _currentAccountId = _authHelper.CurrentAccountId(); + } + + [HttpGet("report")] + public ActionResult> GetRollCallReport([FromQuery] WorkshopWithRollCallServiceQueryParameters searchModel) + { + var result = _workshopWithRollCallHandler.Handle(searchModel); + return result; + } + + [HttpGet("repoert/workfloecount/{workshopId}")] + public async Task> GetWorkFlowCountByWorkshopId(long workshopId) + { + var result = await _workFlowApplication.GetAllWorkFlowCount(workshopId, _currentAccountId); + return result; + } +} \ No newline at end of file