Compare commits

...

53 Commits

Author SHA1 Message Date
63a3027a17 add client checkout list controller 2026-01-18 12:51:58 +03:30
gozareshgir
8ec13ffae1 Merge branch 'master' of https://pm.gozareshgir.ir/gozareshgir/OriginalGozareshgir 2026-01-14 14:40:50 +03:30
gozareshgir
5508d4e88f Checkout Compute Minuts Base 2026-01-14 14:39:51 +03:30
43abb74c61 Merge branch 'Feature/program-manager/chat'
# Conflicts:
#	ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/TaskChat/Queries/GetMessages/GetMessagesQuery.cs
2026-01-14 10:57:07 +03:30
73e6681baa add message type to search query 2026-01-14 10:46:44 +03:30
90b2fd2eab add order for skills in set time 2026-01-14 10:13:57 +03:30
d9c431e20e add project name search for board list 2026-01-13 09:23:53 +03:30
gozareshgir
2746bf69ea Merge branch 'master' into Feature/SmsRepoetApi 2026-01-12 14:38:15 +03:30
gozareshgir
77dbb50512 BlueDeActiveAfterZeroDebt hangfire completed 2026-01-12 14:32:50 +03:30
gozareshgir
1c7e8824c7 DeActiveInstitutionEndOfContract hangfire completed 2026-01-12 13:10:58 +03:30
0eff1b9a66 change default task priority from medium to low 2026-01-12 12:07:43 +03:30
gozareshgir
0d33d79620 unblock hangfire completed 2026-01-11 22:07:58 +03:30
gozareshgir
e4355faffc block and unblock 2026-01-11 21:10:29 +03:30
gozareshgir
577fe5db76 Merge branch 'master' of https://pm.gozareshgir.ir/gozareshgir/OriginalGozareshgir into Feature/SmsRepoetApi 2026-01-11 12:58:36 +03:30
587fa40d81 fix percnetage condition 2026-01-10 10:45:38 +03:30
b741ab9ed2 fix contains no element error for empty skills 2026-01-10 10:34:20 +03:30
b6fde4903a Merge branch 'Feature/institution-contract/sent-to-customer-flag' 2026-01-08 15:03:07 +03:30
0772604432 feat: enhance GetMessagesQuery to include additional time notes in message retrieval 2026-01-08 15:02:43 +03:30
SamSys
ec8333c715 merg from master 2026-01-08 15:00:40 +03:30
SamSys
8aa93e089a legal Action Sms completed 2026-01-08 14:56:33 +03:30
59891d1199 Merge remote-tracking branch 'origin/master' 2026-01-08 14:16:22 +03:30
7cb39b1b92 feat: add UserId filter to ProjectBoardListQuery for enhanced task assignment tracking 2026-01-08 14:16:08 +03:30
SamSys
5580d56874 change logger on program.cs 2026-01-08 14:14:27 +03:30
SamSys
423b49e6e7 Merge branch 'master' of https://github.com/samsyntax24/OriginalGozareshgir 2026-01-08 14:05:30 +03:30
SamSys
0ab3052251 Send Warning and leagal action Message 2026-01-08 14:04:34 +03:30
38027352d6 Merge branch 'Feature/program-manager/priority'
# Conflicts:
#	ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListResponse.cs
2026-01-08 14:00:49 +03:30
43562fb49c Merge branch 'Feature/program-manager/chat'
# Conflicts:
#	.gitignore
#	ServiceHost/appsettings.Development.json
#	ServiceHost/appsettings.json
2026-01-08 13:51:06 +03:30
SamSys
5202779d9f changes 2026-01-08 12:18:01 +03:30
80a58f8cdc feat: add TaskId to ProjectBoardListQueryHandler and ProjectBoardListResponse for enhanced task tracking 2026-01-08 12:14:26 +03:30
7c611825a4 feat: refactor task priority handling to use ProjectTaskPriority across commands, DTOs, and repositories 2026-01-08 12:09:18 +03:30
SamSys
67a85735f0 merge from master 2026-01-08 11:36:18 +03:30
SamSys
bf46dfd1dc Merge branch 'master' of https://github.com/samsyntax24/OriginalGozareshgir 2026-01-08 11:35:10 +03:30
SamSys
a1ed3ad648 logeer change 2026-01-08 11:35:03 +03:30
SamSys
35e6355069 get Warning sms List on new repo 2026-01-08 11:19:25 +03:30
8679abb1e7 feat: enhance ChangeTaskPriorityCommand to support multi-level priority updates for tasks, phases, and projects 2026-01-08 11:18:15 +03:30
c8dddabdff fix: correct logic for editing text messages in TaskChatMessage.cs 2026-01-08 11:05:36 +03:30
6f076bdc77 feat: update application URL in launchSettings and enhance task sorting by priority in ProjectBoardListQueryHandler 2026-01-08 10:46:04 +03:30
SamSys
4de2e12ac5 AmaApiReport 2026-01-07 18:38:12 +03:30
SamSys
23b65cfbfe GetSms Report Expand List 2026-01-07 16:59:21 +03:30
SamSys
48b75d2baa Sms Report get list init 2026-01-07 16:29:03 +03:30
140414b866 feat: add SetIsSent endpoint to update contract send status 2026-01-07 16:23:11 +03:30
4ade9e12a6 feat: add InstitutionContractIsSentFlag to track contract send status 2026-01-07 15:03:21 +03:30
SamSys
63edb33bf5 Sms Report Init 2026-01-07 14:49:44 +03:30
dd7e816767 feat: add SetContractSendFlag method and related request for contract send tracking 2026-01-07 14:46:34 +03:30
1deeff996f feat: implement InstitutionContractSendFlag repository and model for contract send tracking 2026-01-07 14:35:17 +03:30
95d66c2d89 feat: enhance message queries to display real sender names and add system notes for additional times 2026-01-07 11:17:16 +03:30
609daf4353 feat: update file storage paths and enhance thumbnail generation with category support 2026-01-07 10:50:23 +03:30
a81e01ce2b Remove Storage folder from git tracking 2026-01-07 10:44:09 +03:30
2cd838a5e3 feat: enhance thumbnail generation with category support and update storage paths 2026-01-07 10:25:33 +03:30
34bd7ba444 add set file message to TaskChatMessage.cs 2026-01-06 12:21:12 +03:30
43b124664e feat: integrate authentication checks in message command handlers 2026-01-05 16:06:35 +03:30
d2dd67343b feat: add file management entities and services for chat message handling 2026-01-05 15:40:06 +03:30
3d2b5ff6bd feat: implement TaskChatMessage entity and repository for chat message management 2026-01-05 11:45:37 +03:30
143 changed files with 8092 additions and 2065 deletions

4
.gitignore vendored
View File

@@ -364,3 +364,7 @@ MigrationBackup/
.idea .idea
/ServiceHost/appsettings.Development.json /ServiceHost/appsettings.Development.json
/ServiceHost/appsettings.json /ServiceHost/appsettings.json
# Storage folder - ignore all uploaded files, thumbnails, and temporary files
ServiceHost/Storage

View File

@@ -2,6 +2,8 @@
public enum TypeOfSmsSetting public enum TypeOfSmsSetting
{ {
//همه انواع پیامک
All = 0,
/// <summary> /// <summary>
/// پیامک /// پیامک
@@ -23,7 +25,7 @@ public enum TypeOfSmsSetting
/// <summary> /// <summary>
/// پیامک /// پیامک
/// هشدار اول /// هشدار بدهی
/// </summary> /// </summary>
Warning, Warning,
@@ -38,4 +40,14 @@ public enum TypeOfSmsSetting
/// </summary> /// </summary>
InstitutionContractConfirm, InstitutionContractConfirm,
/// <summary>
/// ارسال کد تاییدیه قرارداد مالی
/// </summary>
SendInstitutionContractConfirmationCode,
/// <summary>
/// یادآور وظایف
/// </summary>
TaskReminder,
} }

View File

@@ -18,3 +18,17 @@ public class ApiResultViewModel
public string DeliveryColor { get; set; } public string DeliveryColor { get; set; }
public string FullName { get; set; } public string FullName { get; set; }
} }
public class ApiReportDto
{
public int MessageId { get; set; }
public long Mobile { get; set; }
public string SendUnixTime { get; set; }
public string DeliveryState { get; set; }
public string DeliveryUnixTime { get; set; }
public string DeliveryColor { get; set; }
}

View File

@@ -19,6 +19,13 @@ public interface ISmsService
bool SendAccountsInfo(string number,string fullName, string userName); bool SendAccountsInfo(string number,string fullName, string userName);
Task<ApiResultViewModel> GetByMessageId(int messId); Task<ApiResultViewModel> GetByMessageId(int messId);
Task<List<ApiResultViewModel>> GetApiResult(string startDate, string endDate); Task<List<ApiResultViewModel>> GetApiResult(string startDate, string endDate);
#region ForApi
Task<List<ApiReportDto>> GetApiReport(string startDate, string endDate);
#endregion
string DeliveryStatus(byte? dv); string DeliveryStatus(byte? dv);
string DeliveryColorStatus(byte? dv); string DeliveryColorStatus(byte? dv);
string UnixTimeStampToDateTime(int? unixTimeStamp); string UnixTimeStampToDateTime(int? unixTimeStamp);

View File

@@ -1,5 +1,6 @@
using _0_Framework.Application; using _0_Framework.Application;
using _0_Framework.Application.Enums;
using _0_Framework.Application.Sms; using _0_Framework.Application.Sms;
using Company.Domain.ContarctingPartyAgg; using Company.Domain.ContarctingPartyAgg;
using Company.Domain.InstitutionContractAgg; using Company.Domain.InstitutionContractAgg;
@@ -12,19 +13,21 @@ public class JobSchedulerRegistrator
private readonly IBackgroundJobClient _backgroundJobClient; private readonly IBackgroundJobClient _backgroundJobClient;
private readonly SmsReminder _smsReminder; private readonly SmsReminder _smsReminder;
private readonly IInstitutionContractRepository _institutionContractRepository; private readonly IInstitutionContractRepository _institutionContractRepository;
private readonly IInstitutionContractSmsServiceRepository _institutionContractSmsServiceRepository;
private static DateTime? _lastRunCreateTransaction; private static DateTime? _lastRunCreateTransaction;
private static DateTime? _lastRunSendMonthlySms; private static DateTime? _lastRunSendMonthlySms;
private readonly ISmsService _smsService; private readonly ISmsService _smsService;
private readonly ILogger<JobSchedulerRegistrator> _logger; private readonly ILogger<JobSchedulerRegistrator> _logger;
public JobSchedulerRegistrator(SmsReminder smsReminder, IBackgroundJobClient backgroundJobClient, IInstitutionContractRepository institutionContractRepository, ISmsService smsService, ILogger<JobSchedulerRegistrator> logger) public JobSchedulerRegistrator(SmsReminder smsReminder, IBackgroundJobClient backgroundJobClient, IInstitutionContractRepository institutionContractRepository, ISmsService smsService, ILogger<JobSchedulerRegistrator> logger, IInstitutionContractSmsServiceRepository institutionContractSmsServiceRepository)
{ {
_smsReminder = smsReminder; _smsReminder = smsReminder;
_backgroundJobClient = backgroundJobClient; _backgroundJobClient = backgroundJobClient;
_institutionContractRepository = institutionContractRepository; _institutionContractRepository = institutionContractRepository;
_smsService = smsService; _smsService = smsService;
_logger = logger; _logger = logger;
_institutionContractSmsServiceRepository = institutionContractSmsServiceRepository;
} }
public void Register() public void Register()
@@ -58,17 +61,43 @@ public class JobSchedulerRegistrator
"*/1 * * * *" // هر 1 دقیقه یکبار چک کن "*/1 * * * *" // هر 1 دقیقه یکبار چک کن
); );
//RecurringJob.AddOrUpdate( RecurringJob.AddOrUpdate(
// "InstitutionContract.SendWarningSms", "InstitutionContract.SendWarningSms",
// () => SendWarningSms(), () => SendWarningSms(),
// "*/1 * * * *" // هر 1 دقیقه یکبار چک کن "*/1 * * * *" // هر 1 دقیقه یکبار چک کن
//); );
//RecurringJob.AddOrUpdate( RecurringJob.AddOrUpdate(
// "InstitutionContract.SendLegalActionSms", "InstitutionContract.SendLegalActionSms",
// () => SendLegalActionSms(), () => SendLegalActionSms(),
// "*/1 * * * *" // هر 1 دقیقه یکبار چک کن "*/1 * * * *" // هر 1 دقیقه یکبار چک کن
//); );
RecurringJob.AddOrUpdate(
"InstitutionContract.Block",
() => Block(),
"*/30 * * * *" // هر 30 دقیقه یکبار چک کن
);
RecurringJob.AddOrUpdate(
"InstitutionContract.UnBlock",
() => UnBlock(),
"*/10 * * * *"
);
RecurringJob.AddOrUpdate(
"InstitutionContract.DeActiveInstitutionEndOfContract",
() => DeActiveInstitutionEndOfContract(),
"*/30 * * * *"
);
RecurringJob.AddOrUpdate(
"InstitutionContract.BlueDeActiveAfterZeroDebt",
() => BlueDeActiveAfterZeroDebt(),
"*/10 * * * *"
);
} }
@@ -134,7 +163,7 @@ public class JobSchedulerRegistrator
try try
{ {
await _institutionContractRepository.SendMonthlySms(now); await _institutionContractSmsServiceRepository.SendMonthlySms(now);
_lastRunSendMonthlySms = now; _lastRunSendMonthlySms = now;
Console.WriteLine("Send Monthly sms executed"); Console.WriteLine("Send Monthly sms executed");
@@ -156,7 +185,7 @@ public class JobSchedulerRegistrator
public async System.Threading.Tasks.Task SendReminderSms() public async System.Threading.Tasks.Task SendReminderSms()
{ {
_logger.LogInformation("SendReminderSms job run"); _logger.LogInformation("SendReminderSms job run");
await _institutionContractRepository.SendReminderSmsForBackgroundTask(); await _institutionContractSmsServiceRepository.SendReminderSmsForBackgroundTask();
} }
/// <summary> /// <summary>
@@ -167,7 +196,7 @@ public class JobSchedulerRegistrator
public async System.Threading.Tasks.Task SendBlockSms() public async System.Threading.Tasks.Task SendBlockSms()
{ {
_logger.LogInformation("SendBlockSms job run"); _logger.LogInformation("SendBlockSms job run");
await _institutionContractRepository.SendBlockSmsForBackgroundTask(); await _institutionContractSmsServiceRepository.SendBlockSmsForBackgroundTask();
} }
@@ -179,7 +208,7 @@ public class JobSchedulerRegistrator
public async System.Threading.Tasks.Task SendInstitutionContractConfirmSms() public async System.Threading.Tasks.Task SendInstitutionContractConfirmSms()
{ {
_logger.LogInformation("SendInstitutionContractConfirmSms job run"); _logger.LogInformation("SendInstitutionContractConfirmSms job run");
await _institutionContractRepository.SendInstitutionContractConfirmSmsTask(); await _institutionContractSmsServiceRepository.SendInstitutionContractConfirmSmsTask();
} }
/// <summary> /// <summary>
@@ -190,14 +219,86 @@ public class JobSchedulerRegistrator
public async System.Threading.Tasks.Task SendWarningSms() public async System.Threading.Tasks.Task SendWarningSms()
{ {
_logger.LogInformation("SendWarningSms job run"); _logger.LogInformation("SendWarningSms job run");
await _institutionContractRepository.SendWarningSmsTask(); await _institutionContractSmsServiceRepository.SendWarningOrLegalActionSmsTask(TypeOfSmsSetting.Warning);
} }
/// <summary>
/// پیامک اقدام قضایی
/// </summary>
/// <returns></returns>
[DisableConcurrentExecution(timeoutInSeconds: 100)] [DisableConcurrentExecution(timeoutInSeconds: 100)]
public async System.Threading.Tasks.Task SendLegalActionSms() public async System.Threading.Tasks.Task SendLegalActionSms()
{ {
_logger.LogInformation("SendWarningSms job run"); _logger.LogInformation("SendWarningSms job run");
await _institutionContractRepository.SendLegalActionSmsTask(); await _institutionContractSmsServiceRepository.SendWarningOrLegalActionSmsTask(TypeOfSmsSetting.LegalAction);
}
/// <summary>
/// بلاگ سازی
/// </summary>
/// <returns></returns>
[DisableConcurrentExecution(timeoutInSeconds: 100)]
public async System.Threading.Tasks.Task Block()
{
_logger.LogInformation("block job run");
var now = DateTime.Now;
var executeDate = now.ToFarsi().Substring(8, 2);
if (executeDate == "20")
{
if (now.Hour >= 9 && now.Hour < 10)
{
await _institutionContractSmsServiceRepository.Block(now);
}
}
}
/// <summary>
/// آنبلاک
/// </summary>
/// <returns></returns>
[DisableConcurrentExecution(timeoutInSeconds: 100)]
public async System.Threading.Tasks.Task UnBlock()
{
_logger.LogInformation("UnBlock job run");
await _institutionContractSmsServiceRepository.UnBlock();
}
/// <summary>
/// غیر فعال سازی قراداد های پایان یافته
/// </summary>
/// <returns></returns>
[DisableConcurrentExecution(timeoutInSeconds: 100)]
public async System.Threading.Tasks.Task DeActiveInstitutionEndOfContract()
{
_logger.LogInformation("DeActiveInstitutionEndOfContract job run");
var now = DateTime.Now;
var executeDate = now.ToFarsi().Substring(8, 2);
if (executeDate == "01")
{
if (now.Hour >= 9 && now.Hour < 10)
{
await _institutionContractSmsServiceRepository.DeActiveInstitutionEndOfContract(now);
}
}
}
/// <summary>
/// غیرفعال سازس قرارداد های آبی که بدهی ندارند
/// </summary>
/// <returns></returns>
[DisableConcurrentExecution(timeoutInSeconds: 800)]
public async System.Threading.Tasks.Task BlueDeActiveAfterZeroDebt()
{
_logger.LogInformation("BlueDeActiveAfterZeroDebt job run");
await _institutionContractSmsServiceRepository.BlueDeActiveAfterZeroDebt();
} }
} }

View File

@@ -80,4 +80,6 @@ public interface ICheckoutRepository : IRepository<long, Checkout>
#endregion #endregion
Task<Checkout> GetByWorkshopIdEmployeeIdInDate(long workshopId, long employeeId, DateTime inDate); Task<Checkout> GetByWorkshopIdEmployeeIdInDate(long workshopId, long employeeId, DateTime inDate);
Task<PagedResult<CheckoutListClientDto>> GetListForClient(long workshopId,
CheckoutListClientSearchModel searchModel);
} }

View File

@@ -91,65 +91,7 @@ public interface IInstitutionContractRepository : IRepository<long, InstitutionC
Task<List<InstitutionContractPrintViewModel>> PrintAllAsync(List<long> ids); Task<List<InstitutionContractPrintViewModel>> PrintAllAsync(List<long> ids);
#region ReminderSMS
/// <summary>
/// دریافت لیست - ارسال پیامک
/// فراخوانی از سمت بک گراند سرویس
/// </summary>
/// <returns></returns>
Task<bool> SendReminderSmsForBackgroundTask();
/// <summary>
/// ارسال پیامک صورت حساب ماهانه
/// </summary>
/// <param name="now"></param>
/// <returns></returns>
Task SendMonthlySms(DateTime now);
/// <summary>
/// ارسال پیامک مسدودی از طرف بک گراند سرویس
/// </summary>
/// <returns></returns>
Task SendBlockSmsForBackgroundTask();
/// <summary>
/// دریافت لیست واجد شرایط بلاک
/// جهت ارسال پیامک مسدودی
/// </summary>
/// <param name="checkDate"></param>
/// <returns></returns>
Task<List<BlockSmsListData>> GetBlockListData(DateTime checkDate);
/// <summary>
/// ارسال پیامک مسدودی
/// </summary>
/// <param name="smsListData"></param>
/// <param name="typeOfSms"></param>
/// <param name="sendMessStart"></param>
/// <param name="sendMessEnd"></param>
/// <returns></returns>
Task SendBlockSmsToContractingParties(List<BlockSmsListData> smsListData, string typeOfSms,
string sendMessStart, string sendMessEnd);
/// <summary>
///دریافت لیست بدهکارن
/// جهت ارسال پیامک
/// </summary>
/// <returns></returns>
Task<List<SmsListData>> GetSmsListData(DateTime checkDate, TypeOfSmsSetting typeOfSmsSetting);
/// <summary>
/// ارسال پیامک های یاد آور بدهی
/// </summary>
/// <returns></returns>
Task SendReminderSmsToContractingParties(List<SmsListData> smsListData, string typeOfSms, string sendMessStart, string sendMessEnd);
/// <summary>
/// ارسال پیامک یادآور تایید قراداد مالی
/// </summary>
/// <returns></returns>
Task SendInstitutionContractConfirmSmsTask();
#endregion
#region CreateMontlyTransaction #region CreateMontlyTransaction
@@ -162,24 +104,12 @@ public interface IInstitutionContractRepository : IRepository<long, InstitutionC
#endregion #endregion
#region WarningSms
/// <summary>
/// پیامک های هشدار
/// </summary>
/// <returns></returns>
Task SendWarningSmsTask();
#endregion
#region legalAction
/// <summary>
/// پیامک اقدام قضائی
/// </summary>
/// <returns></returns>
Task SendLegalActionSmsTask();
#endregion
Task<long> GetIdByInstallmentId(long installmentId); Task<long> GetIdByInstallmentId(long installmentId);
Task<InstitutionContract> GetPreviousContract(long currentInstitutionContractId); Task<InstitutionContract> GetPreviousContract(long currentInstitutionContractId);

View File

@@ -0,0 +1,145 @@
using _0_Framework.Application.Enums;
using _0_Framework.Domain;
using CompanyManagment.App.Contracts.InstitutionContract;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Company.Domain.InstitutionContractAgg;
public interface IInstitutionContractSmsServiceRepository : IRepository<long, InstitutionContract>
{
#region reminderSMs
/// <summary>
/// ارسال پیامک یادآور تایید قراداد مالی
/// </summary>
/// <returns></returns>
Task SendInstitutionContractConfirmSmsTask();
#endregion
//هشدار و اقدام قضایی
#region WarningOrLegalActionSmsListData
/// <summary>
/// اجرای تسک پیامک هشدار یا اقدام قضایی
/// </summary>
/// <param name="typeOfSmsSetting"></param>
/// <returns></returns>
Task SendWarningOrLegalActionSmsTask(TypeOfSmsSetting typeOfSmsSetting);
/// <summary>
/// دریافت لیست بدهکاران آبی جهت هشدار یا اقدام قضایی
/// </summary>
/// <param name="typeOfSmsSetting"></param>
/// <returns></returns>
Task<List<SmsListData>> GetWarningOrLegalActionSmsListData(TypeOfSmsSetting typeOfSmsSetting);
/// <summary>
/// ارسال پیامک هشدار یا اقدام قضایی
/// </summary>
/// <param name="smsListData"></param>
/// <param name="typeOfSmsSetting"></param>
/// <returns></returns>
Task SendWarningOrLegalActionSms(List<SmsListData> smsListData, TypeOfSmsSetting typeOfSmsSetting);
#endregion
//بلاک - آنبلاک - پیامک بلاک -
// غیر فعال سازی قراداد های پایان یافته
#region Block
/// <summary>
/// ارسال پیامک مسدودی از طرف بک گراند سرویس
/// </summary>
/// <returns></returns>
Task SendBlockSmsForBackgroundTask();
/// <summary>
/// دریافت لیست واجد شرایط بلاک
/// جهت ارسال پیامک مسدودی
/// </summary>
/// <param name="checkDate"></param>
/// <returns></returns>
Task<List<BlockSmsListData>> GetBlockListData(DateTime checkDate);
/// <summary>
/// ارسال پیامک مسدودی
/// </summary>
/// <param name="smsListData"></param>
/// <param name="typeOfSms"></param>
/// <param name="sendMessStart"></param>
/// <param name="sendMessEnd"></param>
/// <returns></returns>
Task SendBlockSmsToContractingParties(List<BlockSmsListData> smsListData, string typeOfSms,
string sendMessStart, string sendMessEnd);
/// <summary>
/// بلاک سازی
/// </summary>
/// <param name="checkDate"></param>
/// <returns></returns>
Task Block(DateTime checkDate);
/// <summary>
/// دریافت لیست بدهکارانی که باید بلاک شوند
/// </summary>
/// <param name="checkDate"></param>
/// <returns></returns>
Task<List<long>> GetToBeBlockList(DateTime checkDate);
/// <summary>
/// آنبلاک
/// </summary>
/// <returns></returns>
Task UnBlock();
/// <summary>
/// غیر فعالسازی قرارداد های پایان یافته
/// </summary>
/// <param name="checkDate"></param>
/// <returns></returns>
Task DeActiveInstitutionEndOfContract(DateTime checkDate);
/// <summary>
/// غیرفعال سازس قرارداد های آبی که بدهی ندارند
/// </summary>
/// <returns></returns>
Task BlueDeActiveAfterZeroDebt();
#endregion
#region ReminderSMS
/// <summary>
/// دریافت لیست - ارسال پیامک
/// فراخوانی از سمت بک گراند سرویس
/// </summary>
/// <returns></returns>
Task<bool> SendReminderSmsForBackgroundTask();
/// <summary>
/// ارسال پیامک صورت حساب ماهانه
/// </summary>
/// <param name="now"></param>
/// <returns></returns>
Task SendMonthlySms(DateTime now);
/// <summary>
///دریافت لیست بدهکارن
/// جهت ارسال پیامک
/// </summary>
/// <returns></returns>
Task<List<SmsListData>> GetSmsListData(DateTime checkDate, TypeOfSmsSetting typeOfSmsSetting);
/// <summary>
/// ارسال پیامک های یاد آور بدهی
/// </summary>
/// <returns></returns>
Task SendReminderSmsToContractingParties(List<SmsListData> smsListData, string typeOfSms, string sendMessStart, string sendMessEnd);
#endregion
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Company.Domain.InstitutionContractSendFlagAgg;
/// <summary>
/// Interface برای Repository مربوط به فلگ ارسال قرارداد
/// </summary>
public interface IInstitutionContractSendFlagRepository
{
/// <summary>
/// ایجاد یک رکورد جدید برای فلگ ارسال قرارداد
/// </summary>
Task Create(InstitutionContractSendFlag flag);
/// <summary>
/// بازیابی فلگ بر اساس شناسه قرارداد
/// </summary>
Task<InstitutionContractSendFlag> GetByContractId(long contractId);
/// <summary>
/// به‌روزرسانی فلگ ارسال
/// </summary>
Task Update(InstitutionContractSendFlag flag);
/// <summary>
/// بررسی اینکه آیا قرارداد ارسال شده است
/// </summary>
Task<bool> IsContractSent(long contractId);
}

View File

@@ -0,0 +1,82 @@
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Company.Domain.InstitutionContractSendFlagAgg;
/// <summary>
/// نمایندگی فلگ ارسال قرارداد در MongoDB
/// این موجودیت برای ردیابی اینکه آیا قرارداد ارسال شده است استفاده می‌شود
/// </summary>
public class InstitutionContractSendFlag
{
public InstitutionContractSendFlag(long institutionContractId,bool isSent)
{
Id = Guid.NewGuid();
InstitutionContractId = institutionContractId;
IsSent = isSent;
CreatedDate = DateTime.Now;
}
/// <summary>
/// شناسه یکتای MongoDB
/// </summary>
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; }
/// <summary>
/// شناسه قرارداد در SQL
/// </summary>
public long InstitutionContractId { get; set; }
/// <summary>
/// آیا قرارداد ارسال شده است
/// </summary>
public bool IsSent { get; set; }
/// <summary>
/// تاریخ و زمان ارسال
/// </summary>
public DateTime? SentDate { get; set; }
/// <summary>
/// تاریخ و زمان ایجاد رکورد
/// </summary>
public DateTime CreatedDate { get; set; }
/// <summary>
/// تاریخ و زمان آخرین به‌روزرسانی
/// </summary>
public DateTime? LastModifiedDate { get; set; }
/// <summary>
/// علامت‌گذاری قرارداد به عنوان ارسال‌شده
/// </summary>
public void MarkAsSent()
{
IsSent = true;
SentDate = DateTime.Now;
LastModifiedDate = DateTime.Now;
}
/// <summary>
/// بازگردانی علامت ارسال
/// </summary>
public void MarkAsNotSent()
{
IsSent = false;
SentDate = null;
LastModifiedDate = DateTime.Now;
}
/// <summary>
/// به‌روزرسانی علامت آخری اصلاح
/// </summary>
public void UpdateLastModified()
{
LastModifiedDate = DateTime.Now;
}
}

View File

@@ -1,10 +1,30 @@
using CompanyManagment.App.Contracts.SmsResult; using _0_Framework.Domain;
using CompanyManagment.App.Contracts.SmsResult;
using CompanyManagment.App.Contracts.SmsResult.Dto;
using System.Collections.Generic; using System.Collections.Generic;
using _0_Framework.Domain; using System.Threading.Tasks;
namespace Company.Domain.SmsResultAgg; namespace Company.Domain.SmsResultAgg;
public interface ISmsResultRepository : IRepository<long, SmsResult> public interface ISmsResultRepository : IRepository<long, SmsResult>
{ {
#region ForApi
/// <summary>
/// دریافت لیست پیامکها
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
Task<List<SmsReportDto>> GetSmsReportList(SmsReportSearchModel searchModel);
/// <summary>
/// دریافت اکسپند لیست هر تاریخ
/// </summary>
/// <param name="searchModel"></param>
/// <param name="date"></param>
/// <returns></returns>
Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date);
#endregion
List<SmsResultViewModel> Search(SmsResultSearchModel searchModel); List<SmsResultViewModel> Search(SmsResultSearchModel searchModel);
} }

View File

@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Company.Domain.InstitutionContractSendFlagAgg;
using MongoDB.Driver;
namespace CompanyManagement.Infrastructure.Mongo.InstitutionContractSendFlagRepo;
/// <summary>
/// Repository برای مدیریت فلگ ارسال قرارداد در MongoDB
/// </summary>
public class InstitutionContractSendFlagRepository : IInstitutionContractSendFlagRepository
{
private readonly IMongoCollection<InstitutionContractSendFlag> _collection;
public InstitutionContractSendFlagRepository(IMongoDatabase database)
{
_collection = database.GetCollection<InstitutionContractSendFlag>("InstitutionContractSendFlag");
}
public async Task Create(InstitutionContractSendFlag flag)
{
await _collection.InsertOneAsync(flag);
}
public async Task<InstitutionContractSendFlag> GetByContractId(long contractId)
{
var filter = Builders<InstitutionContractSendFlag>.Filter
.Eq(x => x.InstitutionContractId, contractId);
return await _collection.Find(filter).FirstOrDefaultAsync();
}
public async Task Update(InstitutionContractSendFlag flag)
{
var filter = Builders<InstitutionContractSendFlag>.Filter
.Eq(x => x.InstitutionContractId, flag.InstitutionContractId);
await _collection.ReplaceOneAsync(filter, flag);
}
public async Task<bool> IsContractSent(long contractId)
{
var flag = await GetByContractId(contractId);
return flag != null && flag.IsSent;
}
public async Task Remove(long contractId)
{
var filter = Builders<InstitutionContractSendFlag>.Filter
.Eq(x => x.InstitutionContractId, contractId);
await _collection.DeleteOneAsync(filter);
}
}

View File

