From 1deeff996f84768bb3a345bde5ed0b6a988a875b Mon Sep 17 00:00:00 2001 From: mahan Date: Wed, 7 Jan 2026 14:35:17 +0330 Subject: [PATCH 1/8] feat: implement InstitutionContractSendFlag repository and model for contract send tracking --- .../IInstitutionContractSendFlagRepository.cs | 33 ++++++++ .../InstitutionContractSendFlag.cs | 82 +++++++++++++++++++ .../InstitutionContractSendFlagRepository.cs | 57 +++++++++++++ .../PersonalBootstrapper.cs | 5 ++ 4 files changed, 177 insertions(+) create mode 100644 Company.Domain/InstitutionContractSendFlagAgg/IInstitutionContractSendFlagRepository.cs create mode 100644 Company.Domain/InstitutionContractSendFlagAgg/InstitutionContractSendFlag.cs create mode 100644 CompanyManagement.Infrastructure.Mongo/InstitutionContractSendFlagRepo/InstitutionContractSendFlagRepository.cs diff --git a/Company.Domain/InstitutionContractSendFlagAgg/IInstitutionContractSendFlagRepository.cs b/Company.Domain/InstitutionContractSendFlagAgg/IInstitutionContractSendFlagRepository.cs new file mode 100644 index 00000000..7119a252 --- /dev/null +++ b/Company.Domain/InstitutionContractSendFlagAgg/IInstitutionContractSendFlagRepository.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading.Tasks; + +namespace Company.Domain.InstitutionContractSendFlagAgg; + +/// +/// Interface برای Repository مربوط به فلگ ارسال قرارداد +/// +public interface IInstitutionContractSendFlagRepository +{ + /// + /// ایجاد یک رکورد جدید برای فلگ ارسال قرارداد + /// + Task Create(InstitutionContractSendFlag flag); + + /// + /// بازیابی فلگ بر اساس شناسه قرارداد + /// + Task GetByContractId(long contractId); + + /// + /// به‌روزرسانی فلگ ارسال + /// + Task Update(InstitutionContractSendFlag flag); + + /// + /// بررسی اینکه آیا قرارداد ارسال شده است + /// + Task IsContractSent(long contractId); + + +} + diff --git a/Company.Domain/InstitutionContractSendFlagAgg/InstitutionContractSendFlag.cs b/Company.Domain/InstitutionContractSendFlagAgg/InstitutionContractSendFlag.cs new file mode 100644 index 00000000..20d951a0 --- /dev/null +++ b/Company.Domain/InstitutionContractSendFlagAgg/InstitutionContractSendFlag.cs @@ -0,0 +1,82 @@ +using System; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace Company.Domain.InstitutionContractSendFlagAgg; + +/// +/// نمایندگی فلگ ارسال قرارداد در MongoDB +/// این موجودیت برای ردیابی اینکه آیا قرارداد ارسال شده است استفاده می‌شود +/// +public class InstitutionContractSendFlag +{ + public InstitutionContractSendFlag(long institutionContractId,bool isSent) + { + Id = Guid.NewGuid(); + InstitutionContractId = institutionContractId; + IsSent = isSent; + CreatedDate = DateTime.Now; + } + + /// + /// شناسه یکتای MongoDB + /// + [BsonId] + [BsonRepresentation(BsonType.String)] + public Guid Id { get; set; } + + /// + /// شناسه قرارداد در SQL + /// + public long InstitutionContractId { get; set; } + + /// + /// آیا قرارداد ارسال شده است + /// + public bool IsSent { get; set; } + + /// + /// تاریخ و زمان ارسال + /// + public DateTime? SentDate { get; set; } + + /// + /// تاریخ و زمان ایجاد رکورد + /// + public DateTime CreatedDate { get; set; } + + /// + /// تاریخ و زمان آخرین به‌روزرسانی + /// + public DateTime? LastModifiedDate { get; set; } + + + /// + /// علامت‌گذاری قرارداد به عنوان ارسال‌شده + /// + public void MarkAsSent(string reason = null) + { + IsSent = true; + SentDate = DateTime.Now; + LastModifiedDate = DateTime.Now; + } + + /// + /// بازگردانی علامت ارسال + /// + public void MarkAsNotSent() + { + IsSent = false; + SentDate = null; + LastModifiedDate = DateTime.Now; + } + + /// + /// به‌روزرسانی علامت آخری اصلاح + /// + public void UpdateLastModified() + { + LastModifiedDate = DateTime.Now; + } +} + diff --git a/CompanyManagement.Infrastructure.Mongo/InstitutionContractSendFlagRepo/InstitutionContractSendFlagRepository.cs b/CompanyManagement.Infrastructure.Mongo/InstitutionContractSendFlagRepo/InstitutionContractSendFlagRepository.cs new file mode 100644 index 00000000..54f30be0 --- /dev/null +++ b/CompanyManagement.Infrastructure.Mongo/InstitutionContractSendFlagRepo/InstitutionContractSendFlagRepository.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading.Tasks; +using Company.Domain.InstitutionContractSendFlagAgg; +using MongoDB.Driver; + +namespace CompanyManagement.Infrastructure.Mongo.InstitutionContractSendFlagRepo; + +/// +/// Repository برای مدیریت فلگ ارسال قرارداد در MongoDB +/// +public class InstitutionContractSendFlagRepository : IInstitutionContractSendFlagRepository +{ + private readonly IMongoCollection _collection; + + public InstitutionContractSendFlagRepository(IMongoDatabase database) + { + _collection = database.GetCollection("InstitutionContractSendFlag"); + } + + public async Task Create(InstitutionContractSendFlag flag) + { + await _collection.InsertOneAsync(flag); + } + + public async Task GetByContractId(long contractId) + { + var filter = Builders.Filter + .Eq(x => x.InstitutionContractId, contractId); + + return await _collection.Find(filter).FirstOrDefaultAsync(); + } + + + public async Task Update(InstitutionContractSendFlag flag) + { + var filter = Builders.Filter + .Eq(x => x.InstitutionContractId, flag.InstitutionContractId); + + await _collection.ReplaceOneAsync(filter, flag); + } + + public async Task IsContractSent(long contractId) + { + var flag = await GetByContractId(contractId); + return flag != null && flag.IsSent; + } + + public async Task Remove(long contractId) + { + var filter = Builders.Filter + .Eq(x => x.InstitutionContractId, contractId); + + await _collection.DeleteOneAsync(filter); + } + +} + diff --git a/PersonalContractingParty.Config/PersonalBootstrapper.cs b/PersonalContractingParty.Config/PersonalBootstrapper.cs index 4765fafb..aab12f1f 100644 --- a/PersonalContractingParty.Config/PersonalBootstrapper.cs +++ b/PersonalContractingParty.Config/PersonalBootstrapper.cs @@ -61,6 +61,7 @@ using Company.Domain.HolidayItemAgg; using Company.Domain.InstitutionContractAgg; using Company.Domain.InstitutionContractContactInfoAgg; using Company.Domain.InstitutionContractExtensionTempAgg; +using Company.Domain.InstitutionContractSendFlagAgg; using Company.Domain.InstitutionPlanAgg; using Company.Domain.InsuranceAgg; using Company.Domain.InsuranceEmployeeInfoAgg; @@ -123,6 +124,7 @@ using Company.Domain.ZoneAgg; using CompanyManagement.Infrastructure.Excel.SalaryAid; using CompanyManagement.Infrastructure.Mongo.EmployeeFaceEmbeddingRepo; using CompanyManagement.Infrastructure.Mongo.InstitutionContractInsertTempRepo; +using CompanyManagement.Infrastructure.Mongo.InstitutionContractSendFlagRepo; using CompanyManagment.App.Contracts.AdminMonthlyOverview; using CompanyManagment.App.Contracts.AndroidApkVersion; using CompanyManagment.App.Contracts.AuthorizedPerson; @@ -658,6 +660,9 @@ public class PersonalBootstrapper services.AddTransient(); services.AddTransient(); // MongoDB Implementation + // InstitutionContractSendFlag - MongoDB + services.AddTransient(); + services.AddDbContext(x => x.UseSqlServer(connectionString)); } } \ No newline at end of file From dd7e816767e05abf14f0200598e03842a96f89d4 Mon Sep 17 00:00:00 2001 From: mahan Date: Wed, 7 Jan 2026 14:46:34 +0330 Subject: [PATCH 2/8] feat: add SetContractSendFlag method and related request for contract send tracking --- .../InstitutionContractSendFlag.cs | 2 +- .../IInstitutionContractApplication.cs | 8 +++ .../SetInstitutionContractSendFlagRequest.cs | 19 ++++++ .../InstitutionContractApplication.cs | 59 ++++++++++++++++++- 4 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 CompanyManagment.App.Contracts/InstitutionContract/SetInstitutionContractSendFlagRequest.cs diff --git a/Company.Domain/InstitutionContractSendFlagAgg/InstitutionContractSendFlag.cs b/Company.Domain/InstitutionContractSendFlagAgg/InstitutionContractSendFlag.cs index 20d951a0..2b3a7137 100644 --- a/Company.Domain/InstitutionContractSendFlagAgg/InstitutionContractSendFlag.cs +++ b/Company.Domain/InstitutionContractSendFlagAgg/InstitutionContractSendFlag.cs @@ -54,7 +54,7 @@ public class InstitutionContractSendFlag /// /// علامت‌گذاری قرارداد به عنوان ارسال‌شده /// - public void MarkAsSent(string reason = null) + public void MarkAsSent() { IsSent = true; SentDate = DateTime.Now; diff --git a/CompanyManagment.App.Contracts/InstitutionContract/IInstitutionContractApplication.cs b/CompanyManagment.App.Contracts/InstitutionContract/IInstitutionContractApplication.cs index 10266d6c..dc8edb1e 100644 --- a/CompanyManagment.App.Contracts/InstitutionContract/IInstitutionContractApplication.cs +++ b/CompanyManagment.App.Contracts/InstitutionContract/IInstitutionContractApplication.cs @@ -305,6 +305,14 @@ public interface IInstitutionContractApplication Task SetDiscountForCreation(InstitutionContractSetDiscountForCreationRequest request); Task ResetDiscountForCreation(InstitutionContractResetDiscountForExtensionRequest request); Task CreationComplete(InstitutionContractExtensionCompleteRequest request); + + /// + /// تعیین فلگ ارسال قرارداد در MongoDB + /// اگر فلگ وجود نداشتن‌د ایجاد می‌کند + /// + /// درخواست تعیین فلگ + /// نتیجه عملیات + Task SetContractSendFlag(SetInstitutionContractSendFlagRequest request); } public class CreationSetContractingPartyResponse diff --git a/CompanyManagment.App.Contracts/InstitutionContract/SetInstitutionContractSendFlagRequest.cs b/CompanyManagment.App.Contracts/InstitutionContract/SetInstitutionContractSendFlagRequest.cs new file mode 100644 index 00000000..e19bacf5 --- /dev/null +++ b/CompanyManagment.App.Contracts/InstitutionContract/SetInstitutionContractSendFlagRequest.cs @@ -0,0 +1,19 @@ +namespace CompanyManagment.App.Contracts.InstitutionContract; + +/// +/// درخواست برای تعیین فلگ ارسال قرارداد +/// +public class SetInstitutionContractSendFlagRequest +{ + /// + /// شناسه قرارداد + /// + public long InstitutionContractId { get; set; } + + /// + /// آیا قرارداد ارسال شده است + /// + public bool IsSent { get; set; } + +} + diff --git a/CompanyManagment.Application/InstitutionContractApplication.cs b/CompanyManagment.Application/InstitutionContractApplication.cs index 5790aaee..94da79fc 100644 --- a/CompanyManagment.Application/InstitutionContractApplication.cs +++ b/CompanyManagment.Application/InstitutionContractApplication.cs @@ -19,6 +19,7 @@ using Company.Domain.PaymentTransactionAgg; using Company.Domain.RepresentativeAgg; using Company.Domain.RollCallServiceAgg; using Company.Domain.WorkshopAgg; +using Company.Domain.InstitutionContractSendFlagAgg; using CompanyManagment.App.Contracts.FinancialInvoice; using CompanyManagment.App.Contracts.FinancialStatment; using CompanyManagment.App.Contracts.InstitutionContract; @@ -51,6 +52,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication private readonly IPaymentTransactionRepository _paymentTransactionRepository; private readonly IRollCallServiceRepository _rollCallServiceRepository; private readonly ISepehrPaymentGatewayService _sepehrPaymentGatewayService; + private readonly IInstitutionContractSendFlagRepository _institutionContractSendFlagRepository; public InstitutionContractApplication(IInstitutionContractRepository institutionContractRepository, @@ -62,7 +64,8 @@ public class InstitutionContractApplication : IInstitutionContractApplication IAccountApplication accountApplication, ISmsService smsService, IFinancialInvoiceRepository financialInvoiceRepository, IHttpClientFactory httpClientFactory, IPaymentTransactionRepository paymentTransactionRepository, IRollCallServiceRepository rollCallServiceRepository, - ISepehrPaymentGatewayService sepehrPaymentGatewayService,ILogger sepehrGatewayLogger) + ISepehrPaymentGatewayService sepehrPaymentGatewayService,ILogger sepehrGatewayLogger, + IInstitutionContractSendFlagRepository institutionContractSendFlagRepository) { _institutionContractRepository = institutionContractRepository; _contractingPartyRepository = contractingPartyRepository; @@ -80,6 +83,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication _rollCallServiceRepository = rollCallServiceRepository; _sepehrPaymentGatewayService = sepehrPaymentGatewayService; _paymentGateway = new SepehrPaymentGateway(httpClientFactory,sepehrGatewayLogger); + _institutionContractSendFlagRepository = institutionContractSendFlagRepository; } public OperationResult Create(CreateInstitutionContract command) @@ -1820,7 +1824,60 @@ public class InstitutionContractApplication : IInstitutionContractApplication installments.Add(lastInstallment); return installments; } + + } + /// + /// تعیین فلگ ارسال قرارداد + /// اگر فلگ وجود نداشتن‌د ایجاد می‌کند + /// + public async Task SetContractSendFlag(SetInstitutionContractSendFlagRequest request) + { + var operationResult = new OperationResult(); + + try + { + // بازیابی قرارداد از SQL + var contract = _institutionContractRepository.Get(request.InstitutionContractId); + if (contract == null) + return operationResult.Failed("قرارداد مورد نظر یافت نشد"); + + // بررسی اینکه آیا فلگ در MongoDB وجود دارد + var existingFlag = await _institutionContractSendFlagRepository + .GetByContractId(request.InstitutionContractId); + + if (existingFlag != null) + { + // اگر فلگ وجود داشتن‌د، آن را اپدیت کنیم + if (request.IsSent) + { + existingFlag.MarkAsSent(); + } + else + { + existingFlag.MarkAsNotSent(); + } + existingFlag.UpdateLastModified(); + await _institutionContractSendFlagRepository.Update(existingFlag); + } + else + { + // اگر فلگ وجود ندارد، آن را ایجاد کنیم + var newFlag = new InstitutionContractSendFlag( + request.InstitutionContractId, + request.IsSent + ); + + await _institutionContractSendFlagRepository.Create(newFlag); + } + + return operationResult.Succcedded(); + } + catch (Exception ex) + { + return operationResult.Failed($"خطا در تعیین فلگ ارسال: {ex.Message}"); + } + } } #region CustomViewModels From 4ade9e12a6fb0c664f8a981b733be1b67ca407d1 Mon Sep 17 00:00:00 2001 From: mahan Date: Wed, 7 Jan 2026 15:03:21 +0330 Subject: [PATCH 3/8] feat: add InstitutionContractIsSentFlag to track contract send status --- .../IInstitutionContractSendFlagRepository.cs | 1 + .../InstitutionContractSendFlagRepository.cs | 1 + .../GetInstitutionContractListItemsViewModel.cs | 2 ++ .../Repository/InstitutionContractRepository.cs | 14 +++++++++++++- 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Company.Domain/InstitutionContractSendFlagAgg/IInstitutionContractSendFlagRepository.cs b/Company.Domain/InstitutionContractSendFlagAgg/IInstitutionContractSendFlagRepository.cs index 7119a252..b847d2e0 100644 --- a/Company.Domain/InstitutionContractSendFlagAgg/IInstitutionContractSendFlagRepository.cs +++ b/Company.Domain/InstitutionContractSendFlagAgg/IInstitutionContractSendFlagRepository.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; namespace Company.Domain.InstitutionContractSendFlagAgg; diff --git a/CompanyManagement.Infrastructure.Mongo/InstitutionContractSendFlagRepo/InstitutionContractSendFlagRepository.cs b/CompanyManagement.Infrastructure.Mongo/InstitutionContractSendFlagRepo/InstitutionContractSendFlagRepository.cs index 54f30be0..ccc45260 100644 --- a/CompanyManagement.Infrastructure.Mongo/InstitutionContractSendFlagRepo/InstitutionContractSendFlagRepository.cs +++ b/CompanyManagement.Infrastructure.Mongo/InstitutionContractSendFlagRepo/InstitutionContractSendFlagRepository.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using Company.Domain.InstitutionContractSendFlagAgg; using MongoDB.Driver; diff --git a/CompanyManagment.App.Contracts/InstitutionContract/GetInstitutionContractListItemsViewModel.cs b/CompanyManagment.App.Contracts/InstitutionContract/GetInstitutionContractListItemsViewModel.cs index 7ba3aaa0..f69e1e75 100644 --- a/CompanyManagment.App.Contracts/InstitutionContract/GetInstitutionContractListItemsViewModel.cs +++ b/CompanyManagment.App.Contracts/InstitutionContract/GetInstitutionContractListItemsViewModel.cs @@ -96,6 +96,8 @@ public class GetInstitutionContractListItemsViewModel /// مبلغ قسط /// public double InstallmentAmount { get; set; } + + public bool InstitutionContractIsSentFlag { get; set; } } public class InstitutionContractListWorkshop diff --git a/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs b/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs index 4ea52bbb..7f7f00d0 100644 --- a/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs +++ b/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs @@ -11,6 +11,7 @@ using Company.Domain.InstitutionContractAgg; using Company.Domain.InstitutionContractAmendmentTempAgg; using Company.Domain.InstitutionContractContactInfoAgg; using Company.Domain.InstitutionContractExtensionTempAgg; +using Company.Domain.InstitutionContractSendFlagAgg; using Company.Domain.InstitutionPlanAgg; using Company.Domain.SmsResultAgg; using Company.Domain.WorkshopAgg; @@ -42,6 +43,7 @@ using AccountManagement.Application.Contracts.Account; using Company.Domain.InstitutionContractCreationTempAgg; using Company.Domain.RepresentativeAgg; using Company.Domain.TemporaryClientRegistrationAgg; +using Company.Domain.InstitutionContractSendFlagAgg; using ContractingPartyAccount = Company.Domain.ContractingPartyAccountAgg.ContractingPartyAccount; using FinancialStatment = Company.Domain.FinancialStatmentAgg.FinancialStatment; using String = System.String; @@ -57,6 +59,7 @@ public class InstitutionContractRepository : RepositoryBase _institutionExtensionTemp; private readonly IMongoCollection _institutionAmendmentTemp; private readonly IMongoCollection _institutionContractCreationTemp; + private readonly IMongoCollection _institutionContractSendFlag; private readonly IPlanPercentageRepository _planPercentageRepository; private readonly ISmsService _smsService; private readonly ISmsResultRepository _smsResultRepository; @@ -114,6 +117,8 @@ public class InstitutionContractRepository : RepositoryBase("InstitutionContractAmendmentTemp"); _institutionContractCreationTemp = database.GetCollection("InstitutionContractCreationTemp"); + _institutionContractSendFlag = + database.GetCollection("InstitutionContractSendFlag"); } public EditInstitutionContract GetDetails(long id) @@ -1353,6 +1358,12 @@ public class InstitutionContractRepository : RepositoryBase contractIds.Contains(x.InstitutionContractId)) .ToDictionaryAsync(x => x.InstitutionContractId, x => x); + // بارگذاری وضعیت ارسال قراردادها از MongoDB - کوئری مستقیم + var filter = Builders.Filter + .In(x => x.InstitutionContractId, contractIds); + var sendFlagsList = await _institutionContractSendFlag.Find(filter).ToListAsync(); + var sendFlags = sendFlagsList.ToDictionary(x => x.InstitutionContractId, x => x.IsSent); + var financialStatements = _context.FinancialStatments.Include(x => x.FinancialTransactionList) .Where(x => contractingPartyIds.Contains(x.ContractingPartyId)).ToList(); var res = new PagedResult() @@ -1462,7 +1473,8 @@ public class InstitutionContractRepository : RepositoryBase y.Services.ContractInPerson) ?? true, - IsOldContract = x.contract.SigningType == InstitutionContractSigningType.Legacy + IsOldContract = x.contract.SigningType == InstitutionContractSigningType.Legacy, + InstitutionContractIsSentFlag = sendFlags.ContainsKey(x.contract.id) ? sendFlags[x.contract.id] : false }; }).ToList() }; From 140414b866b8ba4764567f5df9f46fdfda24f1aa Mon Sep 17 00:00:00 2001 From: mahan Date: Wed, 7 Jan 2026 16:23:11 +0330 Subject: [PATCH 4/8] feat: add SetIsSent endpoint to update contract send status --- .../IInstitutionContractApplication.cs | 2 +- .../InstitutionContractApplication.cs | 1 + .../Controllers/institutionContractController.cs | 13 ++++++++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CompanyManagment.App.Contracts/InstitutionContract/IInstitutionContractApplication.cs b/CompanyManagment.App.Contracts/InstitutionContract/IInstitutionContractApplication.cs index dc8edb1e..eb230aa4 100644 --- a/CompanyManagment.App.Contracts/InstitutionContract/IInstitutionContractApplication.cs +++ b/CompanyManagment.App.Contracts/InstitutionContract/IInstitutionContractApplication.cs @@ -148,7 +148,7 @@ public interface IInstitutionContractApplication /// شناسه قرارداد /// نتیجه عملیات OperationResult UnSign(long id); - + /// /// ایجاد حساب کاربری برای طرف قرارداد /// diff --git a/CompanyManagment.Application/InstitutionContractApplication.cs b/CompanyManagment.Application/InstitutionContractApplication.cs index 94da79fc..199e6967 100644 --- a/CompanyManagment.Application/InstitutionContractApplication.cs +++ b/CompanyManagment.Application/InstitutionContractApplication.cs @@ -898,6 +898,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication return opration.Succcedded(); } + public void CreateContractingPartyAccount(long contractingPartyid, long accountId) { _institutionContractRepository.CreateContractingPartyAccount(contractingPartyid, accountId); diff --git a/ServiceHost/Areas/Admin/Controllers/institutionContractController.cs b/ServiceHost/Areas/Admin/Controllers/institutionContractController.cs index cb0e2408..17283a00 100644 --- a/ServiceHost/Areas/Admin/Controllers/institutionContractController.cs +++ b/ServiceHost/Areas/Admin/Controllers/institutionContractController.cs @@ -916,6 +916,17 @@ public class institutionContractController : AdminBaseController "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"قرارداد های مالی.xlsx"); } + + /// + /// تنظیم وضعیت ارسال قرارداد + /// + [HttpPost("set-is-sent")] + public async Task> SetIsSent([FromBody] SetInstitutionContractSendFlagRequest request) + { + var result = await _institutionContractApplication.SetContractSendFlag(request); + return result; + } + } public class InstitutionContractCreationGetRepresentativeIdResponse @@ -969,4 +980,4 @@ public class VerifyCodeRequest { public long ContractingPartyId { get; set; } public string verifyCode { get; set; } -} \ No newline at end of file +} From 7cb39b1b92103c45d5b83045383d7bb8b392ab45 Mon Sep 17 00:00:00 2001 From: mahan Date: Thu, 8 Jan 2026 14:16:08 +0330 Subject: [PATCH 5/8] feat: add UserId filter to ProjectBoardListQuery for enhanced task assignment tracking --- .../Queries/ProjectBoardList/ProjectBoardListQuery.cs | 1 + .../Queries/ProjectBoardList/ProjectBoardListQueryHandler.cs | 5 +++++ 2 files changed, 6 insertions(+) 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 2d42152a..73ea9a9e 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,6 @@ namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.Project public record ProjectBoardListQuery: IBaseQuery> { + public long? UserId { get; set; } 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 f23ea7d5..4ff4f377 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 @@ -40,6 +40,11 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler x.Status == request.Status); } + + if (request.UserId is > 0) + { + queryable = queryable.Where(x => x.CurrentAssignedUserId == request.UserId); + } var data = await queryable.ToListAsync(cancellationToken); From 0772604432dbb84a8b25f0562259d74bc07ed9fc Mon Sep 17 00:00:00 2001 From: mahan Date: Thu, 8 Jan 2026 15:02:43 +0330 Subject: [PATCH 6/8] feat: enhance GetMessagesQuery to include additional time notes in message retrieval --- .../Queries/GetMessages/GetMessagesQuery.cs | 127 ++++++++++-------- 1 file changed, 74 insertions(+), 53 deletions(-) diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/TaskChat/Queries/GetMessages/GetMessagesQuery.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/TaskChat/Queries/GetMessages/GetMessagesQuery.cs index fbc3e3b8..9d0deccf 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/TaskChat/Queries/GetMessages/GetMessagesQuery.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/TaskChat/Queries/GetMessages/GetMessagesQuery.cs @@ -1,7 +1,6 @@ using GozareshgirProgramManager.Application._Common.Interfaces; using GozareshgirProgramManager.Application._Common.Models; using GozareshgirProgramManager.Application.Modules.TaskChat.DTOs; -using MediatR; using Microsoft.EntityFrameworkCore; namespace GozareshgirProgramManager.Application.Modules.TaskChat.Queries.GetMessages; @@ -25,6 +24,39 @@ public class GetMessagesQueryHandler : IBaseQueryHandler CreateAdditionalTimeNotes( + IEnumerable additionalTimes, + Dictionary users, + Guid taskId) + { + var notes = new List(); + + foreach (var additionalTime in additionalTimes) + { + var addedByUserName = additionalTime.AddedByUserId.HasValue && users.TryGetValue(additionalTime.AddedByUserId.Value, out var user) + ? user + : "سیستم"; + + var noteContent = $"⏱️ زمان اضافی: {additionalTime.Hours.TotalHours.ToString("F2")} ساعت - {(string.IsNullOrWhiteSpace(additionalTime.Reason) ? "بدون علت" : additionalTime.Reason)} - توسط {addedByUserName}"; + + var noteDto = new MessageDto + { + Id = Guid.NewGuid(), + TaskId = taskId, + SenderUserId = 0, + SenderName = "سیستم", + MessageType = "Note", + TextContent = noteContent, + CreationDate = additionalTime.CreationDate, + IsMine = false + }; + + notes.Add(noteDto); + } + + return notes; + } + public async Task>> Handle(GetMessagesQuery request, CancellationToken cancellationToken) { var currentUserId = _authHelper.GetCurrentUserId(); @@ -44,36 +76,52 @@ public class GetMessagesQueryHandler : IBaseQueryHandler m.SenderUserId).Distinct().ToList(); var users = await _context.Users .Where(u => senderUserIds.Contains(u.Id)) .ToDictionaryAsync(u => u.Id, u => u.FullName, cancellationToken); // ✅ گرفتن تمامی زمان‌های اضافی (Additional Times) برای نمایش به صورت نوت - // در اینجا تمامی TaskSections مربوط به این تسک را می‌گیریم - // و برای هر کدام تمام AdditionalTimes آن را بارگذاری می‌کنیم var taskSections = await _context.TaskSections .Where(ts => ts.TaskId == request.TaskId) .Include(ts => ts.AdditionalTimes) .ToListAsync(cancellationToken); + // ✅ تمام زمان‌های اضافی را یکجا بگیر و مرتب کن + var allAdditionalTimes = taskSections + .SelectMany(ts => ts.AdditionalTimes) + .OrderBy(at => at.CreationDate) + .ToList(); + var messageDtos = new List(); + // ✅ ابتدا زمان‌های اضافی قبل از اولین پیام را اضافه کن (اگر پیامی وجود داشته باشد) + if (messages.Any()) + { + var firstMessageDate = messages.First().CreationDate; + var additionalTimesBeforeFirstMessage = allAdditionalTimes + .Where(at => at.CreationDate < firstMessageDate) + .ToList(); + + messageDtos.AddRange(CreateAdditionalTimeNotes(additionalTimesBeforeFirstMessage, users, request.TaskId)); + } + else + { + // ✅ اگر هیچ پیامی وجود ندارد، همه زمان‌های اضافی را نمایش بده + messageDtos.AddRange(CreateAdditionalTimeNotes(allAdditionalTimes, users, request.TaskId)); + } + foreach (var message in messages) { // ✅ نام فرستنده را از Dictionary Users بگیر، در صورت عدم وجود "کاربر ناشناس" نمایش بده - var senderName = users.ContainsKey(message.SenderUserId) - ? users[message.SenderUserId] - : "کاربر ناشناس"; + var senderName = users.GetValueOrDefault(message.SenderUserId, "کاربر ناشناس"); var dto = new MessageDto { Id = message.Id, TaskId = message.TaskId, SenderUserId = message.SenderUserId, - SenderName = senderName, // ✅ از User واقعی استفاده می‌کنیم + SenderName = senderName, MessageType = message.MessageType.ToString(), TextContent = message.TextContent, ReplyToMessageId = message.ReplyToMessageId, @@ -88,10 +136,7 @@ public class GetMessagesQueryHandler : IBaseQueryHandler ts.AdditionalTimes) - .Where(at => at.AddedAt > message.CreationDate) // ✅ تغییر به AddedAt (زمان واقعی اضافه شدن) - .OrderBy(at => at.AddedAt) - .FirstOrDefault(); + // ✅ پیدا کردن پیام بعدی (اگر وجود داشته باشد) + var currentIndex = messages.IndexOf(message); + var nextMessage = currentIndex < messages.Count - 1 ? messages[currentIndex + 1] : null; - if (additionalTimesAfterMessage != null) + if (nextMessage != null) { - // ✅ تمام AdditionalTimes بین این پیام و پیام قبلی را بگیر - var additionalTimesByDate = taskSections - .SelectMany(ts => ts.AdditionalTimes) - .Where(at => at.AddedAt <= message.CreationDate && - (messageDtos.Count == 1 || at.AddedAt > messageDtos[messageDtos.Count - 2].CreationDate)) - .OrderBy(at => at.AddedAt) + // ✅ زمان‌های اضافی بین این پیام و پیام بعدی + var additionalTimesBetween = allAdditionalTimes + .Where(at => at.CreationDate > message.CreationDate && at.CreationDate < nextMessage.CreationDate) .ToList(); - foreach (var additionalTime in additionalTimesByDate) - { - // ✅ نام کاربری که این زمان اضافی را اضافه کرد - var addedByUserName = additionalTime.AddedByUserId.HasValue && users.TryGetValue(additionalTime.AddedByUserId.Value, out var user) - ? user - : "سیستم"; + messageDtos.AddRange(CreateAdditionalTimeNotes(additionalTimesBetween, users, request.TaskId)); + } + else + { + // ✅ این آخرین پیام است، زمان‌های اضافی بعد از آن را اضافه کن + var additionalTimesAfterLastMessage = allAdditionalTimes + .Where(at => at.CreationDate > message.CreationDate) + .ToList(); - // ✅ محتوای نوت را با اطلاعات کامل ایجاد کن - // نمایش می‌دهد: مقدار زمان + علت + نام کسی که اضافه کرد - var noteContent = $"⏱️ زمان اضافی: {additionalTime.Hours.TotalHours:F2} ساعت - {(string.IsNullOrWhiteSpace(additionalTime.Reason) ? "بدون علت" : additionalTime.Reason)} - توسط {addedByUserName}"; - - // ✅ نوت را به عنوان MessageDto خاصی ایجاد کن - var noteDto = new MessageDto - { - Id = Guid.NewGuid(), - TaskId = request.TaskId, - SenderUserId = 0, // ✅ سیستم برای نشان دادن اینکه یک پیام خودکار است - SenderName = "سیستم", - MessageType = "Note", // ✅ نوع پیام: Note (یادداشت سیستم) - TextContent = noteContent, - CreationDate = additionalTime.AddedAt, // ✅ تاریخ اضافه شدن زمان اضافی - IsMine = false - }; - - messageDtos.Add(noteDto); - } + messageDtos.AddRange(CreateAdditionalTimeNotes(additionalTimesAfterLastMessage, users, request.TaskId)); } } // ✅ مرتب کردن نهایی تمام پیام‌ها (معمولی + نوت‌ها) بر اساس زمان ایجاد - // اینطور که نوت‌های زمان اضافی در جای درست خود قرار می‌گیرند messageDtos = messageDtos.OrderBy(m => m.CreationDate).ToList(); var response = new PaginationResult() From b741ab9ed2ea7cb8e1f2f0094853bafa0fc257d9 Mon Sep 17 00:00:00 2001 From: mahan Date: Sat, 10 Jan 2026 10:34:20 +0330 Subject: [PATCH 7/8] fix contains no element error for empty skills --- .../ProjectDeployBoardDetailsQueryHandler.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryHandler.cs index ddff2247..9d93332d 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryHandler.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryHandler.cs @@ -73,6 +73,7 @@ public class var doneTime = t.Sections.Aggregate(TimeSpan.Zero, (sum, next) => sum.Add(next.GetTotalTimeSpent())); + var skills = t.Sections .Select(s => { @@ -88,7 +89,14 @@ public class skillName, timePercentage); }).ToList(); - var taskPercentage = (int)Math.Round(skills.Average(x => x.TimePercentage)); + + int taskPercentage = 0; + + if (!skills.Any()) + { + taskPercentage = 0; + } + taskPercentage = (int)Math.Round(skills.Average(x => x.TimePercentage)); return new ProjectDeployBoardDetailTaskItem( t.Name, From 587fa40d81c43f9ec129a2bcb420d413dca497ab Mon Sep 17 00:00:00 2001 From: mahan Date: Sat, 10 Jan 2026 10:45:38 +0330 Subject: [PATCH 8/8] fix percnetage condition --- .../ProjectDeployBoardDetailsQueryHandler.cs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryHandler.cs b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryHandler.cs index 9d93332d..07dade77 100644 --- a/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryHandler.cs +++ b/ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectDeployBoardDetail/ProjectDeployBoardDetailsQueryHandler.cs @@ -21,7 +21,7 @@ public record ProjectDeployBoardDetailTaskItem( TimeSpan DoneTimeSpan, int Percentage, List Skills) - : ProjectDeployBoardDetailPhaseItem(Name, TotalTimeSpan, DoneTimeSpan,Percentage); + : ProjectDeployBoardDetailPhaseItem(Name, TotalTimeSpan, DoneTimeSpan, Percentage); public record ProjectDeployBoardDetailItemSkill(string OriginalUserFullName, string SkillName, int TimePercentage); @@ -73,7 +73,7 @@ public class var doneTime = t.Sections.Aggregate(TimeSpan.Zero, (sum, next) => sum.Add(next.GetTotalTimeSpent())); - + var skills = t.Sections .Select(s => { @@ -83,20 +83,23 @@ public class var skillName = s.Skill?.Name ?? "بدون مهارت"; var timePercentage = (int)s.GetProgressPercentage(); - + return new ProjectDeployBoardDetailItemSkill( originalUserFullName, skillName, timePercentage); }).ToList(); - - int taskPercentage = 0; - - if (!skills.Any()) + + int taskPercentage; + + if (skills.Count == 0) { taskPercentage = 0; } - taskPercentage = (int)Math.Round(skills.Average(x => x.TimePercentage)); + else + { + taskPercentage = (int)Math.Round(skills.Average(x => x.TimePercentage)); + } return new ProjectDeployBoardDetailTaskItem( t.Name, @@ -113,7 +116,7 @@ public class (sum, next) => sum.Add(next.DoneTimeSpan)); var phasePercentage = tasksRes.Average(x => x.Percentage); - + var phaseRes = new ProjectDeployBoardDetailPhaseItem(phase.Name, totalTimeSpan, doneTimeSpan, (int)phasePercentage);