Merge remote-tracking branch 'origin/Main' into Main

# Conflicts:
#	CompanyManagment.Application/InstitutionContractApplication.cs
#	CompanyManagment.EFCore/Repository/PlanPercentageRepository.cs
This commit is contained in:
2025-12-23 20:18:37 +03:30
18 changed files with 280 additions and 78 deletions

View File

@@ -36,6 +36,6 @@ jobs:
& "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" ` & "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" `
-verb:sync ` -verb:sync `
-source:contentPath="$publishFolder" ` -source:contentPath="$publishFolder" `
-dest:contentPath="dadmehrg",computerName="https://171.22.24.15:8172/msdeploy.axd?site=dadmehrg",userName="Administrator",password="R2rNpdnetP3j>q5b18",authType="Basic" ` -dest:contentPath="dadmehrg",computerName="https://171.22.24.15:8172/msdeploy.axd?site=dadmehrg",userName="Administrator",password="R",authType="Basic" `
-allowUntrusted ` -allowUntrusted `
-enableRule:AppOffline -enableRule:AppOffline

View File

@@ -39,4 +39,15 @@ public static class StaticWorkshopAccounts
/// که کاربر همچنان به کارگاه دسترسی دارد /// که کاربر همچنان به کارگاه دسترسی دارد
/// </summary> /// </summary>
public static DateTime ContinuesWorkingDate = new DateTime(2150, 1, 1); public static DateTime ContinuesWorkingDate = new DateTime(2150, 1, 1);
/// <summary>
/// لیستی آی دی نقش هایی که مسئول بیمه کارگاه هستند
/// 7 : بیمه ارشد
/// 8 : بیمه ساده
/// </summary>
public static List<long> InsuranceAccountsRoleIds = [7, 8];
} }

View File

@@ -55,7 +55,7 @@ public interface IInstitutionContractRepository : IRepository<long, InstitutionC
void UpdateStatusIfNeeded(long institutionContractId); void UpdateStatusIfNeeded(long institutionContractId);
Task<GetInstitutionVerificationDetailsViewModel> GetVerificationDetails(Guid id); Task<GetInstitutionVerificationDetailsViewModel> GetVerificationDetails(Guid id);
Task<InstitutionContract> GetByPublicIdAsync(Guid id); Task<InstitutionContract> GetByPublicIdAsync(Guid id);
InstitutionContractDiscountResponse CalculateDiscount(InstitutionContractSetDiscountRequest request); InstitutionContractDiscountResponse CalculateDiscount(InstitutionContractSetDiscountRequest request,string contractStart = null);
InstitutionContractDiscountResponse ResetDiscountCreate(InstitutionContractResetDiscountForCreateRequest request); InstitutionContractDiscountResponse ResetDiscountCreate(InstitutionContractResetDiscountForCreateRequest request);

View File

