Merge branch 'master' into Main
This commit is contained in:
@@ -6585,268 +6585,275 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///دریافت لیست پیامک قرادا های آبی بدهکار
|
||||
///دریافت لیست پیامک قراداد های آبی بدهکار
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
//public async Task<List<SmsListData>> GetBlueSmsListData()
|
||||
//{
|
||||
public async Task<List<SmsListData>> GetWarningSmsListData()
|
||||
{
|
||||
|
||||
// var institutionContracts = await _context.InstitutionContractSet.AsQueryable().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,
|
||||
// IsInstallment = x.IsInstallment,
|
||||
// VerificationStatus = x.VerificationStatus,
|
||||
// SigningType = x.SigningType,
|
||||
// }).Where(x => x.IsActiveString == "blue" &&
|
||||
// x.ContractAmountDouble > 0).GroupBy(x => x.ContractingPartyId).Select(x => x.First()).ToListAsync();
|
||||
var institutionContracts = await _context.InstitutionContractSet.AsQueryable().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,
|
||||
IsInstallment = x.IsInstallment,
|
||||
VerificationStatus = x.VerificationStatus,
|
||||
SigningType = x.SigningType,
|
||||
}).Where(x => x.IsActiveString == "blue" &&
|
||||
x.ContractAmountDouble > 0).GroupBy(x => x.ContractingPartyId).Select(x => x.First()).ToListAsync();
|
||||
|
||||
// // قرارداد هایی که بطور یکجا پرداخت شده اند
|
||||
// var paidInFull = institutionContracts.Where(x =>
|
||||
// x.SigningType != InstitutionContractSigningType.Legacy && x.IsInstallment == false && x.SigningType != null).ToList();
|
||||
var institutionContractsIds = institutionContracts.Select(x => x.id).ToList();
|
||||
|
||||
// //حذف قراداد هایی که یکجا پرداخت شده اند از لیست ایجاد سند ماهانه
|
||||
// institutionContracts = institutionContracts.Except(paidInFull).ToList();
|
||||
// قرارداد هایی که بطور یکجا پرداخت شده اند
|
||||
var paidInFull = institutionContracts.Where(x =>
|
||||
x.SigningType != InstitutionContractSigningType.Legacy && x.IsInstallment == false && x.SigningType != null).ToList();
|
||||
|
||||
// var contractingPartyList = await _context.PersonalContractingParties
|
||||
// .Where(x => institutionContracts.Select(ins => ins.ContractingPartyId).Contains(x.id)).ToListAsync();
|
||||
//حذف قراداد هایی که یکجا پرداخت شده اند از لیست ایجاد سند ماهانه
|
||||
institutionContracts = institutionContracts.Except(paidInFull).ToList();
|
||||
|
||||
// var financialStatmentList = await _context.FinancialStatments.AsSplitQuery()
|
||||
// .Where(x => institutionContracts.Select(ins => ins.ContractingPartyId).Contains(x.ContractingPartyId))
|
||||
// .Include(x => x.FinancialTransactionList).Where(x => x.FinancialTransactionList.Count > 0).ToListAsync();
|
||||
var contractingPartyList = await _context.PersonalContractingParties
|
||||
.Where(x => institutionContracts.Select(ins => ins.ContractingPartyId).Contains(x.id)).ToListAsync();
|
||||
|
||||
// var phoneNumberList = await _context.InstitutionContractContactInfos
|
||||
// .Where(x => institutionContracts.Select(ins => ins.Id).Contains(x.InstitutionContractId))
|
||||
// .Where(x => x.SendSms && x.PhoneType == "شماره همراه" && !string.IsNullOrWhiteSpace(x.PhoneNumber) &&
|
||||
// x.PhoneNumber.Length == 11).ToListAsync();
|
||||
// var legalActionSentSms =await _context.SmsResults
|
||||
// .Where(x => x.TypeOfSms == "اقدام قضایی").ToListAsync();
|
||||
// var warningSentSms = await _context.SmsResults.Where(x => x.TypeOfSms.Contains("هشدار")).ToListAsync();
|
||||
var financialStatmentList = await _context.FinancialStatments.AsSplitQuery()
|
||||
.Where(x => institutionContracts.Select(ins => ins.ContractingPartyId).Contains(x.ContractingPartyId))
|
||||
.Include(x => x.FinancialTransactionList).Where(x => x.FinancialTransactionList.Count > 0).ToListAsync();
|
||||
|
||||
// var oldInstitutionContract = institutionContracts.Where(x => x.IsInstallment == false).ToList();
|
||||
// var electronicInstitutionContract = institutionContracts.Where(x => x.IsInstallment == true).ToList();
|
||||
var phoneNumberList = await _context.InstitutionContractContactInfos
|
||||
.Where(x => institutionContracts.Select(ins => ins.Id).Contains(x.InstitutionContractId))
|
||||
.Where(x => x.SendSms && x.PhoneType == "شماره همراه" && !string.IsNullOrWhiteSpace(x.PhoneNumber) &&
|
||||
x.PhoneNumber.Length == 11).ToListAsync();
|
||||
var legalActionSentSms = await _context.SmsResults
|
||||
.Where(x => x.TypeOfSms == "اقدام قضایی").ToListAsync();
|
||||
var warningSentSms = await _context.SmsResults.Where(x => x.TypeOfSms.Contains("هشدار")).ToListAsync();
|
||||
|
||||
// foreach (var item in oldInstitutionContract)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// var contractingParty = GetDetails(item.ContractingPartyId);
|
||||
// bool hasLegalActionSentSms = legalActionSentSms.Any(x => x.InstitutionContractId == item.Id);
|
||||
var oldInstitutionContract = institutionContracts.Where(x => x.IsInstallment == false).ToList();
|
||||
var electronicInstitutionContract = institutionContracts.Where(x => x.IsInstallment == true).ToList();
|
||||
|
||||
// if (!string.IsNullOrWhiteSpace(contractingParty.LName) && !hasLegalActionSentSms)
|
||||
// {
|
||||
// //Thread.Sleep(500);
|
||||
// var partyName = contractingParty.LName;
|
||||
foreach (var item in oldInstitutionContract)
|
||||
{
|
||||
try
|
||||
{
|
||||
var contractingParty = GetDetails(item.ContractingPartyId);
|
||||
bool hasLegalActionSentSms = legalActionSentSms.Any(x => x.InstitutionContractId == item.Id);
|
||||
int year = Convert.ToInt32(item.ContractEndFa.Substring(0, 4));
|
||||
int month = Convert.ToInt32(item.ContractEndFa.Substring(5, 2));
|
||||
var endOfContractNextMonthStart = new PersianDateTime(year, month, 1).AddMonths(1);
|
||||
var endOfContractNextMonthEnd = (($"{endOfContractNextMonthStart}").FindeEndOfMonth()).ToGeorgianDateTime();
|
||||
var now = DateTime.Now
|
||||
|
||||
// if (partyName.Length > 25) partyName = $"{partyName.Substring(0, 25)}";
|
||||
if (!string.IsNullOrWhiteSpace(contractingParty.LName) && !hasLegalActionSentSms && now.Date <= endOfContractNextMonthEnd.Date)
|
||||
{
|
||||
//Thread.Sleep(500);
|
||||
var partyName = contractingParty.LName;
|
||||
|
||||
// var isLegal = contractingParty.IsLegal == "حقوقی" ? true : false;
|
||||
// var isBlock = contractingParty.IsBlock == "true" ? true : false;
|
||||
// var isActive = contractingParty.IsActiveString == "true" ? true : false;
|
||||
// if (!string.IsNullOrWhiteSpace(contractingParty.IsActiveString))
|
||||
// {
|
||||
// var hasFinancialStatement =
|
||||
// financialStatmentList.Any(x => x.ContractingPartyId == item.ContractingPartyId);
|
||||
if (partyName.Length > 25) partyName = $"{partyName.Substring(0, 25)}";
|
||||
|
||||
// var hasPhonNumber = phoneNumberList.Any(x => x.InstitutionContractId == item.Id);
|
||||
var isLegal = contractingParty.IsLegal == "حقوقی" ? true : false;
|
||||
var isBlock = contractingParty.IsBlock == "true" ? true : false;
|
||||
var isActive = contractingParty.IsActiveString == "true" ? true : false;
|
||||
if (!string.IsNullOrWhiteSpace(contractingParty.IsActiveString))
|
||||
{
|
||||
var hasFinancialStatement =
|
||||
financialStatmentList.Any(x => x.ContractingPartyId == item.ContractingPartyId);
|
||||
|
||||
// if (hasFinancialStatement && hasPhonNumber)
|
||||
// {
|
||||
// var phoneNumbers = phoneNumberList.Where(x => x.InstitutionContractId == item.Id)
|
||||
// .Select(x => new CreateContactInfo
|
||||
// {
|
||||
// PhoneType = x.PhoneType,
|
||||
// PhoneNumber = x.PhoneNumber,
|
||||
var hasPhonNumber = phoneNumberList.Any(x => x.InstitutionContractId == item.Id);
|
||||
|
||||
// InstitutionContractId = x.InstitutionContractId,
|
||||
// SendSms = x.SendSms
|
||||
// }).Where(x => x.PhoneNumber.Length == 11).ToList();
|
||||
if (hasFinancialStatement && hasPhonNumber)
|
||||
{
|
||||
var phoneNumbers = phoneNumberList.Where(x => x.InstitutionContractId == item.Id)
|
||||
.Select(x => new CreateContactInfo
|
||||
{
|
||||
PhoneType = x.PhoneType,
|
||||
PhoneNumber = x.PhoneNumber,
|
||||
|
||||
// var transactions = financialStatmentList.FirstOrDefault(x =>
|
||||
// x.ContractingPartyId == item.ContractingPartyId);
|
||||
InstitutionContractId = x.InstitutionContractId,
|
||||
SendSms = x.SendSms
|
||||
}).Where(x => x.PhoneNumber.Length == 11).ToList();
|
||||
|
||||
var transactions = financialStatmentList.FirstOrDefault(x =>
|
||||
x.ContractingPartyId == item.ContractingPartyId);
|
||||
|
||||
|
||||
// var debtor = transactions.FinancialTransactionViewModels.Sum(x => x.Deptor);
|
||||
// var creditor = transactions.FinancialTransactionViewModels.Sum(x => x.Creditor);
|
||||
|
||||
var debtor = transactions.FinancialTransactionViewModels.Sum(x => x.Deptor);
|
||||
var creditor = transactions.FinancialTransactionViewModels.Sum(x => x.Creditor);
|
||||
|
||||
// var id = $"{item.ContractingPartyId}";
|
||||
// var aprove = $"{transactions.Id}";
|
||||
// var balance = debtor - creditor;
|
||||
|
||||
// if (balance > 0) // اگر بدهکار بود
|
||||
// {
|
||||
var id = $"{item.ContractingPartyId}";
|
||||
var aprove = $"{transactions.Id}";
|
||||
var balance = debtor - creditor;
|
||||
|
||||
// if (isLegal)
|
||||
// {
|
||||
// if (item.OfficialCompany == "Official") // حقوقی بدهکار رسمی
|
||||
// {
|
||||
// var balanceToMoney = balance.ToMoney();
|
||||
if (balance > 0) // اگر بدهکار بود
|
||||
{
|
||||
|
||||
// foreach (var number in phoneNumbers)
|
||||
// {
|
||||
|
||||
// var isLastAlarmSend = _context.SmsResults.Any(x => (
|
||||
// x.ContractingPatyId == contractingParty.Id &&
|
||||
// x.Mobile == number.PhoneNumber) && (x.TypeOfSms == "اقدام قضایی" || x.TypeOfSms == "هشدار دوم"));
|
||||
if (isLegal)
|
||||
{
|
||||
if (item.OfficialCompany == "Official") // حقوقی بدهکار رسمی
|
||||
{
|
||||
var balanceToMoney = balance.ToMoney();
|
||||
|
||||
// if (!string.IsNullOrWhiteSpace(number.PhoneNumber) &&
|
||||
// number.PhoneNumber.Length == 11 && !isSend && !isLastAlarmSend)
|
||||
// {
|
||||
foreach (var number in phoneNumbers)
|
||||
{
|
||||
|
||||
// var smsResult = _smsService.MonthlyBill(number.PhoneNumber, 608443,
|
||||
// partyName,
|
||||
// balanceToMoney, id, aprove);
|
||||
// Thread.Sleep(1000);
|
||||
// if (smsResult.IsSuccedded)
|
||||
// {
|
||||
// var createSmsResult = new SmsResult(smsResult.MessageId,
|
||||
// smsResult.Message, typeOfSms, partyName, number.PhoneNumber,
|
||||
// item.ContractingPartyId, item.Id);
|
||||
// _smsResultRepository.Create(createSmsResult);
|
||||
// _smsResultRepository.SaveChanges();
|
||||
// Thread.Sleep(1000);
|
||||
// }
|
||||
var isLastAlarmSend = _context.SmsResults.Any(x => (
|
||||
x.ContractingPatyId == contractingParty.Id &&
|
||||
x.Mobile == number.PhoneNumber) && (x.TypeOfSms == "اقدام قضایی" || x.TypeOfSms == "هشدار دوم"));
|
||||
var t = warningSentSms.Any(x=> x.)
|
||||
if (!string.IsNullOrWhiteSpace(number.PhoneNumber) &&
|
||||
number.PhoneNumber.Length == 11 && !isSend && !isLastAlarmSend)
|
||||
{
|
||||
|
||||
// }
|
||||
// }
|
||||
var smsResult = _smsService.MonthlyBill(number.PhoneNumber, 608443,
|
||||
partyName,
|
||||
balanceToMoney, id, aprove);
|
||||
Thread.Sleep(1000);
|
||||
if (smsResult.IsSuccedded)
|
||||
{
|
||||
var createSmsResult = new SmsResult(smsResult.MessageId,
|
||||
smsResult.Message, typeOfSms, partyName, number.PhoneNumber,
|
||||
item.ContractingPartyId, item.Id);
|
||||
_smsResultRepository.Create(createSmsResult);
|
||||
_smsResultRepository.SaveChanges();
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// }
|
||||
// else if (item.OfficialCompany == "NotOfficial") // حقوقی بدهکار غیر رسمی
|
||||
// {
|
||||
// var balanceToMoney = balance.ToMoney();
|
||||
// foreach (var number in phoneNumbers)
|
||||
// {
|
||||
// var isSend = _context.SmsResults.Any(x =>
|
||||
// x.ContractingPatyId == contractingParty.Id &&
|
||||
// x.Mobile == number.PhoneNumber && x.TypeOfSms == typeOfSms);
|
||||
// var isLastAlarmSend = _context.SmsResults.Any(x => (
|
||||
// x.ContractingPatyId == contractingParty.Id &&
|
||||
// x.Mobile == number.PhoneNumber) && (x.TypeOfSms == "اقدام قضایی" || x.TypeOfSms == "هشدار دوم"));
|
||||
// if (!string.IsNullOrWhiteSpace(number.PhoneNumber) &&
|
||||
// number.PhoneNumber.Length == 11 && !isSend && !isLastAlarmSend)
|
||||
// {
|
||||
}
|
||||
else if (item.OfficialCompany == "NotOfficial") // حقوقی بدهکار غیر رسمی
|
||||
{
|
||||
var balanceToMoney = balance.ToMoney();
|
||||
foreach (var number in phoneNumbers)
|
||||
{
|
||||
var isSend = _context.SmsResults.Any(x =>
|
||||
x.ContractingPatyId == contractingParty.Id &&
|
||||
x.Mobile == number.PhoneNumber && x.TypeOfSms == typeOfSms);
|
||||
var isLastAlarmSend = _context.SmsResults.Any(x => (
|
||||
x.ContractingPatyId == contractingParty.Id &&
|
||||
x.Mobile == number.PhoneNumber) && (x.TypeOfSms == "اقدام قضایی" || x.TypeOfSms == "هشدار دوم"));
|
||||
if (!string.IsNullOrWhiteSpace(number.PhoneNumber) &&
|
||||
number.PhoneNumber.Length == 11 && !isSend && !isLastAlarmSend)
|
||||
{
|
||||
|
||||
// var smsResult = _smsService.MonthlyBill(number.PhoneNumber, 351691,
|
||||
// partyName,
|
||||
// balanceToMoney, id, aprove);
|
||||
// Thread.Sleep(1000);
|
||||
// if (smsResult.IsSuccedded)
|
||||
// {
|
||||
// var createSmsResult = new SmsResult(smsResult.MessageId,
|
||||
// smsResult.Message, typeOfSms, partyName, number.PhoneNumber,
|
||||
// item.ContractingPartyId, item.Id);
|
||||
// _smsResultRepository.Create(createSmsResult);
|
||||
// _smsResultRepository.SaveChanges();
|
||||
// Thread.Sleep(1000);
|
||||
// }
|
||||
var smsResult = _smsService.MonthlyBill(number.PhoneNumber, 351691,
|
||||
partyName,
|
||||
balanceToMoney, id, aprove);
|
||||
Thread.Sleep(1000);
|
||||
if (smsResult.IsSuccedded)
|
||||
{
|
||||
var createSmsResult = new SmsResult(smsResult.MessageId,
|
||||
smsResult.Message, typeOfSms, partyName, number.PhoneNumber,
|
||||
item.ContractingPartyId, item.Id);
|
||||
_smsResultRepository.Create(createSmsResult);
|
||||
_smsResultRepository.SaveChanges();
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (item.OfficialCompany == "Official") // حقیقی بدهکار رسمی
|
||||
// {
|
||||
// var balanceToMoney = balance.ToMoney();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.OfficialCompany == "Official") // حقیقی بدهکار رسمی
|
||||
{
|
||||
var balanceToMoney = balance.ToMoney();
|
||||
|
||||
// foreach (var number in phoneNumbers)
|
||||
// {
|
||||
// var isSend = _context.SmsResults.Any(x =>
|
||||
// x.ContractingPatyId == contractingParty.Id &&
|
||||
// x.Mobile == number.PhoneNumber && x.TypeOfSms == typeOfSms);
|
||||
// var isLastAlarmSend = _context.SmsResults.Any(x => (
|
||||
// x.ContractingPatyId == contractingParty.Id &&
|
||||
// x.Mobile == number.PhoneNumber) && (x.TypeOfSms == "اقدام قضایی" || x.TypeOfSms == "هشدار دوم"));
|
||||
// if (!string.IsNullOrWhiteSpace(number.PhoneNumber) &&
|
||||
// number.PhoneNumber.Length == 11 && !isSend && !isLastAlarmSend)
|
||||
// {
|
||||
foreach (var number in phoneNumbers)
|
||||
{
|
||||
var isSend = _context.SmsResults.Any(x =>
|
||||
x.ContractingPatyId == contractingParty.Id &&
|
||||
x.Mobile == number.PhoneNumber && x.TypeOfSms == typeOfSms);
|
||||
var isLastAlarmSend = _context.SmsResults.Any(x => (
|
||||
x.ContractingPatyId == contractingParty.Id &&
|
||||
x.Mobile == number.PhoneNumber) && (x.TypeOfSms == "اقدام قضایی" || x.TypeOfSms == "هشدار دوم"));
|
||||
if (!string.IsNullOrWhiteSpace(number.PhoneNumber) &&
|
||||
number.PhoneNumber.Length == 11 && !isSend && !isLastAlarmSend)
|
||||
{
|
||||
|
||||
// var smsResult = _smsService.MonthlyBill(number.PhoneNumber, 190430,
|
||||
// partyName,
|
||||
// balanceToMoney, id, aprove);
|
||||
// Thread.Sleep(1000);
|
||||
// if (smsResult.IsSuccedded)
|
||||
// {
|
||||
// var createSmsResult = new SmsResult(smsResult.MessageId,
|
||||
// smsResult.Message, typeOfSms, partyName, number.PhoneNumber,
|
||||
// item.ContractingPartyId, item.Id);
|
||||
// _smsResultRepository.Create(createSmsResult);
|
||||
// _smsResultRepository.SaveChanges();
|
||||
// Thread.Sleep(1000);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
var smsResult = _smsService.MonthlyBill(number.PhoneNumber, 190430,
|
||||
partyName,
|
||||
balanceToMoney, id, aprove);
|
||||
Thread.Sleep(1000);
|
||||
if (smsResult.IsSuccedded)
|
||||
{
|
||||
var createSmsResult = new SmsResult(smsResult.MessageId,
|
||||
smsResult.Message, typeOfSms, partyName, number.PhoneNumber,
|
||||
item.ContractingPartyId, item.Id);
|
||||
_smsResultRepository.Create(createSmsResult);
|
||||
_smsResultRepository.SaveChanges();
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// }
|
||||
// else if (item.OfficialCompany == "NotOfficial") // حقیقی بدهکار غیر رسمی
|
||||
// {
|
||||
// var balanceToMoney = balance.ToMoney();
|
||||
}
|
||||
else if (item.OfficialCompany == "NotOfficial") // حقیقی بدهکار غیر رسمی
|
||||
{
|
||||
var balanceToMoney = balance.ToMoney();
|
||||
|
||||
// foreach (var number in phoneNumbers)
|
||||
// {
|
||||
// var isSend = _context.SmsResults.Any(x =>
|
||||
// x.ContractingPatyId == contractingParty.Id &&
|
||||
// x.Mobile == number.PhoneNumber && x.TypeOfSms == typeOfSms);
|
||||
// var isLastAlarmSend = _context.SmsResults.Any(x => (
|
||||
// x.ContractingPatyId == contractingParty.Id &&
|
||||
// x.Mobile == number.PhoneNumber) && (x.TypeOfSms == "اقدام قضایی" || x.TypeOfSms == "هشدار دوم"));
|
||||
// if (!string.IsNullOrWhiteSpace(number.PhoneNumber) &&
|
||||
// number.PhoneNumber.Length == 11 && !isSend && !isLastAlarmSend)
|
||||
// {
|
||||
// var smsResult = _smsService.MonthlyBill(number.PhoneNumber, 412829,
|
||||
// partyName,
|
||||
// balanceToMoney, id, aprove);
|
||||
// Thread.Sleep(1000);
|
||||
// if (smsResult.IsSuccedded)
|
||||
// {
|
||||
// var createSmsResult = new SmsResult(smsResult.MessageId,
|
||||
// smsResult.Message, typeOfSms, partyName, number.PhoneNumber,
|
||||
// item.ContractingPartyId, item.Id);
|
||||
// _smsResultRepository.Create(createSmsResult);
|
||||
// _smsResultRepository.SaveChanges();
|
||||
// Thread.Sleep(1000);
|
||||
// }
|
||||
foreach (var number in phoneNumbers)
|
||||
{
|
||||
var isSend = _context.SmsResults.Any(x =>
|
||||
x.ContractingPatyId == contractingParty.Id &&
|
||||
x.Mobile == number.PhoneNumber && x.TypeOfSms == typeOfSms);
|
||||
var isLastAlarmSend = _context.SmsResults.Any(x => (
|
||||
x.ContractingPatyId == contractingParty.Id &&
|
||||
x.Mobile == number.PhoneNumber) && (x.TypeOfSms == "اقدام قضایی" || x.TypeOfSms == "هشدار دوم"));
|
||||
if (!string.IsNullOrWhiteSpace(number.PhoneNumber) &&
|
||||
number.PhoneNumber.Length == 11 && !isSend && !isLastAlarmSend)
|
||||
{
|
||||
var smsResult = _smsService.MonthlyBill(number.PhoneNumber, 412829,
|
||||
partyName,
|
||||
balanceToMoney, id, aprove);
|
||||
Thread.Sleep(1000);
|
||||
if (smsResult.IsSuccedded)
|
||||
{
|
||||
var createSmsResult = new SmsResult(smsResult.MessageId,
|
||||
smsResult.Message, typeOfSms, partyName, number.PhoneNumber,
|
||||
item.ContractingPartyId, item.Id);
|
||||
_smsResultRepository.Create(createSmsResult);
|
||||
_smsResultRepository.SaveChanges();
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
|
||||
// string name = item.ContractingPartyName.Length > 18 ? item.ContractingPartyName.Substring(0, 18) : item.ContractingPartyName;
|
||||
// string errMess = $"{name}-خطا";
|
||||
// _smsService.Alarm("09114221321", errMess);
|
||||
// _logger.LogError(e, "BlueWarningSms");
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
string name = item.ContractingPartyName.Length > 18 ? item.ContractingPartyName.Substring(0, 18) : item.ContractingPartyName;
|
||||
string errMess = $"{name}-خطا";
|
||||
_smsService.Alarm("09114221321", errMess);
|
||||
_logger.LogError(e, "BlueWarningSms");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ public class ProjectSectionDto
|
||||
|
||||
public TimeSpan FinalEstimatedHours { get; set; }
|
||||
public TimeSpan TotalTimeSpent { get; set; }
|
||||
public double ProgressPercentage { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
public bool IsInProgress { get; set; }
|
||||
|
||||
|
||||
@@ -166,6 +166,7 @@ public static class ProjectMappingExtensions
|
||||
CreationDate = section.CreationDate,
|
||||
FinalEstimatedHours = section.FinalEstimatedHours,
|
||||
TotalTimeSpent = section.GetTotalTimeSpent(),
|
||||
ProgressPercentage = section.GetProgressPercentage(),
|
||||
IsCompleted = section.IsCompleted(),
|
||||
IsInProgress = section.IsInProgress(),
|
||||
Activities = section.Activities.Select(a => a.ToDto()).ToList(),
|
||||
@@ -188,6 +189,7 @@ public static class ProjectMappingExtensions
|
||||
CreationDate = section.CreationDate,
|
||||
FinalEstimatedHours = section.FinalEstimatedHours,
|
||||
TotalTimeSpent = section.GetTotalTimeSpent(),
|
||||
ProgressPercentage = section.GetProgressPercentage(),
|
||||
IsCompleted = section.IsCompleted(),
|
||||
IsInProgress = section.IsInProgress()
|
||||
// No activities or additional times for summary
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
using GozareshgirProgramManager.Domain.ProjectAgg.Enums;
|
||||
|
||||
namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.GetProjectsList;
|
||||
public record GetProjectListDto
|
||||
|
||||
// Base DTO shared across project, phase, and task
|
||||
public class GetProjectItemDto
|
||||
{
|
||||
public Guid Id { get; init; }
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public int Percentage { get; init; }
|
||||
public ProjectHierarchyLevel Level { get; init; }
|
||||
public Guid? ParentId { get; init; }
|
||||
public bool HasFront { get; set; }
|
||||
public bool HasBackend { get; set; }
|
||||
public bool HasDesign { get; set; }
|
||||
public int TotalHours { get; set; }
|
||||
public int Minutes { get; set; }
|
||||
|
||||
public AssignmentStatus Front { get; set; }
|
||||
public AssignmentStatus Backend { get; set; }
|
||||
public AssignmentStatus Design { get; set; }
|
||||
}
|
||||
|
||||
// Project DTO (no extra fields; inherits from base)
|
||||
public class GetProjectDto : GetProjectItemDto
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Phase DTO (no extra fields; inherits from base)
|
||||
public class GetPhaseDto : GetProjectItemDto
|
||||
{
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using GozareshgirProgramManager.Application._Common.Models;
|
||||
using GozareshgirProgramManager.Domain.ProjectAgg.Entities;
|
||||
using GozareshgirProgramManager.Domain.ProjectAgg.Enums;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq;
|
||||
|
||||
namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.GetProjectsList;
|
||||
|
||||
@@ -17,47 +18,47 @@ public class GetProjectsListQueryHandler : IBaseQueryHandler<GetProjectsListQuer
|
||||
|
||||
public async Task<OperationResult<GetProjectsListResponse>> Handle(GetProjectsListQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
List<GetProjectListDto> projects;
|
||||
var projects = new List<GetProjectDto>();
|
||||
var phases = new List<GetPhaseDto>();
|
||||
var tasks = new List<GetTaskDto>();
|
||||
|
||||
switch (request.HierarchyLevel)
|
||||
{
|
||||
case ProjectHierarchyLevel.Project:
|
||||
projects = await GetProjects(request.ParentId, cancellationToken);
|
||||
await SetSkillFlags(projects, cancellationToken);
|
||||
break;
|
||||
case ProjectHierarchyLevel.Phase:
|
||||
projects = await GetPhases(request.ParentId, cancellationToken);
|
||||
phases = await GetPhases(request.ParentId, cancellationToken);
|
||||
await SetSkillFlags(phases, cancellationToken);
|
||||
break;
|
||||
case ProjectHierarchyLevel.Task:
|
||||
projects = await GetTasks(request.ParentId, cancellationToken);
|
||||
tasks = await GetTasks(request.ParentId, cancellationToken);
|
||||
// Tasks don't need SetSkillFlags because they have Sections list
|
||||
break;
|
||||
default:
|
||||
return OperationResult<GetProjectsListResponse>.Failure("سطح سلسله مراتب نامعتبر است");
|
||||
}
|
||||
await SetSkillFlags(projects, cancellationToken);
|
||||
|
||||
var response = new GetProjectsListResponse(projects);
|
||||
var response = new GetProjectsListResponse(projects, phases, tasks);
|
||||
return OperationResult<GetProjectsListResponse>.Success(response);
|
||||
}
|
||||
|
||||
private async Task<List<GetProjectListDto>> GetProjects(Guid? parentId, CancellationToken cancellationToken)
|
||||
private async Task<List<GetProjectDto>> GetProjects(Guid? parentId, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.Projects.AsQueryable();
|
||||
|
||||
// پروژهها سطح بالا هستند و parentId ندارند، فقط در صورت null بودن parentId نمایش داده میشوند
|
||||
if (parentId.HasValue)
|
||||
{
|
||||
return new List<GetProjectListDto>(); // پروژهها parent ندارند
|
||||
return new List<GetProjectDto>();
|
||||
}
|
||||
|
||||
var projects = await query
|
||||
var entities = await query
|
||||
.OrderByDescending(p => p.CreationDate)
|
||||
.ToListAsync(cancellationToken);
|
||||
var result = new List<GetProjectListDto>();
|
||||
|
||||
foreach (var project in projects)
|
||||
var result = new List<GetProjectDto>();
|
||||
foreach (var project in entities)
|
||||
{
|
||||
var (percentage, totalTime) = await CalculateProjectPercentage(project, cancellationToken);
|
||||
result.Add(new GetProjectListDto
|
||||
result.Add(new GetProjectDto
|
||||
{
|
||||
Id = project.Id,
|
||||
Name = project.Name,
|
||||
@@ -68,28 +69,24 @@ public class GetProjectsListQueryHandler : IBaseQueryHandler<GetProjectsListQuer
|
||||
Minutes = totalTime.Minutes,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<List<GetProjectListDto>> GetPhases(Guid? parentId, CancellationToken cancellationToken)
|
||||
private async Task<List<GetPhaseDto>> GetPhases(Guid? parentId, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.ProjectPhases.AsQueryable();
|
||||
|
||||
if (parentId.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.ProjectId == parentId);
|
||||
}
|
||||
|
||||
var phases = await query
|
||||
var entities = await query
|
||||
.OrderByDescending(p => p.CreationDate)
|
||||
.ToListAsync(cancellationToken);
|
||||
var result = new List<GetProjectListDto>();
|
||||
|
||||
foreach (var phase in phases)
|
||||
var result = new List<GetPhaseDto>();
|
||||
foreach (var phase in entities)
|
||||
{
|
||||
var (percentage, totalTime) = await CalculatePhasePercentage(phase, cancellationToken);
|
||||
result.Add(new GetProjectListDto
|
||||
result.Add(new GetPhaseDto
|
||||
{
|
||||
Id = phase.Id,
|
||||
Name = phase.Name,
|
||||
@@ -100,28 +97,87 @@ public class GetProjectsListQueryHandler : IBaseQueryHandler<GetProjectsListQuer
|
||||
Minutes = totalTime.Minutes,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<List<GetProjectListDto>> GetTasks(Guid? parentId, CancellationToken cancellationToken)
|
||||
private async Task<List<GetTaskDto>> GetTasks(Guid? parentId, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.ProjectTasks.AsQueryable();
|
||||
|
||||
if (parentId.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.PhaseId == parentId);
|
||||
}
|
||||
|
||||
var tasks = await query
|
||||
var entities = await query
|
||||
.OrderByDescending(t => t.CreationDate)
|
||||
.ToListAsync(cancellationToken);
|
||||
var result = new List<GetProjectListDto>();
|
||||
var result = new List<GetTaskDto>();
|
||||
// دریافت تمام Skills
|
||||
var allSkills = await _context.Skills
|
||||
.Select(s => new { s.Id, s.Name })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var task in tasks)
|
||||
foreach (var task in entities)
|
||||
{
|
||||
var (percentage, totalTime) = await CalculateTaskPercentage(task, cancellationToken);
|
||||
result.Add(new GetProjectListDto
|
||||
var sections = await _context.TaskSections
|
||||
.Include(s => s.Activities)
|
||||
.Include(s => s.Skill)
|
||||
.Where(s => s.TaskId == task.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// جمعآوری تمام userId های مورد نیاز
|
||||
var userIds = sections
|
||||
.Where(s => s.CurrentAssignedUserId > 0)
|
||||
.Select(s => s.CurrentAssignedUserId)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
// دریافت اطلاعات کاربران
|
||||
var users = await _context.Users
|
||||
.Where(u => userIds.Contains(u.Id))
|
||||
.Select(u => new { u.Id, u.FullName })
|
||||
.ToDictionaryAsync(u => u.Id, u => u.FullName, cancellationToken);
|
||||
|
||||
// محاسبه SpentTime و RemainingTime
|
||||
var spentTime = TimeSpan.FromTicks(sections.Sum(s => s.Activities.Sum(a => a.GetTimeSpent().Ticks)));
|
||||
var remainingTime = totalTime - spentTime;
|
||||
|
||||
// ساخت section DTOs برای تمام Skills
|
||||
var sectionDtos = allSkills.Select(skill =>
|
||||
{
|
||||
var section = sections.FirstOrDefault(s => s.SkillId == skill.Id);
|
||||
|
||||
if (section == null)
|
||||
{
|
||||
// اگر section وجود نداشت، یک DTO با وضعیت Unassigned برمیگردانیم
|
||||
return new GetTaskSectionDto
|
||||
{
|
||||
Id = Guid.Empty,
|
||||
SkillName = skill.Name ?? string.Empty,
|
||||
SpentTime = TimeSpan.Zero,
|
||||
TotalTime = TimeSpan.Zero,
|
||||
Percentage = 0,
|
||||
UserFullName = string.Empty,
|
||||
AssignmentStatus = AssignmentStatus.Unassigned
|
||||
};
|
||||
}
|
||||
|
||||
// اگر section وجود داشت
|
||||
return new GetTaskSectionDto
|
||||
{
|
||||
Id = section.Id,
|
||||
SkillName = skill.Name ?? string.Empty,
|
||||
SpentTime = TimeSpan.FromTicks(section.Activities.Sum(a => a.GetTimeSpent().Ticks)),
|
||||
TotalTime = section.FinalEstimatedHours,
|
||||
Percentage = (int)section.GetProgressPercentage(),
|
||||
UserFullName = section.CurrentAssignedUserId > 0 && users.ContainsKey(section.CurrentAssignedUserId)
|
||||
? users[section.CurrentAssignedUserId]
|
||||
: string.Empty,
|
||||
AssignmentStatus = GetAssignmentStatus(section)
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
result.Add(new GetTaskDto
|
||||
{
|
||||
Id = task.Id,
|
||||
Name = task.Name,
|
||||
@@ -129,187 +185,175 @@ public class GetProjectsListQueryHandler : IBaseQueryHandler<GetProjectsListQuer
|
||||
ParentId = task.PhaseId,
|
||||
Percentage = percentage,
|
||||
TotalHours = (int)totalTime.TotalHours,
|
||||
Minutes = totalTime.Minutes
|
||||
Minutes = totalTime.Minutes,
|
||||
SpentTime = spentTime,
|
||||
RemainingTime = remainingTime,
|
||||
Sections = sectionDtos,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task SetSkillFlags(List<GetProjectListDto> projects, CancellationToken cancellationToken)
|
||||
private async Task SetSkillFlags<TItem>(List<TItem> items, CancellationToken cancellationToken) where TItem : GetProjectItemDto
|
||||
{
|
||||
if (!projects.Any())
|
||||
if (!items.Any())
|
||||
return;
|
||||
|
||||
var projectIds = projects.Select(x => x.Id).ToList();
|
||||
var hierarchyLevel = projects.First().Level;
|
||||
|
||||
var ids = items.Select(x => x.Id).ToList();
|
||||
var hierarchyLevel = items.First().Level;
|
||||
switch (hierarchyLevel)
|
||||
{
|
||||
case ProjectHierarchyLevel.Project:
|
||||
await SetSkillFlagsForProjects(projects, projectIds, cancellationToken);
|
||||
await SetSkillFlagsForProjects(items, ids, cancellationToken);
|
||||
break;
|
||||
|
||||
case ProjectHierarchyLevel.Phase:
|
||||
await SetSkillFlagsForPhases(projects, projectIds, cancellationToken);
|
||||
break;
|
||||
|
||||
case ProjectHierarchyLevel.Task:
|
||||
await SetSkillFlagsForTasks(projects, projectIds, cancellationToken);
|
||||
await SetSkillFlagsForPhases(items, ids, cancellationToken);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetSkillFlagsForProjects(List<GetProjectListDto> projects, List<Guid> projectIds, CancellationToken cancellationToken)
|
||||
|
||||
private async Task SetSkillFlagsForProjects<TItem>(List<TItem> items, List<Guid> projectIds, CancellationToken cancellationToken) where TItem : GetProjectItemDto
|
||||
{
|
||||
var projectSections = await _context.ProjectSections
|
||||
.Include(x => x.Skill)
|
||||
.Where(s => projectIds.Contains(s.ProjectId))
|
||||
// For projects: gather all phases, then tasks, then sections
|
||||
var phases = await _context.ProjectPhases
|
||||
.Where(ph => projectIds.Contains(ph.ProjectId))
|
||||
.Select(ph => ph.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
var tasks = await _context.ProjectTasks
|
||||
.Where(t => phases.Contains(t.PhaseId))
|
||||
.Select(t => t.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
var sections = await _context.TaskSections
|
||||
.Include(s => s.Skill)
|
||||
.Where(s => tasks.Contains(s.TaskId))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (!projectSections.Any())
|
||||
return;
|
||||
|
||||
foreach (var project in projects)
|
||||
foreach (var item in items)
|
||||
{
|
||||
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");
|
||||
var relatedPhases = phases; // used for filtering tasks by project
|
||||
var relatedTasks = await _context.ProjectTasks
|
||||
.Where(t => t.PhaseId != Guid.Empty && relatedPhases.Contains(t.PhaseId))
|
||||
.Select(t => t.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
var itemSections = sections.Where(s => relatedTasks.Contains(s.TaskId));
|
||||
item.Backend = GetAssignmentStatus(itemSections.FirstOrDefault(x => x.Skill?.Name == "Backend"));
|
||||
item.Front = GetAssignmentStatus(itemSections.FirstOrDefault(x => x.Skill?.Name == "Frontend"));
|
||||
item.Design = GetAssignmentStatus(itemSections.FirstOrDefault(x => x.Skill?.Name == "UI/UX Design"));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetSkillFlagsForPhases(List<GetProjectListDto> projects, List<Guid> phaseIds, CancellationToken cancellationToken)
|
||||
private async Task SetSkillFlagsForPhases<TItem>(List<TItem> items, List<Guid> phaseIds, CancellationToken cancellationToken) where TItem : GetProjectItemDto
|
||||
{
|
||||
var phaseSections = await _context.PhaseSections
|
||||
.Include(x => x.Skill)
|
||||
.Where(s => phaseIds.Contains(s.PhaseId))
|
||||
// For phases: gather tasks, then sections
|
||||
var tasks = await _context.ProjectTasks
|
||||
.Where(t => phaseIds.Contains(t.PhaseId))
|
||||
.Select(t => t.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
var sections = await _context.TaskSections
|
||||
.Include(s => s.Skill)
|
||||
.Where(s => tasks.Contains(s.TaskId))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (!phaseSections.Any())
|
||||
return;
|
||||
|
||||
foreach (var phase in projects)
|
||||
foreach (var item in items)
|
||||
{
|
||||
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<GetProjectListDto> projects, List<Guid> 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");
|
||||
// Filter tasks for this phase
|
||||
var phaseTaskIds = await _context.ProjectTasks
|
||||
.Where(t => t.PhaseId == item.Id)
|
||||
.Select(t => t.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
var itemSections = sections.Where(s => phaseTaskIds.Contains(s.TaskId));
|
||||
item.Backend = GetAssignmentStatus(itemSections.FirstOrDefault(x => x.Skill?.Name == "Backend"));
|
||||
item.Front = GetAssignmentStatus(itemSections.FirstOrDefault(x => x.Skill?.Name == "Frontend"));
|
||||
item.Design = GetAssignmentStatus(itemSections.FirstOrDefault(x => x.Skill?.Name == "UI/UX Design"));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(int Percentage, TimeSpan TotalTime)> CalculateProjectPercentage(Project project, CancellationToken cancellationToken)
|
||||
{
|
||||
// گرفتن تمام فازهای پروژه
|
||||
var phases = await _context.ProjectPhases
|
||||
.Where(ph => ph.ProjectId == project.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (!phases.Any())
|
||||
return (0, TimeSpan.Zero);
|
||||
|
||||
// محاسبه درصد هر فاز و میانگینگیری
|
||||
var phasePercentages = new List<int>();
|
||||
var totalTime = TimeSpan.Zero;
|
||||
|
||||
foreach (var phase in phases)
|
||||
{
|
||||
var (phasePercentage, phaseTime) = await CalculatePhasePercentage(phase, cancellationToken);
|
||||
phasePercentages.Add(phasePercentage);
|
||||
totalTime += phaseTime;
|
||||
}
|
||||
|
||||
var averagePercentage = phasePercentages.Any() ? (int)phasePercentages.Average() : 0;
|
||||
return (averagePercentage, totalTime);
|
||||
}
|
||||
|
||||
private async Task<(int Percentage, TimeSpan TotalTime)> CalculatePhasePercentage(ProjectPhase phase, CancellationToken cancellationToken)
|
||||
{
|
||||
// گرفتن تمام تسکهای فاز
|
||||
var tasks = await _context.ProjectTasks
|
||||
.Where(t => t.PhaseId == phase.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (!tasks.Any())
|
||||
return (0, TimeSpan.Zero);
|
||||
|
||||
// محاسبه درصد هر تسک و میانگینگیری
|
||||
var taskPercentages = new List<int>();
|
||||
var totalTime = TimeSpan.Zero;
|
||||
|
||||
foreach (var task in tasks)
|
||||
{
|
||||
var (taskPercentage, taskTime) = await CalculateTaskPercentage(task, cancellationToken);
|
||||
taskPercentages.Add(taskPercentage);
|
||||
totalTime += taskTime;
|
||||
}
|
||||
|
||||
var averagePercentage = taskPercentages.Any() ? (int)taskPercentages.Average() : 0;
|
||||
return (averagePercentage, totalTime);
|
||||
}
|
||||
|
||||
private async Task<(int Percentage, TimeSpan TotalTime)> CalculateTaskPercentage(ProjectTask task, CancellationToken cancellationToken)
|
||||
{
|
||||
// گرفتن تمام سکشنهای تسک با activities
|
||||
var sections = await _context.TaskSections
|
||||
.Include(s => s.Activities)
|
||||
.Include(x=>x.AdditionalTimes)
|
||||
.Where(s => s.TaskId == task.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (!sections.Any())
|
||||
return (0, TimeSpan.Zero);
|
||||
|
||||
// محاسبه درصد هر سکشن و میانگینگیری
|
||||
var sectionPercentages = new List<int>();
|
||||
var totalTime = TimeSpan.Zero;
|
||||
|
||||
foreach (var section in sections)
|
||||
{
|
||||
var (sectionPercentage, sectionTime) = CalculateSectionPercentage(section);
|
||||
sectionPercentages.Add(sectionPercentage);
|
||||
totalTime += sectionTime;
|
||||
}
|
||||
|
||||
var averagePercentage = sectionPercentages.Any() ? (int)sectionPercentages.Average() : 0;
|
||||
return (averagePercentage, totalTime);
|
||||
}
|
||||
|
||||
private static (int Percentage, TimeSpan TotalTime) CalculateSectionPercentage(TaskSection section)
|
||||
{
|
||||
// محاسبه کل زمان تخمین زده شده (اولیه + اضافی)
|
||||
var totalEstimatedHours = section.FinalEstimatedHours.TotalHours;
|
||||
return ((int)section.GetProgressPercentage(),section.FinalEstimatedHours);
|
||||
}
|
||||
|
||||
// محاسبه کل زمان صرف شده از activities
|
||||
var totalSpentTime = TimeSpan.FromHours(section.Activities.Sum(a => a.GetTimeSpent().TotalHours));
|
||||
private static AssignmentStatus GetAssignmentStatus(TaskSection? section)
|
||||
{
|
||||
// تعیین تکلیف نشده: section وجود ندارد
|
||||
if (section == null)
|
||||
return AssignmentStatus.Unassigned;
|
||||
|
||||
if (totalEstimatedHours <= 0)
|
||||
return (0, section.FinalEstimatedHours);
|
||||
// بررسی وجود user
|
||||
bool hasUser = section.CurrentAssignedUserId > 0;
|
||||
|
||||
// بررسی وجود time (InitialEstimatedHours بزرگتر از صفر باشد)
|
||||
bool hasTime = section.InitialEstimatedHours > TimeSpan.Zero;
|
||||
|
||||
var totalSpentHours = totalSpentTime.TotalHours;
|
||||
// تعیین تکلیف شده: هم user و هم time تعیین شده
|
||||
if (hasUser && hasTime)
|
||||
return AssignmentStatus.Assigned;
|
||||
|
||||
// محاسبه درصد (حداکثر 100%)
|
||||
var percentage = (totalSpentHours / totalEstimatedHours) * 100;
|
||||
return (Math.Min((int)Math.Round(percentage), 100), section.FinalEstimatedHours);
|
||||
// فقط کاربر تعیین شده: user دارد ولی time ندارد
|
||||
if (hasUser && !hasTime)
|
||||
return AssignmentStatus.UserOnly;
|
||||
|
||||
// تعیین تکلیف نشده: نه user دارد نه time
|
||||
return AssignmentStatus.Unassigned;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.GetProjectsList;
|
||||
|
||||
public record GetProjectsListResponse(
|
||||
List<GetProjectListDto> Projects);
|
||||
|
||||
List<GetProjectDto> Projects,
|
||||
List<GetPhaseDto> Phases,
|
||||
List<GetTaskDto> Tasks);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using GozareshgirProgramManager.Domain.ProjectAgg.Enums;
|
||||
|
||||
namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.GetProjectsList;
|
||||
|
||||
public class GetTaskDto
|
||||
{
|
||||
public Guid Id { get; init; }
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public int Percentage { get; init; }
|
||||
public ProjectHierarchyLevel Level { get; init; }
|
||||
public Guid? ParentId { get; init; }
|
||||
public int TotalHours { get; set; }
|
||||
public int Minutes { get; set; }
|
||||
|
||||
// Task-specific fields
|
||||
public TimeSpan SpentTime { get; init; }
|
||||
public TimeSpan RemainingTime { get; init; }
|
||||
public List<GetTaskSectionDto> Sections { get; init; }
|
||||
}
|
||||
|
||||
public class GetTaskSectionDto
|
||||
{
|
||||
public Guid Id { get; init; }
|
||||
public string SkillName { get; init; } = string.Empty;
|
||||
public TimeSpan SpentTime { get; init; }
|
||||
public TimeSpan TotalTime { get; init; }
|
||||
public int Percentage { get; init; }
|
||||
public string UserFullName{ get; init; } = string.Empty;
|
||||
public AssignmentStatus AssignmentStatus { get; set; }
|
||||
|
||||
}
|
||||
@@ -106,6 +106,7 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler<ProjectBoardListQu
|
||||
Progress = new ProjectProgressDto()
|
||||
{
|
||||
CompleteSecond = x.FinalEstimatedHours.TotalSeconds,
|
||||
Percentage = (int)x.GetProgressPercentage(),
|
||||
CurrentSecond = activityTimeData.Sum(a => a.TotalSeconds),
|
||||
Histories = mergedHistories
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@ public class ProjectBoardListResponse
|
||||
public class ProjectProgressDto
|
||||
{
|
||||
public double CurrentSecond { get; set; }
|
||||
public int Percentage { get; set; }
|
||||
public double CompleteSecond { get; set; }
|
||||
public List<ProjectProgressHistoryDto> Histories { get; set; }
|
||||
}
|
||||
|
||||
@@ -12,14 +12,16 @@ public record ProjectDeployBoardDetailsResponse(
|
||||
public record ProjectDeployBoardDetailPhaseItem(
|
||||
string Name,
|
||||
TimeSpan TotalTimeSpan,
|
||||
TimeSpan DoneTimeSpan);
|
||||
TimeSpan DoneTimeSpan,
|
||||
int Percentage);
|
||||
|
||||
public record ProjectDeployBoardDetailTaskItem(
|
||||
string Name,
|
||||
TimeSpan TotalTimeSpan,
|
||||
TimeSpan DoneTimeSpan,
|
||||
int Percentage,
|
||||
List<ProjectDeployBoardDetailItemSkill> Skills)
|
||||
: ProjectDeployBoardDetailPhaseItem(Name, TotalTimeSpan, DoneTimeSpan);
|
||||
: ProjectDeployBoardDetailPhaseItem(Name, TotalTimeSpan, DoneTimeSpan,Percentage);
|
||||
|
||||
public record ProjectDeployBoardDetailItemSkill(string OriginalUserFullName, string SkillName, int TimePercentage);
|
||||
|
||||
@@ -79,22 +81,20 @@ public class
|
||||
|
||||
var skillName = s.Skill?.Name ?? "بدون مهارت";
|
||||
|
||||
var totalTimeSpent = s.GetTotalTimeSpent();
|
||||
|
||||
var timePercentage = s.FinalEstimatedHours.Ticks > 0
|
||||
? (int)((totalTimeSpent.Ticks / (double)s.FinalEstimatedHours.Ticks) * 100)
|
||||
: 0;
|
||||
var timePercentage = (int)s.GetProgressPercentage();
|
||||
|
||||
return new ProjectDeployBoardDetailItemSkill(
|
||||
originalUserFullName,
|
||||
skillName,
|
||||
timePercentage);
|
||||
}).ToList();
|
||||
var taskPercentage = (int)Math.Round(skills.Average(x => x.TimePercentage));
|
||||
|
||||
return new ProjectDeployBoardDetailTaskItem(
|
||||
t.Name,
|
||||
totalTime,
|
||||
doneTime,
|
||||
taskPercentage,
|
||||
skills);
|
||||
}).ToList();
|
||||
|
||||
@@ -104,7 +104,10 @@ public class
|
||||
var doneTimeSpan = tasksRes.Aggregate(TimeSpan.Zero,
|
||||
(sum, next) => sum.Add(next.DoneTimeSpan));
|
||||
|
||||
var phaseRes = new ProjectDeployBoardDetailPhaseItem(phase.Name, totalTimeSpan, doneTimeSpan);
|
||||
var phasePercentage = tasksRes.Average(x => x.Percentage);
|
||||
|
||||
var phaseRes = new ProjectDeployBoardDetailPhaseItem(phase.Name, totalTimeSpan, doneTimeSpan,
|
||||
(int)phasePercentage);
|
||||
|
||||
var res = new ProjectDeployBoardDetailsResponse(phaseRes, tasksRes);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ public record ProjectDeployBoardListItem()
|
||||
public int DoneTasks { get; set; }
|
||||
public TimeSpan TotalTimeSpan { get; set; }
|
||||
public TimeSpan DoneTimeSpan { get; set; }
|
||||
public int Percentage { get; set; }
|
||||
public ProjectDeployStatus DeployStatus { get; set; }
|
||||
}
|
||||
public record GetProjectsDeployBoardListResponse(List<ProjectDeployBoardListItem> Items);
|
||||
@@ -66,7 +67,8 @@ public class ProjectDeployBoardListQueryHandler:IBaseQueryHandler<GetProjectDepl
|
||||
.Select(x => x.TaskId).Distinct().Count(),
|
||||
TotalTimeSpan = TimeSpan.FromTicks(g.Sum(x => x.InitialEstimatedHours.Ticks)),
|
||||
DoneTimeSpan = TimeSpan.FromTicks(g.Sum(x=>x.GetTotalTimeSpent().Ticks)),
|
||||
DeployStatus = g.First().Task.Phase.DeployStatus
|
||||
DeployStatus = g.First().Task.Phase.DeployStatus,
|
||||
Percentage = (int)Math.Round(g.Average(x => x.GetProgressPercentage()))
|
||||
}).ToList();
|
||||
var response = new GetProjectsDeployBoardListResponse(list);
|
||||
return OperationResult<GetProjectsDeployBoardListResponse>.Success(response);
|
||||
|
||||
@@ -157,6 +157,27 @@ public class TaskSection : EntityBase<Guid>
|
||||
return TimeSpan.FromTicks(_activities.Sum(a => a.GetTimeSpent().Ticks));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// محاسبه درصد پیشرفت بر اساس زمان مصرف شده به تایم برآورد شده
|
||||
/// اگر وضعیت Completed باشد، همیشه 100 درصد برمیگرداند
|
||||
/// </summary>
|
||||
public double GetProgressPercentage()
|
||||
{
|
||||
// اگر تسک کامل شده، همیشه 100 درصد
|
||||
if (Status == TaskSectionStatus.Completed)
|
||||
return 100.0;
|
||||
|
||||
// اگر تایم برآورد شده صفر است، درصد صفر است
|
||||
if (FinalEstimatedHours.TotalHours <= 0)
|
||||
return 0.0;
|
||||
|
||||
var timeSpent = GetTotalTimeSpent();
|
||||
var percentage = (timeSpent.TotalMinutes / FinalEstimatedHours.TotalMinutes) * 100.0;
|
||||
|
||||
// محدود کردن درصد به 100 (در صورتی که زمان مصرف شده بیشتر از تخمین باشد)
|
||||
return Math.Min(percentage, 100.0);
|
||||
}
|
||||
|
||||
public bool IsCompleted()
|
||||
{
|
||||
return Status == TaskSectionStatus.Completed;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace GozareshgirProgramManager.Domain.ProjectAgg.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// وضعیت تکلیف دهی برای بخشهای مختلف پروژه
|
||||
/// </summary>
|
||||
public enum AssignmentStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// تعیین تکلیف نشده
|
||||
/// </summary>
|
||||
Unassigned = 0,
|
||||
|
||||
/// <summary>
|
||||
/// تعیین تکلیف شده
|
||||
/// </summary>
|
||||
Assigned = 1,
|
||||
|
||||
/// <summary>
|
||||
/// فقط کاربر تعیین شده
|
||||
/// </summary>
|
||||
UserOnly = 2,
|
||||
}
|
||||
|
||||
@@ -380,13 +380,7 @@ builder.Host.UseSerilog((context, services, configuration) =>
|
||||
.ReadFrom.Configuration(context.Configuration)
|
||||
.ReadFrom.Services(services)
|
||||
.Enrich.FromLogContext();
|
||||
|
||||
// در محیط Development، EF Core Commands را هم لاگ میکنیم
|
||||
if (context.HostingEnvironment.IsDevelopment())
|
||||
{
|
||||
logConfig
|
||||
.MinimumLevel.Override("Microsoft.EntityFrameworkCore.Database.Command", LogEventLevel.Information);
|
||||
}
|
||||
|
||||
|
||||
logConfig.WriteTo.File(
|
||||
path: Path.Combine(logDirectory, "gozareshgir_log.txt"),
|
||||
|
||||
Reference in New Issue
Block a user