add financial statement controller

This commit is contained in:
MahanCh
2025-07-13 14:17:52 +03:30
parent 00012cef52
commit fb52e99c68
10 changed files with 302 additions and 49 deletions

View File

@@ -68,7 +68,6 @@ public class CustomExceptionHandler : IExceptionHandler
problemDetails.Extensions.Add("traceId", context.TraceIdentifier);
await context.Response.WriteAsJsonAsync(problemDetails, cancellationToken: cancellationToken);
return true;

View File

@@ -13,4 +13,6 @@ public interface IFinancialStatmentRepository : IRepository<long, FinancialStatm
FinancialStatmentViewModel GetDetailsByContractingPartyId(long contractingPartyId);
List<FinancialStatmentViewModel> Search(FinancialStatmentSearchModel searchModel);
Task<ClientFinancialStatementViewModel> GetClientFinancialStatement(long accountId,
ClientFinancialStatementSearchModel searchModel);
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using _0_Framework.Application;
using CompanyManagment.App.Contracts.FinancilTransaction;
namespace CompanyManagment.App.Contracts.FinancialStatment;
@@ -13,4 +14,123 @@ public interface IFinancialStatmentApplication
List<FinancialStatmentViewModel> Search(FinancialStatmentSearchModel searchModel);
FinancialStatmentViewModel GetDetailsByContractingPartyId(long contractingPartyId);
/// <summary>
/// نمایش اطلاعات صورت حساب مالی کلاینت
/// </summary>
/// <param name="searchModel"></param>
/// <param name="accountId"></param>
/// <returns>مدل صورت حساب مالی کلاینت</returns>
Task<ClientFinancialStatementViewModel> GetClientFinancialStatement(ClientFinancialStatementSearchModel searchModel,
long accountId);
}
public class ClientFinancialStatementSearchModel
{
/// <summary>
/// از تاریخ
/// </summary>
public string FromDate { get; set; }
/// <summary>
/// تا تاریخ
/// </summary>
public string ToDate { get; set; }
/// <summary>
/// از مبلغ
/// </summary>
public double FromAmount { get; set; }
/// <summary>
/// تا مبلغ
/// </summary>
public double ToAmount { get; set; }
/// <summary>
/// نوع عملیات تراکنش
/// </summary>
public FinancialTransactionType? Type { get; set; }
/// <summary>
/// صفحه بندی
/// </summary>
public int PageIndex { get; set; }
}
public enum FinancialTransactionType
{
/// <summary>
/// ایجاد درآمد
/// </summary>
Debt,
/// <summary>
/// دریافت درآمد
/// </summary>
Credit
}
public class ClientFinancialStatementViewModel
{
/// <summary>
/// آیدی FinancialStatement
/// </summary>
public long Id { get; set; }
/// <summary>
/// جمع بدهکاری
/// </summary>
public double TotalDebt { get; set; }
/// <summary>
/// جمع بستانکاری
/// </summary>
public double TotalCredit { get; set; }
/// <summary>
/// مبلغ قابل پرداخت
/// </summary>
public double TotalAmountPayable { get; set; }
/// <summary>
/// تراکنش ها
/// </summary>
public List<ClientFinancialTransactionViewModel> Transactions { get; set; }
}
public class ClientFinancialTransactionViewModel
{
/// <summary>
/// زمان و تاریخ میلادی
/// </summary>
public DateTime DateTimeGr { get; set; }
/// <summary>
/// تاریخ
/// </summary>
public string DateFa { get; set; }
/// <summary>
/// زمان
/// </summary>
public string TimeFa { get; set; }
/// <summary>
/// شرح
/// </summary>
public string Description { get; set; }
/// <summary>
/// نوع عملیات پرداخت
/// </summary>
public FinancialTransactionType Type { get; set; }
/// <summary>
/// نوع عملیات پرداخت به صورت استرینگ
/// </summary>
public string TypeStr { get; set; }
/// <summary>
/// بدهکار
/// </summary>
public double Debtor { get; set; }
/// <summary>
/// بستانکار
/// </summary>
public double Creditor { get; set; }
/// <summary>
/// باقی مانده
/// </summary>
public double Balance { get; set; }
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using _0_Framework.Application;
using Company.Domain.FinancialStatmentAgg;
using CompanyManagment.App.Contracts.FinancialStatment;
@@ -110,4 +111,10 @@ public class FinancialStatmentApplication : IFinancialStatmentApplication
{
return _financialStatmentRepository.GetDetailsByContractingPartyId(contractingPartyId);
}
public async Task<ClientFinancialStatementViewModel> GetClientFinancialStatement(
ClientFinancialStatementSearchModel searchModel, long accountId)
{
return await _financialStatmentRepository.GetClientFinancialStatement(accountId, searchModel);
}
}

