using AutoMapper; using Complex.Application.Complex; using Complex.Application.Utility; using Complex.Common.Dto; using Complex.Domain.Entities; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Complex.Application.Services.Basic { public class CostService { private readonly IComplexDBContext _complexDBContext; private IMapper _mapper; private ILogger _logger; public CostService(IComplexDBContext complexDBContext, IMapper mapper, ILogger logger) { _complexDBContext = complexDBContext; _mapper = mapper; _logger = logger; } //public ResultDto> ComplexChargeCycle(Guid ComplexId) //{ // var chargeCycles = _complexDBContext.ChargeCycles.Include(c => c.UnitChargeTypes).ThenInclude(c => c.Unit) // .Where(c => c.ComplexId == ComplexId).ToList(); // var chargeCycleObj = chargeCycles.Select(c => _mapper.Map(c)).ToList(); // foreach (var c in chargeCycleObj) // { // c.Units = chargeCycles.Single(a => a.Id == c.Id).UnitChargeTypes.Where(t => !t.ChargeTypeId.HasValue) // .Select(a => a.Unit).Select(u => _mapper.Map(u)).ToList(); // } // return new ResultDto> // { // Data = chargeCycleObj, // IsSuccess = true, // }; //} /// /// لیست مدیریت شارژ یا هر گونه درآمد و مخارج گرفته می شود /// /// /// /// /// /// public ResultDto> CostCyclsList(Guid ComplexId, DateOnly? StartDate = null, DateOnly? EndDate = null, int? IncomeCostTitleId = null, Guid? UnitId = null) { if (IncomeCostTitleId.HasValue && StartDate.HasValue) StartDate = GetStartOfMonth(StartDate.Value); var ChargeCycleQuery = _complexDBContext.CostCycles.Include(c => c.Units).Where(c => c.ComplexId == ComplexId).AsQueryable(); if (StartDate.HasValue) ChargeCycleQuery = ChargeCycleQuery.Where(c => c.StartDate >= StartDate); if (EndDate.HasValue) ChargeCycleQuery = ChargeCycleQuery.Where(c => c.EndDate <= EndDate); if (IncomeCostTitleId.HasValue) ChargeCycleQuery = ChargeCycleQuery.Where(c => c.IncomeCostTitleId == IncomeCostTitleId); else/*مدیریت شارژ ها فقط*/ ChargeCycleQuery = ChargeCycleQuery.Where(c => c.IncomeCostTitleId == null); if (UnitId.HasValue) ChargeCycleQuery = ChargeCycleQuery.Where(c => c.Units == null || c.Units.Any(u => u.Id == UnitId.Value)); var ChargeCycleList = ChargeCycleQuery.ToList().Select(c => new CostCycleDto { ComplexId = c.ComplexId, Amount = c.Amount, EncouragementAmount = c.EncouragementAmount, EncouragementDay = c.EncouragementDay, EndDate = c.EndDate, StartDate = c.StartDate, Id = c.Id, Title = c.Title, Units = c.Units?.Select(u => { var name = u.UnitName ?? ""; // Some units store "GUID#Name" format; extract just the name part var hashIndex = name.IndexOf('#'); var cleanName = hashIndex >= 0 ? name[(hashIndex + 1)..] : name; return new UnitInfoDto { Id = u.Id, Name = cleanName }; }).ToList(), IncomeCostTitleId = c.IncomeCostTitleId, IncomeCostTitle = c.IncomeCostTitle?.Title }).ToList(); return new ResultDto> { IsSuccess = true, RowCount = ChargeCycleList.Count, Data = ChargeCycleList }; } public ResultDto EditCostCycle(CostCycleDto costCycleDto) { var cycle = _complexDBContext.CostCycles.Single(a => a.Id == costCycleDto.Id); var isUsed = _complexDBContext.Transactions.Any(c => c.CostCycleId == costCycleDto.Id); if (isUsed) return new ResultDto { IsSuccess = false, Message = "این آیتم استفاده شده است. شما می توانید این آیتم را حذف نمایید", Data = false }; cycle = _mapper.Map(costCycleDto); cycle.IncomeCostTitleId = costCycleDto.IncomeCostTitleId; if (costCycleDto.Units != null) { var unitIds = costCycleDto.Units.Select(u => u.Id).ToList(); cycle.Units = _complexDBContext.Units.Where(u => unitIds.Contains(u.Id) && u.ComplexId == costCycleDto.ComplexId).ToList(); } foreach (var u in cycle.Units) { var define = DefineUnitCharge(unitId: u.Id, costCycle: cycle); if (!define.IsSuccess) return new ResultDto { Data = false, IsSuccess = true, Message = define.Message }; } _complexDBContext.SaveChanges(); return new ResultDto { Data = true, IsSuccess = true, Message = "ویرایش با موفقیت انجام شد" }; } private DateOnly GetStartOfMonth(DateOnly date) { var persianDate = PersianDate.GetPDate(date.ToDateTime(new TimeOnly(1, 1))); return date.AddDays(-persianDate.Day + 1); } public ResultDto InsertCostCycle(CostCycleDto costCycleDto) { //اگر مدیریت شارژ هست پس تاریخ ها باید از یکم تا پایان ماه بشود //البته باید در نرم افزار فقط امکان انتخاب ماه را داشته باشد //&^*&^*&^&*^*&^*&^&*^*&^*&^&*^*&^*&^*&^&*^&*^*&&^*&^*&^&*^*^*&^*&^*&^&*^*& // if (costCycleDto.IncomeCostTitleId == null) costCycleDto.StartDate = GetStartOfMonth(costCycleDto.StartDate); var newCycle = _mapper.Map(costCycleDto); if (costCycleDto.Units != null) { var unitIds = costCycleDto.Units.Select(u => u.Id).ToList(); newCycle.Units = _complexDBContext.Units.Where(u => unitIds.Contains(u.Id) && costCycleDto.ComplexId == u.ComplexId).ToList(); } //if(newCycle.Units==null) //{ // //باید چک کرد در این بازه ایا با یکی دیگر که همه واحدها را در نظر میگیرد هم پوشانی نداشته باشد // اما چون در پایین چک میشه پس مشکلی ندارد // var IsPublicCostExist=_complexDBContext.CostCycles.Where(c=> // c.ComplexId== newCycle.ComplexId && ) //} _complexDBContext.CostCycles.Add(newCycle); foreach (var u in newCycle.Units) { var define = DefineUnitCharge(unitId: u.Id, costCycle: newCycle, isNew: true); if (!define.IsSuccess) return new ResultDto { IsSuccess = false, Message = define.Message }; } _complexDBContext.SaveChanges(); return new ResultDto { Data = newCycle.Id, IsSuccess = true, Message = "ثبت با موفقیت انجام شد" }; } public ResultDto DefineUnitCharge(Guid unitId, CostCycle costCycle, bool isNew = false) { //var cycle = _complexDBContext.CostCycles.Where(c => c.Id == costCycleId).FirstOrDefault(); var currentDate = DateOnly.FromDateTime(DateTime.Now); ///ایتم هایی که برای بعد از این تاریخ هست باید حذف شوند و مجدد محاسبه شوند ///اما ایتم هایی که هم تاریخ مدیریت شارژ و هم تاریخ امروز به بعد ///البته در مدیریت شارژ تعریفی نباید اجازه داد تاریخ شروع از آن روز به قبل باشد if (!isNew) { var validDateTimeForDel = currentDate; //validDateTimeForDel.AddHours(-validDateTimeForDel.Hour); //validDateTimeForDel.AddMinutes(-validDateTimeForDel.Minute); //validDateTimeForDel.AddSeconds(-validDateTimeForDel.Second); var itemsForDelete = _complexDBContext.UnitCosts.Where(t => t.CostCycleId == costCycle.Id && t.UnitId == unitId && t.Date >= validDateTimeForDel && !t.TransactionId.HasValue); /* نکته: !t.TransactionId.HasValue .مشکل اینجاست که اگر برای اینده باشه و پرداخت کرده باشه باید یک راهکاری اندیشید چون حذف نمیشه!مشکل دارد */ _complexDBContext.UnitCosts.RemoveRange(itemsForDelete); } //اگر شارژ باشد فقط یک بار در ماه هست اما غیر از ان به هر هزینه تعریفی میتواند //که در ماه میتواند چندین هزینه تعریف کند //اگر برای شارژ باشد //if (!cycle.IncomeCostTitleId.HasValue) // deleteCharges = deleteCharges.Where(t => t.ChargeCycleId == ChargeCycleId); //else //deleteCharges = deleteCharges.Where(t => t.ChargeCycleId == cycle.Id); //همه ماهانه هست و روز خاصی نگرفتیم برای اصل و پرداخت نشده ها حذف شده //پس روز اخر ملاک هست if (!costCycle.IncomeCostTitleId.HasValue) while (currentDate <= costCycle.EndDate) { //میبیند برای این واحد ایا چیزی ثبت شده است یا خیر با دوره واحد دیگر //چون بالا برای دوره واحدی که به این تابع امده حذف را انجام دادیم var oldUnitCost = _complexDBContext.UnitCosts.Include(uc => uc.CostCycle).ThenInclude(c => c.Units) .Where(uc => uc.UnitId == unitId && uc.CostCycle.IncomeCostTitleId == null && uc.Date.Month == currentDate.Month).FirstOrDefault(); if (oldUnitCost != null) { //cycle.Units != null====>پس این واحد هم اختصاصیش بوده*/ //اگر این اختصاصی هست و قبلش اختصاصی هم بوده خطا بده if (costCycle.Units != null && oldUnitCost.CostCycle.Units != null) return new ResultDto { IsSuccess = false, Message = "برای برخی از واحدها در این بازه زمانی قبلا به صورت اختصاصی شارژ تعیین شده است" }; //اگر این اختصاصی هست و قبلش غیراختصاصی بوده باید پاک بشه البته برای همین واحد است که حذف میشه if (costCycle.Units != null && oldUnitCost.CostCycle.Units == null) _complexDBContext.UnitCosts.Remove(oldUnitCost); //اگر این غیر اختصاصی هست و قبلش غیر اختصاصی بوده قبلیش باید پاک بشه یا خطا بدهیم //اما خطا نمیدهیم چون ممکنه بازه ها متفاوت باشند ولی اشتراک داشته باشند. بهتره جای دیگه چک بشه //if (cycle.Units == null && oldUnitCost.CostCycle.Units == null) //اگر این غیر اختصاصی هست و قبلیش اختصاصی بوده که نباید ثبت بشه if (costCycle.Units == null && oldUnitCost.CostCycle.Units != null) return new ResultDto { IsSuccess = true, Message = "نیاز به تعریف مجدد برای این واحد نبود" }; if (costCycle.Units == null && oldUnitCost.CostCycle.Units == null) return new ResultDto { IsSuccess = false, Message = "شما در یک بازه زمانی نمیتوانید بیش از یک مورد عمومی تعریف کنید" }; } //-------------- var newUC = new UnitCost { CostCycleId = costCycle.Id, UnitId = unitId, Date = currentDate, TransactionId = null }; currentDate = currentDate.AddMonths(1); _complexDBContext.UnitCosts.Add(newUC); } else { var newUC = new UnitCost { CostCycleId = costCycle.Id, UnitId = unitId, Date = currentDate, TransactionId = null }; _complexDBContext.UnitCosts.Add(newUC); } // _complexDBContext.SaveChanges(); return new ResultDto { Message = "ثبت با موفقیت انجام شد", IsSuccess = true, }; } /// /// بدهی یک واحد /// /// /// /// public ResultDto CalculateCostForUnit(Guid unitId, int? incomeCostTitleId) { var currentDate = DateOnly.FromDateTime(DateTime.Now); var unitCost = _complexDBContext.UnitCosts.Where(uc => unitId == uc.UnitId && !uc.TransactionId.HasValue && uc.CostCycle.IncomeCostTitleId == incomeCostTitleId && uc.Date <= currentDate); var LastPaymentDate = _complexDBContext.UnitCosts.Where(uc => unitId == uc.UnitId && uc.TransactionId.HasValue/*پرداخت شده000000000000*/ && uc.CostCycle.IncomeCostTitleId == incomeCostTitleId && uc.Date <= currentDate) .OrderBy(uc => uc.Date).LastOrDefault()?.Date; var costTitle = "شارژ"; if (incomeCostTitleId.HasValue) costTitle = _complexDBContext.IncomeCostTitles.First(i => i.Id == incomeCostTitleId).Title; return new ResultDto { Data = new CostOfUnitInfoDto { CostMount = unitCost.Sum(c => c.CostCycle.Amount), CostTitle = costTitle, LastPaymentDate = LastPaymentDate, UnitId = unitId }, IsSuccess = true }; } /// /// لیست بدهی های یک واحد /// /// /// public ResultDto> CalculateAllCostForUnits(List unitIds) { var currentDate = DateOnly.FromDateTime(DateTime.Now); var unitCharges = _complexDBContext.UnitCosts.Include(uc => uc.CostCycle).ThenInclude(cc => cc.IncomeCostTitle) .Where(uc => unitIds.Contains(uc.UnitId) && !uc.TransactionId.HasValue && uc.Date <= currentDate).ToList(); var resultList = new List(); foreach (var unitid in unitIds) { resultList.AddRange(unitCharges.Where(u => u.UnitId == unitid).Select(uc => new CostOfUnitInfoDto { CostMount = uc.CostCycle.Amount, CostTitle = uc.CostCycle.IncomeCostTitleId.HasValue ? uc.CostCycle.IncomeCostTitle?.Title : "شارژ", Date = uc.Date, UnitId = unitid, CostCount = unitCharges.Where(u => u.UnitId == unitid).Count() }).ToList()); } return new ResultDto> { Data = resultList, IsSuccess = true }; //{ // Data = unitCharges.Sum(c => c.CostCycle.Amount), // Message = "تمام بدهی" //}; } /// /// تاریخچه پرداختی ها و باقیمانده از آن /// /// /// /// /// /// public ResultDto> CostsForUnits(List unitIds, int? incomeCostTitleId, DateOnly startDate, DateOnly endDate) { var unitCharges = _complexDBContext.UnitCosts.Include(uc => uc.CostCycle).ThenInclude(cc => cc.IncomeCostTitle) .Where(uc => unitIds.Contains(uc.UnitId) //!uc.TransactionId.HasValue && uc.Date >= startDate && uc.Date <= endDate).ToList();//.GroupBy(g => g.UnitId); var resultList = new List(); foreach (var uid in unitIds) { resultList.AddRange(unitCharges.Where(u => u.UnitId == uid).Select(uc => new CostOfUnitInfoDto { UnitCostId = uc.Id, CostMount = uc.CostCycle.Amount, CostTitle = uc.CostCycle.IncomeCostTitleId.HasValue ? uc.CostCycle.IncomeCostTitle?.Title : "شارژ", Date = uc.Date, UnitId = uid, CostCount = unitCharges.Where(u => u.UnitId == uid).Count() }).ToList()); } return new ResultDto> { Data = resultList, IsSuccess = true }; } } public class CostOfUnitInfoDto { public long UnitCostId { set; get; } public Guid UnitId { get; set; } public int CostCount { get; set; } public long CostMount { get; set; } //public long CostCount { set; get; } public DateOnly? LastPaymentDate { get; set; } public DateOnly? Date { get; set; } public string? CostTitle { get; set; } public DateOnly? PaymentDate { set; get; } } public class UnitInfoDto { public Guid Id { set; get; } public string Name { set; get; } } public class CostCycleDto { public Guid Id { set; get; } public Guid ComplexId { set; get; } public string Title { set; get; } public DateOnly StartDate { set; get; } public DateOnly EndDate { set; get; } public long Amount { set; get; } = 0; /// /// روز از ماه برای تشویقی /// public int EncouragementDay { set; get; } = 0; /// /// مبلغ اجاره با احتساب تشویقی /// public long EncouragementAmount { set; get; } = 0; /// /// اگر برای واحدهای خاصی بود /// public virtual List? Units { set; get; } /// /// null یعنی شارژ /// public int? IncomeCostTitleId { set; get; } public string? IncomeCostTitle { set; get; } } }