@@ -22,6 +22,7 @@ using Company.Domain.InstitutionContractAgg;
using Company.Domain.LeftWorkAgg; using Company.Domain.LeftWorkAgg;
using Company.Domain.PaymentTransactionAgg; using Company.Domain.PaymentTransactionAgg;
using Company.Domain.RepresentativeAgg; using Company.Domain.RepresentativeAgg;
using Company.Domain.RollCallServiceAgg;
using Company.Domain.TemporaryClientRegistrationAgg; using Company.Domain.TemporaryClientRegistrationAgg;
using Company.Domain.WorkshopAgg; using Company.Domain.WorkshopAgg;
using CompanyManagment.App.Contracts.FinancialInvoice; using CompanyManagment.App.Contracts.FinancialInvoice;
@@ -59,6 +60,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication
private readonly IFinancialInvoiceRepository _financialInvoiceRepository; private readonly IFinancialInvoiceRepository _financialInvoiceRepository;
private readonly IPaymentGateway _paymentGateway; private readonly IPaymentGateway _paymentGateway;
private readonly IPaymentTransactionRepository _paymentTransactionRepository; private readonly IPaymentTransactionRepository _paymentTransactionRepository;
private readonly IRollCallServiceRepository _rollCallServiceRepository;
public InstitutionContractApplication(IInstitutionContractRepository institutionContractRepository, public InstitutionContractApplication(IInstitutionContractRepository institutionContractRepository,
@@ -70,7 +72,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication
IFinancialStatmentRepository financialStatmentRepository, IContactInfoApplication contactInfoApplication, IFinancialStatmentRepository financialStatmentRepository, IContactInfoApplication contactInfoApplication,
IAccountApplication accountApplication, ISmsService smsService, IUidService uidService, IAccountApplication accountApplication, ISmsService smsService, IUidService uidService,
IFinancialInvoiceRepository financialInvoiceRepository, IHttpClientFactory httpClientFactory, IFinancialInvoiceRepository financialInvoiceRepository, IHttpClientFactory httpClientFactory,
IPaymentTransactionRepository paymentTransactionRepository) IPaymentTransactionRepository paymentTransactionRepository, IRollCallServiceRepository rollCallServiceRepository)
{ {
_institutionContractRepository = institutionContractRepository; _institutionContractRepository = institutionContractRepository;
_contractingPartyRepository = contractingPartyRepository; _contractingPartyRepository = contractingPartyRepository;
@@ -88,6 +90,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication
_uidService = uidService; _uidService = uidService;
_financialInvoiceRepository = financialInvoiceRepository; _financialInvoiceRepository = financialInvoiceRepository;
_paymentTransactionRepository = paymentTransactionRepository; _paymentTransactionRepository = paymentTransactionRepository;
_rollCallServiceRepository = rollCallServiceRepository;
_paymentGateway = new SepehrPaymentGateway(httpClientFactory); _paymentGateway = new SepehrPaymentGateway(httpClientFactory);
} }
@@ -1556,27 +1559,48 @@ public class InstitutionContractApplication : IInstitutionContractApplication
.Where(x => x.WorkshopCreated && x.WorkshopId is > 0).ToList(); .Where(x => x.WorkshopCreated && x.WorkshopId is > 0).ToList();
var currentWorkshops = institutionContract.WorkshopGroup.CurrentWorkshops.ToList(); var currentWorkshops = institutionContract.WorkshopGroup.CurrentWorkshops.ToList();
var accountId = _contractingPartyRepository
.GetAccountByPersonalContractingParty(institutionContract.ContractingPartyId).Id;
foreach (var createdWorkshop in initialCreatedWorkshops) foreach (var createdWorkshop in initialCreatedWorkshops)
{ {
if (currentWorkshops.Any(x => x.WorkshopId == createdWorkshop.WorkshopId)) if (currentWorkshops.Any(x => x.WorkshopId == createdWorkshop.WorkshopId))
{ {
continue; //rollcall serviecs
if (createdWorkshop.Services.RollCall)
{
var ActiveService = _rollCallServiceRepository.GetActiveServiceByWorkshopId(createdWorkshop.WorkshopId!.Value);
var startTime = institutionContract.ContractStartGr;
var endTime = institutionContract.ContractEndGr;
if (ActiveService != null)
{
if (ActiveService.EndService> startTime)
{
startTime = ActiveService.EndService;
}
}
var rollCallService = new RollCallService("BasedOnIC",
startTime, endTime, createdWorkshop.WorkshopId.Value,accountId,createdWorkshop.PersonnelCount,
0,"12");
await _rollCallServiceRepository.CreateAsync(rollCallService);
}
}
else
{
var currentWorkshop = new InstitutionContractWorkshopCurrent(createdWorkshop.WorkshopName,
createdWorkshop.Services.RollCall, createdWorkshop.Services.RollCallInPerson,
createdWorkshop.Services.CustomizeCheckout, createdWorkshop.Services.Contract,
createdWorkshop.Services.ContractInPerson, createdWorkshop.Services.Insurance,
createdWorkshop.Services.InsuranceInPerson,createdWorkshop.PersonnelCount, createdWorkshop.Price,
createdWorkshop.InstitutionContractWorkshopGroupId,createdWorkshop.WorkshopGroup,
createdWorkshop.WorkshopId!.Value, false,createdWorkshop.id);
institutionContract.WorkshopGroup.AddCurrentWorkshop(currentWorkshop);
} }
var currentWorkshop = new InstitutionContractWorkshopCurrent(createdWorkshop.WorkshopName,
createdWorkshop.Services.RollCall, createdWorkshop.Services.RollCallInPerson,
createdWorkshop.Services.CustomizeCheckout, createdWorkshop.Services.Contract,
createdWorkshop.Services.ContractInPerson, createdWorkshop.Services.Insurance,
createdWorkshop.Services.InsuranceInPerson,createdWorkshop.PersonnelCount, createdWorkshop.Price,
createdWorkshop.InstitutionContractWorkshopGroupId,createdWorkshop.WorkshopGroup,
createdWorkshop.WorkshopId!.Value, false,createdWorkshop.id);
institutionContract.WorkshopGroup.AddCurrentWorkshop(currentWorkshop);
} }
if (institutionContract.WorkshopGroup.InitialWorkshops.All(x => x.WorkshopCreated && x.WorkshopId is > 0)) if (institutionContract.WorkshopGroup.InitialWorkshops.All(x => x.WorkshopCreated && x.WorkshopId is > 0))
{ {
institutionContract.Verified(); institutionContract.Verified();
} }
else else
@@ -1626,9 +1650,23 @@ public class InstitutionContractApplication : IInstitutionContractApplication
var transaction = await _institutionContractRepository.BeginTransactionAsync(); var transaction = await _institutionContractRepository.BeginTransactionAsync();
await SetPendingWorkflow(institutionContractId,InstitutionContractSigningType.Physical); await SetPendingWorkflow(institutionContractId,InstitutionContractSigningType.Physical);
var financialStatement = await _financialStatmentRepository
.GetByContractingPartyId(institutionContract.ContractingPartyId);
DateTime today = DateTime.Today;
var description = institutionContract.IsInstallment
? "قسط اول سرویس"
: "پرداخت کل سرویس";
var debtorAmount = institutionContract.IsInstallment
? institutionContract.Installments.First().Amount
: institutionContract.TotalAmount;
var financialTransaction = new FinancialTransaction(0, today, today.ToFarsi(),
description, "debt", "بابت خدمات", debtorAmount, 0, 0);
financialStatement.AddFinancialTransaction(financialTransaction);
await transaction.CommitAsync();
await _institutionContractRepository.SaveChangesAsync(); await _institutionContractRepository.SaveChangesAsync();
await transaction.CommitAsync();
return op.Succcedded(); return op.Succcedded();
} }