View File

@@ -1,12 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using _0_Framework.Application;
using _0_Framework.Application;
using _0_Framework.Exceptions;
using _0_Framework.InfraStructure;
using Company.Domain.FinancialStatmentAgg;
using CompanyManagment.App.Contracts.FinancialStatment;
using CompanyManagment.App.Contracts.FinancilTransaction;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AccountManagement.Application.Contracts.SubAccount;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace CompanyManagment.EFCore.Repository;
@@ -44,16 +48,16 @@ public class FinancialStatmentRepository : RepositoryBase<long, FinancialStatmen
TdateGr = t.TdateGr,
Description = t.TypeOfTransaction == "debt" ? "ایجاد درآمد" + " " + t.DescriptionOption + " " + t.Description : "دریافت درآمد" + " " + t.DescriptionOption + " " + t.Description,
Deptor = t.Deptor,
DeptorString = t.Deptor!=0?t.Deptor.ToMoney():"",
DeptorString = t.Deptor != 0 ? t.Deptor.ToMoney() : "",
Creditor = t.Creditor,
CreditorString = t.Creditor!=0?t.Creditor.ToMoney():"",
CreditorString = t.Creditor != 0 ? t.Creditor.ToMoney() : "",
Balance = t.Balance,
MessageText = t.MessageText,
SentSms = t.SentSms,
SentSmsDateFa = t.SentSmsDateFa,
FinancialStatementId = t.FinancialStatementId,
TypeOfTransaction = t.TypeOfTransaction
}).OrderBy(t=>t.TdateGr).ToList(),
}).OrderBy(t => t.TdateGr).ToList(),
};
}
@@ -63,4 +67,83 @@ public class FinancialStatmentRepository : RepositoryBase<long, FinancialStatmen
{
throw new NotImplementedException();
}
public async Task<ClientFinancialStatementViewModel> GetClientFinancialStatement(long accountId,
ClientFinancialStatementSearchModel searchModel)
{
var contractingPartyId = _context.ContractingPartyAccounts.Any(x => x.AccountId == accountId) ?
_context.ContractingPartyAccounts.FirstOrDefault(x => x.AccountId == accountId)!.PersonalContractingPartyId : 0;
if (contractingPartyId == 0)
throw new BadRequestException("طرف حساب مورد نظر یافت نشد");
var resStatement = await _context.FinancialStatments.Include(x=>x.FinancialTransactionList)
.FirstOrDefaultAsync(x => x.ContractingPartyId == contractingPartyId);
if (resStatement == null)
throw new BadRequestException("صورت حساب مالی شخص یافت نشد");
var resTransaction = resStatement.FinancialTransactionList.ToList();
#region Search
//if (searchModel.FromAmount > 0 || searchModel.ToAmount > 0)
//{
// if (searchModel.FromAmount > 0 && searchModel.ToAmount > 0)
// {
// resTransaction = resTransaction.Where(x => x.>= searchModel.FromAmount && x.Amount <= searchModel);
// }
// else if (searchModel.FromAmount > 0)
// {
// query = query.Where(x => x.Amount >= searchModel.FromAmount);
// }
// else if (searchModel.ToAmount > 0)
// {
// query = query.Where(x => x.Amount <= searchModel.ToAmount);
// }
//}
if (!string.IsNullOrWhiteSpace(searchModel.FromDate) && !string.IsNullOrWhiteSpace(searchModel.ToDate))
{
if (searchModel.FromDate.TryToGeorgianDateTime(out var fromDate) == false)
throw new BadRequestException("تاریخ وارد شده نامعتبر است");
if (searchModel.FromDate.TryToGeorgianDateTime(out var toDate) == false)
throw new BadRequestException("تاریخ وارد شده نامعتبر است");
resTransaction = resTransaction.Where(x => x.TdateGr >= fromDate && x.TdateGr <= toDate).ToList();
}
if (searchModel.Type != null)
{
var type = searchModel.Type switch
{
FinancialTransactionType.Credit => "credit",
FinancialTransactionType.Debt => "debt"
};
resTransaction = resTransaction.Where(x => x.TypeOfTransaction == type).ToList();
}
#endregion
return new ClientFinancialStatementViewModel()
{
Id = resStatement.id,
TotalAmountPayable = resStatement.FinancialTransactionList.Sum(x=>x.Deptor) - resStatement.FinancialTransactionList.Sum(x=>x.Creditor),
TotalCredit = resTransaction.Sum(x=>x.Creditor),
TotalDebt = resTransaction.Sum(x=>x.Deptor),
Transactions = resTransaction.Select(t => new ClientFinancialTransactionViewModel()
{
DateTimeGr = t.TdateGr,
DateFa = t.TdateGr.ToFarsi(),
TimeFa = $"{t.TdateGr:HH:mm}",
Description = t.DescriptionOption + " " + t.Description,
Debtor = t.Deptor,
Creditor = t.Creditor,
Balance = t.Balance,
Type = t.TypeOfTransaction =="debt"? FinancialTransactionType.Debt :FinancialTransactionType.Credit,
TypeStr = t.TypeOfTransaction =="debt"? "ایجاد درآمد" : "دریافت درآمد"
}).OrderByDescending(t => t.DateTimeGr).ToList(),
};
}
}