@@ -62,4 +62,40 @@ public interface ICheckoutApplication
long workshopId, DateTime start, DateTime end); long workshopId, DateTime start, DateTime end);
#endregion #endregion
Task<PagedResult<CheckoutListClientDto>> GetListForClient(long workshopId,
CheckoutListClientSearchModel searchModel);
}
public class CheckoutListClientSearchModel:PaginationRequest
{
public long? EmployeeId { get; set; }
public string Year { get; set; }
public string Month { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
public CheckoutClientListOrderType? OrderType { get; set; }
}
public class CheckoutListClientDto
{
public long Id { get; set; }
public string Year { get; set; }
public string Month { get; set; }
public string EmployeeName { get; set; }
public string ContractNo { get; set; }
public string ContractStart { get; set; }
public string ContractEnd { get; set; }
public bool Signature { get; set; }
}
public enum CheckoutClientListOrderType
{
ByCheckoutCreationDate,
BySignedCheckout,
ByUnSignedCheckout,
ByPersonnelCode,
ByPersonnelCodeDescending,
ByCheckoutStartDate,
ByCheckoutStartDateDescending
} }

View File

@@ -96,6 +96,8 @@ public class GetInstitutionContractListItemsViewModel
/// مبلغ قسط /// مبلغ قسط
/// </summary> /// </summary>
public double InstallmentAmount { get; set; } public double InstallmentAmount { get; set; }
public bool InstitutionContractIsSentFlag { get; set; }
} }
public class InstitutionContractListWorkshop public class InstitutionContractListWorkshop

View File

@@ -305,6 +305,14 @@ public interface IInstitutionContractApplication
Task<InstitutionContractDiscountResponse> SetDiscountForCreation(InstitutionContractSetDiscountForCreationRequest request); Task<InstitutionContractDiscountResponse> SetDiscountForCreation(InstitutionContractSetDiscountForCreationRequest request);
Task<InstitutionContractDiscountResponse> ResetDiscountForCreation(InstitutionContractResetDiscountForExtensionRequest request); Task<InstitutionContractDiscountResponse> ResetDiscountForCreation(InstitutionContractResetDiscountForExtensionRequest request);
Task<OperationResult> CreationComplete(InstitutionContractExtensionCompleteRequest request); Task<OperationResult> CreationComplete(InstitutionContractExtensionCompleteRequest request);
/// <summary>
/// تعیین فلگ ارسال قرارداد در MongoDB
/// اگر فلگ وجود نداشتن‌د ایجاد می‌کند
/// </summary>
/// <param name="request">درخواست تعیین فلگ</param>
/// <returns>نتیجه عملیات</returns>
Task<OperationResult> SetContractSendFlag(SetInstitutionContractSendFlagRequest request);
} }
public class CreationSetContractingPartyResponse public class CreationSetContractingPartyResponse

View File

@@ -0,0 +1,19 @@
namespace CompanyManagment.App.Contracts.InstitutionContract;
/// <summary>
/// درخواست برای تعیین فلگ ارسال قرارداد
/// </summary>
public class SetInstitutionContractSendFlagRequest
{
/// <summary>
/// شناسه قرارداد
/// </summary>
public long InstitutionContractId { get; set; }
/// <summary>
/// آیا قرارداد ارسال شده است
/// </summary>
public bool IsSent { get; set; }
}

View File

@@ -0,0 +1,15 @@
namespace CompanyManagment.App.Contracts.SmsResult.Dto;
/// <summary>
/// وضعیت ارسال پیامک
/// </summary>
public enum SendStatus
{
All=0,
/// <summary>
/// موفق
/// </summary>
Success,
//ناموفق
Failed,
}

View File

@@ -0,0 +1,54 @@
using System;
namespace CompanyManagment.App.Contracts.SmsResult.Dto;
public class SmsReportDto
{
/// <summary>
/// تاریخ ارسال
/// </summary>
public string SentDate { get; set; }
}
public class SmsReportListDto
{
/// <summary>
/// آی دی
/// </summary>
public long Id { get; set; }
/// <summary>
/// آی دی پیامک در sms.ir
/// </summary>
public int MessageId { get; set; }
/// <summary>
/// وضعیت ارسال
/// </summary>
public string Status { get; set; }
/// <summary>
/// نوع پیامک
/// </summary>
public string TypeOfSms { get; set; }
/// <summary>
/// نام طرف حساب
/// </summary>
public string ContractingPartyName { get; set; }
/// <summary>
/// شماره موبایل
/// </summary>
public string Mobile { get; set; }
/// <summary>
/// ساعت و دقیقه
/// </summary>
public string HourAndMinute { get; set; }
}

View File

@@ -0,0 +1,43 @@
using _0_Framework.Application.Enums;
namespace CompanyManagment.App.Contracts.SmsResult.Dto;
public class SmsReportSearchModel
{
//نوع پیامک
public TypeOfSmsSetting TypeOfSms { get; set; }
/// <summary>
/// وضعیت ارسال پیامک
/// </summary>
public SendStatus SendStatus { get; set; }
/// <summary>
/// شماره موبایل
/// </summary>
public string Mobile { get; set; }
/// <summary>
/// آی دی طرف حساب
/// </summary>
public long ContractingPatyId { get; set; }
/// <summary>
/// سال
/// </summary>
public string Year { get; set; }
/// <summary>
/// ماه
/// </summary>
public string Month { get; set; }
/// <summary>
/// تاریخ شروع
/// </summary>
public string StartDateFa { get; set; }
/// <summary>
/// تاریخ پایان
/// </summary>
public string EndDateFa { get; set; }
}

View File

@@ -1,14 +1,34 @@
using System; using _0_Framework.Application;
using CompanyManagment.App.Contracts.SmsResult.Dto;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using _0_Framework.Application;
namespace CompanyManagment.App.Contracts.SmsResult; namespace CompanyManagment.App.Contracts.SmsResult;
public interface ISmsResultApplication public interface ISmsResultApplication
{ {
#region ForApi
/// <summary>
/// دریافت لیست پیامکها
/// </summary>
/// <param name="searchModel"></param>
/// <returns></returns>
Task<List<SmsReportDto>> GetSmsReportList(SmsReportSearchModel searchModel);
/// <summary>
/// دریافت اکسپند لیست هر تاریخ
/// </summary>
/// <param name="searchModel"></param>
/// <param name="date"></param>
/// <returns></returns>
Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date);
#endregion
OperationResult Create(CreateSmsResult command); OperationResult Create(CreateSmsResult command);
List<SmsResultViewModel> Search(SmsResultSearchModel searchModel); List<SmsResultViewModel> Search(SmsResultSearchModel searchModel);
} }

View File

@@ -706,5 +706,10 @@ public class CheckoutApplication : ICheckoutApplication
return _checkoutRepository.GetLastCheckoutsByWorkshopIdForWorkFlow(workshopId, start, end); return _checkoutRepository.GetLastCheckoutsByWorkshopIdForWorkFlow(workshopId, start, end);
} }
public async Task<PagedResult<CheckoutListClientDto>> GetListForClient(long workshopId,CheckoutListClientSearchModel searchModel)
{
return await _checkoutRepository.GetListForClient(workshopId, searchModel);
}
#endregion #endregion
} }

View File

@@ -19,6 +19,7 @@ using Company.Domain.PaymentTransactionAgg;
using Company.Domain.RepresentativeAgg; using Company.Domain.RepresentativeAgg;
using Company.Domain.RollCallServiceAgg; using Company.Domain.RollCallServiceAgg;
using Company.Domain.WorkshopAgg; using Company.Domain.WorkshopAgg;
using Company.Domain.InstitutionContractSendFlagAgg;
using CompanyManagment.App.Contracts.FinancialInvoice; using CompanyManagment.App.Contracts.FinancialInvoice;
using CompanyManagment.App.Contracts.FinancialStatment; using CompanyManagment.App.Contracts.FinancialStatment;
using CompanyManagment.App.Contracts.InstitutionContract; using CompanyManagment.App.Contracts.InstitutionContract;
@@ -51,6 +52,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication
private readonly IPaymentTransactionRepository _paymentTransactionRepository; private readonly IPaymentTransactionRepository _paymentTransactionRepository;
private readonly IRollCallServiceRepository _rollCallServiceRepository; private readonly IRollCallServiceRepository _rollCallServiceRepository;
private readonly ISepehrPaymentGatewayService _sepehrPaymentGatewayService; private readonly ISepehrPaymentGatewayService _sepehrPaymentGatewayService;
private readonly IInstitutionContractSendFlagRepository _institutionContractSendFlagRepository;
public InstitutionContractApplication(IInstitutionContractRepository institutionContractRepository, public InstitutionContractApplication(IInstitutionContractRepository institutionContractRepository,
@@ -62,7 +64,8 @@ public class InstitutionContractApplication : IInstitutionContractApplication
IAccountApplication accountApplication, ISmsService smsService, IAccountApplication accountApplication, ISmsService smsService,
IFinancialInvoiceRepository financialInvoiceRepository, IHttpClientFactory httpClientFactory, IFinancialInvoiceRepository financialInvoiceRepository, IHttpClientFactory httpClientFactory,
IPaymentTransactionRepository paymentTransactionRepository, IRollCallServiceRepository rollCallServiceRepository, IPaymentTransactionRepository paymentTransactionRepository, IRollCallServiceRepository rollCallServiceRepository,
ISepehrPaymentGatewayService sepehrPaymentGatewayService,ILogger<SepehrPaymentGateway> sepehrGatewayLogger) ISepehrPaymentGatewayService sepehrPaymentGatewayService,ILogger<SepehrPaymentGateway> sepehrGatewayLogger,
IInstitutionContractSendFlagRepository institutionContractSendFlagRepository)
{ {
_institutionContractRepository = institutionContractRepository; _institutionContractRepository = institutionContractRepository;
_contractingPartyRepository = contractingPartyRepository; _contractingPartyRepository = contractingPartyRepository;
@@ -80,6 +83,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication
_rollCallServiceRepository = rollCallServiceRepository; _rollCallServiceRepository = rollCallServiceRepository;
_sepehrPaymentGatewayService = sepehrPaymentGatewayService; _sepehrPaymentGatewayService = sepehrPaymentGatewayService;
_paymentGateway = new SepehrPaymentGateway(httpClientFactory,sepehrGatewayLogger); _paymentGateway = new SepehrPaymentGateway(httpClientFactory,sepehrGatewayLogger);
_institutionContractSendFlagRepository = institutionContractSendFlagRepository;
} }
public OperationResult Create(CreateInstitutionContract command) public OperationResult Create(CreateInstitutionContract command)
@@ -894,6 +898,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication
return opration.Succcedded(); return opration.Succcedded();
} }
public void CreateContractingPartyAccount(long contractingPartyid, long accountId) public void CreateContractingPartyAccount(long contractingPartyid, long accountId)
{ {
_institutionContractRepository.CreateContractingPartyAccount(contractingPartyid, accountId); _institutionContractRepository.CreateContractingPartyAccount(contractingPartyid, accountId);
@@ -1820,6 +1825,59 @@ public class InstitutionContractApplication : IInstitutionContractApplication
installments.Add(lastInstallment); installments.Add(lastInstallment);
return installments; return installments;
} }
}
/// <summary>
/// تعیین فلگ ارسال قرارداد
/// اگر فلگ وجود نداشتن‌د ایجاد می‌کند
/// </summary>
public async Task<OperationResult> 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}");
}
} }
} }

View File