View File

@@ -1092,7 +1092,9 @@ public class WorkshopAppliction : IWorkshopApplication
Amount = 1000, Amount = 1000,
MaxPersonValid = 500, MaxPersonValid = 500,
Duration = "12", Duration = "12",
HasCustomizeCheckoutService = command.HasCustomizeCheckoutService HasCustomizeCheckoutService = command.HasCustomizeCheckoutService,
StartService = institutionContract.ContractStartGr,
}; };
_rollCallServiceApplication.Create(commandSave); _rollCallServiceApplication.Create(commandSave);
} }

View File

@@ -1898,7 +1898,7 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
.FirstOrDefaultAsync(x => x.PublicId == id); .FirstOrDefaultAsync(x => x.PublicId == id);
} }
public InstitutionContractDiscountResponse CalculateDiscount(InstitutionContractSetDiscountRequest request) public InstitutionContractDiscountResponse CalculateDiscount(InstitutionContractSetDiscountRequest request,string contractStart=null)
{ {
var baseAmount = request.TotalAmount; var baseAmount = request.TotalAmount;
var discountAmount = (baseAmount * request.DiscountPercentage) / 100; var discountAmount = (baseAmount * request.DiscountPercentage) / 100;
@@ -2359,7 +2359,7 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
IsInstallment = request.IsInstallment, IsInstallment = request.IsInstallment,
OneMonthAmount = selectedPlan.OneMonthPaymentDiscounted.MoneyToDouble() OneMonthAmount = selectedPlan.OneMonthPaymentDiscounted.MoneyToDouble()
}; };
var res = CalculateDiscount(calculateRequest); var res = CalculateDiscount(calculateRequest,selectedPlan.ContractStart);
//این به این دلیل هست که متد caclulate discount یکی از مقادیر رو پر میکنه و ما نیاز داریم هر دو مقدار رو داشته باشیم //این به این دلیل هست که متد caclulate discount یکی از مقادیر رو پر میکنه و ما نیاز داریم هر دو مقدار رو داشته باشیم
if (request.IsInstallment) if (request.IsInstallment)
@@ -2479,7 +2479,7 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
Tax = resetTax.ToMoney(), Tax = resetTax.ToMoney(),
TotalAmount = resetTotalAmount.ToMoney(), TotalAmount = resetTotalAmount.ToMoney(),
Installments = InstitutionMonthlyInstallmentCaculation((int)institutionTemp.Duration.Value, Installments = InstitutionMonthlyInstallmentCaculation((int)institutionTemp.Duration.Value,
paymentAmount, DateTime.Now.ToFarsi()), paymentAmount,selectedPlan.ContractStart),
OneMonthAmount = newOneMonthAmount.ToMoney(), OneMonthAmount = newOneMonthAmount.ToMoney(),
Obligation = resetTotalAmount.ToMoney() Obligation = resetTotalAmount.ToMoney()
}; };

View File