View File

@@ -0,0 +1,26 @@
using _0_Framework.Application;
using CompanyManagment.App.Contracts.FinancialStatment;
using Microsoft.AspNetCore.Mvc;
using ServiceHost.BaseControllers;
namespace ServiceHost.Areas.Client.Controllers;
public class FinancialController : ClientBaseController
{
private readonly IFinancialStatmentApplication _financialStatementApplication;
private readonly IAuthHelper _authHelper;
public FinancialController(IFinancialStatmentApplication financialStatementApplication, IAuthHelper authHelper)
{
_financialStatementApplication = financialStatementApplication;
_authHelper = authHelper;
}
[HttpGet]
public async Task<ActionResult<ClientFinancialStatementViewModel>> GetList(ClientFinancialStatementSearchModel searchModel)
{
var accountId = _authHelper.CurrentAccountId();
var result =await _financialStatementApplication.GetClientFinancialStatement(searchModel,accountId);
return result;
}
}

View File

@@ -1,8 +1,9 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace ServiceHost.BaseControllers;
//[Authorize(Policy = "ClientArea")]
[Authorize(Policy = "ClientArea")]
[Area("Client")]
[ApiExplorerSettings(GroupName = "Client")]
[Route("api/[area]/[controller]")]

View File

@@ -14,48 +14,52 @@ public class RazorJsonEnumOverrideMiddleware
public async Task InvokeAsync(HttpContext context)
{
// نگه‌دار خروجی
var originalBody = context.Response.Body;
using var newBody = new MemoryStream();
context.Response.Body = newBody;
await _next(context); // اجرای مرحله بعدی
// فقط برای مسیرهای غیر /api و وقتی Content-Type = application/json
if (!context.Request.Path.StartsWithSegments("/api", StringComparison.OrdinalIgnoreCase) &&
context.Response.ContentType?.Contains("application/json", StringComparison.OrdinalIgnoreCase) == true)
try
{
newBody.Seek(0, SeekOrigin.Begin);
using var reader = new StreamReader(newBody);
var originalJson = await reader.ReadToEndAsync();
await _next(context);
// JSON رو deserialize و دوباره serialize می‌کنیم بدون EnumConverter
try
if (!context.Request.Path.StartsWithSegments("/api", StringComparison.OrdinalIgnoreCase) &&
context.Response.ContentType?.Contains("application/json", StringComparison.OrdinalIgnoreCase) == true)
{
var obj = JsonSerializer.Deserialize<object>(originalJson);
var newJson = JsonSerializer.Serialize(obj, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
context.Response.Body = originalBody;
context.Response.ContentLength = Encoding.UTF8.GetByteCount(newJson);
await context.Response.WriteAsync(newJson);
return;
}
catch
{
// fallback if deserialization fails
context.Response.Body = originalBody;
newBody.Seek(0, SeekOrigin.Begin);
await newBody.CopyToAsync(originalBody);
return;
}
}
var originalJson = await new StreamReader(newBody).ReadToEndAsync();
// اگر شرط نداشت، همون خروجی قبلی رو منتقل کن
context.Response.Body = originalBody;
newBody.Seek(0, SeekOrigin.Begin);
await newBody.CopyToAsync(originalBody);
try
{
var obj = JsonSerializer.Deserialize<object>(originalJson);
var newJson = JsonSerializer.Serialize(obj, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
context.Response.Body = originalBody;
context.Response.ContentLength = Encoding.UTF8.GetByteCount(newJson);
if (!context.Response.HasStarted)
await context.Response.WriteAsync(newJson);
return;
}
catch
{
context.Response.Body = originalBody;
newBody.Seek(0, SeekOrigin.Begin);
if (!context.Response.HasStarted)
await newBody.CopyToAsync(originalBody);
return;
}
}
context.Response.Body = originalBody;
newBody.Seek(0, SeekOrigin.Begin);
if (!context.Response.HasStarted)
await newBody.CopyToAsync(originalBody);
}
finally
{
context.Response.Body = originalBody;
}
}
}