@@ -1,8 +1,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using _0_Framework.Application; using _0_Framework.Application;
using Company.Domain.SmsResultAgg; using Company.Domain.SmsResultAgg;
using CompanyManagment.App.Contracts.SmsResult; using CompanyManagment.App.Contracts.SmsResult;
using CompanyManagment.App.Contracts.SmsResult.Dto;
namespace CompanyManagment.Application; namespace CompanyManagment.Application;
@@ -15,6 +17,23 @@ public class SmsResultApplication : ISmsResultApplication
_smsResultRepository = smsResultRepository; _smsResultRepository = smsResultRepository;
} }
#region ForApi
public async Task<List<SmsReportDto>> GetSmsReportList(SmsReportSearchModel searchModel)
{
return await _smsResultRepository.GetSmsReportList(searchModel);
}
public async Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date)
{
return await _smsResultRepository.GetSmsReportExpandList(searchModel, date);
}
#endregion
public OperationResult Create(CreateSmsResult command) public OperationResult Create(CreateSmsResult command)
{ {
var op = new OperationResult(); var op = new OperationResult();

View File

@@ -1,13 +1,14 @@
using System; using _0_Framework.Application;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using _0_Framework.Application;
using _0_Framework.Application.Enums; using _0_Framework.Application.Enums;
using Company.Domain.InstitutionContractAgg; using Company.Domain.InstitutionContractAgg;
using Company.Domain.SmsResultAgg; using Company.Domain.SmsResultAgg;
using CompanyManagment.App.Contracts.InstitutionContract; using CompanyManagment.App.Contracts.InstitutionContract;
using CompanyManagment.App.Contracts.SmsResult; using CompanyManagment.App.Contracts.SmsResult;
using CompanyManagment.EFCore.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CompanyManagment.Application; namespace CompanyManagment.Application;
@@ -15,11 +16,13 @@ public class SmsSettingApplication : ISmsSettingApplication
{ {
private readonly ISmsSettingsRepository _smsSettingsRepository; private readonly ISmsSettingsRepository _smsSettingsRepository;
private readonly IInstitutionContractRepository _institutionContractRepository; private readonly IInstitutionContractRepository _institutionContractRepository;
private readonly IInstitutionContractSmsServiceRepository _institutionContractSmsServiceRepository;
public SmsSettingApplication(ISmsSettingsRepository smsSettingsRepository, IInstitutionContractRepository institutionContractRepository) public SmsSettingApplication(ISmsSettingsRepository smsSettingsRepository, IInstitutionContractRepository institutionContractRepository, IInstitutionContractSmsServiceRepository institutionContractSmsServiceRepository)
{ {
_smsSettingsRepository = smsSettingsRepository; _smsSettingsRepository = smsSettingsRepository;
_institutionContractRepository = institutionContractRepository; _institutionContractRepository = institutionContractRepository;
_institutionContractSmsServiceRepository = institutionContractSmsServiceRepository;
} }
@@ -116,12 +119,12 @@ public class SmsSettingApplication : ISmsSettingApplication
public async Task<List<SmsListData>> GetSmsListData(TypeOfSmsSetting typeOfSmsSetting) public async Task<List<SmsListData>> GetSmsListData(TypeOfSmsSetting typeOfSmsSetting)
{ {
return await _institutionContractRepository.GetSmsListData(DateTime.Now, typeOfSmsSetting); return await _institutionContractSmsServiceRepository.GetSmsListData(DateTime.Now, typeOfSmsSetting);
} }
public async Task<List<BlockSmsListData>> GetBlockSmsListData(TypeOfSmsSetting typeOfSmsSetting) public async Task<List<BlockSmsListData>> GetBlockSmsListData(TypeOfSmsSetting typeOfSmsSetting)
{ {
return await _institutionContractRepository.GetBlockListData(DateTime.Now); return await _institutionContractSmsServiceRepository.GetBlockListData(DateTime.Now);
} }
@@ -134,7 +137,7 @@ public class SmsSettingApplication : ISmsSettingApplication
if (command.Any()) if (command.Any())
{ {
await _institutionContractRepository.SendReminderSmsToContractingParties(command, typeOfSms, sendMessStart, sendMessEnd); await _institutionContractSmsServiceRepository.SendReminderSmsToContractingParties(command, typeOfSms, sendMessStart, sendMessEnd);
return op.Succcedded(); return op.Succcedded();
} }
else else
@@ -153,7 +156,7 @@ public class SmsSettingApplication : ISmsSettingApplication
string sendMessEnd = "پایان مسدودی آنی "; string sendMessEnd = "پایان مسدودی آنی ";
if (command.Any()) if (command.Any())
{ {
await _institutionContractRepository.SendBlockSmsToContractingParties(command, typeOfSms, sendMessStart, await _institutionContractSmsServiceRepository.SendBlockSmsToContractingParties(command, typeOfSms, sendMessStart,
sendMessEnd); sendMessEnd);
return op.Succcedded(); return op.Succcedded();
} }

View File

@@ -7,6 +7,7 @@ using System.Linq;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
using System.Threading.Tasks; using System.Threading.Tasks;
using _0_Framework.Application; using _0_Framework.Application;
using _0_Framework.Exceptions;
using _0_Framework.InfraStructure; using _0_Framework.InfraStructure;
using Company.Domain.CheckoutAgg; using Company.Domain.CheckoutAgg;
using Company.Domain.LeftWorkAgg; using Company.Domain.LeftWorkAgg;
@@ -50,9 +51,11 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
private readonly IRollCallMandatoryRepository _rollCallMandatoryRepository; private readonly IRollCallMandatoryRepository _rollCallMandatoryRepository;
private readonly IRollCallEmployeeRepository _rollCallEmployeeRepository; private readonly IRollCallEmployeeRepository _rollCallEmployeeRepository;
public CheckoutRepository(IAuthHelper authHelper, CompanyContext context, IConfiguration configuration, ILeftWorkRepository leftWorkRepository, IWorkingHoursTempApplication workingHoursTempApplication, IRollCallRepository rollCallRepository, IRollCallMandatoryRepository rollCallMandatoryRepository, IRollCallEmployeeRepository rollCallEmployeeRepository) : base(context) public CheckoutRepository(IAuthHelper authHelper, CompanyContext context, IConfiguration configuration,
ILeftWorkRepository leftWorkRepository, IWorkingHoursTempApplication workingHoursTempApplication,
IRollCallRepository rollCallRepository, IRollCallMandatoryRepository rollCallMandatoryRepository,
IRollCallEmployeeRepository rollCallEmployeeRepository) : base(context)
{ {
_authHelper = authHelper; _authHelper = authHelper;
_context = context; _context = context;
_configuration = configuration; _configuration = configuration;
@@ -71,7 +74,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
/// <param name="سال به صورت رشته عددی"></param> /// <param name="سال به صورت رشته عددی"></param>
/// <param name="ماه بصورت رشته عددی"></param> /// <param name="ماه بصورت رشته عددی"></param>
/// <returns></returns> /// <returns></returns>
public (bool hasChekout, double FamilyAlloance, double OverTimePay, double RotatingShift, double Nightwork, double Fridaywork, double YraesPay) HasCheckout(long workshopId, long employeId, string year, string month) public (bool hasChekout, double FamilyAlloance, double OverTimePay, double RotatingShift, double Nightwork, double
Fridaywork, double YraesPay) HasCheckout(long workshopId, long employeId, string year, string month)
{ {
var farisMonthName = Tools.ToFarsiMonthByNumber(month); var farisMonthName = Tools.ToFarsiMonthByNumber(month);
@@ -90,7 +94,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
} }
return (true, res.FamilyAllowance, res.OvertimePay, res.ShiftPay, res.NightworkPay, res.FridayPay,res.YearsPay); return (true, res.FamilyAllowance, res.OvertimePay, res.ShiftPay, res.NightworkPay, res.FridayPay,
res.YearsPay);
} }
public EditCheckout GetDetails(long id) public EditCheckout GetDetails(long id)
@@ -136,7 +141,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
ContractEndGr = x.ContractEnd, ContractEndGr = x.ContractEnd,
ContractStart = x.ContractStart.ToFarsi(), ContractStart = x.ContractStart.ToFarsi(),
ContractEnd = x.ContractEnd.ToFarsi() ContractEnd = x.ContractEnd.ToFarsi()
}) })
.FirstOrDefault(x => x.Id == id); .FirstOrDefault(x => x.Id == id);
} }
@@ -152,7 +156,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
/// <param name="contractEnd"></param> /// <param name="contractEnd"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="NotImplementedException"></exception> /// <exception cref="NotImplementedException"></exception>
public async Task<CreateCheckoutListViewModel> GetContractResultToCreateCheckout(long workshopId, long employeeId, string year, string month, string contractStart, public async Task<CreateCheckoutListViewModel> GetContractResultToCreateCheckout(long workshopId, long employeeId,
string year, string month, string contractStart,
string contractEnd) string contractEnd)
{ {
DateTime startSreach; DateTime startSreach;
@@ -165,7 +170,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
} }
else else
{ {
if (month == "0" && year == "0") if (month == "0" && year == "0")
{ {
DateTime now = DateTime.Now; DateTime now = DateTime.Now;
@@ -195,8 +199,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
startSreach = startStr.ToGeorgianDateTime(); startSreach = startStr.ToGeorgianDateTime();
endSearch = (startStr.FindeEndOfMonth()).ToGeorgianDateTime(); endSearch = (startStr.FindeEndOfMonth()).ToGeorgianDateTime();
} }
} }
var timer = new Stopwatch(); var timer = new Stopwatch();
timer.Start(); timer.Start();
@@ -211,7 +215,9 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
contractWorkshop => contractWorkshop.contract.EmployeeId, contractWorkshop => contractWorkshop.contract.EmployeeId,
employee => employee.id, employee => employee.id,
(contractWorkshop, employee) => new { contractWorkshop, employee }) (contractWorkshop, employee) => new { contractWorkshop, employee })
.Join(_context.LeftWorkList.AsSplitQuery().Where(l => l.WorkshopId == workshopId && l.StartWorkDate < endSearch && l.LeftWorkDate > startSreach), .Join(
_context.LeftWorkList.AsSplitQuery().Where(l =>
l.WorkshopId == workshopId && l.StartWorkDate < endSearch && l.LeftWorkDate > startSreach),
contractWorkshopEmployee => contractWorkshopEmployee.contractWorkshop.contract.EmployeeId, contractWorkshopEmployee => contractWorkshopEmployee.contractWorkshop.contract.EmployeeId,
leftwork => leftwork.EmployeeId, leftwork => leftwork.EmployeeId,
(contractWorkshopEmployee, leftwork) => new { contractWorkshopEmployee, leftwork }) (contractWorkshopEmployee, leftwork) => new { contractWorkshopEmployee, leftwork })
@@ -221,16 +227,15 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
personnelCode => personnelCode.EmployeeId, personnelCode => personnelCode.EmployeeId,
(contractWorkshopEmployeeleftWork, personnelCode) => (contractWorkshopEmployeeleftWork, personnelCode) =>
new { contractWorkshopEmployeeleftWork, personnelCode }) new { contractWorkshopEmployeeleftWork, personnelCode })
.GroupJoin(_context.CheckoutSet.AsSplitQuery(), .GroupJoin(_context.CheckoutSet.AsSplitQuery(),
contractWorkshopEmployeeleftWorkPersonnelCode => contractWorkshopEmployeeleftWorkPersonnelCode contractWorkshopEmployeeleftWorkPersonnelCode => contractWorkshopEmployeeleftWorkPersonnelCode
.contractWorkshopEmployeeleftWork.contractWorkshopEmployee.contractWorkshop.contract.id, .contractWorkshopEmployeeleftWork.contractWorkshopEmployee.contractWorkshop.contract.id,
checkout => checkout.ContractId, checkout => checkout.ContractId,
(contractWorkshopEmployeeleftWorkPersonnelCode, checkout) => (contractWorkshopEmployeeleftWorkPersonnelCode, checkout) =>
new { contractWorkshopEmployeeleftWorkPersonnelCode, checkout }) new { contractWorkshopEmployeeleftWorkPersonnelCode, checkout })
.GroupJoin(_context.EmployeeComputeOptionsSet.Where(o => o.WorkshopId == workshopId), .GroupJoin(_context.EmployeeComputeOptionsSet.Where(o => o.WorkshopId == workshopId),
x => x.contractWorkshopEmployeeleftWorkPersonnelCode.contractWorkshopEmployeeleftWork.leftwork.EmployeeId, x => x.contractWorkshopEmployeeleftWorkPersonnelCode.contractWorkshopEmployeeleftWork.leftwork
.EmployeeId,
option => option.EmployeeId, option => option.EmployeeId,
(x, options) => new { x.checkout, x.contractWorkshopEmployeeleftWorkPersonnelCode, options }) (x, options) => new { x.checkout, x.contractWorkshopEmployeeleftWorkPersonnelCode, options })
.SelectMany( .SelectMany(
@@ -250,11 +255,11 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
DateTime currentStart = ($"{DateTime.Now.ToFarsi().Substring(0, 8)}01").ToGeorgianDateTime(); DateTime currentStart = ($"{DateTime.Now.ToFarsi().Substring(0, 8)}01").ToGeorgianDateTime();
DateTime currentEnd = ($"{DateTime.Now.ToFarsi().FindeEndOfMonth()}").ToGeorgianDateTime(); DateTime currentEnd = ($"{DateTime.Now.ToFarsi().FindeEndOfMonth()}").ToGeorgianDateTime();
var chekoutCreated = result.checkout.FirstOrDefault(x => x.ContractStart < endSearch && x.ContractEnd > startSreach && x.IsActiveString == "true"); var chekoutCreated = result.checkout.FirstOrDefault(x =>
x.ContractStart < endSearch && x.ContractEnd > startSreach && x.IsActiveString == "true");
if (chekoutCreated != null) if (chekoutCreated != null)
{ {
return new CreateCheckoutListViewModel return new CreateCheckoutListViewModel
{ {
Id = chekoutCreated.ContractId, Id = chekoutCreated.ContractId,
@@ -273,6 +278,7 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
EmployeeHasCreateCheckout = true EmployeeHasCreateCheckout = true
}; };
} }
var employeeJoin = result.contractWorkshopEmployeeleftWorkPersonnelCode.contractWorkshopEmployeeleftWork var employeeJoin = result.contractWorkshopEmployeeleftWorkPersonnelCode.contractWorkshopEmployeeleftWork
.contractWorkshopEmployee.employee.id; .contractWorkshopEmployee.employee.id;
@@ -283,11 +289,13 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
var startStatusSearch = leftWork.StartWorkDate > startSreach && leftWork.StartWorkDate <= endSearch var startStatusSearch = leftWork.StartWorkDate > startSreach && leftWork.StartWorkDate <= endSearch
? leftWork.StartWorkDate ? leftWork.StartWorkDate
: startSreach; : startSreach;
var endStatusSearch = leftWork.HasLeft && leftWork.LeftWorkDate > startSreach && leftWork.LeftWorkDate <= endSearch var endStatusSearch = leftWork.HasLeft && leftWork.LeftWorkDate > startSreach &&
leftWork.LeftWorkDate <= endSearch
? leftWork.LeftWorkDate.AddDays(-1) ? leftWork.LeftWorkDate.AddDays(-1)
: startSreach; : startSreach;
bool hasRollCall = bool hasRollCall =
_rollCallEmployeeRepository.HasRollCallRecord(employeeJoin, workshopId, startStatusSearch, endStatusSearch); _rollCallEmployeeRepository.HasRollCallRecord(employeeJoin, workshopId, startStatusSearch,
endStatusSearch);
bool extension = true; bool extension = true;
bool laterThanEnd = false; bool laterThanEnd = false;
@@ -300,11 +308,11 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
.contractWorkshopEmployee.contractWorkshop.contract.ContarctStart; .contractWorkshopEmployee.contractWorkshop.contract.ContarctStart;
var contractEndGr = result.contractWorkshopEmployeeleftWorkPersonnelCode.contractWorkshopEmployeeleftWork var contractEndGr = result.contractWorkshopEmployeeleftWorkPersonnelCode.contractWorkshopEmployeeleftWork
.contractWorkshopEmployee.contractWorkshop.contract.ContractEnd; .contractWorkshopEmployee.contractWorkshop.contract.ContractEnd;
#region HasRollCall #region HasRollCall
if (hasRollCall) if (hasRollCall)
{ {
// اگر ترک کار کرده بود // اگر ترک کار کرده بود
// اگر ترک کارش در بازه انتخاب شده بود // اگر ترک کارش در بازه انتخاب شده بود
if (leftWork.HasLeft && leftWork.LeftWorkDate > startSreach && leftWork.LeftWorkDate <= endSearch) if (leftWork.HasLeft && leftWork.LeftWorkDate > startSreach && leftWork.LeftWorkDate <= endSearch)
@@ -322,7 +330,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
? leftWork.StartWorkDate.ToFarsi() ? leftWork.StartWorkDate.ToFarsi()
: startSreach.ToFarsi(); : startSreach.ToFarsi();
contractEnd = leftWork.LeftWorkDate.AddDays(-1).ToFarsi(); contractEnd = leftWork.LeftWorkDate.AddDays(-1).ToFarsi();
} }
else else
{ {
@@ -334,7 +341,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
? leftWork.StartWorkDate.ToFarsi() ? leftWork.StartWorkDate.ToFarsi()
: startSreach.ToFarsi(); : startSreach.ToFarsi();
contractEnd = leftWork.LeftWorkDate.AddDays(-1).ToFarsi(); contractEnd = leftWork.LeftWorkDate.AddDays(-1).ToFarsi();
} }
} }
else if (endSearch < currentStart) else if (endSearch < currentStart)
@@ -357,7 +363,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
: startSreach.ToFarsi(); : startSreach.ToFarsi();
contractEnd = leftWork.LeftWorkDate.AddDays(-1).ToFarsi(); contractEnd = leftWork.LeftWorkDate.AddDays(-1).ToFarsi();
} }
} }
else if (leftWork.HasLeft && leftWork.LeftWorkDate <= startSreach) else if (leftWork.HasLeft && leftWork.LeftWorkDate <= startSreach)
{ {
@@ -365,7 +370,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
extension = false; extension = false;
description = "به دلیل ترک کار پیش از تاریخ انتخاب شده مجاز به ایجاد فیش نمی باشید"; description = "به دلیل ترک کار پیش از تاریخ انتخاب شده مجاز به ایجاد فیش نمی باشید";
leftWorkDate = leftWork.LeftWorkDate.ToFarsi(); leftWorkDate = leftWork.LeftWorkDate.ToFarsi();
} }
else if (!leftWork.HasLeft && startSreach == currentStart) else if (!leftWork.HasLeft && startSreach == currentStart)
{ {
@@ -376,13 +380,9 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
? leftWork.StartWorkDate.ToFarsi() ? leftWork.StartWorkDate.ToFarsi()
: startSreach.ToFarsi(); : startSreach.ToFarsi();
contractEnd = endSearch.ToFarsi(); contractEnd = endSearch.ToFarsi();
} }
else if (!leftWork.HasLeft && startSreach < currentStart) else if (!leftWork.HasLeft && startSreach < currentStart)
{ {
if (contractStartGr <= startSreach && contractStartGr > endSearch) if (contractStartGr <= startSreach && contractStartGr > endSearch)
{ {
laterThanEnd = true; laterThanEnd = true;
@@ -397,12 +397,11 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
contractStart = leftWork.StartWorkDate > startSreach contractStart = leftWork.StartWorkDate > startSreach
? leftWork.StartWorkDate.ToFarsi() ? leftWork.StartWorkDate.ToFarsi()
: startSreach.ToFarsi(); : startSreach.ToFarsi();
contractEnd = (leftWork.LeftWorkDate > startSreach && leftWork.LeftWorkDate <= endSearch) ? leftWork.LeftWorkDate.AddDays(-1).ToFarsi() : endSearch.ToFarsi(); contractEnd = (leftWork.LeftWorkDate > startSreach && leftWork.LeftWorkDate <= endSearch)
? leftWork.LeftWorkDate.AddDays(-1).ToFarsi()
: endSearch.ToFarsi();
} }
} }
} }
#endregion #endregion
@@ -468,13 +467,11 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
contractEnd = endSearch.ToFarsi(); contractEnd = endSearch.ToFarsi();
} }
} }
} }
#endregion #endregion
return new CreateCheckoutListViewModel return new CreateCheckoutListViewModel
{ {
Id = result.contractWorkshopEmployeeleftWorkPersonnelCode.contractWorkshopEmployeeleftWork Id = result.contractWorkshopEmployeeleftWorkPersonnelCode.contractWorkshopEmployeeleftWork
@@ -498,15 +495,14 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
ContractStart = contractStart, ContractStart = contractStart,
ContractEnd = contractEnd, ContractEnd = contractEnd,
LeftWorkDate = leftWorkDate, LeftWorkDate = leftWorkDate,
EmployeeHasCreateCheckout = result.option != null ? result.option.CreateCheckout : result.contractWorkshopEmployeeleftWorkPersonnelCode.contractWorkshopEmployeeleftWork.contractWorkshopEmployee.contractWorkshop.workshop.CreateCheckout EmployeeHasCreateCheckout = result.option != null
? result.option.CreateCheckout
: result.contractWorkshopEmployeeleftWorkPersonnelCode.contractWorkshopEmployeeleftWork
.contractWorkshopEmployee.contractWorkshop.workshop.CreateCheckout
}; };
}).Where(x => x.EmployeeHasCreateCheckout).OrderByDescending(x => x.Extension).ToList(); }).Where(x => x.EmployeeHasCreateCheckout).OrderByDescending(x => x.Extension).ToList();
Console.WriteLine("process : " + timer.Elapsed); Console.WriteLine("process : " + timer.Elapsed);
return new CreateCheckoutListViewModel() return new CreateCheckoutListViewModel()
@@ -517,7 +513,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
public async Task CreateCkeckout(Checkout command) public async Task CreateCkeckout(Checkout command)
{ {
var creationDates = DateTime.Now; var creationDates = DateTime.Now;
//var result = await _context.Database.ExecuteSqlInterpolatedAsync($"EXEC InsertQuery_Checkout {id},{employeeFullName},{isActiveString},{signature},{fathersName},{nationalCode},{dateOfBirth},{employeeId},{workshopName},{workshopId},{contractNo},{contractStart},{contractEnd},{month},{year},{contractId},{workingHoursId},{monthlySalary},{baseYearsPay},{consumableItems},{housingAllowance},{overtimePay},{nightworkPay},{fridayPay},{missionPay},{shiftPay},{familyAllowance},{bonusesPay},{yearsPay},{leavePay},{insuranceDeduction},{taxDeducation},{installmentDeduction},{salaryAidDeduction},{absenceDeduction},{creationDate},{archiveCode},{personnelCode},{sumOfWorkingDays},{totalClaims},{taxDeducation},{totalPayment}"); //var result = await _context.Database.ExecuteSqlInterpolatedAsync($"EXEC InsertQuery_Checkout {id},{employeeFullName},{isActiveString},{signature},{fathersName},{nationalCode},{dateOfBirth},{employeeId},{workshopName},{workshopId},{contractNo},{contractStart},{contractEnd},{month},{year},{contractId},{workingHoursId},{monthlySalary},{baseYearsPay},{consumableItems},{housingAllowance},{overtimePay},{nightworkPay},{fridayPay},{missionPay},{shiftPay},{familyAllowance},{bonusesPay},{yearsPay},{leavePay},{insuranceDeduction},{taxDeducation},{installmentDeduction},{salaryAidDeduction},{absenceDeduction},{creationDate},{archiveCode},{personnelCode},{sumOfWorkingDays},{totalClaims},{taxDeducation},{totalPayment}");
@@ -536,7 +531,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
if (command.HasInsuranceShareTheSameAsList) if (command.HasInsuranceShareTheSameAsList)
entity.SetInsuranceShare(); entity.SetInsuranceShare();
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
} }
@@ -678,9 +672,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
query = query.Where(x => x.ContractStartGr >= startyearGr && x.ContractEndGr <= endYearGr).ToList(); query = query.Where(x => x.ContractStartGr >= startyearGr && x.ContractEndGr <= endYearGr).ToList();
if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0) if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0)
return query.OrderByDescending(x => x.ContractEndGr).ThenBy(x => x.PersonnelCodeInt).ToList(); return query.OrderByDescending(x => x.ContractEndGr).ThenBy(x => x.PersonnelCodeInt).ToList();
} }
else if (!string.IsNullOrWhiteSpace(searchModel.Year) && !string.IsNullOrWhiteSpace(searchModel.Month) && else if (!string.IsNullOrWhiteSpace(searchModel.Year) && !string.IsNullOrWhiteSpace(searchModel.Month) &&
string.IsNullOrWhiteSpace(searchModel.ContractStart) && string.IsNullOrWhiteSpace(searchModel.ContractStart) &&
@@ -799,7 +790,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
endDate >= x.ContractEndGr && startDate < x.ContractEndGr).ToList(); endDate >= x.ContractEndGr && startDate < x.ContractEndGr).ToList();
if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0) if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0)
return query.OrderBy(x => x.PersonnelCodeInt).ToList(); return query.OrderBy(x => x.PersonnelCodeInt).ToList();
} }
else if (!string.IsNullOrWhiteSpace(searchModel.ContractStart) && else if (!string.IsNullOrWhiteSpace(searchModel.ContractStart) &&
!string.IsNullOrWhiteSpace(searchModel.ContractEnd) && !string.IsNullOrWhiteSpace(searchModel.ContractEnd) &&
@@ -813,17 +803,12 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0) if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0)
return query.OrderByDescending(x => x.ContractEndGr).ThenBy(x => x.PersonnelCodeInt).ToList(); return query.OrderByDescending(x => x.ContractEndGr).ThenBy(x => x.PersonnelCodeInt).ToList();
} }
return query.OrderByDescending(x => x.Id) return query.OrderByDescending(x => x.Id)
.ThenByDescending(x => x.Year).ThenBy(x => x.PersonnelCodeInt).Take(100).ToList(); .ThenByDescending(x => x.Year).ThenBy(x => x.PersonnelCodeInt).Take(100).ToList();
//foreach(var items in query) //foreach(var items in query)
//{ //{
// var employeId = _context.WorkshopEmployers?.Where(x => x.WorkshopId == items.WorkshopId) // var employeId = _context.WorkshopEmployers?.Where(x => x.WorkshopId == items.WorkshopId)
@@ -865,7 +850,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
ContractNo = x.ContractNo, ContractNo = x.ContractNo,
ContractId = x.ContractId, ContractId = x.ContractId,
HasRollCall = x.HasRollCall HasRollCall = x.HasRollCall
}); });
if (searchModel.EmployeeId != 0 && searchModel.WorkshopId != 0) if (searchModel.EmployeeId != 0 && searchModel.WorkshopId != 0)
@@ -884,11 +868,12 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
{ {
var start = searchModel.ContractStart.ToGeorgianDateTime(); var start = searchModel.ContractStart.ToGeorgianDateTime();
var end = searchModel.ContractEnd.ToGeorgianDateTime(); var end = searchModel.ContractEnd.ToGeorgianDateTime();
query = query.Where(x => x.ContractStartGr == start && x.ContractEndGr == end && x.EmployeeId == searchModel.EmployeeId && x.WorkshopId == searchModel.WorkshopId); query = query.Where(x =>
x.ContractStartGr == start && x.ContractEndGr == end && x.EmployeeId == searchModel.EmployeeId &&
x.WorkshopId == searchModel.WorkshopId);
} }
return query.OrderBy(x => x.ContractStartGr).ToList(); return query.OrderBy(x => x.ContractStartGr).ToList();
} }
public List<CheckoutViewModel> PrintAll(List<long> id) public List<CheckoutViewModel> PrintAll(List<long> id)
@@ -969,7 +954,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
SalaryAidDateTimeFa = s.SalaryAidDateTimeFa, SalaryAidDateTimeFa = s.SalaryAidDateTimeFa,
SalaryAidDateTimeGe = s.SalaryAidDateTime SalaryAidDateTimeGe = s.SalaryAidDateTime
}).ToList(), }).ToList(),
CheckoutRollCall = item.CheckoutRollCall != null ? new CheckoutRollCallViewModel() CheckoutRollCall = item.CheckoutRollCall != null
? new CheckoutRollCallViewModel()
{ {
TotalPresentTimeSpan = item.CheckoutRollCall.TotalPresentTimeSpan, TotalPresentTimeSpan = item.CheckoutRollCall.TotalPresentTimeSpan,
TotalBreakTimeSpan = item.CheckoutRollCall.TotalBreakTimeSpan, TotalBreakTimeSpan = item.CheckoutRollCall.TotalBreakTimeSpan,
@@ -977,7 +963,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
TotalPaidLeaveTmeSpan = item.CheckoutRollCall.TotalPaidLeaveTmeSpan, TotalPaidLeaveTmeSpan = item.CheckoutRollCall.TotalPaidLeaveTmeSpan,
TotalMandatoryTimeSpan = item.CheckoutRollCall.TotalMandatoryTimeSpan, TotalMandatoryTimeSpan = item.CheckoutRollCall.TotalMandatoryTimeSpan,
TotalSickLeaveTimeSpan = item.CheckoutRollCall.TotalSickLeaveTimeSpan, TotalSickLeaveTimeSpan = item.CheckoutRollCall.TotalSickLeaveTimeSpan,
RollCallDaysCollection = item.CheckoutRollCall.RollCallDaysCollection.Select(d => new CheckoutRollCallDayViewModel() RollCallDaysCollection = item.CheckoutRollCall.RollCallDaysCollection.Select(d =>
new CheckoutRollCallDayViewModel()
{ {
WorkingTimeSpan = d.WorkingTimeSpan, WorkingTimeSpan = d.WorkingTimeSpan,
BreakTimeSpan = d.BreakTimeSpan, BreakTimeSpan = d.BreakTimeSpan,
@@ -994,11 +981,12 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
SecondEndDate = d.SecondEndDate, SecondEndDate = d.SecondEndDate,
SecondStartDate = d.SecondStartDate, SecondStartDate = d.SecondStartDate,
}).ToList(), }).ToList(),
}
} : null, : null,
HasAmountConflict = item.HasAmountConflict, HasAmountConflict = item.HasAmountConflict,
EmployeeMandatoryHoursTimeSpan = item.EmployeeMandatoryHours, EmployeeMandatoryHoursTimeSpan = item.EmployeeMandatoryHours,
EmployeeMandatoryHoursStr = Tools.ToFarsiHoursAndMinutes(Convert.ToInt32(item.EmployeeMandatoryHours.TotalHours),item.EmployeeMandatoryHours.Minutes,"-") EmployeeMandatoryHoursStr = Tools.ToFarsiHoursAndMinutes(
Convert.ToInt32(item.EmployeeMandatoryHours.TotalHours), item.EmployeeMandatoryHours.Minutes, "-")
}; };
var workshopName = _context.Workshops.FirstOrDefault(x => x.id == ch.WorkshopId); var workshopName = _context.Workshops.FirstOrDefault(x => x.id == ch.WorkshopId);
ch.WorkshopName = workshopName.WorkshopName; ch.WorkshopName = workshopName.WorkshopName;
@@ -1122,9 +1110,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
var totalPaidLeaveTimeSpans = hourlyPaidLeaveTimeSpans.Concat(dailyPaidLeaveTimeSpans); var totalPaidLeaveTimeSpans = hourlyPaidLeaveTimeSpans.Concat(dailyPaidLeaveTimeSpans);
ch.TotalHourlyLeave = new TimeSpan(hourlyPaidLeaveTimeSpans.Sum(x => x.Ticks)); ch.TotalHourlyLeave = new TimeSpan(hourlyPaidLeaveTimeSpans.Sum(x => x.Ticks));
ch.TotalPaidLeave = new TimeSpan(totalPaidLeaveTimeSpans.Sum(x => x.Ticks)).ToFarsiDaysAndHoursAndMinutes("-"); ch.TotalPaidLeave =
new TimeSpan(totalPaidLeaveTimeSpans.Sum(x => x.Ticks)).ToFarsiDaysAndHoursAndMinutes("-");
#endregion #endregion
@@ -1142,7 +1129,9 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
ch.TotalBreakTimeStr = ch.CheckoutRollCall.TotalBreakTimeSpan.ToFarsiHoursAndMinutes("-"); ch.TotalBreakTimeStr = ch.CheckoutRollCall.TotalBreakTimeSpan.ToFarsiHoursAndMinutes("-");
ch.TotalPresentTimeStr = ch.CheckoutRollCall.TotalPresentTimeSpan.ToFarsiHoursAndMinutes("-"); ch.TotalPresentTimeStr = ch.CheckoutRollCall.TotalPresentTimeSpan.ToFarsiHoursAndMinutes("-");
ch.TotalMandatoryTimeStr = ch.CheckoutRollCall.TotalMandatoryTimeSpan.ToFarsiHoursAndMinutes("-"); ch.TotalMandatoryTimeStr = ch.CheckoutRollCall.TotalMandatoryTimeSpan.ToFarsiHoursAndMinutes("-");
ch.TotalPaidLeave = Tools.ToFarsiHoursAndMinutes(Convert.ToInt32(ch.CheckoutRollCall.TotalPaidLeaveTmeSpan.TotalHours), ch.CheckoutRollCall.TotalPaidLeaveTmeSpan.Minutes, "-"); ch.TotalPaidLeave = Tools.ToFarsiHoursAndMinutes(
Convert.ToInt32(ch.CheckoutRollCall.TotalPaidLeaveTmeSpan.TotalHours),
ch.CheckoutRollCall.TotalPaidLeaveTmeSpan.Minutes, "-");
ch.MonthlyRollCall = ch.CheckoutRollCall.RollCallDaysCollection ch.MonthlyRollCall = ch.CheckoutRollCall.RollCallDaysCollection
.Select(x => new CheckoutDailyRollCallViewModel .Select(x => new CheckoutDailyRollCallViewModel
{ {
@@ -1163,13 +1152,12 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
BreakTimeString = $"{(int)(x.BreakTimeSpan.TotalHours)}:{x.BreakTimeSpan.Minutes:00}", BreakTimeString = $"{(int)(x.BreakTimeSpan.TotalHours)}:{x.BreakTimeSpan.Minutes:00}",
RollCallDateFa = x.Date.ToFarsi() RollCallDateFa = x.Date.ToFarsi()
}).ToList(); }).ToList();
} }
else else
{ {
if (ch.HasRollCall) if (ch.HasRollCall)
ch.MonthlyRollCall = _rollCallRepository.GetEmployeeRollCallsForMonth(ch.EmployeeId, ch.WorkshopId, ch.ContractStartGr, ch.ContractEndGr); ch.MonthlyRollCall = _rollCallRepository.GetEmployeeRollCallsForMonth(ch.EmployeeId, ch.WorkshopId,
ch.ContractStartGr, ch.ContractEndGr);
else else
{ {
ch.CreateWorkingHoursTemp.ContractStartGr = ch.ContractStartGr; ch.CreateWorkingHoursTemp.ContractStartGr = ch.ContractStartGr;
@@ -1178,9 +1166,11 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
ch.CreateWorkingHoursTemp.ContractEnd = ch.ContractEndGr.ToFarsi(); ch.CreateWorkingHoursTemp.ContractEnd = ch.ContractEndGr.ToFarsi();
ch.CreateWorkingHoursTemp.EmployeeId = ch.EmployeeId; ch.CreateWorkingHoursTemp.EmployeeId = ch.EmployeeId;
ch.CreateWorkingHoursTemp.WorkshopId = ch.WorkshopId; ch.CreateWorkingHoursTemp.WorkshopId = ch.WorkshopId;
ch.MonthlyRollCall = ConvertStaticToRollCall(ch.CreateWorkingHoursTemp, workshopName.WorkshopHolidayWorking); ch.MonthlyRollCall =
ConvertStaticToRollCall(ch.CreateWorkingHoursTemp, workshopName.WorkshopHolidayWorking);
} }
} }
query.Add(ch); query.Add(ch);
} }
@@ -1191,6 +1181,7 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
printNumer += 1; printNumer += 1;
rec.PrintCounter = printNumer; rec.PrintCounter = printNumer;
} }
return query; return query;
} }
@@ -1216,7 +1207,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
Id = x.id, Id = x.id,
EmployerFullName = x.FName + " " + x.LName, EmployerFullName = x.FName + " " + x.LName,
IsLegal = x.IsLegal, IsLegal = x.IsLegal,
}).Where(x => emp.Contains(x.Id)).ToList(); }).Where(x => emp.Contains(x.Id)).ToList();
if (employerlist.Count > 0) if (employerlist.Count > 0)
{ {
@@ -1245,7 +1235,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
//} //}
var leaveList = _context.LeaveList.Where(x => x.WorkshopId == ch.WorkshopId && x.EmployeeId == ch.EmployeeId && x.LeaveType == "استحقاقی"); var leaveList = _context.LeaveList.Where(x =>
x.WorkshopId == ch.WorkshopId && x.EmployeeId == ch.EmployeeId && x.LeaveType == "استحقاقی");
var leaveViewModel = new List<LeaveViewModel>(); var leaveViewModel = new List<LeaveViewModel>();
foreach (var list in leaveList) foreach (var list in leaveList)
@@ -1370,7 +1361,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
SalaryAidDateTimeFa = s.SalaryAidDateTimeFa, SalaryAidDateTimeFa = s.SalaryAidDateTimeFa,
SalaryAidDateTimeGe = s.SalaryAidDateTime SalaryAidDateTimeGe = s.SalaryAidDateTime
}).ToList(), }).ToList(),
}).SingleOrDefault(x => x.Id == id); }).SingleOrDefault(x => x.Id == id);
var workshopName = _context.Workshops.FirstOrDefault(x => x.id == ch.WorkshopId); var workshopName = _context.Workshops.FirstOrDefault(x => x.id == ch.WorkshopId);
@@ -1390,6 +1380,7 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
}; };
employers.Add(employer); employers.Add(employer);
} }
ch.MaritalStatus = _context.Employees.Find(ch.EmployeeId)?.MaritalStatus; ch.MaritalStatus = _context.Employees.Find(ch.EmployeeId)?.MaritalStatus;
ch.EmployerList = employers; ch.EmployerList = employers;
var workingHours = _workingHoursTempApplication.GetByContractIdConvertToShiftwork4(ch.ContractId); var workingHours = _workingHoursTempApplication.GetByContractIdConvertToShiftwork4(ch.ContractId);
@@ -1484,7 +1475,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
ch.TotalSickLeave = new TimeSpan(sickLeaveTimeSpans.Sum(x => x.Ticks)).ToFarsiDaysAndHoursAndMinutes("-"); ch.TotalSickLeave = new TimeSpan(sickLeaveTimeSpans.Sum(x => x.Ticks)).ToFarsiDaysAndHoursAndMinutes("-");
var hourlyPaidLeaveTimeSpans = hourlyPaidLeave.Select(x => TimeOnly.Parse(x.LeaveHourses).ToTimeSpan()).ToList(); var hourlyPaidLeaveTimeSpans =
hourlyPaidLeave.Select(x => TimeOnly.Parse(x.LeaveHourses).ToTimeSpan()).ToList();
var dailyPaidLeaveTimeSpans = dailyPaidLeave.Select(x => var dailyPaidLeaveTimeSpans = dailyPaidLeave.Select(x =>
{ {
@@ -1497,20 +1489,19 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
ch.TotalHourlyLeave = new TimeSpan(hourlyPaidLeaveTimeSpans.Sum(x => x.Ticks)); ch.TotalHourlyLeave = new TimeSpan(hourlyPaidLeaveTimeSpans.Sum(x => x.Ticks));
ch.TotalPaidLeave = new TimeSpan(totalPaidLeaveTimeSpans.Sum(x => x.Ticks)).ToFarsiDaysAndHoursAndMinutes("-"); ch.TotalPaidLeave = new TimeSpan(totalPaidLeaveTimeSpans.Sum(x => x.Ticks)).ToFarsiDaysAndHoursAndMinutes("-");
#endregion #endregion
if (ch.TotalPaymentHide == false) if (ch.TotalPaymentHide == false)
{ {
ch.TotalClaims = ""; ch.TotalClaims = "";
ch.TotalDeductions = ""; ch.TotalDeductions = "";
ch.TotalPayment = ""; ch.TotalPayment = "";
} }
if (ch.HasRollCall) if (ch.HasRollCall)
ch.MonthlyRollCall = _rollCallRepository.GetEmployeeRollCallsForMonth(ch.EmployeeId, ch.WorkshopId, ch.ContractStartGr, ch.ContractEndGr); ch.MonthlyRollCall = _rollCallRepository.GetEmployeeRollCallsForMonth(ch.EmployeeId, ch.WorkshopId,
ch.ContractStartGr, ch.ContractEndGr);
else else
{ {
ch.CreateWorkingHoursTemp.ContractStartGr = ch.ContractStartGr; ch.CreateWorkingHoursTemp.ContractStartGr = ch.ContractStartGr;
@@ -1519,12 +1510,15 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
ch.CreateWorkingHoursTemp.ContractEnd = ch.ContractEndGr.ToFarsi(); ch.CreateWorkingHoursTemp.ContractEnd = ch.ContractEndGr.ToFarsi();
ch.CreateWorkingHoursTemp.EmployeeId = ch.EmployeeId; ch.CreateWorkingHoursTemp.EmployeeId = ch.EmployeeId;
ch.CreateWorkingHoursTemp.WorkshopId = ch.WorkshopId; ch.CreateWorkingHoursTemp.WorkshopId = ch.WorkshopId;
ch.MonthlyRollCall = ConvertStaticToRollCall(ch.CreateWorkingHoursTemp, workshopName.WorkshopHolidayWorking); ch.MonthlyRollCall =
ConvertStaticToRollCall(ch.CreateWorkingHoursTemp, workshopName.WorkshopHolidayWorking);
} }
return ch; return ch;
} }
private List<CheckoutDailyRollCallViewModel> ConvertStaticToRollCall(CreateWorkingHoursTemp workingHours, bool workshopHolidayWorking) private List<CheckoutDailyRollCallViewModel> ConvertStaticToRollCall(CreateWorkingHoursTemp workingHours,
bool workshopHolidayWorking)
{ {
var rollCalls = _rollCallMandatoryRepository.ConvertStaticHoursToRollCall(workingHours, var rollCalls = _rollCallMandatoryRepository.ConvertStaticHoursToRollCall(workingHours,
workshopHolidayWorking); workshopHolidayWorking);
@@ -1557,7 +1551,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
int dateRange = (int)(lastDayOfCurrentMonth - firstDayOfCurrentMonth).TotalDays + 1; int dateRange = (int)(lastDayOfCurrentMonth - firstDayOfCurrentMonth).TotalDays + 1;
var holidays = _context.HolidayItems.Where(x => x.HolidayYear.Contains(year.ToString())).Select(x => new HolidayItemViewModel var holidays = _context.HolidayItems.Where(x => x.HolidayYear.Contains(year.ToString())).Select(x =>
new HolidayItemViewModel
{ {
Id = x.id, Id = x.id,
Holidaydate = x.Holidaydate.ToFarsi(), Holidaydate = x.Holidaydate.ToFarsi(),
@@ -1570,7 +1565,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
holidays = []; holidays = [];
//all the dates from start to end, to be compared with present days to get absent dates //all the dates from start to end, to be compared with present days to get absent dates
var completeDaysList = Enumerable.Range(0, dateRange).Select(offset => startMonthDay.AddDays(offset).Date).ToList(); var completeDaysList = Enumerable.Range(0, dateRange).Select(offset => startMonthDay.AddDays(offset).Date)
.ToList();
var absentRecords = completeDaysList var absentRecords = completeDaysList
.ExceptBy(rollCalls.Select(x => x.ShiftDate.Date), y => y.Date) .ExceptBy(rollCalls.Select(x => x.ShiftDate.Date), y => y.Date)
@@ -1596,11 +1592,13 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
var presentDays = rollCalls.GroupBy(x => x.ShiftDate.Date).Select(x => var presentDays = rollCalls.GroupBy(x => x.ShiftDate.Date).Select(x =>
{ {
var orderedRollcalls = x.OrderBy(y => y.ShiftDate).ToList(); var orderedRollcalls = x.OrderBy(y => y.ShiftDate).ToList();
var rollCallTimeSpanPerDay = var rollCallTimeSpanPerDay =
new TimeSpan(x.Where(y => y.EndDate != null).Sum(y => y.ShiftEndWithoutRest == null ? (y.EndDate - y.StartDate).Value!.Ticks : (y.ShiftEndWithoutRest - y.StartDate)!.Value.Ticks)); new TimeSpan(x.Where(y => y.EndDate != null).Sum(y =>
y.ShiftEndWithoutRest == null
? (y.EndDate - y.StartDate).Value!.Ticks
: (y.ShiftEndWithoutRest - y.StartDate)!.Value.Ticks));
var breakTimePerDay = new TimeSpan(x.Sum(r => r.BreakTimeSpan.Ticks)); var breakTimePerDay = new TimeSpan(x.Sum(r => r.BreakTimeSpan.Ticks));
var firstRollCall = orderedRollcalls.FirstOrDefault(); var firstRollCall = orderedRollcalls.FirstOrDefault();
@@ -1614,7 +1612,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
secondRCEndDate = secondRollCall.ShiftEndWithoutRest ?? secondRollCall.EndDate; secondRCEndDate = secondRollCall.ShiftEndWithoutRest ?? secondRollCall.EndDate;
return new CheckoutDailyRollCallViewModel() return new CheckoutDailyRollCallViewModel()
{ {
StartDate1 = firstRollCall?.StartDate?.ToString("HH:mm") ?? "", StartDate1 = firstRollCall?.StartDate?.ToString("HH:mm") ?? "",
@@ -1686,12 +1683,15 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
{ {
return new List<CheckoutViewModel>(); return new List<CheckoutViewModel>();
} }
var employers = var employers =
_context.Employers.Where(x => x.ContractingPartyId == contracingPartyAcc.PersonalContractingPartyId).Select(x => x.id).ToList(); _context.Employers.Where(x => x.ContractingPartyId == contracingPartyAcc.PersonalContractingPartyId)
.Select(x => x.id).ToList();
if (employers.Count < 1) if (employers.Count < 1)
{ {
return new List<CheckoutViewModel>(); return new List<CheckoutViewModel>();
} }
var workshopIds = _context.WorkshopEmployers.Where(x => employers.Contains(x.EmployerId)) var workshopIds = _context.WorkshopEmployers.Where(x => employers.Contains(x.EmployerId))
.Select(x => x.WorkshopId).ToList(); .Select(x => x.WorkshopId).ToList();
var checkValid = workshopIds.Any(x => x == searchModel.WorkshopId); var checkValid = workshopIds.Any(x => x == searchModel.WorkshopId);
@@ -1699,7 +1699,9 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
{ {
return new List<CheckoutViewModel>(); return new List<CheckoutViewModel>();
} }
#endregion #endregion
var query = _context.CheckoutSet.Include(w => w.CheckoutWarningMessageList) var query = _context.CheckoutSet.Include(w => w.CheckoutWarningMessageList)
.AsSplitQuery().Select(x => new CheckoutViewModel() .AsSplitQuery().Select(x => new CheckoutViewModel()
{ {
@@ -1741,9 +1743,7 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
{ {
WarningMessage = wm.WarningMessage, WarningMessage = wm.WarningMessage,
TypeOfCheckoutWarning = wm.TypeOfCheckoutWarning, TypeOfCheckoutWarning = wm.TypeOfCheckoutWarning,
}).ToList() }).ToList()
}).Where(x => x.WorkshopId == searchModel.WorkshopId); }).Where(x => x.WorkshopId == searchModel.WorkshopId);
if (searchModel.EmployeeId > 0) if (searchModel.EmployeeId > 0)
{ {
@@ -1857,16 +1857,20 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
default: default:
y2 = $"{searchModel.Year}/{searchModel.Month}/29"; y2 = $"{searchModel.Year}/{searchModel.Month}/29";
break; break;
} }
} }
var start = y.ToGeorgianDateTime(); var start = y.ToGeorgianDateTime();
var end = y2.ToGeorgianDateTime(); var end = y2.ToGeorgianDateTime();
query = query.Where(x => x.ContractStartGr >= start && x.ContractStartGr < end && x.ContractEndGr > start && x.ContractEndGr <= end || query = query.Where(x =>
x.ContractStartGr <= start && x.ContractEndGr >= end || start <= x.ContractStartGr && end > x.ContractStartGr || end >= x.ContractEndGr && start < x.ContractEndGr); x.ContractStartGr >= start && x.ContractStartGr < end && x.ContractEndGr > start &&
x.ContractEndGr <= end ||
x.ContractStartGr <= start && x.ContractEndGr >= end ||
start <= x.ContractStartGr && end > x.ContractStartGr ||
end >= x.ContractEndGr && start < x.ContractEndGr);
} }
if (searchModel.SearchAll) if (searchModel.SearchAll)
return query.OrderByDescending(x => x.Id).ToList(); return query.OrderByDescending(x => x.Id).ToList();
switch (searchModel.Sorting) switch (searchModel.Sorting)
@@ -1897,8 +1901,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
break; break;
default: return query.OrderByDescending(x => x.Id).Skip(searchModel.PageIndex).Take(30).ToList(); default: return query.OrderByDescending(x => x.Id).Skip(searchModel.PageIndex).Take(30).ToList();
} }
} }
#endregion #endregion
@@ -1928,12 +1930,10 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
try try
{ {
var checkout = _context.CheckoutSet.Where(x => x.id == id)?.FirstOrDefault(); var checkout = _context.CheckoutSet.Where(x => x.id == id)?.FirstOrDefault();
_context.CheckoutSet.Remove(checkout); _context.CheckoutSet.Remove(checkout);
_context.SaveChanges(); _context.SaveChanges();
return op.Succcedded(-1, "حذف با موفقیت انجام شد."); return op.Succcedded(-1, "حذف با موفقیت انجام شد.");
} }
catch (Exception) catch (Exception)
{ {
@@ -1951,6 +1951,7 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
notRemoveList.Add(item); notRemoveList.Add(item);
} }
} }
return notRemoveList; return notRemoveList;
} }
@@ -2010,7 +2011,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
// ); // );
var checkouts = var checkouts =
_context.CheckoutSet.Include(w=>w.CheckoutWarningMessageList).Where(x => workshopAcounts.Contains(x.WorkshopId)) _context.CheckoutSet.Include(w => w.CheckoutWarningMessageList)
.Where(x => workshopAcounts.Contains(x.WorkshopId))
.Join(_context.Workshops.AsSplitQuery(), .Join(_context.Workshops.AsSplitQuery(),
ch => ch.WorkshopId, ch => ch.WorkshopId,
workshop => workshop.id, workshop => workshop.id,
@@ -2033,7 +2035,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
{ {
res.ch, res.ch,
res.workshop, res.workshop,
option = _context.EmployeeComputeOptionsSet.FirstOrDefault(x=>x.WorkshopId == res.ch.WorkshopId &&x.EmployeeId == res.ch.EmployeeId), option = _context.EmployeeComputeOptionsSet.FirstOrDefault(x =>
x.WorkshopId == res.ch.WorkshopId && x.EmployeeId == res.ch.EmployeeId),
res.workshopEmployer, res.workshopEmployer,
contractingParty = _context.PersonalContractingParties contractingParty = _context.PersonalContractingParties
.Include(p => p.Employers) .Include(p => p.Employers)
@@ -2050,15 +2053,18 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
hasEmployeeOrWorkshpSearch = true; hasEmployeeOrWorkshpSearch = true;
checkouts = checkouts.Where(x => x.ch.WorkshopId == searchModel.WorkshopId); checkouts = checkouts.Where(x => x.ch.WorkshopId == searchModel.WorkshopId);
} }
if (searchModel.EmployeeId != 0) if (searchModel.EmployeeId != 0)
{ {
hasEmployeeOrWorkshpSearch = true; hasEmployeeOrWorkshpSearch = true;
checkouts = checkouts.Where(x => x.ch.EmployeeId == searchModel.EmployeeId); checkouts = checkouts.Where(x => x.ch.EmployeeId == searchModel.EmployeeId);
} }
if (searchModel.EmployerId != 0) if (searchModel.EmployerId != 0)
{ {
hasEmployeeOrWorkshpSearch = true; hasEmployeeOrWorkshpSearch = true;
checkouts = checkouts.Where(x => x.contractingParty.Employers.Select(c => c.id).Contains(searchModel.EmployerId)); checkouts = checkouts.Where(x =>
x.contractingParty.Employers.Select(c => c.id).Contains(searchModel.EmployerId));
} }
if (!string.IsNullOrWhiteSpace(searchModel.ContractNo)) if (!string.IsNullOrWhiteSpace(searchModel.ContractNo))
@@ -2068,7 +2074,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
} }
//if (searchModel.IsActiveString == null) //if (searchModel.IsActiveString == null)
//{ //{
// checkouts = checkouts.Where(x => x.ch.IsActiveString == "true"); // checkouts = checkouts.Where(x => x.ch.IsActiveString == "true");
@@ -2100,7 +2105,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
checkouts = checkouts.Where(x => x.ch.ContractStart >= startyearGr && x.ch.ContractEnd <= endYearGr); checkouts = checkouts.Where(x => x.ch.ContractStart >= startyearGr && x.ch.ContractEnd <= endYearGr);
if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0) if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0)
checkouts = checkouts.OrderByDescending(x => x.ch.ContractEnd); checkouts = checkouts.OrderByDescending(x => x.ch.ContractEnd);
} }
else if (!string.IsNullOrWhiteSpace(searchModel.Year) && !string.IsNullOrWhiteSpace(searchModel.Month) && else if (!string.IsNullOrWhiteSpace(searchModel.Year) && !string.IsNullOrWhiteSpace(searchModel.Month) &&
string.IsNullOrWhiteSpace(searchModel.ContractStart) && string.IsNullOrWhiteSpace(searchModel.ContractStart) &&
@@ -2220,7 +2224,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
endDate >= x.ch.ContractEnd && startDate < x.ch.ContractEnd); endDate >= x.ch.ContractEnd && startDate < x.ch.ContractEnd);
//if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0) //if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0)
// checkouts = checkouts.OrderBy(x => x.ch.PersonnelCodeInt); // checkouts = checkouts.OrderBy(x => x.ch.PersonnelCodeInt);
} }
else if (!string.IsNullOrWhiteSpace(searchModel.ContractStart) && else if (!string.IsNullOrWhiteSpace(searchModel.ContractStart) &&
!string.IsNullOrWhiteSpace(searchModel.ContractEnd) && !string.IsNullOrWhiteSpace(searchModel.ContractEnd) &&
@@ -2240,22 +2243,20 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
if (!string.IsNullOrEmpty(searchModel.EmployeeName)) if (!string.IsNullOrEmpty(searchModel.EmployeeName))
{ {
hasSearch = true; hasSearch = true;
var employeeList = _context.Employees.Where(x => (!string.IsNullOrEmpty(x.FName) && x.FName.StartsWith(searchModel.EmployeeName)) || var employeeList = _context.Employees.Where(x =>
(!string.IsNullOrEmpty(x.LName) && x.LName.StartsWith(searchModel.EmployeeName))).Select(x => x.id).ToList(); (!string.IsNullOrEmpty(x.FName) && x.FName.StartsWith(searchModel.EmployeeName)) ||
(!string.IsNullOrEmpty(x.LName) && x.LName.StartsWith(searchModel.EmployeeName))).Select(x => x.id)
.ToList();
checkouts = checkouts.Where(x => employeeList.Contains(x.ch.EmployeeId)); checkouts = checkouts.Where(x => employeeList.Contains(x.ch.EmployeeId));
} }
#endregion #endregion
if (!hasSearch && !hasEmployeeOrWorkshpSearch) if (!hasSearch && !hasEmployeeOrWorkshpSearch)
{ {
return checkouts.Select(x => new CheckoutViewModel() return checkouts.Select(x => new CheckoutViewModel()
{ {
Id = x.ch.id, Id = x.ch.id,
EmployeeFullName = x.ch.EmployeeFullName, EmployeeFullName = x.ch.EmployeeFullName,
ContractStart = x.ch.ContractStart.ToFarsi(), ContractStart = x.ch.ContractStart.ToFarsi(),
@@ -2281,26 +2282,21 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
IsBlockCantracingParty = x.contractingParty.IsBlock, IsBlockCantracingParty = x.contractingParty.IsBlock,
HasSignCheckout = x.option != null ? x.option.SignCheckout : x.workshop.SignCheckout, HasSignCheckout = x.option != null ? x.option.SignCheckout : x.workshop.SignCheckout,
IsUpdateNeeded = x.ch.IsUpdateNeeded, IsUpdateNeeded = x.ch.IsUpdateNeeded,
CheckoutWarningMessageList = x.ch.CheckoutWarningMessageList.Select(wm=> new CheckoutWarningMessageModel CheckoutWarningMessageList = x.ch.CheckoutWarningMessageList.Select(wm =>
new CheckoutWarningMessageModel
{ {
WarningMessage = wm.WarningMessage, WarningMessage = wm.WarningMessage,
TypeOfCheckoutWarning = wm.TypeOfCheckoutWarning, TypeOfCheckoutWarning = wm.TypeOfCheckoutWarning,
}).ToList() }).ToList()
}).OrderByDescending(x => x.Id).Take(3000).ToList().DistinctBy(x => x.Id) }).OrderByDescending(x => x.Id).Take(3000).ToList().DistinctBy(x => x.Id)
.OrderByDescending(x => x.Id).ThenByDescending(x => x.Year) .OrderByDescending(x => x.Id).ThenByDescending(x => x.Year)
.ThenBy(x => x.PersonnelCodeInt) .ThenBy(x => x.PersonnelCodeInt)
.Take(50).ToList(); .Take(50).ToList();
} }
else if (hasSearch && !hasEmployeeOrWorkshpSearch) else if (hasSearch && !hasEmployeeOrWorkshpSearch)
{ {
var result = checkouts.Select(x => new CheckoutViewModel() var result = checkouts.Select(x => new CheckoutViewModel()
{ {
Id = x.ch.id, Id = x.ch.id,
EmployeeFullName = x.ch.EmployeeFullName, EmployeeFullName = x.ch.EmployeeFullName,
ContractStart = x.ch.ContractStart.ToFarsi(), ContractStart = x.ch.ContractStart.ToFarsi(),
@@ -2326,19 +2322,17 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
IsBlockCantracingParty = x.contractingParty.IsBlock, IsBlockCantracingParty = x.contractingParty.IsBlock,
HasSignCheckout = x.option != null ? x.option.SignCheckout : x.workshop.SignCheckout, HasSignCheckout = x.option != null ? x.option.SignCheckout : x.workshop.SignCheckout,
IsUpdateNeeded = x.ch.IsUpdateNeeded, IsUpdateNeeded = x.ch.IsUpdateNeeded,
CheckoutWarningMessageList = x.ch.CheckoutWarningMessageList.Select(wm => new CheckoutWarningMessageModel CheckoutWarningMessageList = x.ch.CheckoutWarningMessageList.Select(wm =>
new CheckoutWarningMessageModel
{ {
WarningMessage = wm.WarningMessage, WarningMessage = wm.WarningMessage,
TypeOfCheckoutWarning = wm.TypeOfCheckoutWarning, TypeOfCheckoutWarning = wm.TypeOfCheckoutWarning,
}).ToList() }).ToList()
}).OrderByDescending(x => x.Id) }).OrderByDescending(x => x.Id)
.GroupBy(x => x.Id) .GroupBy(x => x.Id)
.Select(x => x.First()); .Select(x => x.First());
if ((string.IsNullOrWhiteSpace(searchModel.ContractStart) || if ((string.IsNullOrWhiteSpace(searchModel.ContractStart) ||
string.IsNullOrWhiteSpace(searchModel.ContractEnd)) && string.IsNullOrWhiteSpace(searchModel.Month)) string.IsNullOrWhiteSpace(searchModel.ContractEnd)) && string.IsNullOrWhiteSpace(searchModel.Month))
{ {
@@ -2348,7 +2342,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
.OrderByDescending(x => x.ContractStartGr).ToList(); .OrderByDescending(x => x.ContractStartGr).ToList();
} }
else if ((string.IsNullOrWhiteSpace(searchModel.ContractStart) || else if ((string.IsNullOrWhiteSpace(searchModel.ContractStart) ||
string.IsNullOrWhiteSpace(searchModel.ContractEnd)) && !string.IsNullOrWhiteSpace(searchModel.Month) && !string.IsNullOrWhiteSpace(searchModel.Year)) string.IsNullOrWhiteSpace(searchModel.ContractEnd)) &&
!string.IsNullOrWhiteSpace(searchModel.Month) && !string.IsNullOrWhiteSpace(searchModel.Year))
{ {
//اگر فقط سال و ماه رو سرچ کرد //اگر فقط سال و ماه رو سرچ کرد
return result.Take(300) return result.Take(300)
@@ -2366,14 +2361,11 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
return result.ToList().OrderByDescending(x => x.Id) return result.ToList().OrderByDescending(x => x.Id)
.ThenByDescending(x => x.Year).ThenBy(x => x.PersonnelCodeInt).ToList(); .ThenByDescending(x => x.Year).ThenBy(x => x.PersonnelCodeInt).ToList();
} }
else if (hasEmployeeOrWorkshpSearch && !hasSearch) else if (hasEmployeeOrWorkshpSearch && !hasSearch)
{ {
return checkouts.Select(x => new CheckoutViewModel() return checkouts.Select(x => new CheckoutViewModel()
{ {
Id = x.ch.id, Id = x.ch.id,
EmployeeFullName = x.ch.EmployeeFullName, EmployeeFullName = x.ch.EmployeeFullName,
ContractStart = x.ch.ContractStart.ToFarsi(), ContractStart = x.ch.ContractStart.ToFarsi(),
@@ -2399,13 +2391,12 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
IsBlockCantracingParty = x.contractingParty.IsBlock, IsBlockCantracingParty = x.contractingParty.IsBlock,
HasSignCheckout = x.option != null ? x.option.SignCheckout : x.workshop.SignCheckout, HasSignCheckout = x.option != null ? x.option.SignCheckout : x.workshop.SignCheckout,
IsUpdateNeeded = x.ch.IsUpdateNeeded, IsUpdateNeeded = x.ch.IsUpdateNeeded,
CheckoutWarningMessageList = x.ch.CheckoutWarningMessageList.Select(wm => new CheckoutWarningMessageModel CheckoutWarningMessageList = x.ch.CheckoutWarningMessageList.Select(wm =>
new CheckoutWarningMessageModel
{ {
WarningMessage = wm.WarningMessage, WarningMessage = wm.WarningMessage,
TypeOfCheckoutWarning = wm.TypeOfCheckoutWarning, TypeOfCheckoutWarning = wm.TypeOfCheckoutWarning,
}).ToList() }).ToList()
}).GroupBy(x => x.Id).Select(x => x.First()).ToList() }).GroupBy(x => x.Id).Select(x => x.First()).ToList()
.OrderByDescending(x => x.ContractStartGr).ToList(); .OrderByDescending(x => x.ContractStartGr).ToList();
} }
@@ -2413,7 +2404,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
{ {
return checkouts.Select(x => new CheckoutViewModel() return checkouts.Select(x => new CheckoutViewModel()
{ {
Id = x.ch.id, Id = x.ch.id,
EmployeeFullName = x.ch.EmployeeFullName, EmployeeFullName = x.ch.EmployeeFullName,
ContractStart = x.ch.ContractStart.ToFarsi(), ContractStart = x.ch.ContractStart.ToFarsi(),
@@ -2439,26 +2429,21 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
IsBlockCantracingParty = x.contractingParty.IsBlock, IsBlockCantracingParty = x.contractingParty.IsBlock,
HasSignCheckout = x.option != null ? x.option.SignCheckout : x.workshop.SignCheckout, HasSignCheckout = x.option != null ? x.option.SignCheckout : x.workshop.SignCheckout,
IsUpdateNeeded = x.ch.IsUpdateNeeded, IsUpdateNeeded = x.ch.IsUpdateNeeded,
CheckoutWarningMessageList = x.ch.CheckoutWarningMessageList.Select(wm => new CheckoutWarningMessageModel CheckoutWarningMessageList = x.ch.CheckoutWarningMessageList.Select(wm =>
new CheckoutWarningMessageModel
{ {
WarningMessage = wm.WarningMessage, WarningMessage = wm.WarningMessage,
TypeOfCheckoutWarning = wm.TypeOfCheckoutWarning, TypeOfCheckoutWarning = wm.TypeOfCheckoutWarning,
}).ToList() }).ToList()
}).GroupBy(x => x.Id) }).GroupBy(x => x.Id)
.Select(x => x.First()).ToList() .Select(x => x.First()).ToList()
.OrderByDescending(x => x.Id) .OrderByDescending(x => x.Id)
.ThenByDescending(x => x.Year).ThenBy(x => x.PersonnelCodeInt).ToList(); .ThenByDescending(x => x.Year).ThenBy(x => x.PersonnelCodeInt).ToList();
}
} }
}
public async Task<List<CheckoutViewModel>> SearchForMainCheckout(CheckoutSearchModel searchModel) public async Task<List<CheckoutViewModel>> SearchForMainCheckout(CheckoutSearchModel searchModel)
{ {
bool hasSearch = false; bool hasSearch = false;
bool hasEmployeeOrWorkshpSearch = false; bool hasEmployeeOrWorkshpSearch = false;
//List<CheckoutViewModel> query = null; //List<CheckoutViewModel> query = null;
@@ -2476,11 +2461,8 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
.Select(x => x.WorkshopId).ToList(); .Select(x => x.WorkshopId).ToList();
var query = _context.CheckoutSet.Select(x => new CheckoutViewModel() var query = _context.CheckoutSet.Select(x => new CheckoutViewModel()
{ {
Id = x.id, Id = x.id,
EmployeeFullName = x.EmployeeFullName, EmployeeFullName = x.EmployeeFullName,
//var start = ; //var start = ;
@@ -2503,13 +2485,9 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
IsActiveString = x.IsActiveString, IsActiveString = x.IsActiveString,
Signature = x.Signature, Signature = x.Signature,
CreationDate = x.CreationDate, CreationDate = x.CreationDate,
}); });
if (!string.IsNullOrWhiteSpace(searchModel.ContractNo) && searchModel.ContractId != 0) if (!string.IsNullOrWhiteSpace(searchModel.ContractNo) && searchModel.ContractId != 0)
query = query.Where(x => query = query.Where(x =>
x.ContractNo == searchModel.ContractNo && x.ContractId == searchModel.ContractId); x.ContractNo == searchModel.ContractNo && x.ContractId == searchModel.ContractId);
@@ -2518,11 +2496,13 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
hasEmployeeOrWorkshpSearch = true; hasEmployeeOrWorkshpSearch = true;
query = query.Where(x => x.WorkshopId == searchModel.WorkshopId); query = query.Where(x => x.WorkshopId == searchModel.WorkshopId);
} }
if (searchModel.EmployeeId != 0) if (searchModel.EmployeeId != 0)
{ {
hasEmployeeOrWorkshpSearch = true; hasEmployeeOrWorkshpSearch = true;
query = query.Where(x => x.EmployeeId == searchModel.EmployeeId); query = query.Where(x => x.EmployeeId == searchModel.EmployeeId);
} }
if (searchModel.EmployerId != 0) if (searchModel.EmployerId != 0)
{ {
hasEmployeeOrWorkshpSearch = true; hasEmployeeOrWorkshpSearch = true;
@@ -2566,7 +2546,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
query = query.Where(x => x.ContractStartGr >= startyearGr && x.ContractEndGr <= endYearGr); query = query.Where(x => x.ContractStartGr >= startyearGr && x.ContractEndGr <= endYearGr);
if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0) if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0)
query = query.OrderByDescending(x => x.ContractEndGr).ThenBy(x => x.PersonnelCodeInt); query = query.OrderByDescending(x => x.ContractEndGr).ThenBy(x => x.PersonnelCodeInt);
} }
else if (!string.IsNullOrWhiteSpace(searchModel.Year) && !string.IsNullOrWhiteSpace(searchModel.Month) && else if (!string.IsNullOrWhiteSpace(searchModel.Year) && !string.IsNullOrWhiteSpace(searchModel.Month) &&
string.IsNullOrWhiteSpace(searchModel.ContractStart) && string.IsNullOrWhiteSpace(searchModel.ContractStart) &&
@@ -2686,7 +2665,6 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
endDate >= x.ContractEndGr && startDate < x.ContractEndGr); endDate >= x.ContractEndGr && startDate < x.ContractEndGr);
if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0) if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0)
query = query.OrderBy(x => x.PersonnelCodeInt); query = query.OrderBy(x => x.PersonnelCodeInt);
} }
else if (!string.IsNullOrWhiteSpace(searchModel.ContractStart) && else if (!string.IsNullOrWhiteSpace(searchModel.ContractStart) &&
!string.IsNullOrWhiteSpace(searchModel.ContractEnd) && !string.IsNullOrWhiteSpace(searchModel.ContractEnd) &&
@@ -2701,16 +2679,18 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0) if (searchModel.WorkshopId > 0 || searchModel.EmployeeId > 0 || searchModel.EmployerId > 0)
query = query.OrderByDescending(x => x.ContractEndGr).ThenBy(x => x.PersonnelCodeInt); query = query.OrderByDescending(x => x.ContractEndGr).ThenBy(x => x.PersonnelCodeInt);
} }
if (!string.IsNullOrEmpty(searchModel.EmployeeName)) if (!string.IsNullOrEmpty(searchModel.EmployeeName))
{ {
hasSearch = true; hasSearch = true;
var employeeList = _context.Employees.Where(x => (!string.IsNullOrEmpty(x.FName) && x.FName.StartsWith(searchModel.EmployeeName)) || var employeeList = _context.Employees.Where(x =>
(!string.IsNullOrEmpty(x.LName) && x.LName.StartsWith(searchModel.EmployeeName))).Select(x => x.id).ToList(); (!string.IsNullOrEmpty(x.FName) && x.FName.StartsWith(searchModel.EmployeeName)) ||
(!string.IsNullOrEmpty(x.LName) && x.LName.StartsWith(searchModel.EmployeeName))).Select(x => x.id)
.ToList();
query = query.Where(x => employeeList.Contains(x.EmployeeId)); query = query.Where(x => employeeList.Contains(x.EmployeeId));
} }
if (hasSearch) if (hasSearch)
return query.OrderByDescending(x => x.Id) return query.OrderByDescending(x => x.Id)
.ThenByDescending(x => x.Year).ThenBy(x => x.PersonnelCodeInt).ToList(); .ThenByDescending(x => x.Year).ThenBy(x => x.PersonnelCodeInt).ToList();
@@ -2721,22 +2701,17 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
.ThenByDescending(x => x.Year).ThenBy(x => x.PersonnelCodeInt).Take(50).ToList(); .ThenByDescending(x => x.Year).ThenBy(x => x.PersonnelCodeInt).Take(50).ToList();
// Console.WriteLine("return" + watch.Elapsed); // Console.WriteLine("return" + watch.Elapsed);
} }
#endregion #endregion
#region Pooya #region Pooya
public List<(long EmployeeId, DateTime CheckoutStart, DateTime CheckoutEnd)>
public List<(long EmployeeId, DateTime CheckoutStart, DateTime CheckoutEnd)> GetLastCheckoutsByWorkshopIdForWorkFlow(long workshopId, DateTime start, DateTime end) GetLastCheckoutsByWorkshopIdForWorkFlow(long workshopId, DateTime start, DateTime end)
{ {
return _context.CheckoutSet.AsSplitQuery().Where(x =>
x.ContractEnd.Date >= start && x.ContractStart.Date <= end && x.WorkshopId == workshopId).Select(x => new
return _context.CheckoutSet.AsSplitQuery().Where(x => x.ContractEnd.Date >= start && x.ContractStart.Date <= end && x.WorkshopId == workshopId).Select(x => new
{ {
EmployeeId = x.EmployeeId, EmployeeId = x.EmployeeId,
CheckoutEnd = x.ContractEnd, CheckoutEnd = x.ContractEnd,
@@ -2751,5 +2726,88 @@ public class CheckoutRepository : RepositoryBase<long, Checkout>, ICheckoutRepos
x.ContractEnd >= inDate); x.ContractEnd >= inDate);
} }
public async Task<PagedResult<CheckoutListClientDto>> GetListForClient(long workshopId,
CheckoutListClientSearchModel searchModel)
{
var query = _context.CheckoutSet.Where(x => x.WorkshopId == workshopId);
if (searchModel.EmployeeId is > 0)
{
query = query.Where(x => x.EmployeeId == searchModel.EmployeeId);
}
if (!string.IsNullOrWhiteSpace(searchModel.StartDate) && !string.IsNullOrWhiteSpace(searchModel.EndDate))
{
if (!searchModel.StartDate.TryToGeorgianDateTime(out var startDateGr))
throw new BadRequestException("تاریخ شروع جستجو نامعتبر است");
if (!searchModel.EndDate.TryToGeorgianDateTime(out var endDateGr))
{
throw new BadRequestException("تاریخ پایان جستجو نامعتبر است");
}
query = query.Where(x => x.ContractStart <= endDateGr && x.ContractEnd >= startDateGr);
}
if (!string.IsNullOrWhiteSpace(searchModel.Year))
{
query = query.Where(x => x.Year == searchModel.Year);
}
if (!string.IsNullOrWhiteSpace(searchModel.Month))
{
var searchedMonth = Convert.ToInt32(searchModel.Month).ToFarsiMonthByIntNumber();
query = query.Where(x => x.Month == searchedMonth);
}
query = searchModel.OrderType switch
{
CheckoutClientListOrderType.ByCheckoutCreationDate =>
query.OrderBy(x => x.CreationDate),
CheckoutClientListOrderType.ByCheckoutStartDate =>
query.OrderBy(x => x.ContractStart),
CheckoutClientListOrderType.ByCheckoutStartDateDescending =>
query.OrderByDescending(x => x.ContractStart),
CheckoutClientListOrderType.ByPersonnelCode =>
query.OrderBy(x => x.PersonnelCode),
CheckoutClientListOrderType.ByPersonnelCodeDescending =>
query.OrderByDescending(x => x.PersonnelCode),
CheckoutClientListOrderType.BySignedCheckout =>
query.OrderByDescending(x => x.Signature == "1"),
CheckoutClientListOrderType.ByUnSignedCheckout =>
query.OrderBy(x => x.Signature == "1"),
_ => query.OrderByDescending(x => x.id)
};
var list =await query.ApplyPagination(searchModel.PageIndex, searchModel.PageSize).ToListAsync();
var resList = list.Select(x => new CheckoutListClientDto()
{
Id = x.id,
ContractStart = x.ContractStart.ToFarsi(),
ContractEnd = x.ContractEnd.ToFarsi(),
Year = x.Year,
Month = x.Month,
ContractNo = x.ContractNo,
EmployeeName = x.EmployeeFullName,
Signature = x.Signature == "1"
}).ToList();
var res = new PagedResult<CheckoutListClientDto>
{
TotalCount = await query.CountAsync(),
List = resList
};
return res;
}
#endregion #endregion
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,16 @@
using System.Collections.Generic; using _0_Framework.Application;
using System.Linq;
using _0_Framework.Application;
using _0_Framework.InfraStructure; using _0_Framework.InfraStructure;
using Company.Domain.SmsResultAgg; using Company.Domain.SmsResultAgg;
using CompanyManagment.App.Contracts.SmsResult; using CompanyManagment.App.Contracts.SmsResult;
using CompanyManagment.App.Contracts.SmsResult.Dto;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using _0_Framework.Application.Enums;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace CompanyManagment.EFCore.Repository; namespace CompanyManagment.EFCore.Repository;
@@ -15,6 +22,260 @@ public class SmsResultRepository : RepositoryBase<long, SmsResult> , ISmsResultR
_context = context; _context = context;
} }
#region ForApi
public async Task<List<SmsReportDto>> GetSmsReportList(SmsReportSearchModel searchModel)
{
// مرحله 1: همه رکوردها را با projection ساده بگیرید
var rawQuery = await _context.SmsResults
.Select(x => new
{
x.id,
x.ContractingPatyId,
x.Mobile,
x.Status,
x.TypeOfSms,
x.CreationDate,
DateOnly = x.CreationDate.Date // فقط تاریخ بدون ساعت
})
.AsNoTracking()
.ToListAsync(); // اینجا SQL اجرا می‌شود و همه داده‌ها به client می‌آیند
if (searchModel.ContractingPatyId > 0)
{
rawQuery = rawQuery.Where(x => x.ContractingPatyId == searchModel.ContractingPatyId).ToList();
}
if (!string.IsNullOrWhiteSpace(searchModel.Mobile))
{
rawQuery = rawQuery.Where(x => x.Mobile.Contains(searchModel.Mobile)).ToList();
}
if (searchModel.TypeOfSms != TypeOfSmsSetting.All && searchModel.TypeOfSms != TypeOfSmsSetting.Warning)
{
var typeOfSms = "All";
switch (searchModel.TypeOfSms)
{
case TypeOfSmsSetting.InstitutionContractDebtReminder:
typeOfSms = "یادآور بدهی ماهانه";
break;
case TypeOfSmsSetting.MonthlyInstitutionContract:
typeOfSms = "صورت حساب ماهانه";
break;
case TypeOfSmsSetting.BlockContractingParty:
typeOfSms = "اعلام مسدودی طرف حساب";
break;
case TypeOfSmsSetting.LegalAction:
typeOfSms = "اقدام قضایی";
break;
case TypeOfSmsSetting.InstitutionContractConfirm:
typeOfSms = "یادآور تایید قرارداد مالی";
break;
case TypeOfSmsSetting.SendInstitutionContractConfirmationCode:
typeOfSms = "کد تاییدیه قرارداد مالی";
break;
case TypeOfSmsSetting.TaskReminder:
typeOfSms = "یادآور وظایف";
break;
}
rawQuery = rawQuery.Where(x => x.TypeOfSms == typeOfSms).ToList();
}
if (searchModel.TypeOfSms == TypeOfSmsSetting.Warning)
{
rawQuery = rawQuery.Where(x => x.TypeOfSms.Contains("هشدار")).ToList();
}
if (searchModel.SendStatus != SendStatus.All)
{
var status = "All";
switch (searchModel.SendStatus)
{
case SendStatus.Success: status = "موفق";
break;
case SendStatus.Failed: status = "ناموفق";
break;
}
rawQuery = rawQuery.Where(x => x.Status == status).ToList();
}
#region searchByDate
if (!string.IsNullOrWhiteSpace(searchModel.StartDateFa) &&
!string.IsNullOrWhiteSpace(searchModel.EndDateFa))
{
if (searchModel.StartDateFa.TryToGeorgianDateTime(out var startGr) == false ||
searchModel.EndDateFa.TryToGeorgianDateTime(out var endGr) == false)
return new List<SmsReportDto>();
rawQuery = rawQuery.Where(x => x.CreationDate.Date >= startGr.Date && x.CreationDate.Date <= endGr.Date).ToList();
}
else
{
if (!string.IsNullOrWhiteSpace(searchModel.Year) && !string.IsNullOrWhiteSpace(searchModel.Month))
{
var start = searchModel.Year + "/" + searchModel.Month + "/01";
var end = start.FindeEndOfMonth();
var startGr = start.ToGeorgianDateTime();
var endGr = end.ToGeorgianDateTime();
rawQuery = rawQuery.Where(x => x.CreationDate.Date >= startGr.Date && x.CreationDate.Date <= endGr.Date).ToList();
}
else if (!string.IsNullOrWhiteSpace(searchModel.Year) && string.IsNullOrWhiteSpace(searchModel.Month))
{
var start = searchModel.Year + "/01/01";
var findEndOfYear = searchModel.Year + "/12/01";
var end = findEndOfYear.FindeEndOfMonth();
var startGr = start.ToGeorgianDateTime();
var endGr = end.ToGeorgianDateTime();
rawQuery = rawQuery.Where(x => x.CreationDate.Date >= startGr.Date && x.CreationDate.Date <= endGr.Date).ToList();
}
}
#endregion
// مرحله 2: گروه‌بندی و انتخاب آخرین رکورد هر روز روی Client
var grouped = rawQuery
.GroupBy(x => x.DateOnly)
.Select(g => g.OrderByDescending(x => x.CreationDate).First())
.OrderByDescending(x => x.CreationDate)
.ToList();
// مرحله 3: تبدیل به DTO و ToFarsi
var result = grouped.Select(x => new SmsReportDto
{
SentDate = x.CreationDate.ToFarsi()
}).ToList();
return result;
}
public async Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date)
{
if(string.IsNullOrWhiteSpace(date))
return new List<SmsReportListDto>();
if (date.TryToGeorgianDateTime(out var searchDate) == false)
return new List<SmsReportListDto>();
var query = await _context.SmsResults.Where(x => x.CreationDate.Date == searchDate.Date)
.Select(x =>
new
{
x.id,
x.MessageId,
x.Status,
x.TypeOfSms,
x.ContractingPartyName,
x.Mobile,
x.ContractingPatyId,
x.InstitutionContractId,
x.CreationDate,
x.CreationDate.Hour,
x.CreationDate.Minute
}).AsNoTracking()
.ToListAsync(); ;
if (searchModel.ContractingPatyId > 0)
{
query = query.Where(x => x.ContractingPatyId == searchModel.ContractingPatyId).ToList();
}
if (!string.IsNullOrWhiteSpace(searchModel.Mobile))
{
query = query.Where(x => x.Mobile.Contains(searchModel.Mobile)).ToList();
}
if (searchModel.TypeOfSms != TypeOfSmsSetting.All && searchModel.TypeOfSms != TypeOfSmsSetting.Warning)
{
var typeOfSms = "All";
switch (searchModel.TypeOfSms)
{
case TypeOfSmsSetting.InstitutionContractDebtReminder:
typeOfSms = "یادآور بدهی ماهانه";
break;
case TypeOfSmsSetting.MonthlyInstitutionContract:
typeOfSms = "صورت حساب ماهانه";
break;
case TypeOfSmsSetting.BlockContractingParty:
typeOfSms = "اعلام مسدودی طرف حساب";
break;
case TypeOfSmsSetting.LegalAction:
typeOfSms = "اقدام قضایی";
break;
case TypeOfSmsSetting.InstitutionContractConfirm:
typeOfSms = "یادآور تایید قرارداد مالی";
break;
case TypeOfSmsSetting.SendInstitutionContractConfirmationCode:
typeOfSms = "کد تاییدیه قرارداد مالی";
break;
case TypeOfSmsSetting.TaskReminder:
typeOfSms = "یادآور وظایف";
break;
}
query = query.Where(x => x.TypeOfSms == typeOfSms).ToList();
}
if (searchModel.TypeOfSms == TypeOfSmsSetting.Warning)
{
query = query.Where(x => x.TypeOfSms.Contains("هشدار")).ToList();
}
if (searchModel.SendStatus != SendStatus.All)
{
var status = "All";
switch (searchModel.SendStatus)
{
case SendStatus.Success:
status = "موفق";
break;
case SendStatus.Failed:
status = "ناموفق";
break;
}
query = query.Where(x => x.Status == status).ToList();
}
if (query.Count == 0)
return new List<SmsReportListDto>();
var result = query.OrderByDescending(x => x.CreationDate.Hour)
.ThenByDescending(x => x.CreationDate.Minute).Select(x =>
new SmsReportListDto()
{
Id = x.id,
MessageId = x.MessageId,
Status = x.Status,
TypeOfSms = x.TypeOfSms,
ContractingPartyName = x.ContractingPartyName,
Mobile = x.Mobile,
HourAndMinute = x.CreationDate.TimeOfDay.ToString(@"hh\:mm"),
}).ToList();
return result;
}
#endregion
public List<App.Contracts.SmsResult.SmsResultViewModel> Search(SmsResultSearchModel searchModel) public List<App.Contracts.SmsResult.SmsResultViewModel> Search(SmsResultSearchModel searchModel)
{ {

View File

@@ -207,16 +207,11 @@ public class SmsService : ISmsService
} }
public async Task<List<ApiResultViewModel>> GetApiResult(string startDate, string endDate) public async Task<List<ApiResultViewModel>> GetApiResult(string startDate, string endDate)
{ {
var st = new DateTime(2024, 6, 2);
var ed = new DateTime(2024, 7, 1);
if (!string.IsNullOrWhiteSpace(startDate) && startDate.Length == 10) if(startDate.TryToGeorgianDateTime(out var st) == false || endDate.TryToGeorgianDateTime(out var ed) == false)
{ return new List<ApiResultViewModel>();
st = startDate.ToGeorgianDateTime();
}
if (!string.IsNullOrWhiteSpace(endDate) && endDate.Length == 10)
{
ed = endDate.ToGeorgianDateTime();
}
var res = new List<ApiResultViewModel>(); var res = new List<ApiResultViewModel>();
Int32 unixTimestamp = (int)st.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; Int32 unixTimestamp = (int)st.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
Int32 unixTimestamp2 = (int)ed.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; Int32 unixTimestamp2 = (int)ed.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
@@ -248,6 +243,44 @@ public class SmsService : ISmsService
return res; return res;
} }
public async Task<List<ApiReportDto>> GetApiReport(string startDate, string endDate)
{
if (startDate.TryToGeorgianDateTime(out var st) == false || endDate.TryToGeorgianDateTime(out var ed) == false)
return new List<ApiReportDto>();
var res = new List<ApiReportDto>();
Int32 unixTimestamp = (int)st.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
Int32 unixTimestamp2 = (int)ed.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
// int? fromDateUnixTime = null; // unix time - for instance: 1700598600
//int? toDateUnixTime = null; // unix time - for instance: 1703190600
int pageNumber = 2;
int pageSize = 100; // max: 100
SmsIr smsIr = new SmsIr("Og5M562igmzJRhQPnq0GdtieYdLgtfikjzxOmeQBPxJjZtyge5Klc046Lfw1mxSa");
var response = await smsIr.GetArchivedReportAsync(pageNumber, pageSize, unixTimestamp, unixTimestamp2);
MessageReportResult[] messages = response.Data;
foreach (var message in messages)
{
var appendData = new ApiReportDto()
{
MessageId = message.MessageId,
Mobile = message.Mobile,
SendUnixTime = UnixTimeStampToDateTime(message.SendDateTime),
DeliveryState = DeliveryStatus(message.DeliveryState),
DeliveryUnixTime = UnixTimeStampToDateTime(message.DeliveryDateTime),
DeliveryColor = DeliveryColorStatus(message.DeliveryState),
};
res.Add(appendData);
}
return res;
}
public string DeliveryStatus(byte? dv) public string DeliveryStatus(byte? dv)
{ {
string mess = ""; string mess = "";

View File

@@ -89,6 +89,9 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BackgroundInstitutionContract.Task", "BackgroundInstitutionContract\BackgroundInstitutionContract.Task\BackgroundInstitutionContract.Task.csproj", "{F78FBB92-294B-88BA-168D-F0C578B0D7D6}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BackgroundInstitutionContract.Task", "BackgroundInstitutionContract\BackgroundInstitutionContract.Task\BackgroundInstitutionContract.Task.csproj", "{F78FBB92-294B-88BA-168D-F0C578B0D7D6}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ProgramManager", "ProgramManager", "{67AFF7B6-4C4F-464C-A90D-9BDB644D83A9}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ProgramManager", "ProgramManager", "{67AFF7B6-4C4F-464C-A90D-9BDB644D83A9}"
ProjectSection(SolutionItems) = preProject
ProgramManager\appsettings.FileStorage.json = ProgramManager\appsettings.FileStorage.json
EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{48F6F6A5-7340-42F8-9216-BEB7A4B7D5A1}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{48F6F6A5-7340-42F8-9216-BEB7A4B7D5A1}"
EndProject EndProject

View File

@@ -61,6 +61,7 @@ using Company.Domain.HolidayItemAgg;
using Company.Domain.InstitutionContractAgg; using Company.Domain.InstitutionContractAgg;
using Company.Domain.InstitutionContractContactInfoAgg; using Company.Domain.InstitutionContractContactInfoAgg;
using Company.Domain.InstitutionContractExtensionTempAgg; using Company.Domain.InstitutionContractExtensionTempAgg;
using Company.Domain.InstitutionContractSendFlagAgg;
using Company.Domain.InstitutionPlanAgg; using Company.Domain.InstitutionPlanAgg;
using Company.Domain.InsuranceAgg; using Company.Domain.InsuranceAgg;
using Company.Domain.InsuranceEmployeeInfoAgg; using Company.Domain.InsuranceEmployeeInfoAgg;
@@ -123,6 +124,7 @@ using Company.Domain.ZoneAgg;
using CompanyManagement.Infrastructure.Excel.SalaryAid; using CompanyManagement.Infrastructure.Excel.SalaryAid;
using CompanyManagement.Infrastructure.Mongo.EmployeeFaceEmbeddingRepo; using CompanyManagement.Infrastructure.Mongo.EmployeeFaceEmbeddingRepo;
using CompanyManagement.Infrastructure.Mongo.InstitutionContractInsertTempRepo; using CompanyManagement.Infrastructure.Mongo.InstitutionContractInsertTempRepo;
using CompanyManagement.Infrastructure.Mongo.InstitutionContractSendFlagRepo;
using CompanyManagment.App.Contracts.AdminMonthlyOverview; using CompanyManagment.App.Contracts.AdminMonthlyOverview;
using CompanyManagment.App.Contracts.AndroidApkVersion; using CompanyManagment.App.Contracts.AndroidApkVersion;
using CompanyManagment.App.Contracts.AuthorizedPerson; using CompanyManagment.App.Contracts.AuthorizedPerson;
@@ -561,6 +563,7 @@ public class PersonalBootstrapper
services.AddTransient<ISmsSettingsRepository, SmsSettingsRepository>(); services.AddTransient<ISmsSettingsRepository, SmsSettingsRepository>();
services.AddTransient<ISmsSettingApplication, SmsSettingApplication>(); services.AddTransient<ISmsSettingApplication, SmsSettingApplication>();
services.AddTransient<IInstitutionContractSmsServiceRepository, InstitutionContractSmsServiceRepository>();
#endregion #endregion
@@ -658,6 +661,9 @@ public class PersonalBootstrapper
services.AddTransient<ICameraBugReportApplication, CameraBugReportApplication>(); services.AddTransient<ICameraBugReportApplication, CameraBugReportApplication>();
services.AddTransient<ICameraBugReportRepository, CameraBugReportRepository>(); // MongoDB Implementation services.AddTransient<ICameraBugReportRepository, CameraBugReportRepository>(); // MongoDB Implementation
// InstitutionContractSendFlag - MongoDB
services.AddTransient<IInstitutionContractSendFlagRepository, InstitutionContractSendFlagRepository>();
services.AddDbContext<CompanyContext>(x => x.UseSqlServer(connectionString)); services.AddDbContext<CompanyContext>(x => x.UseSqlServer(connectionString));
} }
} }