@@ -1853,19 +1853,24 @@ public class InsuranceListRepository : RepositoryBase<long, InsuranceList>, IIns
return res; return res;
} }
public async Task<List<InsuranceListViewModel>> GetNotCreatedWorkshop(InsuranceListSearchModel searchModel) public async Task<List<InsuranceListViewModel>> GetNotCreatedWorkshop(InsuranceListSearchModel searchModel)
{ {
if (string.IsNullOrEmpty(searchModel.Month) || string.IsNullOrEmpty(searchModel.Year)) if (string.IsNullOrEmpty(searchModel.Month) || string.IsNullOrEmpty(searchModel.Year))
{ {
return []; return [];
} }
var workshopsHasInsuranceAccount = await _accountContext
.AccountLeftWorks
.Where(x => StaticWorkshopAccounts.InsuranceAccountsRoleIds.Contains(x.RoleId) && x.IsActive)
.Select(x => x.WorkshopId).Distinct().ToListAsync();
var acountId = _authHelper.CurrentAccountId(); var acountId = _authHelper.CurrentAccountId();
var accountWorkshopIds = _context.WorkshopAccounts.Where(x => x.AccountId == acountId) var accountWorkshopIds = _context.WorkshopAccounts.Where(x => x.AccountId == acountId && workshopsHasInsuranceAccount.Contains(x.WorkshopId))
.Select(x => x.WorkshopId); .Select(x => x.WorkshopId);
var firstDayOfMonth = $"{searchModel.Year}/{searchModel.Month}/01".ToGeorgianDateTime(); var firstDayOfMonth = $"{searchModel.Year}/{searchModel.Month}/01".ToGeorgianDateTime();
var insuranceWorkshops = _context.Workshops var insuranceWorkshops = _context.Workshops
.Where(x => x.InsuranceCode != null && x.InsuranceCode.Length >= 10 && accountWorkshopIds.Contains(x.id) && .Where(x => accountWorkshopIds.Contains(x.id) &&
x.IsActiveString == "true"); x.IsActiveString == "true");
@@ -1879,36 +1884,36 @@ public class InsuranceListRepository : RepositoryBase<long, InsuranceList>, IIns
.Include(x => x.LeftWorkInsurances) .Include(x => x.LeftWorkInsurances)
.Where(x => x.LeftWorkInsurances.Any(l => l.StartWorkDate <= firstDayOfMonth && .Where(x => x.LeftWorkInsurances.Any(l => l.StartWorkDate <= firstDayOfMonth &&
(l.LeftWorkDate == null || l.LeftWorkDate >= firstDayOfMonth))); (l.LeftWorkDate == null || l.LeftWorkDate >= firstDayOfMonth)));
var query = notCreatedWorkshop.Select(result => new InsuranceListViewModel var query = notCreatedWorkshop.Select(result=>new InsuranceListViewModel
{ {
Year = searchModel.Year, Year = searchModel.Year,
Month = searchModel.Month, Month = searchModel.Month,
WorkShopId = result.id, WorkShopId = result.id,
WorkShopCode = result.InsuranceWorkshopInfo != null WorkShopCode = result.InsuranceWorkshopInfo != null
? result.InsuranceWorkshopInfo.InsuranceCode ? result.InsuranceWorkshopInfo.InsuranceCode
: result.InsuranceCode, : string.IsNullOrWhiteSpace(result.InsuranceCode) ? "کد کارگاهی ندارد" : result.InsuranceCode,
WorkShopName = result.InsuranceWorkshopInfo != null WorkShopName = result.InsuranceWorkshopInfo != null
? result.InsuranceWorkshopInfo.WorkshopName ? result.InsuranceWorkshopInfo.WorkshopName
: result.WorkshopFullName, : result.WorkshopFullName,
TypeOfInsuranceSend = result.TypeOfInsuranceSend == "NormalList" ? "عادی" : TypeOfInsuranceSend = result.TypeOfInsuranceSend == "NormalList" ? "عادی" :
result.TypeOfInsuranceSend == "Govermentlist" ? "کمک دولت" : result.TypeOfInsuranceSend == "Govermentlist" ? "کمک دولت" :
result.TypeOfInsuranceSend == "Familylist" ? "خانوادگی" : "", result.TypeOfInsuranceSend == "Familylist" ? "خانوادگی" : "",
FixedSalary = result.FixedSalary, FixedSalary = result.FixedSalary,
StrFixedSalary = result.FixedSalary ? "دارد" : "ندارد", StrFixedSalary = result.FixedSalary ? "دارد" : "ندارد",
EmployerName = result.InsuranceWorkshopInfo != null EmployerName = result.InsuranceWorkshopInfo != null
? result.InsuranceWorkshopInfo.EmployerName ? result.InsuranceWorkshopInfo.EmployerName
: result.WorkshopFullName, : result.WorkshopFullName,
Branch = "", Branch = "",
City = "", City = "",
ArchiveCode = result.ArchiveCode, ArchiveCode = result.ArchiveCode,
}); });
if (!string.IsNullOrEmpty(searchModel.Month) && searchModel.Month != "0") if (!string.IsNullOrEmpty(searchModel.Month) && searchModel.Month != "0")
query = query.Where(x => x.Month == searchModel.Month).OrderByDescending(x => x.WorkShopName) query = query.Where(x => x.Month == searchModel.Month).OrderByDescending(x => x.WorkShopName)
.ThenByDescending(x => x.EmployerName).ThenByDescending(x => x.Year); .ThenByDescending(x => x.EmployerName).ThenByDescending(x => x.Year);
if (!string.IsNullOrEmpty(searchModel.Year) && searchModel.Year != "0") if (!string.IsNullOrEmpty(searchModel.Year) && searchModel.Year != "0")
query = query.Where(x => x.Year == searchModel.Year).OrderByDescending(x => x.EmployerName) query = query.Where(x => x.Year == searchModel.Year).OrderByDescending(x => x.EmployerName)

View File

@@ -340,12 +340,11 @@ public class PlanPercentageRepository : RepositoryBase<long, PlanPercentage>, IP
.Select(x => x.ItemValue).FirstOrDefault(); .Select(x => x.ItemValue).FirstOrDefault();
var plans = _context.InstitutionPlans.AsQueryable(); var plans = _context.InstitutionPlans.AsQueryable();
if (searchModel.CountPerson > 0) if (searchModel.CountPerson > 0)
plans = plans.Where(x => x.CountPerson == searchModel.CountPerson); plans = plans.Where(x => x.CountPerson == searchModel.CountPerson);
var count = await plans.CountAsync(); var count = await plans.CountAsync();
var planQueryFilter =await plans.ApplyPagination(searchModel.PageIndex, searchModel.PageSize).ToListAsync(); var planQueryFilter =await plans.ApplyPagination(searchModel.PageIndex, searchModel.PageSize).ToListAsync();
var planResult = planQueryFilter.Select(plan => var planResult = planQueryFilter.Select(plan =>
new InstitutionPlanViewModel new InstitutionPlanViewModel

View File

@@ -0,0 +1,49 @@
using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Application._Common.Models;
using GozareshgirProgramManager.Domain._Common;
using GozareshgirProgramManager.Domain.ProjectAgg.Enums;
using GozareshgirProgramManager.Domain.ProjectAgg.Repositories;
using Microsoft.EntityFrameworkCore;
namespace GozareshgirProgramManager.Application.Modules.Projects.Commands.AutoStopOverTimeTaskSections;
public record AutoStopOverTimeTaskSectionsCommand : IBaseCommand;
public class AutoStopOverTimeTaskSectionsCommandHandler : IBaseCommandHandler<AutoStopOverTimeTaskSectionsCommand>
{
private readonly ITaskSectionRepository _taskSectionRepository;
private readonly IUnitOfWork _unitOfWork;
public AutoStopOverTimeTaskSectionsCommandHandler(ITaskSectionRepository taskSectionRepository,
IUnitOfWork unitOfWork)
{
_taskSectionRepository = taskSectionRepository;
_unitOfWork = unitOfWork;
}
public async Task<OperationResult> Handle(AutoStopOverTimeTaskSectionsCommand request,
CancellationToken cancellationToken)
{
try
{
// دریافت تمام تسک‌های در حال انجام
var taskSections = await _taskSectionRepository.GetActiveSectionsIncludeAllAsync(cancellationToken);
foreach (var taskSection in taskSections)
{
// استفاده از متد Domain برای بررسی و متوقف کردن خودکار
taskSection.AutoStopIfOverTime();
}
// ذخیره تغییرات در دیتابیس
await _unitOfWork.SaveChangesAsync(cancellationToken);
return OperationResult.Success();
}
catch (Exception ex)
{
return OperationResult.Failure($"خطا در ناتمام کردن تسک‌های overtime: {ex.Message}");
}
}
}

View File

@@ -1,3 +1,4 @@
using System.Globalization;
using GozareshgirProgramManager.Application._Common.Interfaces; using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Application._Common.Models; using GozareshgirProgramManager.Application._Common.Models;
using GozareshgirProgramManager.Domain._Common; using GozareshgirProgramManager.Domain._Common;
@@ -7,21 +8,23 @@ namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.Project
public record ProjectBoardDetailQuery(Guid SectionId) : IBaseQuery<ProjectBoardDetailResponse>; public record ProjectBoardDetailQuery(Guid SectionId) : IBaseQuery<ProjectBoardDetailResponse>;
public record ProjectBoardDetailResponse(List<ProjectBoardDetailUserResponse> Users, string TotalTime); public record ProjectBoardDetailResponse(List<ProjectBoardDetailUserResponse> Users, string TotalTime,string RemainingTime );
public record ProjectBoardDetailUserResponse public record ProjectBoardDetailUserResponse
{ {
public List<ProjectBoardDetailUserHistoryResponse> Histories { get; set; } = new(); public List<ProjectBoardDetailUserHistoryResponse> Histories { get; init; }
public string UserFullName { get; set; } public string UserFullName { get; init; }
public long UserId { get; set; } public string TotalTime { get; init; }
public string SpentTime { get; init; }
public long UserId { get; init; }
} }
public class ProjectBoardDetailUserHistoryResponse public record ProjectBoardDetailUserHistoryResponse
{ {
public string Date { get; set; } public string Date { get; init; }
public string startTime { get; set; } public string startTime { get; init; }
public string EndTime { get; set; } public string EndTime { get; init; }
public string TotalTime { get; set; } public string TotalTime { get; init; }
} }
public class ProjectBoardDetailQueryHandler : IBaseQueryHandler<ProjectBoardDetailQuery, ProjectBoardDetailResponse> public class ProjectBoardDetailQueryHandler : IBaseQueryHandler<ProjectBoardDetailQuery, ProjectBoardDetailResponse>
@@ -38,6 +41,7 @@ public class ProjectBoardDetailQueryHandler : IBaseQueryHandler<ProjectBoardDeta
{ {
var section = await _programManagerDbContext.TaskSections var section = await _programManagerDbContext.TaskSections
.Include(x => x.Activities) .Include(x => x.Activities)
.Include(x=>x.AdditionalTimes)
.FirstOrDefaultAsync(x => x.Id == request.SectionId, cancellationToken: cancellationToken); .FirstOrDefaultAsync(x => x.Id == request.SectionId, cancellationToken: cancellationToken);
if (section == null) if (section == null)
@@ -49,16 +53,22 @@ public class ProjectBoardDetailQueryHandler : IBaseQueryHandler<ProjectBoardDeta
.Where(x => userIds.Contains(x.Id)) .Where(x => userIds.Contains(x.Id))
.ToDictionaryAsync(x => x.Id, x => x.FullName, cancellationToken); .ToDictionaryAsync(x => x.Id, x => x.FullName, cancellationToken);
var totalTimeSpan = section.Activities var totalActivityTimeSpan = section.Activities
.Select(x => x.GetTimeSpent()) .Select(x => x.GetTimeSpent())
.Aggregate(TimeSpan.Zero, (sum, next) => sum.Add(next)); .Aggregate(TimeSpan.Zero, (sum, next) => sum.Add(next));
var finalTime = section.FinalEstimatedHours;
var remainingTimeSpan = finalTime >= totalActivityTimeSpan
? TimeSpan.FromTicks(finalTime.Ticks - totalActivityTimeSpan.Ticks)
: TimeSpan.Zero;
var users = section.Activities.GroupBy(x => x.UserId).Select(x => var users = section.Activities.GroupBy(x => x.UserId).Select(x =>
{ {
return new ProjectBoardDetailUserResponse() return new ProjectBoardDetailUserResponse()
{ {
UserId = x.Key, UserId = x.Key,
UserFullName = usersDict[x.Key], UserFullName = usersDict[x.Key],
TotalTime = TimeSpan.FromTicks(x.Sum(h=>h.GetTimeSpent().Ticks)).TotalHours.ToString(CultureInfo.InvariantCulture),
Histories = x.Select(h => new ProjectBoardDetailUserHistoryResponse() Histories = x.Select(h => new ProjectBoardDetailUserHistoryResponse()
{ {
Date = h.StartDate.ToFarsi(), Date = h.StartDate.ToFarsi(),
@@ -68,7 +78,8 @@ public class ProjectBoardDetailQueryHandler : IBaseQueryHandler<ProjectBoardDeta
}).ToList() }).ToList()
}; };
}).ToList(); }).ToList();
var response = new ProjectBoardDetailResponse(users, $"{(int)totalTimeSpan.TotalHours}:{totalTimeSpan.Minutes:D2}"); var response = new ProjectBoardDetailResponse(users, $"{(int)totalActivityTimeSpan.TotalHours}:{totalActivityTimeSpan.Minutes:D2}",
$"{(int)remainingTimeSpan.TotalHours}:{remainingTimeSpan.Minutes:D2}");
return OperationResult<ProjectBoardDetailResponse>.Success(response); return OperationResult<ProjectBoardDetailResponse>.Success(response);
} }
} }

View File

@@ -217,4 +217,39 @@ public class TaskSection : EntityBase<Guid>
var finalEstimate = FinalEstimatedHours; var finalEstimate = FinalEstimatedHours;
return totalSpent < finalEstimate; return totalSpent < finalEstimate;
} }
/// <summary>
/// اگر زمان کار شده بیش از تایم تعیین شده باشد، تسک را متوقف می‌کند
/// و EndDate را به طوری تنظیم می‌کند که کل زمان برابر با FinalEstimatedHours شود
/// </summary>
public void AutoStopIfOverTime()
{
if (Status != TaskSectionStatus.InProgress)
return;
var activeActivity = _activities.FirstOrDefault(a => a.IsActive);
if (activeActivity == null)
return;
// محاسبه کل زمان صرف شده تا کنون (بدون فعالیت فعال)
var totalTimeSpentExcludingActive = _activities.Where(a => !a.IsActive).Sum(a => a.GetTimeSpent().Ticks);
var totalTimeSpentTimeSpan = TimeSpan.FromTicks(totalTimeSpentExcludingActive);
var finalEstimate = FinalEstimatedHours;
// اگر زمان صرف شده (بدون فعالیت فعال) + فعالیت فعال > تایم تعیین شده
var activeTimeSpent = activeActivity.GetTimeSpent();
if (totalTimeSpentTimeSpan + activeTimeSpent > finalEstimate)
{
// محاسبه مدت زمانی که این فعالیت باید برای رسیدن به FinalEstimatedHours داشته باشد
var remainingTime = finalEstimate - totalTimeSpentTimeSpan;
// EndDate = StartDate + remainingTime
var adjustedEndDate = activeActivity.StartDate.Add(remainingTime);
// متوقف کردن فعالیت با EndDate دقیق شده
activeActivity.StopWorkWithSpecificTime(adjustedEndDate, "متوقف خودکار - بیش از تایم تعیین شده");
UpdateStatus(TaskSectionStatus.Incomplete);
}
}
} }

