Compare commits
25 Commits
Feature/pr
...
c2fdc217b9
| Author | SHA1 | Date | |
|---|---|---|---|
| c2fdc217b9 | |||
| db0047d3d3 | |||
|
|
a14a78309e | ||
|
|
4ccade4c7a | ||
| 085d138bc5 | |||
| 61015ae5c1 | |||
| 57a5000124 | |||
| 7cbb9eef69 | |||
|
|
7a065e9d16 | ||
|
|
e2bab8c1ce | ||
|
|
b088d3089d | ||
|
|
45b4690066 | ||
| 179de86840 | |||
| e0d10510e0 | |||
| a55492b16a | |||
| 9596c8f8b6 | |||
|
|
2fc124bf6d | ||
|
|
1d88ca0fbb | ||
|
|
e2911dfc2a | ||
|
|
cfb96d1277 | ||
|
|
b5c5be2cb6 | ||
|
|
f5c8888137 | ||
|
|
4d7923936e | ||
|
|
532065e3a8 | ||
|
|
f7bfa37a77 |
@@ -45,6 +45,11 @@ public enum TypeOfSmsSetting
|
||||
/// </summary>
|
||||
SendInstitutionContractConfirmationCode,
|
||||
|
||||
/// <summary>
|
||||
/// لینک تاییدیه ایجاد قرارداد مالی
|
||||
/// </summary>
|
||||
SendInstitutionContractConfirmationLink,
|
||||
|
||||
/// <summary>
|
||||
/// یادآور وظایف
|
||||
/// </summary>
|
||||
|
||||
@@ -30,5 +30,22 @@ public class ApiReportDto
|
||||
public string DeliveryState { get; set; }
|
||||
public string DeliveryUnixTime { get; set; }
|
||||
public string DeliveryColor { get; set; }
|
||||
public string FullName { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class SmsDetailsDto
|
||||
{
|
||||
|
||||
|
||||
public string MessageText { 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; }
|
||||
public string FullName { get; set; }
|
||||
|
||||
}
|
||||
@@ -16,15 +16,21 @@ public interface ISmsService
|
||||
/// <param name="code"></param>
|
||||
/// <returns></returns>
|
||||
Task<SentSmsViewModel> SendVerifyCodeToClient(string number, string code);
|
||||
bool SendAccountsInfo(string number,string fullName, string userName);
|
||||
bool SendAccountsInfo(string number, string fullName, string userName);
|
||||
Task<ApiResultViewModel> GetByMessageId(int messId);
|
||||
Task<List<ApiResultViewModel>> GetApiResult(string startDate, string endDate);
|
||||
|
||||
#region ForApi
|
||||
|
||||
Task<List<ApiReportDto>> GetApiReport(string startDate, string endDate);
|
||||
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// دریافت جزئیات پیامک
|
||||
/// </summary>
|
||||
/// <param name="messId"></param>
|
||||
/// <param name="fullName"></param>
|
||||
/// <returns></returns>
|
||||
Task<SmsDetailsDto> GetSmsDetailsByMessageId(int messId, string fullName);
|
||||
#endregion
|
||||
|
||||
string DeliveryStatus(byte? dv);
|
||||
string DeliveryColorStatus(byte? dv);
|
||||
@@ -33,9 +39,9 @@ public interface ISmsService
|
||||
#region Mahan
|
||||
|
||||
Task<double> GetCreditAmount();
|
||||
|
||||
|
||||
public Task<bool> SendInstitutionCreationVerificationLink(string number, string fullName, Guid institutionId, long contractingPartyId, long institutionContractId, string typeOfSms = null);
|
||||
|
||||
|
||||
public Task<bool> SendInstitutionVerificationCode(string number, string code, string contractingPartyFullName,
|
||||
long contractingPartyId, long institutionContractId);
|
||||
|
||||
@@ -68,7 +74,7 @@ public interface ISmsService
|
||||
/// <param name="aprove"></param>
|
||||
/// <returns></returns>
|
||||
Task<(byte status, string message, int messaeId, bool isSucceded)> MonthlyBill(string number, int tamplateId, string fullname, string amount, string id, string aprove);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// پیامک مسدودی طرف حساب
|
||||
/// قراردادهای قدیم
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using _0_Framework.Domain;
|
||||
using _0_Framework.Application.Enums;
|
||||
using _0_Framework.Domain;
|
||||
using CompanyManagment.App.Contracts.SmsResult;
|
||||
using CompanyManagment.App.Contracts.SmsResult.Dto;
|
||||
using System.Collections.Generic;
|
||||
@@ -22,8 +23,9 @@ public interface ISmsResultRepository : IRepository<long, SmsResult>
|
||||
/// </summary>
|
||||
/// <param name="searchModel"></param>
|
||||
/// <param name="date"></param>
|
||||
/// <param name="typeOfSmsSetting"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date);
|
||||
Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date, string typeOfSmsSetting);
|
||||
|
||||
#endregion
|
||||
List<SmsResultViewModel> Search(SmsResultSearchModel searchModel);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using _0_Framework.Application.Enums;
|
||||
using _0_Framework.Domain;
|
||||
using CompanyManagment.App.Contracts.SmsResult;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Company.Domain.SmsResultAgg;
|
||||
@@ -27,4 +28,25 @@ public interface ISmsSettingsRepository : IRepository<long, SmsSetting>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
Task RemoveItem(long id);
|
||||
|
||||
|
||||
#region ForApi
|
||||
|
||||
/// <summary>
|
||||
/// دریافت لیست پیامک های خودکار بر اساس نوع آن
|
||||
/// Api
|
||||
/// </summary>
|
||||
/// <param name="typeOfSmsSetting"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<SmsSettingDto>> GetSmsSettingList(TypeOfSmsSetting typeOfSmsSetting);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// دریافت اطلاعات تنظیمات پیامک جهت ویرایش
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
Task<SmsSettingDto> GetSmsSettingDataToEdit(long id);
|
||||
#endregion
|
||||
}
|
||||
@@ -7,6 +7,7 @@ public class InstitutionContractExtensionCompleteRequest
|
||||
public Guid TemporaryId { get; set; }
|
||||
public bool IsInstallment { get; set; }
|
||||
public long LawId { get; set; }
|
||||
public bool CancelSendVerificationSms { get; set; }
|
||||
}
|
||||
|
||||
public class InstitutionContractCreationCompleteRequest
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace CompanyManagment.App.Contracts.InstitutionContract;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CompanyManagment.App.Contracts.InstitutionContract;
|
||||
|
||||
/// <summary>
|
||||
/// لیست پیامکهای بدهکاران قرارداد ملی
|
||||
@@ -113,10 +115,30 @@ public class BlockSmsListData
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// لیست قراداد های آبی
|
||||
/// جهت ارسال هشدار یا اقدام قضائی
|
||||
///پیامک آنی یادآور
|
||||
/// </summary>
|
||||
public class BlueWarningSmsData
|
||||
public class InstantReminderSendSms
|
||||
{
|
||||
/// <summary>
|
||||
/// نام طرف حساب
|
||||
/// </summary>
|
||||
public string FullName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// مبلغ بدهی
|
||||
/// </summary>
|
||||
public string Amount { get; set; }
|
||||
public List<InstantReminderSmsList> InstantReminderSmsList { get; set; }
|
||||
}
|
||||
|
||||
public class InstantReminderSmsList
|
||||
{
|
||||
/// <summary>
|
||||
/// شماره تماس طرف حساب
|
||||
/// </summary>
|
||||
public string PhoneNumber { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -60,3 +60,43 @@ public class SmsSettingViewModel
|
||||
/// </summary>
|
||||
public List<EditSmsSetting> EditSmsSettings { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// لیست تنظیمات پیامک خودکار
|
||||
/// </summary>
|
||||
public class SmsSettingDto
|
||||
{
|
||||
/// <summary>
|
||||
/// آی دی
|
||||
/// </summary>
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// عدد روز از ماه
|
||||
/// </summary>
|
||||
public int DayOfMonth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// نمایش ساعت و دقیقه
|
||||
/// </summary>
|
||||
public string TimeOfDayDisplay { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class CreateSmsSettingDto
|
||||
{
|
||||
/// <summary>
|
||||
/// عدد روز از ماه
|
||||
/// </summary>
|
||||
public int DayOfMonth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// نمایش ساعت و دقیقه
|
||||
/// </summary>
|
||||
public string TimeOfDayDisplay { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,11 @@ public class SmsReportDto
|
||||
/// </summary>
|
||||
public string SentDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// نوع پیامک
|
||||
/// </summary>
|
||||
public string TypeOfSms { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using _0_Framework.Application;
|
||||
using _0_Framework.Application.Enums;
|
||||
using CompanyManagment.App.Contracts.SmsResult.Dto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -24,8 +25,9 @@ public interface ISmsResultApplication
|
||||
/// </summary>
|
||||
/// <param name="searchModel"></param>
|
||||
/// <param name="date"></param>
|
||||
/// <param name="typeOfSmsSetting"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date);
|
||||
Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date, string typeOfSmsSetting);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -75,4 +75,45 @@ public interface ISmsSettingApplication
|
||||
/// <param name="command"></param>
|
||||
/// <returns></returns>
|
||||
Task<OperationResult> InstantSendBlockSms(List<BlockSmsListData> command);
|
||||
|
||||
|
||||
#region ForApi
|
||||
|
||||
/// <summary>
|
||||
/// دریافت لیست پیامک های خودکار بر اساس نوع آن
|
||||
/// Api
|
||||
/// </summary>
|
||||
/// <param name="typeOfSmsSetting"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<SmsSettingDto>> GetSmsSettingList(TypeOfSmsSetting typeOfSmsSetting);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت اطلاعات تنظیمات پیامک جهت ویرایش
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
Task<SmsSettingDto> GetSmsSettingDataToEdit(long id);
|
||||
|
||||
/// <summary>
|
||||
/// ویرایش تنظیمات پیامک
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <returns></returns>
|
||||
Task<OperationResult> EditSmsSetting(SmsSettingDto command);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت لیست ارسال آنی
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<List<InstantReminderSendSms>> GetInstantReminderSmsListData(TypeOfSmsSetting typeOfSmsSetting);
|
||||
|
||||
/// <summary>
|
||||
/// ارسال پیامک آنی
|
||||
/// </summary>
|
||||
/// <param name="typeOfSmsSetting"></param>
|
||||
/// <param name="phoneNumbers"></param>
|
||||
/// <returns></returns>
|
||||
Task<OperationResult> InstantSmsSendApi(TypeOfSmsSetting typeOfSmsSetting, List<string> phoneNumbers);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,20 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using _0_Framework.Application;
|
||||
using _0_Framework.Application;
|
||||
using _0_Framework.Application.Enums;
|
||||
using _0_Framework.Application.Sms;
|
||||
using Company.Domain.SmsResultAgg;
|
||||
using CompanyManagment.App.Contracts.SmsResult;
|
||||
using CompanyManagment.App.Contracts.SmsResult.Dto;
|
||||
using CompanyManagment.EFCore.Services;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SmsResult = Company.Domain.SmsResultAgg.SmsResult;
|
||||
|
||||
namespace CompanyManagment.Application;
|
||||
|
||||
public class SmsResultApplication : ISmsResultApplication
|
||||
{
|
||||
private readonly ISmsResultRepository _smsResultRepository;
|
||||
private readonly ISmsService _smsService;
|
||||
|
||||
public SmsResultApplication(ISmsResultRepository smsResultRepository)
|
||||
public SmsResultApplication(ISmsResultRepository smsResultRepository, ISmsService smsService)
|
||||
{
|
||||
_smsResultRepository = smsResultRepository;
|
||||
_smsService = smsService;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +31,9 @@ public class SmsResultApplication : ISmsResultApplication
|
||||
return await _smsResultRepository.GetSmsReportList(searchModel);
|
||||
}
|
||||
|
||||
public async Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date)
|
||||
public async Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date, string typeOfSmsSetting)
|
||||
{
|
||||
return await _smsResultRepository.GetSmsReportExpandList(searchModel, date);
|
||||
return await _smsResultRepository.GetSmsReportExpandList(searchModel, date, typeOfSmsSetting);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -67,4 +73,6 @@ public class SmsResultApplication : ISmsResultApplication
|
||||
}).ToList();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -9,6 +9,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace CompanyManagment.Application;
|
||||
|
||||
@@ -17,12 +19,15 @@ public class SmsSettingApplication : ISmsSettingApplication
|
||||
private readonly ISmsSettingsRepository _smsSettingsRepository;
|
||||
private readonly IInstitutionContractRepository _institutionContractRepository;
|
||||
private readonly IInstitutionContractSmsServiceRepository _institutionContractSmsServiceRepository;
|
||||
private readonly IHostEnvironment _hostEnvironment;
|
||||
|
||||
public SmsSettingApplication(ISmsSettingsRepository smsSettingsRepository, IInstitutionContractRepository institutionContractRepository, IInstitutionContractSmsServiceRepository institutionContractSmsServiceRepository)
|
||||
public SmsSettingApplication(ISmsSettingsRepository smsSettingsRepository, IInstitutionContractRepository institutionContractRepository, IInstitutionContractSmsServiceRepository institutionContractSmsServiceRepository, IHostEnvironment hostEnvironment)
|
||||
{
|
||||
_smsSettingsRepository = smsSettingsRepository;
|
||||
_institutionContractRepository = institutionContractRepository;
|
||||
_institutionContractSmsServiceRepository = institutionContractSmsServiceRepository;
|
||||
_hostEnvironment = hostEnvironment;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +136,12 @@ public class SmsSettingApplication : ISmsSettingApplication
|
||||
public async Task<OperationResult> InstantSendReminderSms(List<SmsListData> command)
|
||||
{
|
||||
var op = new OperationResult();
|
||||
if (_hostEnvironment.IsDevelopment())
|
||||
{
|
||||
|
||||
return op.Failed(" در محیط توسعه امکان ارسال وجود ندارد ");
|
||||
|
||||
}
|
||||
string typeOfSms = "یادآور بدهی ماهانه";
|
||||
string sendMessStart = "شروع یادآور آنی";
|
||||
string sendMessEnd = "پایان یادآور آنی";
|
||||
@@ -151,6 +162,13 @@ public class SmsSettingApplication : ISmsSettingApplication
|
||||
public async Task<OperationResult> InstantSendBlockSms(List<BlockSmsListData> command)
|
||||
{
|
||||
var op = new OperationResult();
|
||||
|
||||
if (_hostEnvironment.IsDevelopment())
|
||||
{
|
||||
|
||||
return op.Failed(" در محیط توسعه امکان ارسال وجود ندارد ");
|
||||
|
||||
}
|
||||
string typeOfSms = "اعلام مسدودی طرف حساب";
|
||||
string sendMessStart = "شروع مسدودی آنی";
|
||||
string sendMessEnd = "پایان مسدودی آنی ";
|
||||
@@ -165,4 +183,166 @@ public class SmsSettingApplication : ISmsSettingApplication
|
||||
return op.Failed("موردی انتخاب نشده است");
|
||||
}
|
||||
}
|
||||
|
||||
#region ForApi
|
||||
|
||||
/// <summary>
|
||||
/// دریافت لیست پیامک های خودکار بر اساس نوع آن
|
||||
/// Api
|
||||
/// </summary>
|
||||
/// <param name="typeOfSmsSetting"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<List<SmsSettingDto>> GetSmsSettingList(TypeOfSmsSetting typeOfSmsSetting)
|
||||
{
|
||||
return await _smsSettingsRepository.GetSmsSettingList(typeOfSmsSetting);
|
||||
}
|
||||
|
||||
|
||||
public async Task<SmsSettingDto> GetSmsSettingDataToEdit(long id)
|
||||
{
|
||||
return await _smsSettingsRepository.GetSmsSettingDataToEdit(id);
|
||||
}
|
||||
|
||||
|
||||
public async Task<OperationResult> EditSmsSetting(SmsSettingDto command)
|
||||
{
|
||||
var op = new OperationResult();
|
||||
var editSmsSetting = _smsSettingsRepository.Get(command.Id);
|
||||
var timeSpan = new TimeSpan();
|
||||
if (string.IsNullOrWhiteSpace(command.TimeOfDayDisplay))
|
||||
return op.Failed("ساعت وارد نشده است");
|
||||
|
||||
try
|
||||
{
|
||||
timeSpan = TimeSpan.ParseExact(command.TimeOfDayDisplay, @"hh\:mm", null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return op.Failed("فرمت ساعت اشتباه است");
|
||||
}
|
||||
|
||||
if (command.DayOfMonth < 1 || command.DayOfMonth > 31)
|
||||
{
|
||||
return op.Failed("عدد روز می بایست بین 1 تا 31 باشد");
|
||||
}
|
||||
|
||||
if (_smsSettingsRepository.Exists(x => x.DayOfMonth == command.DayOfMonth && x.TimeOfDay == timeSpan && x.TypeOfSmsSetting == editSmsSetting.TypeOfSmsSetting && x.id != command.Id))
|
||||
return op.Failed("رکورد ایجاد شده تکراری است");
|
||||
|
||||
|
||||
editSmsSetting.Edit(command.DayOfMonth, timeSpan);
|
||||
await _smsSettingsRepository.SaveChangesAsync();
|
||||
|
||||
return op.Succcedded();
|
||||
}
|
||||
|
||||
|
||||
public async Task<List<InstantReminderSendSms>> GetInstantReminderSmsListData(TypeOfSmsSetting typeOfSmsSetting)
|
||||
{
|
||||
var result = new List<InstantReminderSendSms>();
|
||||
|
||||
|
||||
if (typeOfSmsSetting == TypeOfSmsSetting.InstitutionContractDebtReminder)
|
||||
{
|
||||
var data = await _institutionContractSmsServiceRepository.GetSmsListData(DateTime.Now, TypeOfSmsSetting.InstitutionContractDebtReminder);
|
||||
|
||||
if (data.Any())
|
||||
{
|
||||
result = data.GroupBy(x => x.PartyName).Select(m => new InstantReminderSendSms()
|
||||
{
|
||||
FullName = m.Key,
|
||||
Amount = m.Select(c => c.Amount).First(),
|
||||
InstantReminderSmsList = m.Select(c => new InstantReminderSmsList()
|
||||
{
|
||||
PhoneNumber = c.PhoneNumber,
|
||||
}).ToList()
|
||||
|
||||
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
if (typeOfSmsSetting == TypeOfSmsSetting.BlockContractingParty)
|
||||
{
|
||||
var data = await _institutionContractSmsServiceRepository.GetBlockListData(DateTime.Now);
|
||||
|
||||
if (data.Any())
|
||||
{
|
||||
result = data.GroupBy(x => x.PartyName).Select(m => new InstantReminderSendSms()
|
||||
{
|
||||
FullName = m.Key,
|
||||
Amount = m.Select(c => c.Amount).First(),
|
||||
InstantReminderSmsList = m.Select(c => new InstantReminderSmsList()
|
||||
{
|
||||
PhoneNumber = c.PhoneNumber,
|
||||
}).ToList()
|
||||
|
||||
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<OperationResult> InstantSmsSendApi(TypeOfSmsSetting typeOfSmsSetting, List<string> phoneNumbers)
|
||||
{
|
||||
|
||||
var op = new OperationResult();
|
||||
if (_hostEnvironment.IsDevelopment())
|
||||
{
|
||||
var str = "";
|
||||
foreach (var item in phoneNumbers)
|
||||
{
|
||||
str += $" {item}, ";
|
||||
}
|
||||
return op.Failed(" در محیط توسعه امکان ارسال وجود ندارد " + " لیست ارسال شما " + str);
|
||||
|
||||
}
|
||||
if (typeOfSmsSetting == TypeOfSmsSetting.InstitutionContractDebtReminder)
|
||||
{
|
||||
if (phoneNumbers.Any())
|
||||
{
|
||||
var data = await _institutionContractSmsServiceRepository.GetSmsListData(DateTime.Now, TypeOfSmsSetting.InstitutionContractDebtReminder);
|
||||
|
||||
if (data.Any())
|
||||
{
|
||||
phoneNumbers = phoneNumbers.Where(x => x.Length == 11).ToList();
|
||||
var sendItems = data.Where(x => phoneNumbers.Contains(x.PhoneNumber)).ToList();
|
||||
var res = await InstantSendReminderSms(sendItems);
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
return op.Succcedded();
|
||||
}
|
||||
|
||||
return op.Failed("موردی انتخاب نشده است");
|
||||
|
||||
}
|
||||
|
||||
if (typeOfSmsSetting == TypeOfSmsSetting.BlockContractingParty)
|
||||
{
|
||||
if (phoneNumbers.Any())
|
||||
{
|
||||
var data = await _institutionContractSmsServiceRepository.GetBlockListData(DateTime.Now);
|
||||
|
||||
if (data.Any())
|
||||
{
|
||||
phoneNumbers = phoneNumbers.Where(x => x.Length == 11).ToList();
|
||||
var sendItems = data.Where(x => phoneNumbers.Contains(x.PhoneNumber)).ToList();
|
||||
var res = await InstantSendBlockSms(sendItems);
|
||||
return res;
|
||||
}
|
||||
return op.Succcedded();
|
||||
}
|
||||
return op.Failed("موردی انتخاب نشده است");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return op.Failed("خطای انتخاب نوع ارسال");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -2269,7 +2269,8 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
|
||||
extenstionTemp
|
||||
);
|
||||
|
||||
var workshopIds = prevInstitutionContracts.WorkshopGroup?.CurrentWorkshops?.Select(x => x.WorkshopId.Value)??[];
|
||||
var workshopIds = prevInstitutionContracts.WorkshopGroup?.CurrentWorkshops?.Select(x => x.WorkshopId.Value) ??
|
||||
[];
|
||||
|
||||
var workshopsNotInInstitution = employerWorkshopIds.Where(x => !workshopIds.Contains(x)).ToList();
|
||||
|
||||
@@ -2317,7 +2318,7 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
|
||||
WorkshopId = workshop?.id ?? 0,
|
||||
RollCallInPerson = service.RollCallInPerson
|
||||
};
|
||||
}).ToList()??[];
|
||||
}).ToList() ?? [];
|
||||
var notIncludeWorskhopsLeftWork = await _context.LeftWorkList
|
||||
.Where(x => workshopsNotInInstitution.Contains(x.WorkshopId) && x.StartWorkDate <= DateTime.Now &&
|
||||
x.LeftWorkDate >= DateTime.Now)
|
||||
@@ -2959,8 +2960,11 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
|
||||
|
||||
await SaveChangesAsync();
|
||||
|
||||
await _smsService.SendInstitutionCreationVerificationLink(contractingParty.Phone, contractingPartyFullName,
|
||||
entity.PublicId, contractingParty.id, entity.id);
|
||||
if (!request.CancelSendVerificationSms)
|
||||
{
|
||||
await _smsService.SendInstitutionCreationVerificationLink(contractingParty.Phone, contractingPartyFullName,
|
||||
entity.PublicId, contractingParty.id, entity.id);
|
||||
}
|
||||
|
||||
|
||||
await SaveChangesAsync();
|
||||
@@ -3363,10 +3367,10 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
|
||||
institution.VerificationStatus == InstitutionContractVerificationStatus.PendingForVerify
|
||||
? null
|
||||
: institution.VerifierFullName,
|
||||
VerifierPhoneNumber =
|
||||
VerifierPhoneNumber =
|
||||
institution.VerificationStatus == InstitutionContractVerificationStatus.PendingForVerify
|
||||
? null
|
||||
: institution.VerifierPhoneNumber,
|
||||
? null
|
||||
: institution.VerifierPhoneNumber,
|
||||
VerifyCode = institution.VerificationStatus == InstitutionContractVerificationStatus.PendingForVerify
|
||||
? null
|
||||
: institution.VerifyCode,
|
||||
@@ -4366,8 +4370,8 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
|
||||
bool tempCreated = false;
|
||||
if (creationTemp == null)
|
||||
{
|
||||
creationTemp = new InstitutionContractCreationTemp();
|
||||
await _institutionContractCreationTemp.InsertOneAsync(creationTemp);
|
||||
creationTemp = new InstitutionContractCreationTemp();
|
||||
await _institutionContractCreationTemp.InsertOneAsync(creationTemp);
|
||||
}
|
||||
|
||||
List<WorkshopTempViewModel> workshopDetails = [];
|
||||
@@ -5109,9 +5113,11 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
|
||||
|
||||
await SaveChangesAsync();
|
||||
|
||||
await _smsService.SendInstitutionCreationVerificationLink(contractingParty.Phone, contractingPartyFullName,
|
||||
entity.PublicId, contractingParty.id, entity.id);
|
||||
|
||||
if (!request.CancelSendVerificationSms)
|
||||
{
|
||||
await _smsService.SendInstitutionCreationVerificationLink(contractingParty.Phone, contractingPartyFullName,
|
||||
entity.PublicId, contractingParty.id, entity.id);
|
||||
}
|
||||
|
||||
await SaveChangesAsync();
|
||||
await transaction.CommitAsync();
|
||||
|
||||
@@ -2109,11 +2109,24 @@ public class InstitutionContractSmsServiceRepository : RepositoryBase<long, Inst
|
||||
int successProcess = 1;
|
||||
int countList = smsListData.Count;
|
||||
|
||||
#region Test
|
||||
|
||||
|
||||
//for (int i = 0; i < 100; i++)
|
||||
//{
|
||||
// var percent = (successProcess / (double)countList) * 100;
|
||||
// await _hubContext.Clients.Group(SendSmsHub.GetGroupName(7))
|
||||
// .SendAsync("showStatus", (int)percent);
|
||||
// Thread.Sleep(1000);
|
||||
// successProcess += 1;
|
||||
//}
|
||||
#endregion
|
||||
|
||||
foreach (var item in smsListData)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
if (item.TypeOfSmsMethod == "MonthlyBill")
|
||||
{
|
||||
var res = await _smsService.MonthlyBill(item.PhoneNumber, item.TemplateId, item.PartyName,
|
||||
|
||||
@@ -82,25 +82,45 @@ public class RollCallEmployeeRepository : RepositoryBase<long, RollCallEmployee>
|
||||
|
||||
var service = _rollCallServiceRepository.GetAllServiceByWorkshopId(workshopId);
|
||||
|
||||
//اگر سرویس حضور غیاب نداشت
|
||||
if (!service.Any(x => x.StartService.Date <= contractStart.Date && x.EndService.Date >= contractEnd.Date))
|
||||
return false;
|
||||
//var rollCallEmployee = GetByEmployeeIdAndWorkshopId(employeeId, workshopId);
|
||||
|
||||
//if (rollCallEmployee == null)
|
||||
// return false;
|
||||
var rollCallEmployee = _context.RollCallEmployees
|
||||
.Where(x => x.EmployeeId == employeeId && x.WorkshopId == workshopId)
|
||||
.Include(x => x.EmployeesStatus);
|
||||
if (!rollCallEmployee.Any())
|
||||
var rollCallEmployee = _context.RollCallEmployees.Include(xs => xs.EmployeesStatus)
|
||||
.FirstOrDefault(x => x.EmployeeId == employeeId && x.WorkshopId == workshopId);
|
||||
//اگر تنظیمات حضور غیاب نداشت
|
||||
if (rollCallEmployee == null)
|
||||
return false;
|
||||
|
||||
//اگر استاتوس نداشت
|
||||
if (!rollCallEmployee.EmployeesStatus.Any())
|
||||
return false;
|
||||
|
||||
var a = rollCallEmployee.Any(x => x.EmployeesStatus.Any(s =>
|
||||
(s.StartDate <= contractStart.Date && s.EndDate.Date >= contractEnd.Date) ||
|
||||
(s.StartDate.Date <= contractStart.Date && s.EndDate.Date > contractStart.Date)));
|
||||
//var result = _employeeRollCallStatusRepository.w(x => x.RollCallEmployeeId == rollCallEmployee.Id &&
|
||||
// (x.StartDate.Date <= contractStart.Date && x.EndDate.Date >= contractEnd.Date) ||
|
||||
// (x.StartDate.Date <= contractStart.Date && x.EndDate.Date > contractStart.Date));
|
||||
return a;
|
||||
var leftWork =
|
||||
_context.LeftWorkList.FirstOrDefault(x =>
|
||||
x.StartWorkDate <= contractEnd.Date && x.LeftWorkDate > contractStart);
|
||||
if (leftWork == null)
|
||||
return false;
|
||||
|
||||
var status = rollCallEmployee.EmployeesStatus.FirstOrDefault(s =>
|
||||
(s.StartDate <= contractStart.Date && s.EndDate.Date >= contractEnd.Date));
|
||||
//اگر استاتوس کامل پوشش داد
|
||||
if (status != null)
|
||||
return true;
|
||||
|
||||
|
||||
status = rollCallEmployee.EmployeesStatus.FirstOrDefault(s =>
|
||||
(s.StartDate.Date <= contractStart.Date && s.EndDate.Date > contractStart.Date &&
|
||||
s.EndDate.Date < contractEnd.Date));
|
||||
//اگر قبل از پایان فیس استاتوس قطع شده ولی ترک کار داره
|
||||
if (status != null && leftWork.HasLeft)
|
||||
return true;
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
public List<RollCallEmployeeViewModel> GetByWorkshopId(long workshopId)
|
||||
{
|
||||
|
||||
@@ -77,6 +77,9 @@ public class SmsResultRepository : RepositoryBase<long, SmsResult>, ISmsResultRe
|
||||
case TypeOfSmsSetting.SendInstitutionContractConfirmationCode:
|
||||
typeOfSms = "کد تاییدیه قرارداد مالی";
|
||||
break;
|
||||
case TypeOfSmsSetting.SendInstitutionContractConfirmationLink:
|
||||
typeOfSms = "لینک تاییدیه ایجاد قرارداد مالی";
|
||||
break;
|
||||
case TypeOfSmsSetting.TaskReminder:
|
||||
typeOfSms = "یادآور وظایف";
|
||||
break;
|
||||
@@ -147,7 +150,7 @@ public class SmsResultRepository : RepositoryBase<long, SmsResult>, ISmsResultRe
|
||||
|
||||
// مرحله 2: گروهبندی و انتخاب آخرین رکورد هر روز روی Client
|
||||
var grouped = rawQuery
|
||||
.GroupBy(x => x.DateOnly)
|
||||
.GroupBy(x => (x.DateOnly, x.TypeOfSms))
|
||||
.Select(g => g.OrderByDescending(x => x.CreationDate).First())
|
||||
.OrderByDescending(x => x.CreationDate)
|
||||
.ToList();
|
||||
@@ -155,15 +158,16 @@ public class SmsResultRepository : RepositoryBase<long, SmsResult>, ISmsResultRe
|
||||
// مرحله 3: تبدیل به DTO و ToFarsi
|
||||
var result = grouped.Select(x => new SmsReportDto
|
||||
{
|
||||
SentDate = x.CreationDate.ToFarsi()
|
||||
SentDate = x.CreationDate.ToFarsi(),
|
||||
TypeOfSms = x.TypeOfSms
|
||||
}).ToList();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public async Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date)
|
||||
public async Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date, string typeOfSmsSetting)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(date))
|
||||
if(string.IsNullOrWhiteSpace(date) || string.IsNullOrWhiteSpace(typeOfSmsSetting))
|
||||
return new List<SmsReportListDto>();
|
||||
|
||||
if (date.TryToGeorgianDateTime(out var searchDate) == false)
|
||||
@@ -198,41 +202,12 @@ public class SmsResultRepository : RepositoryBase<long, SmsResult>, ISmsResultRe
|
||||
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)
|
||||
if (typeOfSmsSetting.Contains("هشدار"))
|
||||
{
|
||||
query = query.Where(x => x.TypeOfSms.Contains("هشدار")).ToList();
|
||||
}
|
||||
query = query.Where(x => x.TypeOfSms == typeOfSmsSetting).ToList();
|
||||
|
||||
if (searchModel.SendStatus != SendStatus.All)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using _0_Framework.Application.Enums;
|
||||
using _0_Framework.InfraStructure;
|
||||
@@ -68,4 +69,48 @@ public class SmsSettingsRepository : RepositoryBase<long, SmsSetting>, ISmsSetti
|
||||
_context.SmsSettings.Remove(removeItem);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
#region ForApi
|
||||
/// <summary>
|
||||
/// دریافت لیست پیامک های خودکار بر اساس نوع آن
|
||||
/// Api
|
||||
/// </summary>
|
||||
/// <param name="typeOfSmsSetting"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<List<SmsSettingDto>> GetSmsSettingList(TypeOfSmsSetting typeOfSmsSetting)
|
||||
{
|
||||
|
||||
var data = await _context.SmsSettings
|
||||
.Where(x => x.TypeOfSmsSetting == typeOfSmsSetting)
|
||||
.OrderBy(x => x.DayOfMonth).ThenBy(x => x.TimeOfDay)
|
||||
.Select(x =>
|
||||
new SmsSettingDto()
|
||||
{
|
||||
Id = x.id,
|
||||
DayOfMonth = x.DayOfMonth,
|
||||
TimeOfDayDisplay = x.TimeOfDay.ToString(@"hh\:mm")
|
||||
}).ToListAsync();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public async Task<SmsSettingDto> GetSmsSettingDataToEdit(long id)
|
||||
{
|
||||
var edit = new SmsSettingDto();
|
||||
var getItem = await _context.SmsSettings.FirstOrDefaultAsync(x => x.id == id);
|
||||
|
||||
if (getItem != null)
|
||||
{
|
||||
edit.Id = getItem.id;
|
||||
edit.TimeOfDayDisplay = getItem.TimeOfDay.ToString(@"hh\:mm");
|
||||
edit.DayOfMonth = getItem.DayOfMonth;
|
||||
|
||||
}
|
||||
|
||||
return edit;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -2859,7 +2859,7 @@ public class YearlySalaryRepository : RepositoryBase<long, YearlySalary>, IYearl
|
||||
var contactCanToleaveList = new List<ContractsCanToLeave>();
|
||||
var allContractsBetween = _context.Contracts.AsSplitQuery().Include(x => x.WorkingHoursList)
|
||||
.Where(x => x.WorkshopIds == workshopId && x.EmployeeId == employeeId &&
|
||||
x.ContractEnd >= startDate && x.ContarctStart <= endDate).ToList();
|
||||
x.ContractEnd >= startDate && x.ContarctStart <= endDate).OrderBy(x=>x.ContarctStart).ToList();
|
||||
var isWorkshopStaticCheckout = _context.Workshops.FirstOrDefault(x => x.id == workshopId)!.IsStaticCheckout;
|
||||
int mandatoryDays = 0;
|
||||
double allCanToLeave = 0;
|
||||
|
||||
@@ -205,6 +205,26 @@ public class SmsService : ISmsService
|
||||
};
|
||||
return appendData;
|
||||
}
|
||||
|
||||
public async Task<SmsDetailsDto> GetSmsDetailsByMessageId(int messId, string fullName)
|
||||
{
|
||||
|
||||
SmsIr smsIr = new SmsIr("Og5M562igmzJRhQPnq0GdtieYdLgtfikjzxOmeQBPxJjZtyge5Klc046Lfw1mxSa");
|
||||
var response = await smsIr.GetReportAsync(messId);
|
||||
MessageReportResult messages = response.Data;
|
||||
|
||||
var appendData = new SmsDetailsDto()
|
||||
{
|
||||
Mobile = messages.Mobile,
|
||||
MessageText = messages.MessageText,
|
||||
SendUnixTime = UnixTimeStampToDateTime(messages.SendDateTime),
|
||||
DeliveryState = DeliveryStatus(messages.DeliveryState),
|
||||
DeliveryUnixTime = UnixTimeStampToDateTime(messages.DeliveryDateTime),
|
||||
DeliveryColor = DeliveryColorStatus(messages.DeliveryState),
|
||||
FullName = fullName
|
||||
};
|
||||
return appendData;
|
||||
}
|
||||
public async Task<List<ApiResultViewModel>> GetApiResult(string startDate, string endDate)
|
||||
{
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ public record SetTimeProjectCommand(
|
||||
|
||||
public class SetTimeSectionTime
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
public string Description { get; set; }
|
||||
public int Hours { get; set; }
|
||||
public int Minutes { get; set; }
|
||||
|
||||
@@ -349,6 +349,15 @@ public class SetTimeProjectCommandHandler : IBaseCommandHandler<SetTimeProjectCo
|
||||
return OperationResult.Success();
|
||||
}
|
||||
|
||||
private void ValidateTotalTimeNotLessThanSpent(TimeSpan newTotalTime, TimeSpan currentTotalSpent)
|
||||
{
|
||||
if (newTotalTime < currentTotalSpent)
|
||||
{
|
||||
throw new BadRequestException(
|
||||
$"تایم کل سکشن نمیتواند کمتر از زمان مصرف شده ({currentTotalSpent.TotalHours:F2} ساعت) باشد");
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSectionTime(TaskSection section, SetTimeProjectSkillItem sectionItem, long? addedByUserId)
|
||||
{
|
||||
var initData = sectionItem.InitData;
|
||||
@@ -363,18 +372,62 @@ public class SetTimeProjectCommandHandler : IBaseCommandHandler<SetTimeProjectCo
|
||||
// تنظیم زمان اولیه
|
||||
section.UpdateInitialEstimatedHours(initialTime, initData.Description);
|
||||
|
||||
section.ClearAdditionalTimes();
|
||||
// افزودن زمانهای اضافی
|
||||
bool hasAdditionalTime = false;
|
||||
foreach (var additionalTime in sectionItem.AdditionalTime)
|
||||
// مدیریت هوشمند زمانهای اضافی
|
||||
var existingAdditionalTimes = section.AdditionalTimes.ToList();
|
||||
var incomingAdditionalTimes = sectionItem.AdditionalTime ?? [];
|
||||
|
||||
bool hasRealChange = false;
|
||||
|
||||
// حذف آیتمهایی که دیگر در لیست نیستند
|
||||
foreach (var existingTime in existingAdditionalTimes)
|
||||
{
|
||||
var additionalTimeSpan = TimeSpan.FromHours(additionalTime.Hours).Add(TimeSpan.FromMinutes(additionalTime.Minutes));
|
||||
section.AddAdditionalTime(additionalTimeSpan, additionalTime.Description, addedByUserId);
|
||||
hasAdditionalTime = true;
|
||||
var stillExists = incomingAdditionalTimes.Any(x => x.Id == existingTime.Id);
|
||||
if (!stillExists)
|
||||
{
|
||||
section.RemoveAdditionalTime(existingTime.Id);
|
||||
hasRealChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ویرایش یا اضافه کردن آیتمهای جدید
|
||||
foreach (var additionalTime in incomingAdditionalTimes)
|
||||
{
|
||||
var additionalTimeSpan = TimeSpan.FromHours(additionalTime.Hours)
|
||||
.Add(TimeSpan.FromMinutes(additionalTime.Minutes));
|
||||
|
||||
if (additionalTimeSpan <= TimeSpan.Zero)
|
||||
continue;
|
||||
|
||||
var existingAdditionalTime = existingAdditionalTimes.FirstOrDefault(x => x.Id == additionalTime.Id);
|
||||
|
||||
if (existingAdditionalTime != null)
|
||||
{
|
||||
// اگر آیتم با این ID وجود دارد، بررسی کن اگر تغییر کرده باشد
|
||||
if (existingAdditionalTime.HasChanged(additionalTimeSpan, additionalTime.Description))
|
||||
{
|
||||
// ویرایش بدون حذف و ایجاد دوباره
|
||||
existingAdditionalTime.Update(additionalTimeSpan, additionalTime.Description);
|
||||
hasRealChange = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// اگر ID نداشت یا ID جدید بود، اضافه کن
|
||||
if (additionalTime.Id == null || additionalTime.Id == Guid.Empty)
|
||||
{
|
||||
section.AddAdditionalTime(additionalTimeSpan, additionalTime.Description, addedByUserId);
|
||||
hasRealChange = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// تغییر status به Incomplete فقط اگر زمان اضافی اضافه شده باشد و در وضعیتی غیر از ReadyToStart باشد
|
||||
if (hasAdditionalTime && section.Status != TaskSectionStatus.ReadyToStart)
|
||||
// اعتبارسنجی بعد از اعمال تمام تغییرات
|
||||
var currentTotalSpent = section.GetTotalTimeSpent();
|
||||
var newTotalTime = section.FinalEstimatedHours;
|
||||
ValidateTotalTimeNotLessThanSpent(newTotalTime, currentTotalSpent);
|
||||
|
||||
// تغییر status به Incomplete فقط اگر تغییری واقعی اعمال شده باشد و در وضعیتی غیر از ReadyToStart باشد
|
||||
if (hasRealChange && section.Status != TaskSectionStatus.ReadyToStart)
|
||||
{
|
||||
// اگر سکشن درحال انجام است، باید متوقف شود قبل از تغییر status
|
||||
if (section.Status == TaskSectionStatus.InProgress)
|
||||
|
||||
@@ -228,28 +228,30 @@ public class GetProjectsListQueryHandler : IBaseQueryHandler<GetProjectsListQuer
|
||||
// For projects: gather all phases, then tasks, then sections
|
||||
var phases = await _context.ProjectPhases
|
||||
.Where(ph => projectIds.Contains(ph.ProjectId))
|
||||
.Select(ph => ph.Id)
|
||||
.Select(ph => new { ph.Id, ph.ProjectId })
|
||||
.ToListAsync(cancellationToken);
|
||||
var phaseIds = phases.Select(ph => ph.Id).ToList();
|
||||
var tasks = await _context.ProjectTasks
|
||||
.Where(t => phases.Contains(t.PhaseId))
|
||||
.Select(t => t.Id)
|
||||
.Where(t => phaseIds.Contains(t.PhaseId))
|
||||
.Select(t => new { t.Id, t.PhaseId })
|
||||
.ToListAsync(cancellationToken);
|
||||
var taskIds = tasks.Select(t => t.Id).ToList();
|
||||
var sections = await _context.TaskSections
|
||||
.Include(s => s.Skill)
|
||||
.Where(s => tasks.Contains(s.TaskId))
|
||||
.Where(s => taskIds.Contains(s.TaskId))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// Convert to tuple list for AggregatePhaseStatuses
|
||||
var tasksList = tasks.Select(t => (t.Id, t.PhaseId)).ToList();
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
var relatedPhases = phases; // used for filtering tasks by project
|
||||
var relatedTasks = await _context.ProjectTasks
|
||||
.Where(t => t.PhaseId != Guid.Empty && relatedPhases.Contains(t.PhaseId))
|
||||
.Select(t => t.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
var itemSections = sections.Where(s => relatedTasks.Contains(s.TaskId));
|
||||
item.Backend = GetAssignmentStatus(itemSections.FirstOrDefault(x => x.Skill?.Name == "Backend"));
|
||||
item.Front = GetAssignmentStatus(itemSections.FirstOrDefault(x => x.Skill?.Name == "Frontend"));
|
||||
item.Design = GetAssignmentStatus(itemSections.FirstOrDefault(x => x.Skill?.Name == "UI/UX Design"));
|
||||
var projectPhaseIds = phases.Where(ph => ph.ProjectId == item.Id).Select(ph => ph.Id).ToList();
|
||||
|
||||
// برای هر Skill، وضعیتهای تمام Phases را تجمیع کنیم
|
||||
item.Backend = AggregatePhaseStatuses(projectPhaseIds, tasksList, sections, "Backend");
|
||||
item.Front = AggregatePhaseStatuses(projectPhaseIds, tasksList, sections, "Frontend");
|
||||
item.Design = AggregatePhaseStatuses(projectPhaseIds, tasksList, sections, "UI/UX Design");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,24 +261,22 @@ public class GetProjectsListQueryHandler : IBaseQueryHandler<GetProjectsListQuer
|
||||
// For phases: gather tasks, then sections
|
||||
var tasks = await _context.ProjectTasks
|
||||
.Where(t => phaseIds.Contains(t.PhaseId))
|
||||
.Select(t => t.Id)
|
||||
.Select(t => new { t.Id, t.PhaseId })
|
||||
.ToListAsync(cancellationToken);
|
||||
var taskIds = tasks.Select(t => t.Id).ToList();
|
||||
var sections = await _context.TaskSections
|
||||
.Include(s => s.Skill)
|
||||
.Where(s => tasks.Contains(s.TaskId))
|
||||
.Where(s => taskIds.Contains(s.TaskId))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
// Filter tasks for this phase
|
||||
var phaseTaskIds = await _context.ProjectTasks
|
||||
.Where(t => t.PhaseId == item.Id)
|
||||
.Select(t => t.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
var phaseTaskIds = tasks.Where(t => t.PhaseId == item.Id).Select(t => t.Id).ToList();
|
||||
var itemSections = sections.Where(s => phaseTaskIds.Contains(s.TaskId));
|
||||
item.Backend = GetAssignmentStatus(itemSections.FirstOrDefault(x => x.Skill?.Name == "Backend"));
|
||||
item.Front = GetAssignmentStatus(itemSections.FirstOrDefault(x => x.Skill?.Name == "Frontend"));
|
||||
item.Design = GetAssignmentStatus(itemSections.FirstOrDefault(x => x.Skill?.Name == "UI/UX Design"));
|
||||
|
||||
item.Backend = AggregateAssignmentStatus(itemSections.Where(x => x.Skill?.Name == "Backend"));
|
||||
item.Front = AggregateAssignmentStatus(itemSections.Where(x => x.Skill?.Name == "Frontend"));
|
||||
item.Design = AggregateAssignmentStatus(itemSections.Where(x => x.Skill?.Name == "UI/UX Design"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,4 +380,57 @@ public class GetProjectsListQueryHandler : IBaseQueryHandler<GetProjectsListQuer
|
||||
// تعیین تکلیف نشده: نه user دارد نه time
|
||||
return AssignmentStatus.Unassigned;
|
||||
}
|
||||
}
|
||||
|
||||
private static AssignmentStatus AggregatePhaseStatuses(
|
||||
List<Guid> phaseIds,
|
||||
List<(Guid Id, Guid PhaseId)> tasks,
|
||||
List<TaskSection> sections,
|
||||
string skillName)
|
||||
{
|
||||
var phaseStatuses = new List<AssignmentStatus>();
|
||||
|
||||
foreach (var phaseId in phaseIds)
|
||||
{
|
||||
var phaseTaskIds = tasks.Where(t => t.PhaseId == phaseId).Select(t => t.Id).ToList();
|
||||
var phaseSections = sections.Where(s => phaseTaskIds.Contains(s.TaskId) && s.Skill?.Name == skillName);
|
||||
var phaseStatus = AggregateAssignmentStatus(phaseSections);
|
||||
phaseStatuses.Add(phaseStatus);
|
||||
}
|
||||
|
||||
// الآن تجمیع وضعیتهای Phases
|
||||
if (!phaseStatuses.Any())
|
||||
return AssignmentStatus.Unassigned;
|
||||
|
||||
// اگر هر یکی Unassigned باشد → Unassigned
|
||||
if (phaseStatuses.Any(s => s == AssignmentStatus.Unassigned))
|
||||
return AssignmentStatus.Unassigned;
|
||||
|
||||
// اگر Unassigned نیست و هر یکی UserOnly باشد → UserOnly
|
||||
if (phaseStatuses.Any(s => s == AssignmentStatus.UserOnly))
|
||||
return AssignmentStatus.UserOnly;
|
||||
|
||||
// فقط اگر همه Assigned باشند → Assigned
|
||||
return AssignmentStatus.Assigned;
|
||||
}
|
||||
|
||||
private static AssignmentStatus AggregateAssignmentStatus(IEnumerable<TaskSection> sections)
|
||||
{
|
||||
var sectionList = sections.ToList();
|
||||
if (!sectionList.Any())
|
||||
return AssignmentStatus.Unassigned;
|
||||
|
||||
var statuses = sectionList.Select(GetAssignmentStatus).ToList();
|
||||
|
||||
// اگر هر یکی Unassigned باشد → Unassigned (بدترین وضعیت)
|
||||
if (statuses.Any(s => s == AssignmentStatus.Unassigned))
|
||||
return AssignmentStatus.Unassigned;
|
||||
|
||||
// اگر Unassigned نیست و هر یکی UserOnly باشد → UserOnly (وضعیت متوسط)
|
||||
if (statuses.Any(s => s == AssignmentStatus.UserOnly))
|
||||
return AssignmentStatus.UserOnly;
|
||||
|
||||
// فقط اگر همه Assigned باشند → Assigned (بهترین وضعیت)
|
||||
return AssignmentStatus.Assigned;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ public record ProjectSetTimeResponseSkill
|
||||
|
||||
public class ProjectSetTimeResponseSectionAdditionalTime
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public int Hours { get; init; }
|
||||
public int Minutes { get; init; }
|
||||
public string Description { get; init; }
|
||||
|
||||
@@ -70,6 +70,7 @@ public class ProjectSetTimeDetailsQueryHandler
|
||||
AdditionalTimes = section?.AdditionalTimes
|
||||
.Select(x => new ProjectSetTimeResponseSectionAdditionalTime
|
||||
{
|
||||
Id = x.Id,
|
||||
Description = x.Reason ?? "",
|
||||
Hours = (int)x.Hours.TotalHours,
|
||||
Minutes = x.Hours.Minutes,
|
||||
|
||||
@@ -26,4 +26,15 @@ public class TaskSectionAdditionalTime : EntityBase<Guid>
|
||||
{
|
||||
Reason = reason;
|
||||
}
|
||||
|
||||
public void Update(TimeSpan hours, string? reason = null)
|
||||
{
|
||||
Hours = hours;
|
||||
Reason = reason;
|
||||
}
|
||||
|
||||
public bool HasChanged(TimeSpan newHours, string? newReason)
|
||||
{
|
||||
return Hours != newHours || Reason != newReason;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public class FileUploadService : IFileUploadService
|
||||
await _fileRepository.SaveChangesAsync();
|
||||
|
||||
// آپلود فایل به استوریج
|
||||
var categoryFolder = GetCategoryFolderName(category);
|
||||
var categoryFolder = category.ToString();
|
||||
var uploadResult = await _fileStorageService.UploadAsync(
|
||||
fileStream,
|
||||
uploadedFile.UniqueFileName,
|
||||
@@ -227,21 +227,5 @@ public class FileUploadService : IFileUploadService
|
||||
|
||||
return FileType.Document;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت نام پوشه بر اساس دستهبندی
|
||||
/// </summary>
|
||||
private string GetCategoryFolderName(FileCategory category)
|
||||
{
|
||||
return category switch
|
||||
{
|
||||
FileCategory.TaskChatMessage => "TaskChatMessage",
|
||||
FileCategory.TaskAttachment => "TaskAttachment",
|
||||
FileCategory.ProjectDocument => "ProjectDocument",
|
||||
FileCategory.UserProfilePhoto => "UserProfilePhoto",
|
||||
FileCategory.Report => "Report",
|
||||
_ => "Other"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using _0_Framework.Application.Sms;
|
||||
using _0_Framework.Application;
|
||||
using _0_Framework.Application.Enums;
|
||||
using _0_Framework.Application.Sms;
|
||||
using CompanyManagment.App.Contracts.InstitutionContract;
|
||||
using CompanyManagment.App.Contracts.SmsResult;
|
||||
using CompanyManagment.App.Contracts.SmsResult.Dto;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -9,12 +12,14 @@ namespace ServiceHost.Areas.Admin.Controllers;
|
||||
public class SmsReportController : AdminBaseController
|
||||
{
|
||||
private readonly ISmsResultApplication _smsResultApplication;
|
||||
private readonly ISmsSettingApplication _smsSettingApplication;
|
||||
private readonly ISmsService _smsService;
|
||||
|
||||
public SmsReportController(ISmsResultApplication smsResultApplication, ISmsService smsService)
|
||||
public SmsReportController(ISmsResultApplication smsResultApplication, ISmsService smsService, ISmsSettingApplication smsSettingApplication)
|
||||
{
|
||||
_smsResultApplication = smsResultApplication;
|
||||
_smsService = smsService;
|
||||
_smsSettingApplication = smsSettingApplication;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -35,11 +40,25 @@ public class SmsReportController : AdminBaseController
|
||||
/// </summary>
|
||||
/// <param name="searchModel"></param>
|
||||
/// <param name="date"></param>
|
||||
/// <param name="typeOfSmsSetting"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("GetExpandedList")]
|
||||
public async Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date)
|
||||
public async Task<List<SmsReportListDto>> GetSmsReportExpandList(SmsReportSearchModel searchModel, string date, string typeOfSmsSetting)
|
||||
{
|
||||
var result =await _smsResultApplication.GetSmsReportExpandList(searchModel, date);
|
||||
var result =await _smsResultApplication.GetSmsReportExpandList(searchModel, date, typeOfSmsSetting);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت جزئیات پیامک
|
||||
/// </summary>
|
||||
/// <param name="messId"></param>
|
||||
/// <param name="fullName"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("GetSmsDetails")]
|
||||
public async Task<SmsDetailsDto> GetSmsDetails(int messId, string fullName)
|
||||
{
|
||||
var result =await _smsService.GetSmsDetailsByMessageId(messId, fullName);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -56,4 +75,207 @@ public class SmsReportController : AdminBaseController
|
||||
return result;
|
||||
}
|
||||
|
||||
//تنظیمات پیامک خودکار
|
||||
#region SmsSettings
|
||||
|
||||
/// <summary>
|
||||
/// لیست تنظیمات پیامک - یادآور
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("ReminderSmsSettingList")]
|
||||
public async Task<List<SmsSettingDto>> ReminderSmsSettingList()
|
||||
{
|
||||
var result = await _smsSettingApplication.GetSmsSettingList(TypeOfSmsSetting.InstitutionContractDebtReminder);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// لیست تنظیمات پیامک - مسدودی
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("BlockSmsSettingList")]
|
||||
public async Task<List<SmsSettingDto>> BlockSmsSettingList()
|
||||
{
|
||||
var result = await _smsSettingApplication.GetSmsSettingList(TypeOfSmsSetting.BlockContractingParty);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// لیست تنظیمات پیامک - هشدار قضایی
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("WarningSmsSettingList")]
|
||||
public async Task<List<SmsSettingDto>> WarningSmsSettingList()
|
||||
{
|
||||
var result = await _smsSettingApplication.GetSmsSettingList(TypeOfSmsSetting.Warning);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// لیست تنظیمات پیامک - اقدام قضایی
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("LegalActionSmsSettingList")]
|
||||
public async Task<List<SmsSettingDto>> LegalActionSmsSettingList()
|
||||
{
|
||||
var result = await _smsSettingApplication.GetSmsSettingList(TypeOfSmsSetting.LegalAction);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// لیست تنظیمات پیامک - تایید قراداد مالی
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("ContractConfirmSmsSettingList")]
|
||||
public async Task<List<SmsSettingDto>> ContractConfirmSmsSettingList()
|
||||
{
|
||||
var result = await _smsSettingApplication.GetSmsSettingList(TypeOfSmsSetting.InstitutionContractConfirm);
|
||||
return result;
|
||||
}
|
||||
|
||||
//=====================Create=========================
|
||||
|
||||
/// <summary>
|
||||
/// ایجاد پیامک یادآور
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("CreateReminderSmsSetting")]
|
||||
public async Task<ActionResult<OperationResult>> CreateReminderSmsSetting([FromBody] CreateSmsSettingDto command)
|
||||
{
|
||||
var result = await _smsSettingApplication.CreateSmsSetting(command.DayOfMonth, command.TimeOfDayDisplay, TypeOfSmsSetting.InstitutionContractDebtReminder);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ایجاد پیامک مسدودی
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("CreateBlockSmsSetting")]
|
||||
public async Task<ActionResult<OperationResult>> CreateBlockSmsSetting([FromBody] CreateSmsSettingDto command)
|
||||
{
|
||||
var result = await _smsSettingApplication.CreateSmsSetting(command.DayOfMonth, command.TimeOfDayDisplay, TypeOfSmsSetting.BlockContractingParty);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ایجاد پیامک هشدار قضایی
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("CreateWarningSmsSetting")]
|
||||
public async Task<ActionResult<OperationResult>> CreateWarningSmsSetting([FromBody] CreateSmsSettingDto command)
|
||||
{
|
||||
var result = await _smsSettingApplication.CreateSmsSetting(command.DayOfMonth, command.TimeOfDayDisplay, TypeOfSmsSetting.Warning);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ایجاد پیامک اقدام قضایی
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("CreateLegalActionSmsSetting")]
|
||||
public async Task<ActionResult<OperationResult>> CreateLegalActionSmsSetting([FromBody] CreateSmsSettingDto command)
|
||||
{
|
||||
var result = await _smsSettingApplication.CreateSmsSetting(command.DayOfMonth, command.TimeOfDayDisplay, TypeOfSmsSetting.LegalAction);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ایجاد پیامک تایید قرارداد مالی
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("CreateContractConfirmSmsSetting")]
|
||||
public async Task<ActionResult<OperationResult>> CreateContractConfirmSmsSetting([FromBody] CreateSmsSettingDto command)
|
||||
{
|
||||
var result = await _smsSettingApplication.CreateSmsSetting(command.DayOfMonth, command.TimeOfDayDisplay, TypeOfSmsSetting.InstitutionContractConfirm);
|
||||
return result;
|
||||
}
|
||||
//=====================Edit=========================
|
||||
|
||||
/// <summary>
|
||||
/// دریافت اطلاعات ویرایش تنظیمات پیامک
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("GetEditData")]
|
||||
public async Task<SmsSettingDto> GetEditData(long id)
|
||||
{
|
||||
return await _smsSettingApplication.GetSmsSettingDataToEdit(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ویرایش تنظیمات پیامک
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("EditSmsSetting")]
|
||||
public async Task<ActionResult<OperationResult>> EditSmsSetting([FromBody] SmsSettingDto command)
|
||||
{
|
||||
var result =await _smsSettingApplication.EditSmsSetting(command);
|
||||
return result;
|
||||
}
|
||||
|
||||
//=====================Remove=========================
|
||||
|
||||
/// <summary>
|
||||
/// حذف تنظیمات پیامک
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult<OperationResult>> RemoveSmsSetting(long id)
|
||||
{
|
||||
await _smsSettingApplication.RemoveSetting(id);
|
||||
return Ok(new OperationResult().Succcedded());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت لیست ارسال آنی یادآور
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("GetInstantReminderSmsListData")]
|
||||
public async Task<List<InstantReminderSendSms>> GetInstantReminderSmsListData()
|
||||
{
|
||||
var result =await _smsSettingApplication.GetInstantReminderSmsListData(TypeOfSmsSetting.InstitutionContractDebtReminder);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت لیست ارسال آنی مسدودی
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("GetInstantBlockSmsListData")]
|
||||
public async Task<List<InstantReminderSendSms>> GetInstantBlockSmsListData()
|
||||
{
|
||||
var result = await _smsSettingApplication.GetInstantReminderSmsListData(TypeOfSmsSetting.BlockContractingParty);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ارسال پیامک آنی یادآور
|
||||
/// </summary>
|
||||
/// <param name="phoneNumbers"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("InstantReminderSmsSend")]
|
||||
public async Task<ActionResult<OperationResult>> InstantReminderSmsSend([FromBody] List<string> phoneNumbers)
|
||||
{
|
||||
var result = await _smsSettingApplication.InstantSmsSendApi(TypeOfSmsSetting.InstitutionContractDebtReminder, phoneNumbers);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ارسال پیامک آنی مسدودی
|
||||
/// </summary>
|
||||
/// <param name="phoneNumbers"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("InstantBlockSmsSend")]
|
||||
public async Task<ActionResult<OperationResult>> InstantBlockSmsSend([FromBody] List<string> phoneNumbers)
|
||||
{
|
||||
var result = await _smsSettingApplication.InstantSmsSendApi(TypeOfSmsSetting.BlockContractingParty, phoneNumbers);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -796,12 +796,17 @@ public class IndexModel : PageModel
|
||||
|
||||
var firstContract = _contractApplication.GetDetails(ContractsId[0]);
|
||||
var workshop = _workshopApplication.GetDetails(firstContract.WorkshopIds);
|
||||
|
||||
|
||||
//int i = 0;
|
||||
foreach (var item in ContractsId)
|
||||
{
|
||||
var contract = _contractApplication.GetDetails(item);
|
||||
|
||||
|
||||
//=============== استثنا علی خادم دهقان - میز اداری پویا========
|
||||
if (workshop.Id == 482 && contract.EmployeeId == 7175)
|
||||
workshop.IsOldContract = false;
|
||||
//==============================================================
|
||||
|
||||
//var workingHours = _workingHoursApplication.GetByContractId(contract.Id);
|
||||
var workingHours = _workingHoursTempApplication.GetByContractIdConvertToShiftwork4(contract.Id);
|
||||
var separation = _contractApplication.contractSeparation(ConvertYear, ConvertMonth,
|
||||
|
||||
@@ -190,7 +190,7 @@
|
||||
بانک ها </a>
|
||||
</li>
|
||||
<li permission="314">
|
||||
<a class="clik3" asp-page="/Company/SmsResult/Index">
|
||||
<a class="clik3" href="https://admin@(AppSetting.Value.Domain)/automatic-sms-reporting">
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg" style="width: 7px;margin: 0 6px;">
|
||||
<circle cx="6.5" cy="6.5" r="6.5" fill="white"/>
|
||||
</svg>
|
||||
|
||||
@@ -253,7 +253,7 @@
|
||||
بانک ها </a>
|
||||
</li>
|
||||
<li permission="314">
|
||||
<a class="clik3" asp-area="Admin" asp-page="/Company/SmsResult/Index">
|
||||
<a class="clik3" href="https://admin@(AppSetting.Value.Domain)/automatic-sms-reporting">
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg" style="width: 7px;margin: 0 6px;">
|
||||
<circle cx="6.5" cy="6.5" r="6.5" fill="white" />
|
||||
</svg>
|
||||
|
||||
Reference in New Issue
Block a user