View File

@@ -9,6 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentValidation" Version="12.1.1" /> <PackageReference Include="FluentValidation" Version="12.1.1" />
<PackageReference Include="MediatR" Version="14.0.0" /> <PackageReference Include="MediatR" Version="14.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.1" />
</ItemGroup> </ItemGroup>
@@ -18,4 +19,10 @@
<ProjectReference Include="..\..\Domain\GozareshgirProgramManager.Domain\GozareshgirProgramManager.Domain.csproj" /> <ProjectReference Include="..\..\Domain\GozareshgirProgramManager.Domain\GozareshgirProgramManager.Domain.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Http.Features">
<HintPath>C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\10.0.1\Microsoft.AspNetCore.Http.Features.dll</HintPath>
</Reference>
</ItemGroup>
</Project> </Project>

View File

@@ -209,22 +209,38 @@ public class CreateOrEditCheckoutCommandHandler : IBaseCommandHandler<CreateOrEd
} }
} }
//حقوق نهایی ////حقوق نهایی
var monthlySalaryPay = (totalHoursWorked * monthlySalaryDefined) / mandatoryHours; //var monthlySalaryPay = (totalHoursWorked * monthlySalaryDefined) / mandatoryHours;
// اگر اضافه کار داشت حقوق تعین شده به عنوان حقوق نهایی در نظر گرفته میشود //// اگر اضافه کار داشت حقوق تعین شده به عنوان حقوق نهایی در نظر گرفته میشود
monthlySalaryPay = monthlySalaryPay > monthlySalaryDefined ? monthlySalaryDefined : monthlySalaryPay; //monthlySalaryPay = monthlySalaryPay > monthlySalaryDefined ? monthlySalaryDefined : monthlySalaryPay;
//حقوق کسر شده ////حقوق کسر شده
var deductionFromSalary = monthlySalaryDefined - monthlySalaryPay; //var deductionFromSalary = monthlySalaryDefined - monthlySalaryPay;
//new chang salary compute
var monthlySalaryPay = totalHoursWorked * monthlySalaryDefined;
//زمان باقی مانده //زمان باقی مانده
var remainingTime = totalHoursWorked - mandatoryHours; var remainingTime = totalHoursWorked - mandatoryHours;
//تناسب به دقیقه
#region MyRegion
//var monthlySalaryDefinedTest = monthlySalaryDefined * mandatoryHours;
//var monthlySalaryPayTest = totalHoursWorked * monthlySalaryDefined;
////// اگر اضافه کار داشت حقوق تعین شده به عنوان حقوق نهایی در نظر گرفته میشود
//monthlySalaryPayTest = monthlySalaryPayTest > monthlySalaryDefinedTest ? monthlySalaryDefinedTest : monthlySalaryPayTest;
//////حقوق کسر شده
//var deductionFromSalaryTest = monthlySalaryDefinedTest - monthlySalaryPayTest;
#endregion
var computeResult = new ComputeResultDto var computeResult = new ComputeResultDto
{ {
MandatoryHours = mandatoryHours, MandatoryHours = mandatoryHours,
MonthlySalaryPay = monthlySalaryPay, MonthlySalaryPay = monthlySalaryPay,
DeductionFromSalary = deductionFromSalary, DeductionFromSalary = 0 /*deductionFromSalary*/,
RemainingHours = remainingTime RemainingHours = remainingTime
}; };
Console.WriteLine(mandatoryHours); Console.WriteLine(mandatoryHours);