View File

@@ -40,6 +40,22 @@ public class TaskSectionActivity : EntityBase<Guid>
IsActive = false; IsActive = false;
} }
/// <summary>
/// متوقف کردن فعالیت با مشخص کردن EndDate دقیق
/// </summary>
public void StopWorkWithSpecificTime(DateTime endDate, string? endNotes = null)
{
if (!IsActive)
throw new InvalidOperationException("این فعالیت قبلاً متوقف شده است.");
if (endDate < StartDate)
throw new InvalidOperationException("تاریخ پایان نمی‌تواند قبل از تاریخ شروع باشد.");
EndDate = endDate;
EndNotes = endNotes;
IsActive = false;
}
public TimeSpan GetTimeSpent() public TimeSpan GetTimeSpent()
{ {
if (IsActive) if (IsActive)

View File

@@ -1,3 +1,4 @@
using System.Collections;
using GozareshgirProgramManager.Domain._Common; using GozareshgirProgramManager.Domain._Common;
using GozareshgirProgramManager.Domain.ProjectAgg.Entities; using GozareshgirProgramManager.Domain.ProjectAgg.Entities;
@@ -11,4 +12,5 @@ public interface ITaskSectionRepository: IRepository<Guid,TaskSection>
Task<TaskSection?> GetByIdWithFullDataAsync(Guid id, CancellationToken cancellationToken = default); Task<TaskSection?> GetByIdWithFullDataAsync(Guid id, CancellationToken cancellationToken = default);
Task<List<TaskSection>> GetAssignedToUserAsync(long userId); Task<List<TaskSection>> GetAssignedToUserAsync(long userId);
Task<List<TaskSection>> GetActiveSectionsIncludeAllAsync(CancellationToken cancellationToken);
} }