View File

@@ -26,6 +26,8 @@ using System.Text.Json;
using Microsoft.AspNetCore.CookiePolicy;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Diagnostics;
var builder = WebApplication.CreateBuilder(args);
@@ -291,6 +293,7 @@ builder.Services.AddCors(options =>
builder.Services.AddExceptionHandler<CustomExceptionHandler>();
var app = builder.Build();
app.UseCors("AllowSpecificOrigins");
@@ -323,6 +326,8 @@ if (app.Environment.IsDevelopment())
//Create Http Pipeline
#region Create Http Pipeline
if (builder.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
@@ -333,17 +338,23 @@ else
app.UseHsts();
}
app.UseExceptionHandler(options => { }); // این خط CustomExceptionHandler رو فعال می‌کنه
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthorization();
#region Mahan

View File

@@ -95,6 +95,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Areas\Client\Controllers\" />
<Folder Include="Storage\Task\" />
<Folder Include="Storage\Ticket\" />
<Folder Include="wwwroot\AdminTheme\js\faceApi\" />
@@ -387,7 +388,6 @@
<None Include="Areas\Client\Pages\Shared\_ValidationScriptsPartial.cshtml" />
<None Include="Areas\Client\Pages\_ViewImports.cshtml" />
<None Include="Areas\Client\Pages\_ViewStart.cshtml" />
<None Include="Pages\error\403.cshtml" />
<None Include="Pages\error\404.cshtml" />
<None Include="wwwroot\AdminTheme\assets\datatables-new\js\dataTables.bootstrap4.min.js" />
<None Include="wwwroot\AdminTheme\assets\datatables-new\js\dataTables.responsive.min.js" />