View File

@@ -1,10 +1,11 @@
using GozareshgirProgramManager.Application._Common.Interfaces; using DNTPersianUtils.Core;
using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Application._Common.Models; using GozareshgirProgramManager.Application._Common.Models;
using GozareshgirProgramManager.Application.Modules.SalaryPaymentSettings.Queries.GetUserListWhoHaveSettings; using GozareshgirProgramManager.Application.Modules.SalaryPaymentSettings.Queries.GetUserListWhoHaveSettings;
using GozareshgirProgramManager.Domain._Common; using GozareshgirProgramManager.Domain._Common;
using GozareshgirProgramManager.Domain.CheckoutAgg.Enums; using GozareshgirProgramManager.Domain.CheckoutAgg.Enums;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using PersianTools.Core; using PersianDateTime = PersianTools.Core.PersianDateTime;
namespace GozareshgirProgramManager.Application.Modules.Checkouts.Queries.GetUserToGropCreate; namespace GozareshgirProgramManager.Application.Modules.Checkouts.Queries.GetUserToGropCreate;
@@ -45,8 +46,8 @@ public class GetUserToGroupCreatingQueryHandler : IBaseQueryHandler<GetUserToGro
"ایجاد فیش فقط برای ماه های گذشته امکان پذیر است"); "ایجاد فیش فقط برای ماه های گذشته امکان پذیر است");
var lastMonthStart = lastMonth; //var lastMonthStart = lastMonth;
var lastMonthEnd = lastMonth; var lastMonthEnd = ((selectedDate.ToFarsi().FindeEndOfMonth())).ToGeorgianDateTime();
var query = var query =
await (from u in _context.Users await (from u in _context.Users
@@ -60,8 +61,8 @@ public class GetUserToGroupCreatingQueryHandler : IBaseQueryHandler<GetUserToGro
// LEFT JOIN // LEFT JOIN
//فیش //فیش
join ch in _context.Checkouts join ch in _context.Checkouts
.Where(x => x.CheckoutStartDate < lastMonthStart .Where(x => x.CheckoutStartDate < lastMonthEnd
&& x.CheckoutEndDate >= lastMonthStart) && x.CheckoutEndDate > selectedDate)
on u.Id equals ch.UserId into chJoin on u.Id equals ch.UserId into chJoin
from ch in chJoin.DefaultIfEmpty() from ch in chJoin.DefaultIfEmpty()

View File

@@ -10,7 +10,7 @@ public record AddTaskToPhaseCommand(
Guid PhaseId, Guid PhaseId,
string Name, string Name,
string? Description = null, string? Description = null,
TaskPriority Priority = TaskPriority.Medium, ProjectTaskPriority Priority = ProjectTaskPriority.Medium,
int OrderIndex = 0, int OrderIndex = 0,
DateTime? DueDate = null DateTime? DueDate = null
) : IBaseCommand; ) : IBaseCommand;

View File

@@ -6,35 +6,103 @@ using GozareshgirProgramManager.Domain.ProjectAgg.Repositories;
namespace GozareshgirProgramManager.Application.Modules.Projects.Commands.ChangeTaskPriority; namespace GozareshgirProgramManager.Application.Modules.Projects.Commands.ChangeTaskPriority;
/// <summary> public record ChangeTaskPriorityCommand(
/// Command to change a task priority. Guid Id,
/// </summary> ProjectHierarchyLevel Level,
public record ChangeTaskPriorityCommand(Guid TaskId, TaskPriority Priority) : IBaseCommand; ProjectTaskPriority Priority
) : IBaseCommand;
public class ChangeTaskPriorityCommandHandler : IBaseCommandHandler<ChangeTaskPriorityCommand> public class ChangeTaskPriorityCommandHandler : IBaseCommandHandler<ChangeTaskPriorityCommand>
{ {
private readonly IProjectTaskRepository _taskRepository; private readonly IProjectTaskRepository _taskRepository;
private readonly IProjectPhaseRepository _phaseRepository;
private readonly IProjectRepository _projectRepository;
private readonly IUnitOfWork _unitOfWork; private readonly IUnitOfWork _unitOfWork;
public ChangeTaskPriorityCommandHandler(IProjectTaskRepository taskRepository, IUnitOfWork unitOfWork) public ChangeTaskPriorityCommandHandler(
IProjectTaskRepository taskRepository,
IProjectPhaseRepository phaseRepository,
IProjectRepository projectRepository,
IUnitOfWork unitOfWork)
{ {
_taskRepository = taskRepository; _taskRepository = taskRepository;
_phaseRepository = phaseRepository;
_projectRepository = projectRepository;
_unitOfWork = unitOfWork; _unitOfWork = unitOfWork;
} }
public async Task<OperationResult> Handle(ChangeTaskPriorityCommand request, CancellationToken cancellationToken) public async Task<OperationResult> Handle(ChangeTaskPriorityCommand request, CancellationToken cancellationToken)
{ {
var task = await _taskRepository.GetByIdAsync(request.TaskId, cancellationToken); switch (request.Level)
{
case ProjectHierarchyLevel.Task:
return await HandleTaskLevelAsync(request.Id, request.Priority, cancellationToken);
case ProjectHierarchyLevel.Phase:
return await HandlePhaseLevelAsync(request.Id, request.Priority, cancellationToken);
case ProjectHierarchyLevel.Project:
return await HandleProjectLevelAsync(request.Id, request.Priority, cancellationToken);
default:
return OperationResult.Failure("سطح نامعتبر است");
}
}
// Task-level priority update
private async Task<OperationResult> HandleTaskLevelAsync(Guid taskId, ProjectTaskPriority priority, CancellationToken ct)
{
var task = await _taskRepository.GetByIdAsync(taskId, ct);
if (task is null) if (task is null)
return OperationResult.NotFound("تسک یافت نشد"); return OperationResult.NotFound("تسک یافت نشد");
// Idempotent: if already same priority, skip extra work if (task.Priority != priority)
if (task.Priority != request.Priority)
{ {
task.SetPriority(request.Priority); task.SetPriority(priority);
} }
await _unitOfWork.SaveChangesAsync(cancellationToken); await _unitOfWork.SaveChangesAsync(ct);
return OperationResult.Success();
}
// Phase-level bulk priority update
private async Task<OperationResult> HandlePhaseLevelAsync(Guid phaseId, ProjectTaskPriority priority, CancellationToken ct)
{
var phase = await _phaseRepository.GetWithTasksAsync(phaseId);
if (phase is null)
return OperationResult.NotFound("فاز یافت نشد");
var tasks = phase.Tasks?.ToList() ?? new List<Domain.ProjectAgg.Entities.ProjectTask>();
foreach (var t in tasks)
{
if (t.Priority != priority)
{
t.SetPriority(priority);
}
}
await _unitOfWork.SaveChangesAsync(ct);
return OperationResult.Success();
}
// Project-level bulk priority update across all phases
private async Task<OperationResult> HandleProjectLevelAsync(Guid projectId, ProjectTaskPriority priority, CancellationToken ct)
{
var project = await _projectRepository.GetWithFullHierarchyAsync(projectId);
if (project is null)
return OperationResult.NotFound("پروژه یافت نشد");
var phases = project.Phases?.ToList() ?? new List<Domain.ProjectAgg.Entities.ProjectPhase>();
foreach (var phase in phases)
{
var tasks = phase.Tasks?.ToList() ?? new List<Domain.ProjectAgg.Entities.ProjectTask>();
foreach (var t in tasks)
{
if (t.Priority != priority)
{
t.SetPriority(priority);
}
}
}
await _unitOfWork.SaveChangesAsync(ct);
return OperationResult.Success(); return OperationResult.Success();
} }
} }

View File

@@ -15,7 +15,7 @@ public class GetTaskDto
// Task-specific fields // Task-specific fields
public TimeSpan SpentTime { get; init; } public TimeSpan SpentTime { get; init; }
public TimeSpan RemainingTime { get; init; } public TimeSpan RemainingTime { get; init; }
public TaskPriority Priority { get; set; } public ProjectTaskPriority Priority { get; set; }
public List<GetTaskSectionDto> Sections { get; init; } public List<GetTaskSectionDto> Sections { get; init; }
} }

View File

@@ -6,5 +6,7 @@ namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.Project
public record ProjectBoardListQuery: IBaseQuery<List<ProjectBoardListResponse>> public record ProjectBoardListQuery: IBaseQuery<List<ProjectBoardListResponse>>
{ {
public long? UserId { get; set; }
public string? SearchText { get; set; }
public TaskSectionStatus? Status { get; set; } public TaskSectionStatus? Status { get; set; }
} }

View File

@@ -3,7 +3,6 @@ using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Application._Common.Models; using GozareshgirProgramManager.Application._Common.Models;
using GozareshgirProgramManager.Domain.ProjectAgg.Enums; using GozareshgirProgramManager.Domain.ProjectAgg.Enums;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query.Internal;
namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectBoardList; namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.ProjectBoardList;
@@ -24,7 +23,8 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler<ProjectBoardListQu
var currentUserId = _authHelper.GetCurrentUserId(); var currentUserId = _authHelper.GetCurrentUserId();
var queryable = _programManagerDbContext.TaskSections.AsNoTracking() var queryable = _programManagerDbContext.TaskSections.AsNoTracking()
.Where(x => x.InitialEstimatedHours > TimeSpan.Zero && x.Status != TaskSectionStatus.Completed) .Where(x => x.InitialEstimatedHours > TimeSpan.Zero
&& x.Status != TaskSectionStatus.Completed)
.Include(x => x.Task) .Include(x => x.Task)
.ThenInclude(x => x.Phase) .ThenInclude(x => x.Phase)
.ThenInclude(x => x.Project) .ThenInclude(x => x.Project)
@@ -41,9 +41,22 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler<ProjectBoardListQu
queryable = queryable.Where(x => x.Status == request.Status); queryable = queryable.Where(x => x.Status == request.Status);
} }
if (request.UserId is > 0)
{
queryable = queryable.Where(x => x.CurrentAssignedUserId == request.UserId);
}
if (!string.IsNullOrWhiteSpace(request.SearchText))
{
queryable = queryable.Where(x=>x.Task.Name.Contains(request.SearchText)
|| x.Task.Phase.Name.Contains(request.SearchText)
|| x.Task.Phase.Project.Name.Contains(request.SearchText));
}
var data = await queryable.ToListAsync(cancellationToken); var data = await queryable.ToListAsync(cancellationToken);
var activityUserIds = data.SelectMany(x => x.Activities).Select(a => a.UserId).Distinct().ToList(); var activityUserIds = data.SelectMany(x => x.Activities)
.Select(a => a.UserId).Distinct().ToList();
var assignedUser = data.Select(x => x.CurrentAssignedUserId) var assignedUser = data.Select(x => x.CurrentAssignedUserId)
.Concat(data.Select(x => x.OriginalAssignedUserId)).ToList(); .Concat(data.Select(x => x.OriginalAssignedUserId)).ToList();
var allUserIds = activityUserIds.Concat(assignedUser).Distinct().ToList(); var allUserIds = activityUserIds.Concat(assignedUser).Distinct().ToList();
@@ -53,7 +66,9 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler<ProjectBoardListQu
.ToDictionaryAsync(x => x.Id, x => x.FullName, cancellationToken); .ToDictionaryAsync(x => x.Id, x => x.FullName, cancellationToken);
var result = data .OrderByDescending(x => x.CurrentAssignedUserId == currentUserId) var result = data
.OrderByDescending(x => x.CurrentAssignedUserId == currentUserId)
.ThenByDescending(x=>x.Task.Priority)
.ThenBy(x => GetStatusOrder(x.Status)) .ThenBy(x => GetStatusOrder(x.Status))
.Select(x => .Select(x =>
{ {
@@ -65,7 +80,7 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler<ProjectBoardListQu
{ {
Activity = a, Activity = a,
TimeSpent = timeSpent, TimeSpent = timeSpent,
TotalSeconds = timeSpent.TotalSeconds, timeSpent.TotalSeconds,
FormattedTime = timeSpent.ToString(@"hh\:mm") FormattedTime = timeSpent.ToString(@"hh\:mm")
}; };
}).ToList(); }).ToList();
@@ -103,6 +118,7 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler<ProjectBoardListQu
ProjectName = x.Task.Phase.Project.Name, ProjectName = x.Task.Phase.Project.Name,
TaskName = x.Task.Name, TaskName = x.Task.Name,
SectionStatus = x.Status, SectionStatus = x.Status,
TaskPriority = x.Task.Priority,
Progress = new ProjectProgressDto() Progress = new ProjectProgressDto()
{ {
CompleteSecond = x.FinalEstimatedHours.TotalSeconds, CompleteSecond = x.FinalEstimatedHours.TotalSeconds,
@@ -114,6 +130,7 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler<ProjectBoardListQu
AssignedUser = x.CurrentAssignedUserId == x.OriginalAssignedUserId ? null AssignedUser = x.CurrentAssignedUserId == x.OriginalAssignedUserId ? null
: users.GetValueOrDefault(x.CurrentAssignedUserId, "ناشناس"), : users.GetValueOrDefault(x.CurrentAssignedUserId, "ناشناس"),
SkillName = x.Skill?.Name??"-", SkillName = x.Skill?.Name??"-",
TaskId = x.TaskId
}; };
}).ToList(); }).ToList();

View File

@@ -13,6 +13,8 @@ public class ProjectBoardListResponse
public string? AssignedUser { get; set; } public string? AssignedUser { get; set; }
public string OriginalUser { get; set; } public string OriginalUser { get; set; }
public string SkillName { get; set; } public string SkillName { get; set; }
public ProjectTaskPriority TaskPriority { get; set; }
public Guid TaskId { get; set; }
} }
public class ProjectProgressDto public class ProjectProgressDto

View File

@@ -73,6 +73,7 @@ public class
var doneTime = t.Sections.Aggregate(TimeSpan.Zero, var doneTime = t.Sections.Aggregate(TimeSpan.Zero,
(sum, next) => sum.Add(next.GetTotalTimeSpent())); (sum, next) => sum.Add(next.GetTotalTimeSpent()));
var skills = t.Sections var skills = t.Sections
.Select(s => .Select(s =>
{ {
@@ -88,7 +89,17 @@ public class
skillName, skillName,
timePercentage); timePercentage);
}).ToList(); }).ToList();
var taskPercentage = (int)Math.Round(skills.Average(x => x.TimePercentage));
int taskPercentage;
if (skills.Count == 0)
{
taskPercentage = 0;
}
else
{
taskPercentage = (int)Math.Round(skills.Average(x => x.TimePercentage));
}
return new ProjectDeployBoardDetailTaskItem( return new ProjectDeployBoardDetailTaskItem(
t.Name, t.Name,

View File

@@ -56,6 +56,7 @@ public class ProjectSetTimeDetailsQueryHandler
var skills = await _context.Skills var skills = await _context.Skills
.AsNoTracking() .AsNoTracking()
.OrderBy(x=>x.CreationDate)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
var res = new ProjectSetTimeResponse( var res = new ProjectSetTimeResponse(
@@ -84,7 +85,7 @@ public class ProjectSetTimeDetailsQueryHandler
UserId = section?.OriginalAssignedUserId ?? 0, UserId = section?.OriginalAssignedUserId ?? 0,
SkillId = skill.Id, SkillId = skill.Id,
}; };
}).OrderBy(x => x.SkillId).ToList(), }).ToList(),
task.Id, task.Id,
level); level);
@@ -114,6 +115,7 @@ public class ProjectSetTimeDetailsQueryHandler
var skills = await _context.Skills var skills = await _context.Skills
.AsNoTracking() .AsNoTracking()
.OrderBy(x=>x.CreationDate)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
var res = new ProjectSetTimeResponse( var res = new ProjectSetTimeResponse(
@@ -135,7 +137,7 @@ public class ProjectSetTimeDetailsQueryHandler
UserId = section?.UserId ?? 0, UserId = section?.UserId ?? 0,
SkillId = skill.Id, SkillId = skill.Id,
}; };
}).OrderBy(x => x.SkillId).ToList(), }).ToList(),
phase.Id, phase.Id,
level); level);
@@ -165,6 +167,7 @@ public class ProjectSetTimeDetailsQueryHandler
var skills = await _context.Skills var skills = await _context.Skills
.AsNoTracking() .AsNoTracking()
.OrderBy(x=>x.CreationDate)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
var res = new ProjectSetTimeResponse( var res = new ProjectSetTimeResponse(
@@ -186,7 +189,7 @@ public class ProjectSetTimeDetailsQueryHandler
UserId = section?.UserId ?? 0, UserId = section?.UserId ?? 0,
SkillId = skill.Id, SkillId = skill.Id,
}; };
}).OrderBy(x => x.SkillId).ToList(), }).ToList(),
project.Id, project.Id,
level); level);

View File

@@ -0,0 +1,47 @@
using GozareshgirProgramManager.Application._Common.Models;
using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Domain._Common.Exceptions;
using GozareshgirProgramManager.Domain.TaskChatAgg.Repositories;
namespace GozareshgirProgramManager.Application.Modules.TaskChat.Commands.DeleteMessage;
public record DeleteMessageCommand(Guid MessageId) : IBaseCommand;
public class DeleteMessageCommandHandler : IBaseCommandHandler<DeleteMessageCommand>
{
private readonly ITaskChatMessageRepository _repository;
private readonly IAuthHelper _authHelper;
public DeleteMessageCommandHandler(ITaskChatMessageRepository repository, IAuthHelper authHelper)
{
_repository = repository;
_authHelper = authHelper;
}
public async Task<OperationResult> Handle(DeleteMessageCommand request, CancellationToken cancellationToken)
{
var currentUserId = _authHelper.GetCurrentUserId()??
throw new UnAuthorizedException("کاربر احراز هویت نشده است");
var message = await _repository.GetByIdAsync(request.MessageId);
if (message == null)
{
return OperationResult.NotFound("پیام یافت نشد");
}
try
{
message.DeleteMessage(currentUserId);
await _repository.UpdateAsync(message);
await _repository.SaveChangesAsync();
// TODO: SignalR notification
return OperationResult.Success();
}
catch (Exception ex)
{
return OperationResult.ValidationError(ex.Message);
}
}
}

View File

@@ -0,0 +1,51 @@
using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Application._Common.Models;
using GozareshgirProgramManager.Domain._Common.Exceptions;
using GozareshgirProgramManager.Domain.TaskChatAgg.Repositories;
using MediatR;
namespace GozareshgirProgramManager.Application.Modules.TaskChat.Commands.EditMessage;
public record EditMessageCommand(
Guid MessageId,
string NewTextContent
) : IBaseCommand;
public class EditMessageCommandHandler : IBaseCommandHandler<EditMessageCommand>
{
private readonly ITaskChatMessageRepository _repository;
private readonly IAuthHelper _authHelper;
public EditMessageCommandHandler(ITaskChatMessageRepository repository, IAuthHelper authHelper)
{
_repository = repository;
_authHelper = authHelper;
}
public async Task<OperationResult> Handle(EditMessageCommand request, CancellationToken cancellationToken)
{
var currentUserId = _authHelper.GetCurrentUserId()??
throw new UnAuthorizedException("کاربر احراز هویت نشده است");
var message = await _repository.GetByIdAsync(request.MessageId);
if (message == null)
{
return OperationResult.NotFound("پیام یافت نشد");
}
try
{
message.EditMessage(request.NewTextContent, currentUserId);
await _repository.UpdateAsync(message);
await _repository.SaveChangesAsync();
// TODO: SignalR notification
return OperationResult.Success();
}
catch (Exception ex)
{
return OperationResult.ValidationError(ex.Message);
}
}
}

View File

@@ -0,0 +1,46 @@
using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Application._Common.Models;
using GozareshgirProgramManager.Domain._Common.Exceptions;
using GozareshgirProgramManager.Domain.TaskChatAgg.Repositories;
using MediatR;
namespace GozareshgirProgramManager.Application.Modules.TaskChat.Commands.PinMessage;
public record PinMessageCommand(Guid MessageId) : IBaseCommand;
public class PinMessageCommandHandler : IBaseCommandHandler<PinMessageCommand>
{
private readonly ITaskChatMessageRepository _repository;
private readonly IAuthHelper _authHelper;
public PinMessageCommandHandler(ITaskChatMessageRepository repository, IAuthHelper authHelper)
{
_repository = repository;
_authHelper = authHelper;
}
public async Task<OperationResult> Handle(PinMessageCommand request, CancellationToken cancellationToken)
{
var currentUserId = _authHelper.GetCurrentUserId()??
throw new UnAuthorizedException("کاربر احراز هویت نشده است");
var message = await _repository.GetByIdAsync(request.MessageId);
if (message == null)
{
return OperationResult.NotFound("پیام یافت نشد");
}
try
{
message.PinMessage(currentUserId);
await _repository.UpdateAsync(message);
await _repository.SaveChangesAsync();
return OperationResult.Success();
}
catch (Exception ex)
{
return OperationResult.ValidationError(ex.Message);
}
}
}

View File