View File

@@ -1,4 +1,5 @@
using GozareshgirProgramManager.Domain.ProjectAgg.Entities; using GozareshgirProgramManager.Domain.ProjectAgg.Entities;
using GozareshgirProgramManager.Domain.ProjectAgg.Enums;
using GozareshgirProgramManager.Domain.ProjectAgg.Repositories; using GozareshgirProgramManager.Domain.ProjectAgg.Repositories;
using GozareshgirProgramManager.Infrastructure.Persistence._Common; using GozareshgirProgramManager.Infrastructure.Persistence._Common;
using GozareshgirProgramManager.Infrastructure.Persistence.Context; using GozareshgirProgramManager.Infrastructure.Persistence.Context;
@@ -35,4 +36,13 @@ public class TaskSectionRepository:RepositoryBase<Guid,TaskSection>,ITaskSection
.Where(x => x.CurrentAssignedUserId == userId) .Where(x => x.CurrentAssignedUserId == userId)
.ToListAsync(); .ToListAsync();
} }
public Task<List<TaskSection>> GetActiveSectionsIncludeAllAsync(CancellationToken cancellationToken)
{
return _context.TaskSections
.Where(x => x.Status == TaskSectionStatus.InProgress)
.Include(x => x.Activities)
.Include(x => x.AdditionalTimes)
.ToListAsync(cancellationToken);
}
} }

