first commit

This commit is contained in:
2026-06-02 20:48:16 +03:30
commit 8e92c983a2
233 changed files with 15744 additions and 0 deletions

View File

@@ -0,0 +1,414 @@
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<CostService> logger)
{
_complexDBContext = complexDBContext;
_mapper = mapper;
_logger = logger;
}
//public ResultDto<List<ChargeCycleDto>> 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<ChargeCycleDto>(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<UnitDto>(u)).ToList();
// }
// return new ResultDto<List<ChargeCycleDto>>
// {
// Data = chargeCycleObj,
// IsSuccess = true,
// };
//}
/// <summary>
/// لیست مدیریت شارژ یا هر گونه درآمد و مخارج گرفته می شود
/// </summary>
/// <param name="ComplexId"></param>
/// <param name="StartDate"></param>
/// <param name="EndDate"></param>
/// <param name="IncomeCostTitleId"></param>
/// <returns></returns>
public ResultDto<List<CostCycleDto>> CostCyclsList(Guid ComplexId, DateOnly? StartDate = null,
DateOnly? EndDate = null, int? IncomeCostTitleId = 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);
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,
UnitIds = c.Units?.Select(u => u.Id).ToList(),
UnitNames = c.Units?.Select(u => u.Id + "#" + u.UnitName).ToList(),
IncomeCostTitleId = c.IncomeCostTitleId,
IncomeCostTitle = c.IncomeCostTitle?.Title
}).ToList();
return new ResultDto<List<CostCycleDto>>
{
IsSuccess = true,
RowCount = ChargeCycleList.Count,
Data = ChargeCycleList
};
}
public ResultDto<bool> 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<bool>
{
IsSuccess = false,
Message = "این آیتم استفاده شده است. شما می توانید این آیتم را حذف نمایید",
Data = false
};
cycle = _mapper.Map<CostCycle>(costCycleDto);
cycle.IncomeCostTitleId = costCycleDto.IncomeCostTitleId;
if (costCycleDto.UnitIds != null)
cycle.Units = _complexDBContext.Units.Where(u => costCycleDto.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<bool> { Data = false, IsSuccess = true, Message = define.Message };
}
_complexDBContext.SaveChanges();
return new ResultDto<bool> { 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<Guid> InsertCostCycle(CostCycleDto costCycleDto)
{
//اگر مدیریت شارژ هست پس تاریخ ها باید از یکم تا پایان ماه بشود
//البته باید در نرم افزار فقط امکان انتخاب ماه را داشته باشد
//&^*&^*&^&*^*&^*&^&*^*&^*&^&*^*&^*&^*&^&*^&*^*&&^*&^*&^&*^*^*&^*&^*&^&*^*&
//
if (costCycleDto.IncomeCostTitleId == null)
costCycleDto.StartDate = GetStartOfMonth(costCycleDto.StartDate);
var newCycle = _mapper.Map<CostCycle>(costCycleDto);
if (costCycleDto.UnitIds != null)
newCycle.Units = _complexDBContext.Units.Where(u => costCycleDto.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<Guid> { IsSuccess = false, Message = define.Message };
}
_complexDBContext.SaveChanges();
return new ResultDto<Guid> { 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,
};
}
/// <summary>
/// بدهی یک واحد
/// </summary>
/// <param name="unitId"></param>
/// <param name="incomeCostTitleId"></param>
/// <returns></returns>
public ResultDto<CostOfUnitInfoDto> 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<CostOfUnitInfoDto>
{
Data = new CostOfUnitInfoDto
{
CostMount = unitCost.Sum(c => c.CostCycle.Amount),
CostTitle = costTitle,
LastPaymentDate = LastPaymentDate,
UnitId = unitId
},
IsSuccess = true
};
}
/// <summary>
/// لیست بدهی های یک واحد
/// </summary>
/// <param name="unitId"></param>
/// <returns></returns>
public ResultDto<List<CostOfUnitInfoDto>> CalculateAllCostForUnits(List<Guid> 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<CostOfUnitInfoDto>();
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<List<CostOfUnitInfoDto>>
{
Data = resultList,
IsSuccess = true
};
//{
// Data = unitCharges.Sum(c => c.CostCycle.Amount),
// Message = "تمام بدهی"
//};
}
/// <summary>
/// تاریخچه پرداختی ها و باقیمانده از آن
/// </summary>
/// <param name="unitId"></param>
/// <param name="incomeCostTitleId"></param>
/// <param name="startDate"></param>
/// <param name="endDate"></param>
/// <returns></returns>
public ResultDto<List<CostOfUnitInfoDto>> CostsForUnits(List<Guid> 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<CostOfUnitInfoDto>();
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<List<CostOfUnitInfoDto>>
{
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 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;
/// <summary>
/// روز از ماه برای تشویقی
/// </summary>
public int EncouragementDay { set; get; } = 0;
/// <summary>
/// مبلغ اجاره با احتساب تشویقی
/// </summary>
public long EncouragementAmount { set; get; } = 0;
/// <summary>
/// اگر برای واحدهای خاصی بود
/// </summary>
/// <summary>
/// اگر برای واحدهای خاصی بود
/// </summary>
public virtual List<Guid>? UnitIds { set; get; }
public virtual List<string>? UnitNames { set; get; }
/// <summary>
/// null یعنی شارژ
/// </summary>
public int? IncomeCostTitleId { set; get; }
public string? IncomeCostTitle { set; get; }
}
}