@@ -0,0 +1,212 @@
using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Application._Common.Models;
using GozareshgirProgramManager.Application.Modules.TaskChat.DTOs;
using GozareshgirProgramManager.Application.Services.FileManagement;
using GozareshgirProgramManager.Domain._Common.Exceptions;
using GozareshgirProgramManager.Domain.TaskChatAgg.Entities;
using GozareshgirProgramManager.Domain.TaskChatAgg.Repositories;
using GozareshgirProgramManager.Domain.TaskChatAgg.Enums;
using GozareshgirProgramManager.Domain.FileManagementAgg.Entities;
using GozareshgirProgramManager.Domain.FileManagementAgg.Repositories;
using GozareshgirProgramManager.Domain.FileManagementAgg.Enums;
using GozareshgirProgramManager.Domain.ProjectAgg.Repositories;
using MediatR;
using Microsoft.AspNetCore.Http;
namespace GozareshgirProgramManager.Application.Modules.TaskChat.Commands.SendMessage;
public record SendMessageCommand(
Guid TaskId,
MessageType MessageType,
string? TextContent,
IFormFile? File,
Guid? ReplyToMessageId
) : IBaseCommand<MessageDto>;
public class SendMessageCommandHandler : IBaseCommandHandler<SendMessageCommand, MessageDto>
{
private readonly ITaskChatMessageRepository _messageRepository;
private readonly IUploadedFileRepository _fileRepository;
private readonly IProjectTaskRepository _taskRepository;
private readonly IFileStorageService _fileStorageService;
private readonly IThumbnailGeneratorService _thumbnailService;
private readonly IAuthHelper _authHelper;
public SendMessageCommandHandler(
ITaskChatMessageRepository messageRepository,
IUploadedFileRepository fileRepository,
IProjectTaskRepository taskRepository,
IFileStorageService fileStorageService,
IThumbnailGeneratorService thumbnailService, IAuthHelper authHelper)
{
_messageRepository = messageRepository;
_fileRepository = fileRepository;
_taskRepository = taskRepository;
_fileStorageService = fileStorageService;
_thumbnailService = thumbnailService;
_authHelper = authHelper;
}
public async Task<OperationResult<MessageDto>> Handle(SendMessageCommand request, CancellationToken cancellationToken)
{
var currentUserId = _authHelper.GetCurrentUserId()
?? throw new UnAuthorizedException("کاربر احراز هویت نشده است");
var task = await _taskRepository.GetByIdAsync(request.TaskId, cancellationToken);
if (task == null)
{
return OperationResult<MessageDto>.NotFound("تسک یافت نشد");
}
Guid? uploadedFileId = null;
if (request.File != null)
{
if (request.File.Length == 0)
{
return OperationResult<MessageDto>.ValidationError("فایل خالی است");
}
const long maxFileSize = 100 * 1024 * 1024;
if (request.File.Length > maxFileSize)
{
return OperationResult<MessageDto>.ValidationError("حجم فایل بیش از حد مجاز است (حداکثر 100MB)");
}
var fileType = DetectFileType(request.File.ContentType, Path.GetExtension(request.File.FileName));
var uploadedFile = new UploadedFile(
originalFileName: request.File.FileName,
fileSizeBytes: request.File.Length,
mimeType: request.File.ContentType,
fileType: fileType,
category: FileCategory.TaskChatMessage,
uploadedByUserId: currentUserId,
storageProvider: StorageProvider.LocalFileSystem
);
await _fileRepository.AddAsync(uploadedFile);
await _fileRepository.SaveChangesAsync();
try
{
using var stream = request.File.OpenReadStream();
var uploadResult = await _fileStorageService.UploadAsync(
stream,
uploadedFile.UniqueFileName,
"TaskChatMessage"
);
uploadedFile.CompleteUpload(uploadResult.StoragePath, uploadResult.StorageUrl);
if (fileType == FileType.Image)
{
var dimensions = await _thumbnailService.GetImageDimensionsAsync(uploadResult.StoragePath);
if (dimensions.HasValue)
{
uploadedFile.SetImageDimensions(dimensions.Value.Width, dimensions.Value.Height);
}
var thumbnail = await _thumbnailService
.GenerateImageThumbnailAsync(uploadResult.StoragePath, category: "TaskChatMessage");
if (thumbnail.HasValue)
{
uploadedFile.SetThumbnail(thumbnail.Value.ThumbnailUrl);
}
}
await _fileRepository.UpdateAsync(uploadedFile);
await _fileRepository.SaveChangesAsync();
uploadedFileId = uploadedFile.Id;
}
catch (Exception ex)
{
await _fileRepository.DeleteAsync(uploadedFile);
await _fileRepository.SaveChangesAsync();
return OperationResult<MessageDto>.ValidationError($"خطا در آپلود فایل: {ex.Message}");
}
}
var message = new TaskChatMessage(
taskId: request.TaskId,
senderUserId: currentUserId,
messageType: request.MessageType,
textContent: request.TextContent,
uploadedFileId
);
if (request.ReplyToMessageId.HasValue)
{
message.SetReplyTo(request.ReplyToMessageId.Value);
}
await _messageRepository.AddAsync(message);
await _messageRepository.SaveChangesAsync();
if (uploadedFileId.HasValue)
{
var file = await _fileRepository.GetByIdAsync(uploadedFileId.Value);
if (file != null)
{
file.SetReference("TaskChatMessage", message.Id.ToString());
await _fileRepository.UpdateAsync(file);
await _fileRepository.SaveChangesAsync();
}
}
var dto = new MessageDto
{
Id = message.Id,
TaskId = message.TaskId,
SenderUserId = message.SenderUserId,
SenderName = "کاربر",
MessageType = message.MessageType.ToString(),
TextContent = message.TextContent,
ReplyToMessageId = message.ReplyToMessageId,
IsEdited = message.IsEdited,
IsPinned = message.IsPinned,
CreationDate = message.CreationDate,
IsMine = true
};
if (uploadedFileId.HasValue)
{
var file = await _fileRepository.GetByIdAsync(uploadedFileId.Value);
if (file != null)
{
dto.File = new MessageFileDto
{
Id = file.Id,
FileName = file.OriginalFileName,
FileUrl = file.StorageUrl ?? "",
FileSizeBytes = file.FileSizeBytes,
FileType = file.FileType.ToString(),
ThumbnailUrl = file.ThumbnailUrl,
ImageWidth = file.ImageWidth,
ImageHeight = file.ImageHeight,
DurationSeconds = file.DurationSeconds
};
}
}
return OperationResult<MessageDto>.Success(dto);
}
private FileType DetectFileType(string mimeType, string extension)
{
if (mimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
return FileType.Image;
if (mimeType.StartsWith("video/", StringComparison.OrdinalIgnoreCase))
return FileType.Video;
if (mimeType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase))
return FileType.Audio;
if (new[] { ".zip", ".rar", ".7z", ".tar", ".gz" }.Contains(extension.ToLower()))
return FileType.Archive;
return FileType.Document;
}
}

View File

@@ -0,0 +1,46 @@
using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Application._Common.Models;
using GozareshgirProgramManager.Domain._Common.Exceptions;
using GozareshgirProgramManager.Domain.TaskChatAgg.Repositories;
using MediatR;
namespace GozareshgirProgramManager.Application.Modules.TaskChat.Commands.UnpinMessage;
public record UnpinMessageCommand(Guid MessageId) : IBaseCommand;
public class UnpinMessageCommandHandler : IBaseCommandHandler<UnpinMessageCommand>
{
private readonly ITaskChatMessageRepository _repository;
private readonly IAuthHelper _authHelper;
public UnpinMessageCommandHandler(ITaskChatMessageRepository repository, IAuthHelper authHelper)
{
_repository = repository;
_authHelper = authHelper;
}
public async Task<OperationResult> Handle(UnpinMessageCommand request, CancellationToken cancellationToken)
{
var currentUserId = _authHelper.GetCurrentUserId()??
throw new UnAuthorizedException("کاربر احراز هویت نشده است");
var message = await _repository.GetByIdAsync(request.MessageId);
if (message == null)
{
return OperationResult.NotFound("پیام یافت نشد");
}
try
{
message.UnpinMessage(currentUserId);
await _repository.UpdateAsync(message);
await _repository.SaveChangesAsync();
return OperationResult.Success();
}
catch (Exception ex)
{
return OperationResult.ValidationError(ex.Message);
}
}
}

View File

@@ -0,0 +1,63 @@
namespace GozareshgirProgramManager.Application.Modules.TaskChat.DTOs;
public class SendMessageDto
{
public Guid TaskId { get; set; }
public string MessageType { get; set; } = string.Empty; // "Text", "File", "Image", "Voice", "Video"
public string? TextContent { get; set; }
public Guid? FileId { get; set; }
public Guid? ReplyToMessageId { get; set; }
}
public class MessageDto
{
public Guid Id { get; set; }
public Guid TaskId { get; set; }
public long SenderUserId { get; set; }
public string SenderName { get; set; } = string.Empty;
public string MessageType { get; set; } = string.Empty;
public string? TextContent { get; set; }
public MessageFileDto? File { get; set; }
public Guid? ReplyToMessageId { get; set; }
public MessageDto? ReplyToMessage { get; set; }
public bool IsEdited { get; set; }
public DateTime? EditedDate { get; set; }
public bool IsPinned { get; set; }
public DateTime? PinnedDate { get; set; }
public long? PinnedByUserId { get; set; }
public DateTime CreationDate { get; set; }
public bool IsMine { get; set; }
}
public class MessageFileDto
{
public Guid Id { get; set; }
public string FileName { get; set; } = string.Empty;
public string FileUrl { get; set; } = string.Empty;
public long FileSizeBytes { get; set; }
public string FileType { get; set; } = string.Empty;
public string? ThumbnailUrl { get; set; }
public int? ImageWidth { get; set; }
public int? ImageHeight { get; set; }
public int? DurationSeconds { get; set; }
public string FileSizeFormatted
{
get
{
const long kb = 1024;
const long mb = kb * 1024;
const long gb = mb * 1024;
if (FileSizeBytes >= gb)
return $"{FileSizeBytes / (double)gb:F2} GB";
if (FileSizeBytes >= mb)
return $"{FileSizeBytes / (double)mb:F2} MB";
if (FileSizeBytes >= kb)
return $"{FileSizeBytes / (double)kb:F2} KB";
return $"{FileSizeBytes} Bytes";
}
}
}

View File

@@ -0,0 +1,216 @@
using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Application._Common.Models;
using GozareshgirProgramManager.Application.Modules.TaskChat.DTOs;
using GozareshgirProgramManager.Domain.TaskChatAgg.Enums;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace GozareshgirProgramManager.Application.Modules.TaskChat.Queries.GetMessages;
public record GetMessagesQuery(
Guid TaskId,
MessageType? MessageType,
int Page = 1,
int PageSize = 50
) : IBaseQuery<PaginationResult<MessageDto>>;
public class GetMessagesQueryHandler : IBaseQueryHandler<GetMessagesQuery, PaginationResult<MessageDto>>
{
private readonly IProgramManagerDbContext _context;
private readonly IAuthHelper _authHelper;
public GetMessagesQueryHandler(IProgramManagerDbContext context, IAuthHelper authHelper)
{
_context = context;
_authHelper = authHelper;
}
private List<MessageDto> CreateAdditionalTimeNotes(
IEnumerable<Domain.ProjectAgg.Entities.TaskSectionAdditionalTime> additionalTimes,
Dictionary<long, string> users,
Guid taskId)
{
var notes = new List<MessageDto>();
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<OperationResult<PaginationResult<MessageDto>>> Handle(GetMessagesQuery request, CancellationToken cancellationToken)
{
var currentUserId = _authHelper.GetCurrentUserId();
var skip = (request.Page - 1) * request.PageSize;
var query = _context.TaskChatMessages
.Where(m => m.TaskId == request.TaskId && !m.IsDeleted)
.Include(m => m.ReplyToMessage)
.OrderBy(m => m.CreationDate).AsQueryable();
if (request.MessageType.HasValue)
{
query = query.Where(m => m.MessageType == request.MessageType.Value);
}
var totalCount = await query.CountAsync(cancellationToken);
var messages = await query
.Skip(skip)
.Take(request.PageSize)
.ToListAsync(cancellationToken);
// ✅ گرفتن تمامی کاربران برای نمایش نام کامل فرستنده به جای "کاربر"
var senderUserIds = messages.Select(m => 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) برای نمایش به صورت نوت
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<MessageDto>();
// ✅ ابتدا زمان‌های اضافی قبل از اولین پیام را اضافه کن (اگر پیامی وجود داشته باشد)
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.GetValueOrDefault(message.SenderUserId, "کاربر ناشناس");
var dto = new MessageDto
{
Id = message.Id,
TaskId = message.TaskId,
SenderUserId = message.SenderUserId,
SenderName = senderName,
MessageType = message.MessageType.ToString(),
TextContent = message.TextContent,
ReplyToMessageId = message.ReplyToMessageId,
IsEdited = message.IsEdited,
EditedDate = message.EditedDate,
IsPinned = message.IsPinned,
PinnedDate = message.PinnedDate,
PinnedByUserId = message.PinnedByUserId,
CreationDate = message.CreationDate,
IsMine = message.SenderUserId == currentUserId
};
if (message.ReplyToMessage != null)
{
var replySenderName = users.GetValueOrDefault(message.ReplyToMessage.SenderUserId, "کاربر ناشناس");
dto.ReplyToMessage = new MessageDto
{
Id = message.ReplyToMessage.Id,
SenderUserId = message.ReplyToMessage.SenderUserId,
SenderName = replySenderName,
TextContent = message.ReplyToMessage.TextContent,
CreationDate = message.ReplyToMessage.CreationDate
};
}
if (message.FileId.HasValue)
{
var file = await _context.UploadedFiles.FirstOrDefaultAsync(f => f.Id == message.FileId.Value, cancellationToken);
if (file != null)
{
dto.File = new MessageFileDto
{
Id = file.Id,
FileName = file.OriginalFileName,
FileUrl = file.StorageUrl ?? "",
FileSizeBytes = file.FileSizeBytes,
FileType = file.FileType.ToString(),
ThumbnailUrl = file.ThumbnailUrl,
ImageWidth = file.ImageWidth,
ImageHeight = file.ImageHeight,
DurationSeconds = file.DurationSeconds
};
}
}
messageDtos.Add(dto);
// ✅ پیدا کردن پیام بعدی (اگر وجود داشته باشد)
var currentIndex = messages.IndexOf(message);
var nextMessage = currentIndex < messages.Count - 1 ? messages[currentIndex + 1] : null;
if (nextMessage != null)
{
// ✅ زمان‌های اضافی بین این پیام و پیام بعدی
var additionalTimesBetween = allAdditionalTimes
.Where(at => at.CreationDate > message.CreationDate && at.CreationDate < nextMessage.CreationDate)
.ToList();
messageDtos.AddRange(CreateAdditionalTimeNotes(additionalTimesBetween, users, request.TaskId));
}
else
{
// ✅ این آخرین پیام است، زمان‌های اضافی بعد از آن را اضافه کن
var additionalTimesAfterLastMessage = allAdditionalTimes
.Where(at => at.CreationDate > message.CreationDate)
.ToList();
messageDtos.AddRange(CreateAdditionalTimeNotes(additionalTimesAfterLastMessage, users, request.TaskId));
}
}
// ✅ مرتب کردن نهایی تمام پیام‌ها (معمولی + نوت‌ها) بر اساس زمان ایجاد
messageDtos = messageDtos.OrderBy(m => m.CreationDate).ToList();
var response = new PaginationResult<MessageDto>()
{
List = messageDtos,
TotalCount = totalCount,
};
return OperationResult<PaginationResult<MessageDto>>.Success(response);
}
}

View File

@@ -0,0 +1,82 @@
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.GetPinnedMessages;
public record GetPinnedMessagesQuery(Guid TaskId) : IBaseQuery<List<MessageDto>>;
public class GetPinnedMessagesQueryHandler : IBaseQueryHandler<GetPinnedMessagesQuery, List<MessageDto>>
{
private readonly IProgramManagerDbContext _context;
private readonly IAuthHelper _authHelper;
public GetPinnedMessagesQueryHandler(IProgramManagerDbContext context, IAuthHelper authHelper)
{
_context = context;
_authHelper = authHelper;
}
public async Task<OperationResult<List<MessageDto>>> Handle(GetPinnedMessagesQuery request, CancellationToken cancellationToken)
{
var currentUserId = _authHelper.GetCurrentUserId();
var messages = await _context.TaskChatMessages
.Where(m => m.TaskId == request.TaskId && m.IsPinned && !m.IsDeleted)
.Include(m => m.ReplyToMessage)
.OrderByDescending(m => m.PinnedDate)
.ToListAsync(cancellationToken);
// ✅ گرفتن تمامی کاربران برای نمایش نام کامل فرستنده
var senderUserIds = messages.Select(m => m.SenderUserId).Distinct().ToList();
var users = await _context.Users
.Where(u => senderUserIds.Contains(u.Id))
.ToDictionaryAsync(u => u.Id, u => u.FullName, cancellationToken);
var messageDtos = new List<MessageDto>();
foreach (var message in messages)
{
// ✅ نام فرستنده را از User واقعی بگیر (به جای "کاربر" ثابت)
var senderName = users.GetValueOrDefault(message.SenderUserId, "کاربر ناشناس");
var dto = new MessageDto
{
Id = message.Id,
TaskId = message.TaskId,
SenderUserId = message.SenderUserId,
SenderName = senderName,
MessageType = message.MessageType.ToString(),
TextContent = message.TextContent,
IsPinned = message.IsPinned,
PinnedDate = message.PinnedDate,
PinnedByUserId = message.PinnedByUserId,
CreationDate = message.CreationDate,
IsMine = message.SenderUserId == currentUserId
};
if (message.FileId.HasValue)
{
var file = await _context.UploadedFiles.FirstOrDefaultAsync(f => f.Id == message.FileId.Value, cancellationToken);
if (file != null)
{
dto.File = new MessageFileDto
{
Id = file.Id,
FileName = file.OriginalFileName,
FileUrl = file.StorageUrl ?? "",
FileSizeBytes = file.FileSizeBytes,
FileType = file.FileType.ToString(),
ThumbnailUrl = file.ThumbnailUrl
};
}
}
messageDtos.Add(dto);
}
return OperationResult<List<MessageDto>>.Success(messageDtos);
}
}

View File

@@ -0,0 +1,72 @@
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.SearchMessages;
public record SearchMessagesQuery(
Guid TaskId,
string SearchText,
int Page = 1,
int PageSize = 20
) : IBaseQuery<List<MessageDto>>;
public class SearchMessagesQueryHandler : IBaseQueryHandler<SearchMessagesQuery, List<MessageDto>>
{
private readonly IProgramManagerDbContext _context;
private readonly IAuthHelper _authHelper;
public SearchMessagesQueryHandler(IProgramManagerDbContext context, IAuthHelper authHelper)
{
_context = context;
_authHelper = authHelper;
}
public async Task<OperationResult<List<MessageDto>>> Handle(SearchMessagesQuery request, CancellationToken cancellationToken)
{
var currentUserId = _authHelper.GetCurrentUserId();
var skip = (request.Page - 1) * request.PageSize;
var messages = await _context.TaskChatMessages
.Where(m => m.TaskId == request.TaskId &&
m.TextContent != null &&
m.TextContent.Contains(request.SearchText) &&
!m.IsDeleted)
.Include(m => m.ReplyToMessage)
.OrderByDescending(m => m.CreationDate)
.Skip(skip)
.Take(request.PageSize)
.ToListAsync(cancellationToken);
// ✅ گرفتن تمامی کاربران برای نمایش نام کامل فرستنده
var senderUserIds = messages.Select(m => m.SenderUserId).Distinct().ToList();
var users = await _context.Users
.Where(u => senderUserIds.Contains(u.Id))
.ToDictionaryAsync(u => u.Id, u => u.FullName, cancellationToken);
var messageDtos = new List<MessageDto>();
foreach (var message in messages)
{
// ✅ نام فرستنده را از User واقعی بگیر
var senderName = users.GetValueOrDefault(message.SenderUserId, "کاربر ناشناس");
var dto = new MessageDto
{
Id = message.Id,
TaskId = message.TaskId,
SenderUserId = message.SenderUserId,
SenderName = senderName,
MessageType = message.MessageType.ToString(),
TextContent = message.TextContent,
CreationDate = message.CreationDate,
IsMine = message.SenderUserId == currentUserId
};
messageDtos.Add(dto);
}
return OperationResult<List<MessageDto>>.Success(messageDtos);
}
}

View File

@@ -0,0 +1,38 @@
using GozareshgirProgramManager.Domain.FileManagementAgg.Entities;
namespace GozareshgirProgramManager.Application.Services.FileManagement;
/// <summary>
/// سرویس ذخیره‌سازی فایل
/// </summary>
public interface IFileStorageService
{
/// <summary>
/// آپلود فایل
/// </summary>
Task<(string StoragePath, string StorageUrl)> UploadAsync(
Stream fileStream,
string uniqueFileName,
string category);
/// <summary>
/// حذف فایل
/// </summary>
Task DeleteAsync(string storagePath);
/// <summary>
/// دریافت فایل
/// </summary>
Task<Stream?> GetFileStreamAsync(string storagePath);
/// <summary>
/// بررسی وجود فایل
/// </summary>
Task<bool> ExistsAsync(string storagePath);
/// <summary>
/// دریافت URL فایل
/// </summary>
string GetFileUrl(string storagePath);
}

View File

@@ -0,0 +1,34 @@
namespace GozareshgirProgramManager.Application.Services.FileManagement;
/// <summary>
/// سرویس تولید thumbnail برای تصاویر و ویدیوها
/// </summary>
public interface IThumbnailGeneratorService
{
/// <summary>
/// تولید thumbnail برای تصویر
/// </summary>
Task<(string ThumbnailPath, string ThumbnailUrl)?> GenerateImageThumbnailAsync(
string imagePath,
string category,
int width = 200,
int height = 200);
/// <summary>
/// تولید thumbnail برای ویدیو
/// </summary>
Task<(string ThumbnailPath, string ThumbnailUrl)?> GenerateVideoThumbnailAsync(
string videoPath,
string category);
/// <summary>
/// حذف thumbnail
/// </summary>
Task DeleteThumbnailAsync(string thumbnailPath);
/// <summary>
/// دریافت ابعاد تصویر
/// </summary>
Task<(int Width, int Height)?> GetImageDimensionsAsync(string imagePath);
}

View File

@@ -7,6 +7,8 @@ using GozareshgirProgramManager.Domain.SalaryPaymentSettingAgg.Entities;
using GozareshgirProgramManager.Domain.SkillAgg.Entities; using GozareshgirProgramManager.Domain.SkillAgg.Entities;
using GozareshgirProgramManager.Domain.UserAgg.Entities; using GozareshgirProgramManager.Domain.UserAgg.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using GozareshgirProgramManager.Domain.TaskChatAgg.Entities;
using GozareshgirProgramManager.Domain.FileManagementAgg.Entities;
namespace GozareshgirProgramManager.Application._Common.Interfaces; namespace GozareshgirProgramManager.Application._Common.Interfaces;
@@ -26,6 +28,9 @@ public interface IProgramManagerDbContext
DbSet<ProjectTask> ProjectTasks { get; set; } DbSet<ProjectTask> ProjectTasks { get; set; }
DbSet<TaskChatMessage> TaskChatMessages { get; set; }
DbSet<UploadedFile> UploadedFiles { get; set; }
DbSet<Skill> Skills { get; set; } DbSet<Skill> Skills { get; set; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default); Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
} }

View File

@@ -0,0 +1,244 @@
using GozareshgirProgramManager.Domain._Common;
using GozareshgirProgramManager.Domain._Common.Exceptions;
using GozareshgirProgramManager.Domain.FileManagementAgg.Enums;
using GozareshgirProgramManager.Domain.FileManagementAgg.Events;
using FileType = GozareshgirProgramManager.Domain.FileManagementAgg.Enums.FileType;
namespace GozareshgirProgramManager.Domain.FileManagementAgg.Entities;
/// <summary>
/// فایل آپلود شده - Aggregate Root
/// مدیریت مرکزی تمام فایل‌های سیستم
/// </summary>
public class UploadedFile : EntityBase<Guid>
{
private UploadedFile()
{
}
public UploadedFile(
string originalFileName,
long fileSizeBytes,
string mimeType,
FileType fileType,
FileCategory category,
long uploadedByUserId,
StorageProvider storageProvider = StorageProvider.LocalFileSystem)
{
OriginalFileName = originalFileName;
FileSizeBytes = fileSizeBytes;
MimeType = mimeType;
FileType = fileType;
Category = category;
UploadedByUserId = uploadedByUserId;
UploadDate = DateTime.Now;
StorageProvider = storageProvider;
Status = FileStatus.Uploading;
// Generate unique file name
FileExtension = Path.GetExtension(originalFileName);
UniqueFileName = $"{Guid.NewGuid()}{FileExtension}";
ValidateFile();
AddDomainEvent(new FileUploadStartedEvent(Id, originalFileName, uploadedByUserId));
}
// اطلاعات فایل
public string OriginalFileName { get; private set; } = string.Empty;
public string UniqueFileName { get; private set; } = string.Empty;
public string FileExtension { get; private set; } = string.Empty;
public long FileSizeBytes { get; private set; }
public string MimeType { get; private set; } = string.Empty;
public FileType FileType { get; private set; }
public FileCategory Category { get; private set; }
// ذخیره‌سازی
public StorageProvider StorageProvider { get; private set; }
public string? StoragePath { get; private set; }
public string? StorageUrl { get; private set; }
public string? ThumbnailUrl { get; private set; }
// متادیتا
public long UploadedByUserId { get; private set; }
public DateTime UploadDate { get; private set; }
public FileStatus Status { get; private set; }
// اطلاعات تصویر (اختیاری - برای Image)
public int? ImageWidth { get; private set; }
public int? ImageHeight { get; private set; }
// اطلاعات صوت/ویدیو (اختیاری)
public int? DurationSeconds { get; private set; }
// امنیت
public DateTime? VirusScanDate { get; private set; }
public bool? IsVirusScanPassed { get; private set; }
public string? VirusScanResult { get; private set; }
// Soft Delete
public bool IsDeleted { get; private set; }
public DateTime? DeletedDate { get; private set; }
public long? DeletedByUserId { get; private set; }
// Reference tracking (چه entityهایی از این فایل استفاده می‌کنند)
public string? ReferenceEntityType { get; private set; }
public string? ReferenceEntityId { get; private set; }
private void ValidateFile()
{
if (string.IsNullOrWhiteSpace(OriginalFileName))
{
throw new BadRequestException("نام فایل نمی‌تواند خالی باشد");
}
if (FileSizeBytes <= 0)
{
throw new BadRequestException("حجم فایل باید بیشتر از صفر باشد");
}
if (string.IsNullOrWhiteSpace(MimeType))
{
throw new BadRequestException("نوع MIME فایل باید مشخص شود");
}
// محدودیت حجم (مثلاً 100MB)
const long maxSizeBytes = 100 * 1024 * 1024; // 100MB
if (FileSizeBytes > maxSizeBytes)
{
throw new BadRequestException($"حجم فایل نباید بیشتر از {maxSizeBytes / (1024 * 1024)} مگابایت باشد");
}
}
public void CompleteUpload(string storagePath, string storageUrl)
{
if (Status != FileStatus.Uploading)
{
throw new BadRequestException("فایل قبلاً آپلود شده است");
}
if (string.IsNullOrWhiteSpace(storagePath))
{
throw new BadRequestException("مسیر ذخیره‌سازی نمی‌تواند خالی باشد");
}
if (string.IsNullOrWhiteSpace(storageUrl))
{
throw new BadRequestException("URL فایل نمی‌تواند خالی باشد");
}
StoragePath = storagePath;
StorageUrl = storageUrl;
Status = FileStatus.Active;
AddDomainEvent(new FileUploadCompletedEvent(Id, OriginalFileName, StorageUrl, UploadedByUserId));
}
public void SetThumbnail(string thumbnailUrl)
{
if (FileType != FileType.Image && FileType != FileType.Video)
{
throw new BadRequestException("فقط می‌توان برای تصاویر و ویدیوها thumbnail تنظیم کرد");
}
ThumbnailUrl = thumbnailUrl;
}
public void SetImageDimensions(int width, int height)
{
if (FileType != FileType.Image)
{
throw new BadRequestException("فقط می‌توان برای تصاویر ابعاد تنظیم کرد");
}
if (width <= 0 || height <= 0)
{
throw new BadRequestException("ابعاد تصویر باید بیشتر از صفر باشد");
}
ImageWidth = width;
ImageHeight = height;
}
public void SetDuration(int durationSeconds)
{
if (FileType != FileType.Audio && FileType != FileType.Video)
{
throw new BadRequestException("فقط می‌توان برای فایل‌های صوتی و تصویری مدت زمان تنظیم کرد");
}
if (durationSeconds <= 0)
{
throw new BadRequestException("مدت زمان باید بیشتر از صفر باشد");
}
DurationSeconds = durationSeconds;
}
public void MarkAsDeleted(long deletedByUserId)
{
if (IsDeleted)
{
throw new BadRequestException("فایل قبلاً حذف شده است");
}
IsDeleted = true;
DeletedDate = DateTime.Now;
DeletedByUserId = deletedByUserId;
Status = FileStatus.Deleted;
AddDomainEvent(new FileDeletedEvent(Id, OriginalFileName, deletedByUserId));
}
public void SetReference(string entityType, string entityId)
{
ReferenceEntityType = entityType;
ReferenceEntityId = entityId;
}
public bool IsImage()
{
return FileType == FileType.Image;
}
public bool IsVideo()
{
return FileType == FileType.Video;
}
public bool IsAudio()
{
return FileType == FileType.Audio;
}
public bool IsDocument()
{
return FileType == FileType.Document;
}
public bool IsUploadedBy(long userId)
{
return UploadedByUserId == userId;
}
public bool IsActive()
{
return Status == FileStatus.Active && !IsDeleted;
}
public string GetFileSizeFormatted()
{
const long kb = 1024;
const long mb = kb * 1024;
const long gb = mb * 1024;
if (FileSizeBytes >= gb)
return $"{FileSizeBytes / (double)gb:F2} GB";
if (FileSizeBytes >= mb)
return $"{FileSizeBytes / (double)mb:F2} MB";
if (FileSizeBytes >= kb)
return $"{FileSizeBytes / (double)kb:F2} KB";
return $"{FileSizeBytes} Bytes";
}
}

View File

@@ -0,0 +1,15 @@
namespace GozareshgirProgramManager.Domain.FileManagementAgg.Enums;
/// <summary>
/// دسته‌بندی فایل - مشخص می‌کند فایل در کجا استفاده شده
/// </summary>
public enum FileCategory
{
TaskChatMessage = 1, // پیام چت تسک
TaskAttachment = 2, // ضمیمه تسک
ProjectDocument = 3, // مستندات پروژه
UserProfilePhoto = 4, // عکس پروفایل کاربر
Report = 5, // گزارش
Other = 6 // سایر
}

View File

@@ -0,0 +1,13 @@
namespace GozareshgirProgramManager.Domain.FileManagementAgg.Enums;
/// <summary>
/// وضعیت فایل
/// </summary>
public enum FileStatus
{
Uploading = 1, // در حال آپلود
Active = 2, // فعال و قابل استفاده
Deleted = 5, // حذف شده (Soft Delete)
Archived = 6 // آرشیو شده
}

View File

@@ -0,0 +1,15 @@
namespace GozareshgirProgramManager.Domain.FileManagementAgg.Enums;
/// <summary>
/// نوع فایل
/// </summary>
public enum FileType
{
Document = 1, // اسناد (PDF, Word, Excel, etc.)
Image = 2, // تصویر
Video = 3, // ویدیو
Audio = 4, // صوت
Archive = 5, // فایل فشرده (ZIP, RAR)
Other = 6 // سایر
}

View File

@@ -0,0 +1,10 @@
namespace GozareshgirProgramManager.Domain.FileManagementAgg.Enums;
/// <summary>
/// نوع ذخیره‌ساز فایل
/// </summary>
public enum StorageProvider
{
LocalFileSystem = 1, // دیسک محلی سرور
}

View File

@@ -0,0 +1,36 @@
using GozareshgirProgramManager.Domain._Common;
namespace GozareshgirProgramManager.Domain.FileManagementAgg.Events;
// File Upload Events
public record FileUploadStartedEvent(Guid FileId, string FileName, long UploadedByUserId) : IDomainEvent
{
public DateTime OccurredOn { get; init; } = DateTime.Now;
}
public record FileUploadCompletedEvent(Guid FileId, string FileName, string StorageUrl, long UploadedByUserId) : IDomainEvent
{
public DateTime OccurredOn { get; init; } = DateTime.Now;
}
public record FileDeletedEvent(Guid FileId, string FileName, long DeletedByUserId) : IDomainEvent
{
public DateTime OccurredOn { get; init; } = DateTime.Now;
}
// Virus Scan Events
public record FileQuarantinedEvent(Guid FileId, string FileName) : IDomainEvent
{
public DateTime OccurredOn { get; init; } = DateTime.Now;
}
public record FileVirusScanPassedEvent(Guid FileId, string FileName) : IDomainEvent
{
public DateTime OccurredOn { get; init; } = DateTime.Now;
}
public record FileInfectedEvent(Guid FileId, string FileName, string ScanResult) : IDomainEvent
{
public DateTime OccurredOn { get; init; } = DateTime.Now;
}

View File