View File

@@ -1,6 +1,7 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using GozareshgirProgramManager.Application._Common.Models; using GozareshgirProgramManager.Application._Common.Models;
using GozareshgirProgramManager.Application.Modules.Projects.Commands.AssignProject; using GozareshgirProgramManager.Application.Modules.Projects.Commands.AssignProject;
using GozareshgirProgramManager.Application.Modules.Projects.Commands.AutoStopOverTimeTaskSections;
using GozareshgirProgramManager.Application.Modules.Projects.Commands.ChangeStatusSection; using GozareshgirProgramManager.Application.Modules.Projects.Commands.ChangeStatusSection;
using GozareshgirProgramManager.Application.Modules.Projects.Commands.CreateProject; using GozareshgirProgramManager.Application.Modules.Projects.Commands.CreateProject;
using GozareshgirProgramManager.Application.Modules.Projects.Commands.DeleteProject; using GozareshgirProgramManager.Application.Modules.Projects.Commands.DeleteProject;
@@ -98,6 +99,9 @@ public class ProjectController : ProgramManagerBaseController
[HttpGet("board")] [HttpGet("board")]
public async Task<ActionResult<OperationResult<List<ProjectBoardListResponse>>>> GetProjectBoard([FromQuery] ProjectBoardListQuery query) public async Task<ActionResult<OperationResult<List<ProjectBoardListResponse>>>> GetProjectBoard([FromQuery] ProjectBoardListQuery query)
{ {
// اجرای Command برای متوقف کردن تسک‌های overtime قبل از نمایش
await _mediator.Send(new AutoStopOverTimeTaskSectionsCommand());
var res = await _mediator.Send(query); var res = await _mediator.Send(query);
return res; return res;
} }