@@ -0,0 +1,91 @@
using GozareshgirProgramManager.Domain.FileManagementAgg.Entities;
using GozareshgirProgramManager.Domain.FileManagementAgg.Enums;
namespace GozareshgirProgramManager.Domain.FileManagementAgg.Repositories;
/// <summary>
/// Repository برای مدیریت فایل‌های آپلود شده
/// </summary>
public interface IUploadedFileRepository
{
/// <summary>
/// دریافت فایل بر اساس شناسه
/// </summary>
Task<UploadedFile?> GetByIdAsync(Guid fileId);
/// <summary>
/// دریافت فایل بر اساس نام یکتا
/// </summary>
Task<UploadedFile?> GetByUniqueFileNameAsync(string uniqueFileName);
/// <summary>
/// دریافت لیست فایل‌های یک کاربر
/// </summary>
Task<List<UploadedFile>> GetUserFilesAsync(long userId, int pageNumber, int pageSize);
/// <summary>
/// دریافت فایل‌های یک دسته خاص
/// </summary>
Task<List<UploadedFile>> GetByCategoryAsync(FileCategory category, int pageNumber, int pageSize);
/// <summary>
/// دریافت فایل‌های با وضعیت خاص
/// </summary>
Task<List<UploadedFile>> GetByStatusAsync(FileStatus status, int pageNumber, int pageSize);
/// <summary>
/// دریافت فایل‌های یک Reference خاص
/// </summary>
Task<List<UploadedFile>> GetByReferenceAsync(string entityType, string entityId);
/// <summary>
/// جستجو در فایل‌ها بر اساس نام
/// </summary>
Task<List<UploadedFile>> SearchByNameAsync(string searchTerm, int pageNumber, int pageSize);
/// <summary>
/// دریافت تعداد کل فایل‌های یک کاربر
/// </summary>
Task<int> GetUserFilesCountAsync(long userId);
/// <summary>
/// دریافت مجموع حجم فایل‌های یک کاربر (به بایت)
/// </summary>
Task<long> GetUserTotalFileSizeAsync(long userId);
/// <summary>
/// دریافت فایل‌های منقضی شده برای پاک‌سازی
/// </summary>
Task<List<UploadedFile>> GetExpiredFilesAsync(DateTime olderThan);
/// <summary>
/// اضافه کردن فایل جدید
/// </summary>
Task<UploadedFile> AddAsync(UploadedFile file);
/// <summary>
/// به‌روزرسانی فایل
/// </summary>
Task UpdateAsync(UploadedFile file);
/// <summary>
/// حذف فیزیکی فایل (فقط برای cleanup)
/// </summary>
Task DeleteAsync(UploadedFile file);
/// <summary>
/// ذخیره تغییرات
/// </summary>
Task<int> SaveChangesAsync();
/// <summary>
/// بررسی وجود فایل
/// </summary>
Task<bool> ExistsAsync(Guid fileId);
/// <summary>
/// بررسی وجود فایل با نام یکتا
/// </summary>
Task<bool> ExistsByUniqueFileNameAsync(string uniqueFileName);
}

View File

@@ -20,7 +20,7 @@ public class ProjectTask : ProjectHierarchyNode
{ {
PhaseId = phaseId; PhaseId = phaseId;
_sections = new List<TaskSection>(); _sections = new List<TaskSection>();
Priority = TaskPriority.Medium; Priority = ProjectTaskPriority.Low;
AddDomainEvent(new TaskCreatedEvent(Id, phaseId, name)); AddDomainEvent(new TaskCreatedEvent(Id, phaseId, name));
} }
@@ -30,7 +30,7 @@ public class ProjectTask : ProjectHierarchyNode
// Task-specific properties // Task-specific properties
public Enums.TaskStatus Status { get; private set; } = Enums.TaskStatus.NotStarted; public Enums.TaskStatus Status { get; private set; } = Enums.TaskStatus.NotStarted;
public TaskPriority Priority { get; private set; } public ProjectTaskPriority Priority { get; private set; }
public DateTime? StartDate { get; private set; } public DateTime? StartDate { get; private set; }
public DateTime? EndDate { get; private set; } public DateTime? EndDate { get; private set; }
public DateTime? DueDate { get; private set; } public DateTime? DueDate { get; private set; }
@@ -119,7 +119,7 @@ public class ProjectTask : ProjectHierarchyNode
AddDomainEvent(new TaskStatusUpdatedEvent(Id, status)); AddDomainEvent(new TaskStatusUpdatedEvent(Id, status));
} }
public void SetPriority(TaskPriority priority) public void SetPriority(ProjectTaskPriority priority)
{ {
Priority = priority; Priority = priority;
AddDomainEvent(new TaskPriorityUpdatedEvent(Id, priority)); AddDomainEvent(new TaskPriorityUpdatedEvent(Id, priority));

View File

@@ -3,7 +3,7 @@ namespace GozareshgirProgramManager.Domain.ProjectAgg.Enums;
/// <summary> /// <summary>
/// اولویت تسک /// اولویت تسک
/// </summary> /// </summary>
public enum TaskPriority public enum ProjectTaskPriority
{ {
/// <summary> /// <summary>
/// پایین /// پایین

View File

@@ -78,7 +78,7 @@ public record TaskStatusUpdatedEvent(Guid TaskId, TaskStatus Status) : IDomainEv
public DateTime OccurredOn { get; init; } = DateTime.Now; public DateTime OccurredOn { get; init; } = DateTime.Now;
} }
public record TaskPriorityUpdatedEvent(Guid TaskId, TaskPriority Priority) : IDomainEvent public record TaskPriorityUpdatedEvent(Guid TaskId, ProjectTaskPriority Priority) : IDomainEvent
{ {
public DateTime OccurredOn { get; init; } = DateTime.Now; public DateTime OccurredOn { get; init; } = DateTime.Now;
} }

View File

@@ -36,7 +36,7 @@ public interface IProjectTaskRepository : IRepository<Guid, ProjectTask>
/// <summary> /// <summary>
/// Get tasks by priority /// Get tasks by priority
/// </summary> /// </summary>
Task<List<ProjectTask>> GetByPriorityAsync(ProjectAgg.Enums.TaskPriority priority); Task<List<ProjectTask>> GetByPriorityAsync(ProjectAgg.Enums.ProjectTaskPriority priority);
/// <summary> /// <summary>
/// Get tasks assigned to user /// Get tasks assigned to user

View File

@@ -0,0 +1,183 @@
using GozareshgirProgramManager.Domain._Common;
using GozareshgirProgramManager.Domain._Common.Exceptions;
using GozareshgirProgramManager.Domain.TaskChatAgg.Events;
using MessageType = GozareshgirProgramManager.Domain.TaskChatAgg.Enums.MessageType;
namespace GozareshgirProgramManager.Domain.TaskChatAgg.Entities;
/// <summary>
/// پیام چت تسک - Aggregate Root
/// هر کسی که به تسک دسترسی داشته باشد می‌تواند پیام‌ها را ببیند و ارسال کند
/// نیازی به مدیریت گروه و ممبر نیست چون دسترسی از طریق خود تسک کنترل می‌شود
/// </summary>
public class TaskChatMessage : EntityBase<Guid>
{
private TaskChatMessage()
{
}
public TaskChatMessage(Guid taskId, long senderUserId, MessageType messageType,
string? textContent = null,Guid? fileId = null)
{
TaskId = taskId;
SenderUserId = senderUserId;
MessageType = messageType;
TextContent = textContent;
IsEdited = false;
IsDeleted = false;
IsPinned = false;
if (fileId.HasValue)
{
SetFile(fileId.Value);
}
ValidateMessage();
AddDomainEvent(new TaskChatMessageSentEvent(Id, taskId, senderUserId, messageType));
}
// Reference به Task (Foreign Key فقط - بدون Navigation Property برای جلوگیری از coupling)
public Guid TaskId { get; private set; }
public long SenderUserId { get; private set; }
public MessageType MessageType { get; private set; }
// محتوای متنی (برای پیام‌های Text و Caption برای فایل/تصویر)
public string? TextContent { get; private set; }
// ارجاع به فایل (برای پیام‌های File, Voice, Image, Video)
public Guid? FileId { get; private set; }
// پیام Reply
public Guid? ReplyToMessageId { get; private set; }
public TaskChatMessage? ReplyToMessage { get; private set; }
// وضعیت پیام
public bool IsEdited { get; private set; }
public DateTime? EditedDate { get; private set; }
public bool IsDeleted { get; private set; }
public DateTime? DeletedDate { get; private set; }
public bool IsPinned { get; private set; }
public DateTime? PinnedDate { get; private set; }
public long? PinnedByUserId { get; private set; }
private void ValidateMessage()
{
// ✅ بررسی پیام‌های متنی
if (MessageType == MessageType.Text && string.IsNullOrWhiteSpace(TextContent))
{
throw new BadRequestException("پیام متنی نمی‌تواند خالی باشد");
}
// ✅ بررسی پیام‌های فایلی - باید FileId داشته باشند
if ((MessageType == MessageType.File || MessageType == MessageType.Voice ||
MessageType == MessageType.Image || MessageType == MessageType.Video)
&& FileId == null)
{
throw new BadRequestException("پیام‌های فایلی باید شناسه فایل داشته باشند");
}
// ✅ بررسی یادداشت‌های سیستم - باید محتوای متنی داشته باشند
if (MessageType == MessageType.Note && string.IsNullOrWhiteSpace(TextContent))
{
throw new BadRequestException("یادداشت نمی‌تواند خالی باشد");
}
}
public void SetFile(Guid fileId)
{
if (MessageType != MessageType.File && MessageType != MessageType.Image &&
MessageType != MessageType.Video && MessageType != MessageType.Voice)
{
throw new BadRequestException("فقط می‌توان برای پیام‌های فایل، تصویر، ویدیو و صدا شناسه فایل تنظیم کرد");
}
FileId = fileId;
}
public void EditMessage(string newTextContent, long editorUserId)
{
if (IsDeleted)
{
throw new BadRequestException("نمی‌توان پیام حذف شده را ویرایش کرد");
}
if (editorUserId != SenderUserId)
{
throw new BadRequestException("فقط فرستنده می‌تواند پیام را ویرایش کند");
}
if ((MessageType != MessageType.Text && !string.IsNullOrWhiteSpace(TextContent)))
{
throw new BadRequestException("فقط پیام‌های متنی قابل ویرایش هستند");
}
if (string.IsNullOrWhiteSpace(newTextContent))
{
throw new BadRequestException("محتوای پیام نمی‌تواند خالی باشد");
}
TextContent = newTextContent;
IsEdited = true;
EditedDate = DateTime.Now;
AddDomainEvent(new TaskChatMessageEditedEvent(Id, TaskId, editorUserId));
}
public void DeleteMessage(long deleterUserId)
{
if (IsDeleted)
{
throw new BadRequestException("پیام قبلاً حذف شده است");
}
if (deleterUserId != SenderUserId)
{
throw new BadRequestException("فقط فرستنده می‌تواند پیام را حذف کند");
}
IsDeleted = true;
DeletedDate = DateTime.Now;
AddDomainEvent(new TaskChatMessageDeletedEvent(Id, TaskId, deleterUserId));
}
public void PinMessage(long pinnerUserId)
{
if (IsDeleted)
{
throw new BadRequestException("نمی‌توان پیام حذف شده را پین کرد");
}
if (IsPinned)
{
throw new BadRequestException("این پیام قبلاً پین شده است");
}
IsPinned = true;
PinnedDate = DateTime.Now;
PinnedByUserId = pinnerUserId;
AddDomainEvent(new TaskChatMessagePinnedEvent(Id, TaskId, pinnerUserId));
}
public void UnpinMessage(long unpinnerUserId)
{
if (!IsPinned)
{
throw new BadRequestException("این پیام پین نشده است");
}
IsPinned = false;
PinnedDate = null;
PinnedByUserId = null;
AddDomainEvent(new TaskChatMessageUnpinnedEvent(Id, TaskId, unpinnerUserId));
}
public void SetReplyTo(Guid replyToMessageId)
{
ReplyToMessageId = replyToMessageId;
}
public bool IsSentBy(long userId)
{
return SenderUserId == userId;
}
}

View File

@@ -0,0 +1,15 @@
namespace GozareshgirProgramManager.Domain.TaskChatAgg.Enums;
/// <summary>
/// نوع پیام در چت تسک
/// </summary>
public enum MessageType
{
Text = 1, // پیام متنی
File = 2, // فایل (اسناد، PDF، و غیره)
Image = 3, // تصویر
Voice = 4, // پیام صوتی
Video = 5, // ویدیو
Note = 6, // ✅ یادداشت سیستم (برای زمان اضافی و اطلاعات خودکار)
}

View File

@@ -0,0 +1,31 @@
using GozareshgirProgramManager.Domain._Common;
using GozareshgirProgramManager.Domain.TaskChatAgg.Enums;
namespace GozareshgirProgramManager.Domain.TaskChatAgg.Events;
// Message Events
public record TaskChatMessageSentEvent(Guid MessageId, Guid TaskId, long SenderUserId, MessageType MessageType) : IDomainEvent
{
public DateTime OccurredOn { get; init; } = DateTime.Now;
}
public record TaskChatMessageEditedEvent(Guid MessageId, Guid TaskId, long EditorUserId) : IDomainEvent
{
public DateTime OccurredOn { get; init; } = DateTime.Now;
}
public record TaskChatMessageDeletedEvent(Guid MessageId, Guid TaskId, long DeleterUserId) : IDomainEvent
{
public DateTime OccurredOn { get; init; } = DateTime.Now;
}
public record TaskChatMessagePinnedEvent(Guid MessageId, Guid TaskId, long PinnerUserId) : IDomainEvent
{
public DateTime OccurredOn { get; init; } = DateTime.Now;
}
public record TaskChatMessageUnpinnedEvent(Guid MessageId, Guid TaskId, long UnpinnerUserId) : IDomainEvent
{
public DateTime OccurredOn { get; init; } = DateTime.Now;
}

View File

@@ -0,0 +1,75 @@
using GozareshgirProgramManager.Domain.TaskChatAgg.Entities;
namespace GozareshgirProgramManager.Domain.TaskChatAgg.Repositories;
/// <summary>
/// Repository برای مدیریت پیام‌های چت تسک
/// </summary>
public interface ITaskChatMessageRepository
{
/// <summary>
/// دریافت پیام بر اساس شناسه
/// </summary>
Task<TaskChatMessage?> GetByIdAsync(Guid messageId);
/// <summary>
/// دریافت لیست پیام‌های یک تسک (با صفحه‌بندی)
/// </summary>
Task<List<TaskChatMessage>> GetTaskMessagesAsync(Guid taskId, int pageNumber, int pageSize);
/// <summary>
/// دریافت تعداد کل پیام‌های یک تسک
/// </summary>
Task<int> GetTaskMessageCountAsync(Guid taskId);
/// <summary>
/// دریافت پیام‌های پین شده یک تسک
/// </summary>
Task<List<TaskChatMessage>> GetPinnedMessagesAsync(Guid taskId);
/// <summary>
/// دریافت آخرین پیام یک تسک
/// </summary>
Task<TaskChatMessage?> GetLastMessageAsync(Guid taskId);
/// <summary>
/// جستجو در پیام‌های یک تسک
/// </summary>
Task<List<TaskChatMessage>> SearchMessagesAsync(Guid taskId, string searchText, int pageNumber, int pageSize);
/// <summary>
/// دریافت پیام‌های یک کاربر خاص در یک تسک
/// </summary>
Task<List<TaskChatMessage>> GetUserMessagesAsync(Guid taskId, long userId, int pageNumber, int pageSize);
/// <summary>
/// دریافت پیام‌های با فایل (تصویر، ویدیو، فایل و...) - پیام‌هایی که FileId دارند
/// </summary>
Task<List<TaskChatMessage>> GetMediaMessagesAsync(Guid taskId, int pageNumber, int pageSize);
/// <summary>
/// اضافه کردن پیام جدید
/// </summary>
Task<TaskChatMessage> AddAsync(TaskChatMessage message);
/// <summary>
/// به‌روزرسانی پیام
/// </summary>
Task UpdateAsync(TaskChatMessage message);
/// <summary>
/// حذف فیزیکی پیام (در صورت نیاز - معمولاً استفاده نمی‌شود)
/// </summary>
Task DeleteAsync(TaskChatMessage message);
/// <summary>
/// ذخیره تغییرات
/// </summary>
Task<int> SaveChangesAsync();
/// <summary>
/// بررسی وجود پیام
/// </summary>
Task<bool> ExistsAsync(Guid messageId);
}

View File

@@ -4,9 +4,11 @@
using FluentValidation; using FluentValidation;
using GozareshgirProgramManager.Application._Common.Behaviors; using GozareshgirProgramManager.Application._Common.Behaviors;
using GozareshgirProgramManager.Application._Common.Interfaces; using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Application.Services.FileManagement;
using GozareshgirProgramManager.Domain._Common; using GozareshgirProgramManager.Domain._Common;
using GozareshgirProgramManager.Domain.CheckoutAgg.Repositories; using GozareshgirProgramManager.Domain.CheckoutAgg.Repositories;
using GozareshgirProgramManager.Domain.CustomerAgg.Repositories; using GozareshgirProgramManager.Domain.CustomerAgg.Repositories;
using GozareshgirProgramManager.Domain.FileManagementAgg.Repositories;
using GozareshgirProgramManager.Domain.ProjectAgg.Repositories; using GozareshgirProgramManager.Domain.ProjectAgg.Repositories;
using GozareshgirProgramManager.Domain.RoleAgg.Repositories; using GozareshgirProgramManager.Domain.RoleAgg.Repositories;
using GozareshgirProgramManager.Domain.RoleAgg.Repositories; using GozareshgirProgramManager.Domain.RoleAgg.Repositories;
@@ -14,6 +16,7 @@ using GozareshgirProgramManager.Domain.SalaryPaymentSettingAgg.Repositories;
using GozareshgirProgramManager.Domain.SalaryPaymentSettingAgg.Repositories; using GozareshgirProgramManager.Domain.SalaryPaymentSettingAgg.Repositories;
using GozareshgirProgramManager.Domain.SkillAgg.Repositories; using GozareshgirProgramManager.Domain.SkillAgg.Repositories;
using GozareshgirProgramManager.Domain.SkillAgg.Repositories; using GozareshgirProgramManager.Domain.SkillAgg.Repositories;
using GozareshgirProgramManager.Domain.TaskChatAgg.Repositories;
using GozareshgirProgramManager.Domain.UserAgg.Repositories; using GozareshgirProgramManager.Domain.UserAgg.Repositories;
using GozareshgirProgramManager.Infrastructure.Persistence; using GozareshgirProgramManager.Infrastructure.Persistence;
using GozareshgirProgramManager.Infrastructure.Persistence.Context; using GozareshgirProgramManager.Infrastructure.Persistence.Context;
@@ -82,6 +85,14 @@ public static class DependencyInjection
services.AddScoped<IUserRefreshTokenRepository, UserRefreshTokenRepository>(); services.AddScoped<IUserRefreshTokenRepository, UserRefreshTokenRepository>();
// File Management & Task Chat
services.AddScoped<IUploadedFileRepository, Persistence.Repositories.FileManagement.UploadedFileRepository>();
services.AddScoped<ITaskChatMessageRepository, Persistence.Repositories.TaskChat.TaskChatMessageRepository>();
// File Storage Services
services.AddScoped<IFileStorageService, Services.FileManagement.LocalFileStorageService>();
services.AddScoped<IThumbnailGeneratorService, Services.FileManagement.ThumbnailGeneratorService>();
// JWT Settings // JWT Settings
services.Configure<JwtSettings>(configuration.GetSection("JwtSettings")); services.Configure<JwtSettings>(configuration.GetSection("JwtSettings"));

View File

@@ -15,11 +15,21 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.1" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
<!--<PackageReference Include="System.Text.Encodings.Web" Version="10.0.0" />--> <!--<PackageReference Include="System.Text.Encodings.Web" Version="10.0.0" />-->
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Application\GozareshgirProgramManager.Application\GozareshgirProgramManager.Application.csproj" /> <ProjectReference Include="..\..\Application\GozareshgirProgramManager.Application\GozareshgirProgramManager.Application.csproj" />
<ProjectReference Include="..\..\Domain\GozareshgirProgramManager.Domain\GozareshgirProgramManager.Domain.csproj" /> <ProjectReference Include="..\..\Domain\GozareshgirProgramManager.Domain\GozareshgirProgramManager.Domain.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions">
<HintPath>C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\10.0.1\Microsoft.AspNetCore.Hosting.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Hosting.Abstractions">
<HintPath>C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\10.0.1\Microsoft.Extensions.Hosting.Abstractions.dll</HintPath>
</Reference>
</ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,158 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace GozareshgirProgramManager.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class addtaskchatuploadedfile : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "TaskChatMessages",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TaskId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
SenderUserId = table.Column<long>(type: "bigint", nullable: false),
MessageType = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
TextContent = table.Column<string>(type: "nvarchar(4000)", maxLength: 4000, nullable: true),
FileId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
ReplyToMessageId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsEdited = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
EditedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeletedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
IsPinned = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
PinnedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
PinnedByUserId = table.Column<long>(type: "bigint", nullable: true),
CreationDate = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_TaskChatMessages", x => x.Id);
table.ForeignKey(
name: "FK_TaskChatMessages_TaskChatMessages_ReplyToMessageId",
column: x => x.ReplyToMessageId,
principalTable: "TaskChatMessages",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "UploadedFiles",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
OriginalFileName = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false),
UniqueFileName = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false),
FileExtension = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
FileSizeBytes = table.Column<long>(type: "bigint", nullable: false),
MimeType = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
FileType = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
Category = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
StorageProvider = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
StoragePath = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
StorageUrl = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
ThumbnailUrl = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
UploadedByUserId = table.Column<long>(type: "bigint", nullable: false),
UploadDate = table.Column<DateTime>(type: "datetime2", nullable: false),
Status = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
ImageWidth = table.Column<int>(type: "int", nullable: true),
ImageHeight = table.Column<int>(type: "int", nullable: true),
DurationSeconds = table.Column<int>(type: "int", nullable: true),
VirusScanDate = table.Column<DateTime>(type: "datetime2", nullable: true),
IsVirusScanPassed = table.Column<bool>(type: "bit", nullable: true),
VirusScanResult = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeletedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
DeletedByUserId = table.Column<long>(type: "bigint", nullable: true),
ReferenceEntityType = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
ReferenceEntityId = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
CreationDate = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UploadedFiles", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_TaskChatMessages_CreationDate",
table: "TaskChatMessages",
column: "CreationDate");
migrationBuilder.CreateIndex(
name: "IX_TaskChatMessages_FileId",
table: "TaskChatMessages",
column: "FileId");
migrationBuilder.CreateIndex(
name: "IX_TaskChatMessages_IsDeleted",
table: "TaskChatMessages",
column: "IsDeleted");
migrationBuilder.CreateIndex(
name: "IX_TaskChatMessages_ReplyToMessageId",
table: "TaskChatMessages",
column: "ReplyToMessageId");
migrationBuilder.CreateIndex(
name: "IX_TaskChatMessages_SenderUserId",
table: "TaskChatMessages",
column: "SenderUserId");
migrationBuilder.CreateIndex(
name: "IX_TaskChatMessages_TaskId",
table: "TaskChatMessages",
column: "TaskId");
migrationBuilder.CreateIndex(
name: "IX_TaskChatMessages_TaskId_IsPinned",
table: "TaskChatMessages",
columns: new[] { "TaskId", "IsPinned" });
migrationBuilder.CreateIndex(
name: "IX_UploadedFiles_Category",
table: "UploadedFiles",
column: "Category");
migrationBuilder.CreateIndex(
name: "IX_UploadedFiles_IsDeleted",
table: "UploadedFiles",
column: "IsDeleted");
migrationBuilder.CreateIndex(
name: "IX_UploadedFiles_ReferenceEntityType_ReferenceEntityId",
table: "UploadedFiles",
columns: new[] { "ReferenceEntityType", "ReferenceEntityId" });
migrationBuilder.CreateIndex(
name: "IX_UploadedFiles_Status",
table: "UploadedFiles",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_UploadedFiles_UniqueFileName",
table: "UploadedFiles",
column: "UniqueFileName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_UploadedFiles_UploadedByUserId",
table: "UploadedFiles",
column: "UploadedByUserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "TaskChatMessages");
migrationBuilder.DropTable(
name: "UploadedFiles");
}
}
}

View File

@@ -102,6 +102,131 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations
b.ToTable("Customers", (string)null); b.ToTable("Customers", (string)null);
}); });
modelBuilder.Entity("GozareshgirProgramManager.Domain.FileManagementAgg.Entities.UploadedFile", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTime>("CreationDate")
.HasColumnType("datetime2");
b.Property<long?>("DeletedByUserId")
.HasColumnType("bigint");
b.Property<DateTime?>("DeletedDate")
.HasColumnType("datetime2");
b.Property<int?>("DurationSeconds")
.HasColumnType("int");
b.Property<string>("FileExtension")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<long>("FileSizeBytes")
.HasColumnType("bigint");
b.Property<string>("FileType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<int?>("ImageHeight")
.HasColumnType("int");
b.Property<int?>("ImageWidth")
.HasColumnType("int");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<bool?>("IsVirusScanPassed")
.HasColumnType("bit");
b.Property<string>("MimeType")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("OriginalFileName")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<string>("ReferenceEntityId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("ReferenceEntityType")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("StoragePath")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("StorageProvider")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("StorageUrl")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("ThumbnailUrl")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("UniqueFileName")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<DateTime>("UploadDate")
.HasColumnType("datetime2");
b.Property<long>("UploadedByUserId")
.HasColumnType("bigint");
b.Property<DateTime?>("VirusScanDate")
.HasColumnType("datetime2");
b.Property<string>("VirusScanResult")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.HasKey("Id");
b.HasIndex("Category");
b.HasIndex("IsDeleted");
b.HasIndex("Status");
b.HasIndex("UniqueFileName")
.IsUnique();
b.HasIndex("UploadedByUserId");
b.HasIndex("ReferenceEntityType", "ReferenceEntityId");
b.ToTable("UploadedFiles", (string)null);
});
modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.PhaseSection", b => modelBuilder.Entity("GozareshgirProgramManager.Domain.ProjectAgg.Entities.PhaseSection", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@@ -495,6 +620,81 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations
b.ToTable("Skills", (string)null); b.ToTable("Skills", (string)null);
}); });
modelBuilder.Entity("GozareshgirProgramManager.Domain.TaskChatAgg.Entities.TaskChatMessage", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationDate")
.HasColumnType("datetime2");
b.Property<DateTime?>("DeletedDate")
.HasColumnType("datetime2");
b.Property<DateTime?>("EditedDate")
.HasColumnType("datetime2");
b.Property<Guid?>("FileId")
.HasColumnType("uniqueidentifier");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<bool>("IsEdited")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<bool>("IsPinned")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<string>("MessageType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<long?>("PinnedByUserId")
.HasColumnType("bigint");
b.Property<DateTime?>("PinnedDate")
.HasColumnType("datetime2");
b.Property<Guid?>("ReplyToMessageId")
.HasColumnType("uniqueidentifier");
b.Property<long>("SenderUserId")
.HasColumnType("bigint");
b.Property<Guid>("TaskId")
.HasColumnType("uniqueidentifier");
b.Property<string>("TextContent")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.HasKey("Id");
b.HasIndex("CreationDate");
b.HasIndex("FileId");
b.HasIndex("IsDeleted");
b.HasIndex("ReplyToMessageId");
b.HasIndex("SenderUserId");
b.HasIndex("TaskId");
b.HasIndex("TaskId", "IsPinned");
b.ToTable("TaskChatMessages", (string)null);
});
modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.User", b => modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.User", b =>
{ {
b.Property<long>("Id") b.Property<long>("Id")
@@ -779,6 +979,16 @@ namespace GozareshgirProgramManager.Infrastructure.Migrations
b.Navigation("WorkingHoursList"); b.Navigation("WorkingHoursList");
}); });
modelBuilder.Entity("GozareshgirProgramManager.Domain.TaskChatAgg.Entities.TaskChatMessage", b =>
{
b.HasOne("GozareshgirProgramManager.Domain.TaskChatAgg.Entities.TaskChatMessage", "ReplyToMessage")
.WithMany()
.HasForeignKey("ReplyToMessageId")
.OnDelete(DeleteBehavior.NoAction);
b.Navigation("ReplyToMessage");
});
modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.User", b => modelBuilder.Entity("GozareshgirProgramManager.Domain.UserAgg.Entities.User", b =>
{ {
b.OwnsMany("GozareshgirProgramManager.Domain.RoleUserAgg.RoleUser", "RoleUser", b1 => b.OwnsMany("GozareshgirProgramManager.Domain.RoleUserAgg.RoleUser", "RoleUser", b1 =>

View File

@@ -1,14 +1,16 @@
using GozareshgirProgramManager.Application._Common.Interfaces; using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Domain.CheckoutAgg.Entities; using GozareshgirProgramManager.Domain.CheckoutAgg.Entities;
using GozareshgirProgramManager.Domain.CustomerAgg; using GozareshgirProgramManager.Domain.CustomerAgg;
using GozareshgirProgramManager.Application._Common.Interfaces; using GozareshgirProgramManager.Application._Common.Interfaces;
using GozareshgirProgramManager.Domain.CustomerAgg; using GozareshgirProgramManager.Domain.CustomerAgg;
using GozareshgirProgramManager.Domain.FileManagementAgg.Entities;
using GozareshgirProgramManager.Domain.ProjectAgg.Entities; using GozareshgirProgramManager.Domain.ProjectAgg.Entities;
using GozareshgirProgramManager.Domain.RoleAgg.Entities; using GozareshgirProgramManager.Domain.RoleAgg.Entities;
using GozareshgirProgramManager.Domain.RoleUserAgg; using GozareshgirProgramManager.Domain.RoleUserAgg;
using GozareshgirProgramManager.Domain.SalaryPaymentSettingAgg.Entities; using GozareshgirProgramManager.Domain.SalaryPaymentSettingAgg.Entities;
using GozareshgirProgramManager.Domain.UserAgg.Entities; using GozareshgirProgramManager.Domain.UserAgg.Entities;
using GozareshgirProgramManager.Domain.SkillAgg.Entities; using GozareshgirProgramManager.Domain.SkillAgg.Entities;
using GozareshgirProgramManager.Domain.TaskChatAgg.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace GozareshgirProgramManager.Infrastructure.Persistence.Context; namespace GozareshgirProgramManager.Infrastructure.Persistence.Context;
@@ -40,6 +42,13 @@ public class ProgramManagerDbContext : DbContext, IProgramManagerDbContext
public DbSet<Role> Roles { get; set; } = null!; public DbSet<Role> Roles { get; set; } = null!;
public DbSet<Skill> Skills { get; set; } = null!; public DbSet<Skill> Skills { get; set; } = null!;
// File Management
public DbSet<UploadedFile> UploadedFiles { get; set; } = null!;
// Task Chat
public DbSet<TaskChatMessage> TaskChatMessages { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ProgramManagerDbContext).Assembly); modelBuilder.ApplyConfigurationsFromAssembly(typeof(ProgramManagerDbContext).Assembly);

View File

@@ -0,0 +1,87 @@
using GozareshgirProgramManager.Domain.TaskChatAgg.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace GozareshgirProgramManager.Infrastructure.Persistence.Mappings;
public class TaskChatMessageMapping : IEntityTypeConfiguration<TaskChatMessage>
{
public void Configure(EntityTypeBuilder<TaskChatMessage> builder)
{
builder.ToTable("TaskChatMessages");
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.ValueGeneratedNever();
// Task Reference
builder.Property(x => x.TaskId)
.IsRequired();
builder.HasIndex(x => x.TaskId);
// Sender
builder.Property(x => x.SenderUserId)
.IsRequired();
builder.HasIndex(x => x.SenderUserId);
// Message Type
builder.Property(x => x.MessageType)
.IsRequired()
.HasConversion<string>()
.HasMaxLength(50);
// Content
builder.Property(x => x.TextContent)
.HasMaxLength(4000);
// File Reference
builder.Property(x => x.FileId);
builder.HasIndex(x => x.FileId);
// Reply
builder.Property(x => x.ReplyToMessageId);
builder.HasOne(x => x.ReplyToMessage)
.WithMany()
.HasForeignKey(x => x.ReplyToMessageId)
.OnDelete(DeleteBehavior.NoAction);
// Status
builder.Property(x => x.IsEdited)
.IsRequired()
.HasDefaultValue(false);
builder.Property(x => x.EditedDate);
builder.Property(x => x.IsDeleted)
.IsRequired()
.HasDefaultValue(false);
builder.Property(x => x.DeletedDate);
builder.HasIndex(x => x.IsDeleted);
// Pin
builder.Property(x => x.IsPinned)
.IsRequired()
.HasDefaultValue(false);
builder.Property(x => x.PinnedDate);
builder.Property(x => x.PinnedByUserId);
builder.HasIndex(x => new { x.TaskId, x.IsPinned });
// Audit
builder.Property(x => x.CreationDate)
.IsRequired();
builder.HasIndex(x => x.CreationDate);
// Query Filter - پیام‌های حذف نشده
builder.HasQueryFilter(x => !x.IsDeleted);
}
}

View File

@@ -0,0 +1,121 @@
using GozareshgirProgramManager.Domain.FileManagementAgg.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace GozareshgirProgramManager.Infrastructure.Persistence.Mappings;
public class UploadedFileMapping : IEntityTypeConfiguration<UploadedFile>
{
public void Configure(EntityTypeBuilder<UploadedFile> builder)
{
builder.ToTable("UploadedFiles");
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.ValueGeneratedNever();
// اطلاعات فایل
builder.Property(x => x.OriginalFileName)
.IsRequired()
.HasMaxLength(500);
builder.Property(x => x.UniqueFileName)
.IsRequired()
.HasMaxLength(500);
builder.HasIndex(x => x.UniqueFileName)
.IsUnique();
builder.Property(x => x.FileExtension)
.IsRequired()
.HasMaxLength(50);
builder.Property(x => x.FileSizeBytes)
.IsRequired();
builder.Property(x => x.MimeType)
.IsRequired()
.HasMaxLength(200);
builder.Property(x => x.FileType)
.IsRequired()
.HasConversion<string>()
.HasMaxLength(50);
builder.Property(x => x.Category)
.IsRequired()
.HasConversion<string>()
.HasMaxLength(100);
// ذخیره‌سازی
builder.Property(x => x.StorageProvider)
.IsRequired()
.HasConversion<string>()
.HasMaxLength(50);
builder.Property(x => x.StoragePath)
.HasMaxLength(1000);
builder.Property(x => x.StorageUrl)
.HasMaxLength(1000);
builder.Property(x => x.ThumbnailUrl)
.HasMaxLength(1000);
// متادیتا
builder.Property(x => x.UploadedByUserId)
.IsRequired();
builder.Property(x => x.UploadDate)
.IsRequired();
builder.Property(x => x.Status)
.IsRequired()
.HasConversion<string>()
.HasMaxLength(50);
builder.HasIndex(x => x.Status);
builder.HasIndex(x => x.UploadedByUserId);
builder.HasIndex(x => x.Category);
// اطلاعات تصویر
builder.Property(x => x.ImageWidth);
builder.Property(x => x.ImageHeight);
// اطلاعات صوت/ویدیو
builder.Property(x => x.DurationSeconds);
// امنیت
builder.Property(x => x.VirusScanDate);
builder.Property(x => x.IsVirusScanPassed);
builder.Property(x => x.VirusScanResult)
.HasMaxLength(500);
// Soft Delete
builder.Property(x => x.IsDeleted)
.IsRequired()
.HasDefaultValue(false);
builder.Property(x => x.DeletedDate);
builder.Property(x => x.DeletedByUserId);
builder.HasIndex(x => x.IsDeleted);
// Reference Tracking
builder.Property(x => x.ReferenceEntityType)
.HasMaxLength(100);
builder.Property(x => x.ReferenceEntityId)
.HasMaxLength(100);
builder.HasIndex(x => new { x.ReferenceEntityType, x.ReferenceEntityId });
// Audit
builder.Property(x => x.CreationDate)
.IsRequired();
// Query Filter - فایل‌های حذف نشده
builder.HasQueryFilter(x => !x.IsDeleted);
}
}

View File

@@ -0,0 +1,134 @@
using GozareshgirProgramManager.Domain.FileManagementAgg.Entities;
using GozareshgirProgramManager.Domain.FileManagementAgg.Enums;
using GozareshgirProgramManager.Domain.FileManagementAgg.Repositories;
using GozareshgirProgramManager.Infrastructure.Persistence.Context;
using Microsoft.EntityFrameworkCore;
namespace GozareshgirProgramManager.Infrastructure.Persistence.Repositories.FileManagement;
public class UploadedFileRepository : IUploadedFileRepository
{
private readonly ProgramManagerDbContext _context;
public UploadedFileRepository(ProgramManagerDbContext context)
{
_context = context;
}
public async Task<UploadedFile?> GetByIdAsync(Guid fileId)
{
return await _context.UploadedFiles
.FirstOrDefaultAsync(x => x.Id == fileId);
}
public async Task<UploadedFile?> GetByUniqueFileNameAsync(string uniqueFileName)
{
return await _context.UploadedFiles
.FirstOrDefaultAsync(x => x.UniqueFileName == uniqueFileName);
}
public async Task<List<UploadedFile>> GetUserFilesAsync(long userId, int pageNumber, int pageSize)
{
return await _context.UploadedFiles
.Where(x => x.UploadedByUserId == userId)
.OrderByDescending(x => x.UploadDate)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<List<UploadedFile>> GetByCategoryAsync(FileCategory category, int pageNumber, int pageSize)
{
return await _context.UploadedFiles
.Where(x => x.Category == category)
.OrderByDescending(x => x.UploadDate)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<List<UploadedFile>> GetByStatusAsync(FileStatus status, int pageNumber, int pageSize)
{
return await _context.UploadedFiles
.Where(x => x.Status == status)
.OrderByDescending(x => x.UploadDate)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<List<UploadedFile>> GetByReferenceAsync(string entityType, string entityId)
{
return await _context.UploadedFiles
.Where(x => x.ReferenceEntityType == entityType && x.ReferenceEntityId == entityId)
.OrderByDescending(x => x.UploadDate)
.ToListAsync();
}
public async Task<List<UploadedFile>> SearchByNameAsync(string searchTerm, int pageNumber, int pageSize)
{
return await _context.UploadedFiles
.Where(x => x.OriginalFileName.Contains(searchTerm))
.OrderByDescending(x => x.UploadDate)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<int> GetUserFilesCountAsync(long userId)
{
return await _context.UploadedFiles
.CountAsync(x => x.UploadedByUserId == userId);
}
public async Task<long> GetUserTotalFileSizeAsync(long userId)
{
return await _context.UploadedFiles
.Where(x => x.UploadedByUserId == userId)
.SumAsync(x => x.FileSizeBytes);
}
public async Task<List<UploadedFile>> GetExpiredFilesAsync(DateTime olderThan)
{
return await _context.UploadedFiles
.IgnoreQueryFilters() // Include deleted files
.Where(x => x.IsDeleted && x.DeletedDate < olderThan)
.ToListAsync();
}
public async Task<UploadedFile> AddAsync(UploadedFile file)
{
await _context.UploadedFiles.AddAsync(file);
return file;
}
public Task UpdateAsync(UploadedFile file)
{
_context.UploadedFiles.Update(file);
return Task.CompletedTask;
}
public Task DeleteAsync(UploadedFile file)
{
_context.UploadedFiles.Remove(file);
return Task.CompletedTask;
}
public async Task<int> SaveChangesAsync()
{
return await _context.SaveChangesAsync();
}
public async Task<bool> ExistsAsync(Guid fileId)
{
return await _context.UploadedFiles
.AnyAsync(x => x.Id == fileId);
}
public async Task<bool> ExistsByUniqueFileNameAsync(string uniqueFileName)
{
return await _context.UploadedFiles
.AnyAsync(x => x.UniqueFileName == uniqueFileName);
}
}

View File

@@ -58,7 +58,7 @@ public class ProjectTaskRepository : RepositoryBase<Guid, ProjectTask>, IProject
.ToListAsync(); .ToListAsync();
} }
public Task<List<ProjectTask>> GetByPriorityAsync(TaskPriority priority) public Task<List<ProjectTask>> GetByPriorityAsync(ProjectTaskPriority priority)
{ {
return _context.ProjectTasks return _context.ProjectTasks
.Where(t => t.Priority == priority) .Where(t => t.Priority == priority)

View File

@@ -0,0 +1,122 @@
using GozareshgirProgramManager.Domain.TaskChatAgg.Entities;
using GozareshgirProgramManager.Domain.TaskChatAgg.Repositories;
using GozareshgirProgramManager.Infrastructure.Persistence.Context;
using Microsoft.EntityFrameworkCore;
namespace GozareshgirProgramManager.Infrastructure.Persistence.Repositories.TaskChat;
public class TaskChatMessageRepository : ITaskChatMessageRepository
{
private readonly ProgramManagerDbContext _context;
public TaskChatMessageRepository(ProgramManagerDbContext context)
{
_context = context;
}
public async Task<TaskChatMessage?> GetByIdAsync(Guid messageId)
{
return await _context.TaskChatMessages
.Include(x => x.ReplyToMessage)
.FirstOrDefaultAsync(x => x.Id == messageId);
}
public async Task<List<TaskChatMessage>> GetTaskMessagesAsync(Guid taskId, int pageNumber, int pageSize)
{
return await _context.TaskChatMessages
.Where(x => x.TaskId == taskId)
.Include(x => x.ReplyToMessage)
.OrderBy(x => x.CreationDate)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<int> GetTaskMessageCountAsync(Guid taskId)
{
return await _context.TaskChatMessages
.CountAsync(x => x.TaskId == taskId);
}
public async Task<List<TaskChatMessage>> GetPinnedMessagesAsync(Guid taskId)
{
return await _context.TaskChatMessages
.Where(x => x.TaskId == taskId && x.IsPinned)
.Include(x => x.ReplyToMessage)
.OrderByDescending(x => x.PinnedDate)
.ToListAsync();
}
public async Task<TaskChatMessage?> GetLastMessageAsync(Guid taskId)
{
return await _context.TaskChatMessages
.Where(x => x.TaskId == taskId)
.OrderByDescending(x => x.CreationDate)
.FirstOrDefaultAsync();
}
public async Task<List<TaskChatMessage>> SearchMessagesAsync(Guid taskId, string searchText, int pageNumber, int pageSize)
{
return await _context.TaskChatMessages
.Where(x => x.TaskId == taskId &&
x.TextContent != null &&
x.TextContent.Contains(searchText))
.Include(x => x.ReplyToMessage)
.OrderByDescending(x => x.CreationDate)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<List<TaskChatMessage>> GetUserMessagesAsync(Guid taskId, long userId, int pageNumber, int pageSize)
{
return await _context.TaskChatMessages
.Where(x => x.TaskId == taskId && x.SenderUserId == userId)
.Include(x => x.ReplyToMessage)
.OrderByDescending(x => x.CreationDate)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<List<TaskChatMessage>> GetMediaMessagesAsync(Guid taskId, int pageNumber, int pageSize)
{
return await _context.TaskChatMessages
.Where(x => x.TaskId == taskId && x.FileId != null)
.Include(x => x.ReplyToMessage)
.OrderByDescending(x => x.CreationDate)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<TaskChatMessage> AddAsync(TaskChatMessage message)
{
await _context.TaskChatMessages.AddAsync(message);
return message;
}
public Task UpdateAsync(TaskChatMessage message)
{
_context.TaskChatMessages.Update(message);
return Task.CompletedTask;
}
public Task DeleteAsync(TaskChatMessage message)
{
_context.TaskChatMessages.Remove(message);
return Task.CompletedTask;
}
public async Task<int> SaveChangesAsync()
{
return await _context.SaveChangesAsync();
}
public async Task<bool> ExistsAsync(Guid messageId)
{
return await _context.TaskChatMessages
.AnyAsync(x => x.Id == messageId);
}
}

View File

@@ -0,0 +1,108 @@
using GozareshgirProgramManager.Application.Services.FileManagement;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace GozareshgirProgramManager.Infrastructure.Services.FileManagement;
/// <summary>
/// سرویس ذخیره‌سازی فایل در سیستم فایل محلی
/// </summary>
public class LocalFileStorageService : IFileStorageService
{
private readonly string _uploadBasePath;
private readonly string _baseUrl;
public LocalFileStorageService(IConfiguration configuration,
IHttpContextAccessor httpContextAccessor, IHostEnvironment env)
{
// محاسبه مسیر پایه: اگر env نبود، از مسیر فعلی استفاده کن
var contentRoot = env.ContentRootPath;
_uploadBasePath = Path.Combine(contentRoot, "Storage");
// Base URL برای دسترسی به فایل‌ها
var request = httpContextAccessor.HttpContext?.Request;
if (request != null)
{
_baseUrl = $"{request.Scheme}://{request.Host}/storage";
}
else
{
_baseUrl = configuration["FileStorage:BaseUrl"] ?? "http://localhost:5000/storage";
}
// ایجاد پوشه اگر وجود نداشت
if (!Directory.Exists(_uploadBasePath))
{
Directory.CreateDirectory(_uploadBasePath);
}
}
public async Task<(string StoragePath, string StorageUrl)> UploadAsync(
Stream fileStream,
string uniqueFileName,
string category)
{
// ایجاد پوشه دسته‌بندی (مثلاً: Uploads/TaskChatMessage)
var categoryPath = Path.Combine(_uploadBasePath, category);
if (!Directory.Exists(categoryPath))
{
Directory.CreateDirectory(categoryPath);
}
// ایجاد زیرپوشه بر اساس تاریخ (مثلاً: Uploads/TaskChatMessage/2026/01)
var datePath = Path.Combine(categoryPath, DateTime.Now.Year.ToString(),
DateTime.Now.Month.ToString("00"));
if (!Directory.Exists(datePath))
{
Directory.CreateDirectory(datePath);
}
// مسیر کامل فایل
var storagePath = Path.Combine(datePath, uniqueFileName);
// ذخیره فایل
await using var fileStreamOutput = new FileStream(storagePath, FileMode.Create, FileAccess.Write);
await fileStream.CopyToAsync(fileStreamOutput);
// URL فایل
var relativePath = Path.GetRelativePath(_uploadBasePath, storagePath)
.Replace("\\", "/");
var storageUrl = $"{_baseUrl}/{relativePath}";
return (storagePath, storageUrl);
}
public Task DeleteAsync(string storagePath)
{
if (File.Exists(storagePath))
{
File.Delete(storagePath);
}
return Task.CompletedTask;
}
public Task<Stream?> GetFileStreamAsync(string storagePath)
{
if (!File.Exists(storagePath))
{
return Task.FromResult<Stream?>(null);
}
var stream = new FileStream(storagePath, FileMode.Open, FileAccess.Read, FileShare.Read);
return Task.FromResult<Stream?>(stream);
}
public Task<bool> ExistsAsync(string storagePath)
{
return Task.FromResult(File.Exists(storagePath));
}
public string GetFileUrl(string storagePath)
{
var relativePath = Path.GetRelativePath(_uploadBasePath, storagePath)
.Replace("\\", "/");
return $"{_baseUrl}/{relativePath}";
}
}

View File

@@ -0,0 +1,144 @@
using GozareshgirProgramManager.Application.Services.FileManagement;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Formats.Jpeg;
namespace GozareshgirProgramManager.Infrastructure.Services.FileManagement;
/// <summary>
/// سرویس تولید thumbnail با استفاده از ImageSharp
/// </summary>
public class ThumbnailGeneratorService : IThumbnailGeneratorService
{
private readonly string _thumbnailBasePath;
private readonly string _baseUrl;
public ThumbnailGeneratorService(IConfiguration configuration,
IHttpContextAccessor httpContextAccessor)
{
_thumbnailBasePath = configuration["FileStorage:ThumbnailPath"]
?? Path.Combine(Directory.GetCurrentDirectory(), "storage", "Thumbnails");
var request = httpContextAccessor.HttpContext?.Request;
if (request != null)
{
_baseUrl = $"{request.Scheme}://{request.Host}/storage";
}
else
{
_baseUrl = configuration["FileStorage:BaseUrl"] ?? "http://localhost:5000/storage";
}
if (!Directory.Exists(_thumbnailBasePath))
{
Directory.CreateDirectory(_thumbnailBasePath);
}
}
public async Task<(string ThumbnailPath, string ThumbnailUrl)?> GenerateImageThumbnailAsync(
string imagePath,
string category,
int width = 200,
int height = 200)
{
try
{
if (!File.Exists(imagePath))
{
return null;
}
// بارگذاری تصویر
using var image = await Image.LoadAsync(imagePath);
// Resize با حفظ نسبت
image.Mutate(x => x.Resize(new ResizeOptions
{
Size = new Size(width, height),
Mode = ResizeMode.Max
}));
// نام فایل thumbnail
var fileName = Path.GetFileNameWithoutExtension(imagePath);
var extension = Path.GetExtension(imagePath);
var thumbnailFileName = $"{fileName}_thumb{extension}";
// دریافت مسیر و URL توسط متد private
var (thumbnailPath, thumbnailUrl) = GetThumbnailPathAndUrl(thumbnailFileName, category);
// ذخیره thumbnail با کیفیت 80
await image.SaveAsync(thumbnailPath, new JpegEncoder { Quality = 80 });
return (thumbnailPath, thumbnailUrl);
}
catch (Exception)
{
// در صورت خطا null برمی‌گردانیم
return null;
}
}
public async Task<(string ThumbnailPath, string ThumbnailUrl)?> GenerateVideoThumbnailAsync(string videoPath, string category = "general")
{
// TODO: برای Video thumbnail باید از FFmpeg استفاده کنیم
// فعلاً یک placeholder image برمی‌گردانیم
await Task.CompletedTask;
return null;
}
public Task DeleteThumbnailAsync(string thumbnailPath)
{
if (File.Exists(thumbnailPath))
{
File.Delete(thumbnailPath);
}
return Task.CompletedTask;
}
public async Task<(int Width, int Height)?> GetImageDimensionsAsync(string imagePath)
{
try
{
if (!File.Exists(imagePath))
{
return null;
}
var imageInfo = await Image.IdentifyAsync(imagePath);
return (imageInfo.Width, imageInfo.Height);
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// دریافت مسیر فیزیکی و URL برای thumbnail بر اساس category
/// </summary>
private (string ThumbnailPath, string ThumbnailUrl) GetThumbnailPathAndUrl(string thumbnailFileName, string category)
{
var categoryFolder = string.IsNullOrWhiteSpace(category) ? "general" : category;
var categoryPath = Path.Combine(Directory.GetCurrentDirectory(), "storage", categoryFolder);
if (!Directory.Exists(categoryPath))
{
Directory.CreateDirectory(categoryPath);
}
var thumbnailSubPath = Path.Combine(categoryPath, "Thumbnails");
if (!Directory.Exists(thumbnailSubPath))
{
Directory.CreateDirectory(thumbnailSubPath);
}
var thumbnailPath = Path.Combine(thumbnailSubPath, thumbnailFileName);
var thumbnailUrl = $"{_baseUrl}/{categoryFolder}/thumbnails/{thumbnailFileName}";
return (thumbnailPath, thumbnailUrl);
}
}

View File

@@ -0,0 +1,147 @@
using GozareshgirProgramManager.Application._Common.Models;
using GozareshgirProgramManager.Application.Modules.TaskChat.Commands.DeleteMessage;
using GozareshgirProgramManager.Application.Modules.TaskChat.Commands.EditMessage;
using GozareshgirProgramManager.Application.Modules.TaskChat.Commands.PinMessage;
using GozareshgirProgramManager.Application.Modules.TaskChat.Commands.SendMessage;
using GozareshgirProgramManager.Application.Modules.TaskChat.Commands.UnpinMessage;
using GozareshgirProgramManager.Application.Modules.TaskChat.DTOs;
using GozareshgirProgramManager.Application.Modules.TaskChat.Queries.GetMessages;
using GozareshgirProgramManager.Application.Modules.TaskChat.Queries.GetPinnedMessages;
using GozareshgirProgramManager.Application.Modules.TaskChat.Queries.SearchMessages;
using GozareshgirProgramManager.Domain.TaskChatAgg.Enums;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using ServiceHost.BaseControllers;
namespace ServiceHost.Areas.Admin.Controllers.ProgramManager;
/// <summary>
/// کنترلر مدیریت چت تسک
/// </summary>
public class TaskChatController : ProgramManagerBaseController
{
private readonly IMediator _mediator;
public TaskChatController(IMediator mediator)
{
_mediator = mediator;
}
/// <summary>
/// دریافت لیست پیام‌های یک تسک
/// </summary>
/// <param name="taskId">شناسه تسک</param>
/// <param name="messageType">نوع پیام</param>
/// <param name="page">صفحه (پیش‌فرض: 1)</param>
/// <param name="pageSize">تعداد در هر صفحه (پیش‌فرض: 50)</param>
[HttpGet("{taskId:guid}/messages")]
public async Task<ActionResult<OperationResult<PaginationResult<MessageDto>>>> GetMessages(
Guid taskId,
[FromQuery] MessageType? messageType,
[FromQuery] int page = 1,
[FromQuery] int pageSize = 50)
{
var query = new GetMessagesQuery(taskId,messageType, page, pageSize);
var result = await _mediator.Send(query);
return result;
}
/// <summary>
/// دریافت پیام‌های پین شده یک تسک
/// </summary>
/// <param name="taskId">شناسه تسک</param>
[HttpGet("{taskId:guid}/messages/pinned")]
public async Task<ActionResult<OperationResult<List<MessageDto>>>> GetPinnedMessages(Guid taskId)
{
var query = new GetPinnedMessagesQuery(taskId);
var result = await _mediator.Send(query);
return result;
}
/// <summary>
/// جستجو در پیام‌های یک تسک
/// </summary>
/// <param name="taskId">شناسه تسک</param>
/// <param name="search">متن جستجو</param>
/// <param name="page">صفحه</param>
/// <param name="pageSize">تعداد در هر صفحه</param>
[HttpGet("{taskId:guid}/messages/search")]
public async Task<ActionResult<OperationResult<List<MessageDto>>>> SearchMessages(
Guid taskId,
[FromQuery] string search,
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20)
{
var query = new SearchMessagesQuery(taskId, search, page, pageSize);
var result = await _mediator.Send(query);
return result;
}
/// <summary>
/// ارسال پیام جدید (با یا بدون فایل)
/// </summary>
[HttpPost("messages")]
public async Task<ActionResult<OperationResult<MessageDto>>> SendMessage(
[FromForm] SendMessageCommand command)
{
var result = await _mediator.Send(command);
return result;
}
/// <summary>
/// ویرایش پیام (فقط متن)
/// </summary>
/// <param name="messageId">شناسه پیام</param>
/// <param name="request">محتوای جدید</param>
[HttpPut("messages/{messageId:guid}")]
public async Task<ActionResult<OperationResult>> EditMessage(
Guid messageId,
[FromBody] EditMessageRequest request)
{
var command = new EditMessageCommand(messageId, request.NewTextContent);
var result = await _mediator.Send(command);
return result;
}
/// <summary>
/// حذف پیام
/// </summary>
/// <param name="messageId">شناسه پیام</param>
[HttpDelete("messages/{messageId:guid}")]
public async Task<ActionResult<OperationResult>> DeleteMessage(Guid messageId)
{
var command = new DeleteMessageCommand(messageId);
var result = await _mediator.Send(command);
return result;
}
/// <summary>
/// پین کردن پیام
/// </summary>
/// <param name="messageId">شناسه پیام</param>
[HttpPost("messages/{messageId:guid}/pin")]
public async Task<ActionResult<OperationResult>> PinMessage(Guid messageId)
{
var command = new PinMessageCommand(messageId);
var result = await _mediator.Send(command);
return result;
}
/// <summary>
/// برداشتن پین پیام
/// </summary>
/// <param name="messageId">شناسه پیام</param>
[HttpPost("messages/{messageId:guid}/unpin")]
public async Task<ActionResult<OperationResult>> UnpinMessage(Guid messageId)
{
var command = new UnpinMessageCommand(messageId);
var result = await _mediator.Send(command);
return result;
}
}
public class EditMessageRequest
{
public string NewTextContent { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,59 @@
using _0_Framework.Application.Sms;
using CompanyManagment.App.Contracts.SmsResult;
using CompanyManagment.App.Contracts.SmsResult.Dto;
using Microsoft.AspNetCore.Mvc;
using ServiceHost.BaseControllers;
namespace ServiceHost.Areas.Admin.Controllers;
public class SmsReportController : AdminBaseController
{
private readonly ISmsResultApplication _smsResultApplication;
private readonly ISmsService _smsService;
public SmsReportController(ISmsResultApplication smsResultApplication, ISmsService smsService)
{
_smsResultApplication = smsResultApplication;
_smsService = smsService;
}
/// <summary>
/// دریافت لیست پیامک ها
/// </summary>
/// <param name="searchModel"></param>
/// <returns></returns>
[HttpGet]
public async Task<List<SmsReportDto>> GetSmsReportList(SmsReportSearchModel searchModel)
{
var result =await _smsResultApplication.GetSmsReportList(searchModel);
return result;
}
/// <summary>
/// دریافت اطلاعات هر تاریخ برای اکسپند
/// </summary>
/// <param name="searchModel"></param>
/// <param name="date"></param>
/// <returns></returns>
[HttpGet("GetExpandedList")]
public async Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date)
{
var result =await _smsResultApplication.GetSmsReportExpandList(searchModel, date);
return result;
}
/// <summary>
/// گزارش ای پی آی
/// </summary>
/// <param name="startDate"></param>
/// <param name="endDate"></param>
/// <returns></returns>
[HttpGet("GetApiReport")]
public async Task<List<ApiReportDto>> GetApiReport(string startDate, string endDate)
{
var result =await _smsService.GetApiReport(startDate, endDate);
return result;
}
}

View File

@@ -916,6 +916,17 @@ public class institutionContractController : AdminBaseController
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
$"قرارداد های مالی.xlsx"); $"قرارداد های مالی.xlsx");
} }
/// <summary>
/// تنظیم وضعیت ارسال قرارداد
/// </summary>
[HttpPost("set-is-sent")]
public async Task<ActionResult<OperationResult>> SetIsSent([FromBody] SetInstitutionContractSendFlagRequest request)
{
var result = await _institutionContractApplication.SetContractSendFlag(request);
return result;
}
} }
public class InstitutionContractCreationGetRepresentativeIdResponse public class InstitutionContractCreationGetRepresentativeIdResponse

View File

@@ -1289,7 +1289,7 @@
تمدید قرارداد تمدید قرارداد
</p> </p>
</a> </a>
<a permission="30715" class="btn btn-inverse pull-left rad" style="background-color: #f57373;border: 1px solid #f57373;margin-left:5px;" <a class="btn btn-inverse pull-left rad" style="background-color: #f57373;border: 1px solid #f57373;margin-left:5px;"
asp-page="./FinancialStatments" asp-route-name="@item.ContractingPartyName" asp-route-id="@item.ContractingPartyId" asp-route-pageNumber="0"> asp-page="./FinancialStatments" asp-route-name="@item.ContractingPartyName" asp-route-id="@item.ContractingPartyId" asp-route-pageNumber="0">
<i class="fa fa-file-text-o faSize"></i> <i class="fa fa-file-text-o faSize"></i>
<p> <p>

View File

@@ -0,0 +1,25 @@
using _0_Framework.Application;
using CompanyManagment.App.Contracts.Checkout;
using Microsoft.AspNetCore.Mvc;
using ServiceHost.BaseControllers;
namespace ServiceHost.Areas.Client.Controllers;
public class CheckoutController:ClientBaseController
{
private readonly ICheckoutApplication _checkoutApplication;
private readonly long _workshopId;
public CheckoutController(ICheckoutApplication checkoutApplication,IAuthHelper authHelper)
{
_checkoutApplication = checkoutApplication;
_workshopId = authHelper.GetWorkshopId();
}
[HttpGet]
public async Task<ActionResult<PagedResult<CheckoutListClientDto>>> GetList(CheckoutListClientSearchModel searchModel)
{
var res =await _checkoutApplication.GetListForClient(_workshopId, searchModel);
return res;
}
}

View File

@@ -63,10 +63,17 @@ if (!Directory.Exists(logDirectory))
Directory.CreateDirectory(logDirectory); Directory.CreateDirectory(logDirectory);
} }
// فقط برای فایل از Serilog استفاده می‌شود
// تنظیمات MinimumLevel از appsettings.json خوانده می‌شود
Log.Logger = new LoggerConfiguration() Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext() //NO EF Core log
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
//NO DbCommand log
.MinimumLevel.Override("Microsoft.EntityFrameworkCore.Database.Command", LogEventLevel.Warning)
//NO Microsoft Public log
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
//.MinimumLevel.Information()
.WriteTo.File( .WriteTo.File(
path: Path.Combine(logDirectory, "gozareshgir_log.txt"), path: Path.Combine(logDirectory, "gozareshgir_log.txt"),
rollingInterval: RollingInterval.Day, rollingInterval: RollingInterval.Day,
@@ -373,7 +380,8 @@ builder.Services.AddParbad().ConfigureGateways(gateways =>
}); });
// فقط Serilog برای File استفاده می‌شه، کنسول از لاگر پیش‌فرض ASP.NET استفاده می‌کنه if (builder.Environment.IsDevelopment())
{
builder.Host.UseSerilog((context, services, configuration) => builder.Host.UseSerilog((context, services, configuration) =>
{ {
var logConfig = configuration var logConfig = configuration
@@ -391,6 +399,12 @@ builder.Host.UseSerilog((context, services, configuration) =>
); );
}, writeToProviders: true); // این باعث میشه کنسول پیش‌فرض هم کار کنه }, writeToProviders: true); // این باعث میشه کنسول پیش‌فرض هم کار کنه
}
else
{
builder.Host.UseSerilog();
}
Log.Information("SERILOG STARTED SUCCESSFULLY"); Log.Information("SERILOG STARTED SUCCESSFULLY");
var app = builder.Build(); var app = builder.Build();
@@ -486,6 +500,24 @@ app.UseHttpsRedirection();
app.UseStaticFiles(); app.UseStaticFiles();
// Static files برای فایل‌های آپلود شده
var uploadsPath = builder.Configuration["FileStorage:LocalPath"] ?? Path.Combine(Directory.GetCurrentDirectory(), "Storage");
if (!Directory.Exists(uploadsPath))
{
Directory.CreateDirectory(uploadsPath);
}
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(uploadsPath),
RequestPath = "/storage",
OnPrepareResponse = ctx =>
{
// Cache برای فایل‌ها (30 روز)
ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=2592000");
}
});
app.UseCookiePolicy(); app.UseCookiePolicy();

View File

@@ -19,7 +19,7 @@
"sqlDebugging": true, "sqlDebugging": true,
"dotnetRunMessages": "true", "dotnetRunMessages": "true",
"nativeDebugging": true, "nativeDebugging": true,
"applicationUrl": "https://localhost:5004;http://localhost:5003;https://192.168.0.117:5006", "applicationUrl": "https://localhost:5004;http://localhost:5003;",
"jsWebView2Debugging": false, "jsWebView2Debugging": false,
"hotReloadEnabled": true "hotReloadEnabled": true
}, },
@@ -44,7 +44,7 @@
"sqlDebugging": true, "sqlDebugging": true,
"dotnetRunMessages": "true", "dotnetRunMessages": "true",
"nativeDebugging": true, "nativeDebugging": true,
"applicationUrl": "https://localhost:5004;http://localhost:5003;https://192.168.0.117:5006;", "applicationUrl": "https://localhost:5004;http://localhost:5003;",
"jsWebView2Debugging": false, "jsWebView2Debugging": false,
"hotReloadEnabled": true "hotReloadEnabled": true
} }

Some files were not shown because too many files have changed in this diff Show More