View File

@@ -850,7 +850,7 @@ public class institutionContractController : AdminBaseController
} }
[HttpPost("mannual-verify/{id}")] [HttpPost("mannual-verify/{id}")]
public async Task<ActionResult<OperationResult>> VerifyInstitutionContractMannualy(long id) public async Task<ActionResult<OperationResult>> VerifyInstitutionContractManually(long id)
{ {
var res= await _institutionContractApplication.VerifyInstitutionContractManually(id); var res= await _institutionContractApplication.VerifyInstitutionContractManually(id);
return res; return res;

View File

@@ -74,8 +74,8 @@
</form> </form>
<div class="lineDiv"></div> <div class="lineDiv"></div>
<div class="row m-t-20"> <div class="row m-t-20">
<div class="col-4"></div> <div class="col-md-4 visible-md visible-lg"></div>
<div class="col-2"> <div class="col-6 col-md-2">
<form asp-page-handler="UploadFrontEnd" id="12" method="post"> <form asp-page-handler="UploadFrontEnd" id="12" method="post">
<button type="submit" <button type="submit"
class="btn btn-danger" class="btn btn-danger"
@@ -84,14 +84,14 @@
</button> </button>
</form> </form>
</div> </div>
<div class="col-2"> <div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-secondary" <button type="button" class="btn btn-outline-secondary"
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#logModal"> data-bs-target="#logModal">
مشاهده لاگ Deploy مشاهده لاگ Deploy
</button> </button>
</div> </div>
<div class="col-4"></div> <div class="col-md-4 visible-md visible-lg"></div>
</div> </div>
<div class="lineDiv"></div> <div class="lineDiv"></div>

View File

@@ -342,19 +342,39 @@ namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk
var validAccountId = _authHelper.CurrentAccountId(); var validAccountId = _authHelper.CurrentAccountId();
if (validAccountId == 2 || validAccountId == 322) if (validAccountId == 2 || validAccountId == 322)
{ {
var batPath = @"C:\next-ui\deploy-next-ui.bat"; //var batPath = @"C:\next-ui\deploy-next-ui.bat";
//var psi = new ProcessStartInfo
//{
// FileName = batPath,
// UseShellExecute = true, // خیلی مهم
// Verb = "runas", // اجرای Administrator
// CreateNoWindow = true,
// WindowStyle = ProcessWindowStyle.Hidden
//};
//Process.Start(psi);
var psi = new ProcessStartInfo var psi = new ProcessStartInfo
{ {
FileName = batPath, FileName = "cmd.exe",
UseShellExecute = true, // خیلی مهم Arguments = "/c schtasks /run /tn \"DeployNextUI\"",
Verb = "runas", // اجرای Administrator UseShellExecute = false,
CreateNoWindow = true, CreateNoWindow = true
WindowStyle = ProcessWindowStyle.Hidden
}; };
Process.Start(psi); Process.Start(psi);
//var psi = new ProcessStartInfo
//{
// FileName = @"C:\next-ui\deploy-next-ui.bat",
// UseShellExecute = false,
// CreateNoWindow = false
//};
//Process.Start(psi);
TempData["Message"] = "فرآیند Deploy شروع شد. لاگ را بررسی کنید."; TempData["Message"] = "فرآیند Deploy شروع شد. لاگ را بررسی کنید.";
return RedirectToPage(); return RedirectToPage();
} }