Added the payment request details API

This commit is contained in:
ashutosh.nehete 2025-11-03 15:43:37 +05:30
parent cf24f3af32
commit 4c0ef1f585
23 changed files with 7971 additions and 76 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,107 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_Changed_ExpensesType_To_ExpenseCategory : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Expenses_ExpensesTypeMaster_ExpensesTypeId",
table: "Expenses");
migrationBuilder.RenameColumn(
name: "ExpensesTypeId",
table: "Expenses",
newName: "ExpenseCategoryId");
migrationBuilder.RenameIndex(
name: "IX_Expenses_ExpensesTypeId",
table: "Expenses",
newName: "IX_Expenses_ExpenseCategoryId");
migrationBuilder.AddColumn<Guid>(
name: "ProjectId",
table: "AdvancePaymentTransactions",
type: "char(36)",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
collation: "ascii_general_ci");
migrationBuilder.AddColumn<string>(
name: "Title",
table: "AdvancePaymentTransactions",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_AdvancePaymentTransactions_ProjectId",
table: "AdvancePaymentTransactions",
column: "ProjectId");
migrationBuilder.AddForeignKey(
name: "FK_AdvancePaymentTransactions_Projects_ProjectId",
table: "AdvancePaymentTransactions",
column: "ProjectId",
principalTable: "Projects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Expenses_ExpenseCategoryMasters_ExpenseCategoryId",
table: "Expenses",
column: "ExpenseCategoryId",
principalTable: "ExpenseCategoryMasters",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_AdvancePaymentTransactions_Projects_ProjectId",
table: "AdvancePaymentTransactions");
migrationBuilder.DropForeignKey(
name: "FK_Expenses_ExpenseCategoryMasters_ExpenseCategoryId",
table: "Expenses");
migrationBuilder.DropIndex(
name: "IX_AdvancePaymentTransactions_ProjectId",
table: "AdvancePaymentTransactions");
migrationBuilder.DropColumn(
name: "ProjectId",
table: "AdvancePaymentTransactions");
migrationBuilder.DropColumn(
name: "Title",
table: "AdvancePaymentTransactions");
migrationBuilder.RenameColumn(
name: "ExpenseCategoryId",
table: "Expenses",
newName: "ExpensesTypeId");
migrationBuilder.RenameIndex(
name: "IX_Expenses_ExpenseCategoryId",
table: "Expenses",
newName: "IX_Expenses_ExpensesTypeId");
migrationBuilder.AddForeignKey(
name: "FK_Expenses_ExpensesTypeMaster_ExpensesTypeId",
table: "Expenses",
column: "ExpensesTypeId",
principalTable: "ExpensesTypeMaster",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@ -2097,15 +2097,24 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<bool>("IsActive") b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)"); .HasColumnType("tinyint(1)");
b.Property<Guid>("ProjectId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId") b.Property<Guid>("TenantId")
.HasColumnType("char(36)"); .HasColumnType("char(36)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("CreatedById"); b.HasIndex("CreatedById");
b.HasIndex("EmployeeId"); b.HasIndex("EmployeeId");
b.HasIndex("ProjectId");
b.HasIndex("TenantId"); b.HasIndex("TenantId");
b.ToTable("AdvancePaymentTransactions"); b.ToTable("AdvancePaymentTransactions");
@ -2310,13 +2319,13 @@ namespace Marco.Pms.DataAccess.Migrations
.IsRequired() .IsRequired()
.HasColumnType("longtext"); .HasColumnType("longtext");
b.Property<Guid>("ExpenseCategoryId")
.HasColumnType("char(36)");
b.Property<string>("ExpenseUId") b.Property<string>("ExpenseUId")
.IsRequired() .IsRequired()
.HasColumnType("longtext"); .HasColumnType("longtext");
b.Property<Guid>("ExpensesTypeId")
.HasColumnType("char(36)");
b.Property<string>("GSTNumber") b.Property<string>("GSTNumber")
.HasColumnType("longtext"); .HasColumnType("longtext");
@ -2369,7 +2378,7 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasIndex("CreatedById"); b.HasIndex("CreatedById");
b.HasIndex("ExpensesTypeId"); b.HasIndex("ExpenseCategoryId");
b.HasIndex("PaidById"); b.HasIndex("PaidById");
@ -6208,6 +6217,12 @@ namespace Marco.Pms.DataAccess.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant") b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany() .WithMany()
.HasForeignKey("TenantId") .HasForeignKey("TenantId")
@ -6218,6 +6233,8 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("Employee"); b.Navigation("Employee");
b.Navigation("Project");
b.Navigation("Tenant"); b.Navigation("Tenant");
}); });
@ -6298,9 +6315,9 @@ namespace Marco.Pms.DataAccess.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("Marco.Pms.Model.Master.ExpensesTypeMaster", "ExpensesType") b.HasOne("Marco.Pms.Model.Expenses.ExpenseCategoryMaster", "ExpenseCategory")
.WithMany() .WithMany()
.HasForeignKey("ExpensesTypeId") .HasForeignKey("ExpenseCategoryId")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
@ -6346,7 +6363,7 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("CreatedBy"); b.Navigation("CreatedBy");
b.Navigation("ExpensesType"); b.Navigation("ExpenseCategory");
b.Navigation("PaidBy"); b.Navigation("PaidBy");

View File

@ -5,7 +5,7 @@ namespace Marco.Pms.Model.Dtos.Expenses
public class CreateExpensesDto public class CreateExpensesDto
{ {
public required Guid ProjectId { get; set; } public required Guid ProjectId { get; set; }
public required Guid ExpensesTypeId { get; set; } public required Guid ExpenseCategoryId { get; set; }
public required Guid PaymentModeId { get; set; } public required Guid PaymentModeId { get; set; }
public required Guid PaidById { get; set; } public required Guid PaidById { get; set; }
public DateTime TransactionDate { get; set; } = DateTime.Now; public DateTime TransactionDate { get; set; } = DateTime.Now;

View File

@ -6,7 +6,7 @@ namespace Marco.Pms.Model.Dtos.Expenses
{ {
public required Guid Id { get; set; } public required Guid Id { get; set; }
public required Guid ProjectId { get; set; } public required Guid ProjectId { get; set; }
public required Guid ExpensesTypeId { get; set; } public required Guid ExpenseCategoryId { get; set; }
public required Guid PaymentModeId { get; set; } public required Guid PaymentModeId { get; set; }
public required Guid PaidById { get; set; } public required Guid PaidById { get; set; }
public DateTime TransactionDate { get; set; } = DateTime.Now; public DateTime TransactionDate { get; set; } = DateTime.Now;

View File

@ -1,4 +1,5 @@
using Marco.Pms.Model.Employees; using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
@ -10,6 +11,12 @@ namespace Marco.Pms.Model.Expenses
public Guid Id { get; set; } public Guid Id { get; set; }
public string FinanceUIdPrefix { get; set; } = default!; public string FinanceUIdPrefix { get; set; } = default!;
public int FinanceUIdPostfix { get; set; } public int FinanceUIdPostfix { get; set; }
public string Title { get; set; } = default!;
public Guid ProjectId { get; set; }
[ValidateNever]
[ForeignKey("ProjectId")]
public Project? Project { get; set; }
public Guid EmployeeId { get; set; } public Guid EmployeeId { get; set; }
[ValidateNever] [ValidateNever]

View File

@ -15,11 +15,11 @@ namespace Marco.Pms.Model.Expenses
[ValidateNever] [ValidateNever]
[ForeignKey("ProjectId")] [ForeignKey("ProjectId")]
public Project? Project { get; set; } public Project? Project { get; set; }
public Guid ExpensesTypeId { get; set; } public Guid ExpenseCategoryId { get; set; }
[ValidateNever] [ValidateNever]
[ForeignKey("ExpensesTypeId")] [ForeignKey("ExpenseCategoryId")]
public ExpensesTypeMaster? ExpensesType { get; set; } public ExpenseCategoryMaster? ExpenseCategory { get; set; }
public Guid PaymentModeId { get; set; } public Guid PaymentModeId { get; set; }
[ValidateNever] [ValidateNever]

View File

@ -6,7 +6,7 @@
public List<Guid>? StatusIds { get; set; } public List<Guid>? StatusIds { get; set; }
public List<Guid>? CreatedByIds { get; set; } public List<Guid>? CreatedByIds { get; set; }
public List<Guid>? PaidById { get; set; } public List<Guid>? PaidById { get; set; }
public List<Guid>? ExpenseTypeIds { get; set; } public List<Guid>? ExpenseCategoryIds { get; set; }
public bool IsTransactionDate { get; set; } = false; public bool IsTransactionDate { get; set; } = false;
public DateTime? StartDate { get; set; } public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; } public DateTime? EndDate { get; set; }

View File

@ -8,7 +8,7 @@ namespace Marco.Pms.Model.MongoDBModels.Expenses
{ {
public string Id { get; set; } = string.Empty; public string Id { get; set; } = string.Empty;
public ProjectBasicMongoDB Project { get; set; } = new ProjectBasicMongoDB(); public ProjectBasicMongoDB Project { get; set; } = new ProjectBasicMongoDB();
public ExpensesTypeMasterMongoDB ExpensesType { get; set; } = new ExpensesTypeMasterMongoDB(); public ExpenseCategoryMasterMongoDB ExpenseCategory { get; set; } = new ExpenseCategoryMasterMongoDB();
public PaymentModeMatserMongoDB PaymentMode { get; set; } = new PaymentModeMatserMongoDB(); public PaymentModeMatserMongoDB PaymentMode { get; set; } = new PaymentModeMatserMongoDB();
public BasicEmployeeMongoDB PaidBy { get; set; } = new BasicEmployeeMongoDB(); public BasicEmployeeMongoDB PaidBy { get; set; } = new BasicEmployeeMongoDB();
public BasicEmployeeMongoDB CreatedBy { get; set; } = new BasicEmployeeMongoDB(); public BasicEmployeeMongoDB CreatedBy { get; set; } = new BasicEmployeeMongoDB();

View File

@ -0,0 +1,11 @@
namespace Marco.Pms.Model.MongoDBModels.Masters
{
public class ExpenseCategoryMasterMongoDB
{
public string Id { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public bool NoOfPersonsRequired { get; set; }
public string Description { get; set; } = string.Empty;
public string TenantId { get; set; } = string.Empty;
}
}

View File

@ -9,7 +9,7 @@ namespace Marco.Pms.Model.ViewModels.Expenses
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public ProjectInfoVM? Project { get; set; } public ProjectInfoVM? Project { get; set; }
public ExpensesTypeMasterVM? ExpensesType { get; set; } public ExpensesCategoryMasterVM? ExpensesType { get; set; }
public PaymentModeMatserVM? PaymentMode { get; set; } public PaymentModeMatserVM? PaymentMode { get; set; }
public BasicEmployeeVM? PaidBy { get; set; } public BasicEmployeeVM? PaidBy { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; } public BasicEmployeeVM? CreatedBy { get; set; }

View File

@ -8,7 +8,7 @@ namespace Marco.Pms.Model.ViewModels.Expanses
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public ProjectInfoVM? Project { get; set; } public ProjectInfoVM? Project { get; set; }
public ExpensesTypeMasterVM? ExpensesType { get; set; } public ExpensesCategoryMasterVM? ExpensesCategory { get; set; }
public PaymentModeMatserVM? PaymentMode { get; set; } public PaymentModeMatserVM? PaymentMode { get; set; }
public BasicEmployeeVM? PaidBy { get; set; } public BasicEmployeeVM? PaidBy { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; } public BasicEmployeeVM? CreatedBy { get; set; }

View File

@ -0,0 +1,12 @@
namespace Marco.Pms.Model.ViewModels.Expenses
{
public class PaymentRequestAttachmentVM
{
public Guid Id { get; set; }
public string? FileName { get; set; }
public string? Url { get; set; }
public string? ThumbUrl { get; set; }
public long FileSize { get; set; }
public string? ContentType { get; set; }
}
}

View File

@ -0,0 +1,32 @@
using Marco.Pms.Model.Expenses;
using Marco.Pms.Model.Master;
using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.Master;
using Marco.Pms.Model.ViewModels.Projects;
namespace Marco.Pms.Model.ViewModels.Expenses
{
public class PaymentRequestDetailsVM
{
public Guid Id { get; set; }
public string? Title { get; set; }
public string? Description { get; set; }
public string? PaymentRequestUID { get; set; }
public string? Payee { get; set; }
public CurrencyMaster? Currency { get; set; }
public double Amount { get; set; }
public DateTime DueDate { get; set; }
public BasicProjectVM? Project { get; set; }
public RecurringPayment? RecurringPayment { get; set; }
public ExpensesCategoryMasterVM? ExpenseCategory { get; set; }
public ExpensesStatusMasterVM? ExpenseStatus { get; set; }
public bool IsAdvancePayment { get; set; }
public DateTime CreatedAt { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; }
public DateTime UpdatedAt { get; set; }
public BasicEmployeeVM? UpdatedBy { get; set; }
public List<ExpensesStatusMasterVM>? NextStatus { get; set; }
public List<PaymentRequestAttachmentVM>? Attachments { get; set; }
public bool IsActive { get; set; }
}
}

View File

@ -18,14 +18,11 @@ namespace Marco.Pms.Model.ViewModels.Expenses
public DateTime DueDate { get; set; } public DateTime DueDate { get; set; }
public BasicProjectVM? Project { get; set; } public BasicProjectVM? Project { get; set; }
public RecurringPayment? RecurringPayment { get; set; } public RecurringPayment? RecurringPayment { get; set; }
public ExpensesTypeMasterVM? ExpenseCategory { get; set; } public ExpensesCategoryMasterVM? ExpenseCategory { get; set; }
public ExpensesStatusMasterVM? ExpenseStatus { get; set; } public ExpensesStatusMasterVM? ExpenseStatus { get; set; }
public bool IsAdvancePayment { get; set; } public bool IsAdvancePayment { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; } public BasicEmployeeVM? CreatedBy { get; set; }
public DateTime? UpdatedAt { get; set; } public bool IsActive { get; set; }
public Guid? UpdatedById { get; set; }
public BasicEmployeeVM? UpdatedBy { get; set; }
} }
} }

View File

@ -1,6 +1,6 @@
namespace Marco.Pms.Model.ViewModels.Master namespace Marco.Pms.Model.ViewModels.Master
{ {
public class ExpensesTypeMasterVM public class ExpensesCategoryMasterVM
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;

View File

@ -642,7 +642,7 @@ namespace Marco.Pms.Services.Controllers
baseQuery = baseQuery.Where(e => e.ProjectId == projectId); baseQuery = baseQuery.Where(e => e.ProjectId == projectId);
if (categoryId.HasValue) if (categoryId.HasValue)
baseQuery = baseQuery.Where(e => e.ExpensesTypeId == categoryId); baseQuery = baseQuery.Where(e => e.ExpenseCategoryId == categoryId);
// Single server-side group/aggregate by project // Single server-side group/aggregate by project
var report = await baseQuery var report = await baseQuery
@ -716,10 +716,10 @@ namespace Marco.Pms.Services.Controllers
// Project to a minimal shape before grouping to avoid loading navigation graphs // Project to a minimal shape before grouping to avoid loading navigation graphs
// Group by expense type name; adjust to the correct key if ExpensesCategory is an enum or navigation // Group by expense type name; adjust to the correct key if ExpensesCategory is an enum or navigation
var query = baseQuery var query = baseQuery
.Where(e => e.ExpensesType != null) .Where(e => e.ExpenseCategory != null)
.Select(e => new .Select(e => new
{ {
ExpenseTypeName = e.ExpensesType!.Name, // If enum, use e.ExpensesCategory.ToString() ExpenseTypeName = e.ExpenseCategory!.Name, // If enum, use e.ExpensesCategory.ToString()
Amount = e.Amount, Amount = e.Amount,
StatusId = e.StatusId StatusId = e.StatusId
}) })

View File

@ -48,10 +48,10 @@ namespace Marco.Pms.Services.Controllers
} }
[HttpGet("details")] [HttpGet("details")]
public async Task<IActionResult> GetExpenseDetails([FromQuery] Guid? id, [FromQuery] string? financeUId) public async Task<IActionResult> GetExpenseDetails([FromQuery] Guid? id, [FromQuery] string? expenseUId)
{ {
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _expensesService.GetExpenseDetailsAsync(id, financeUId, loggedInEmployee, tenantId); var response = await _expensesService.GetExpenseDetailsAsync(id, expenseUId, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
@ -134,6 +134,15 @@ namespace Marco.Pms.Services.Controllers
var response = await _expensesService.GetPaymentRequestListAsync(searchString, filter, isActive, pageSize, pageNumber, loggedInEmployee, tenantId); var response = await _expensesService.GetPaymentRequestListAsync(searchString, filter, isActive, pageSize, pageNumber, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
[HttpGet("get/payment-request/details/{id?}")]
public async Task<IActionResult> GetPaymentRequestDetails(Guid? id, [FromQuery] string? paymentRequestUId)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _expensesService.GetPaymentRequestDetailsAsync(id, paymentRequestUId, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}
[HttpPost("payment-request/create")] [HttpPost("payment-request/create")]
public async Task<IActionResult> CreatePaymentRequest([FromBody] PaymentRequestDto model) public async Task<IActionResult> CreatePaymentRequest([FromBody] PaymentRequestDto model)
{ {
@ -146,6 +155,7 @@ namespace Marco.Pms.Services.Controllers
} }
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
[HttpPut("payment-request/edit/{id}")] [HttpPut("payment-request/edit/{id}")]
public async Task<IActionResult> EditPaymentRequest(Guid id, [FromBody] PaymentRequestDto model) public async Task<IActionResult> EditPaymentRequest(Guid id, [FromBody] PaymentRequestDto model)
{ {

View File

@ -1144,7 +1144,7 @@ namespace Marco.Pms.Services.Helpers
var expenseIds = model.Select(m => m.Id).ToList(); var expenseIds = model.Select(m => m.Id).ToList();
var projectIds = model.Select(m => m.ProjectId).ToList(); var projectIds = model.Select(m => m.ProjectId).ToList();
var statusIds = model.Select(m => m.StatusId).ToList(); var statusIds = model.Select(m => m.StatusId).ToList();
var expensesTypeIds = model.Select(m => m.ExpensesTypeId).ToList(); var expensesTypeIds = model.Select(m => m.ExpenseCategoryId).ToList();
var paymentModeIds = model.Select(m => m.PaymentModeId).ToList(); var paymentModeIds = model.Select(m => m.PaymentModeId).ToList();
var createdByIds = model.Select(m => m.CreatedById).ToList(); var createdByIds = model.Select(m => m.CreatedById).ToList();
var reviewedByIds = model.Select(m => m.ReviewedById).ToList(); var reviewedByIds = model.Select(m => m.ReviewedById).ToList();
@ -1273,7 +1273,7 @@ namespace Marco.Pms.Services.Helpers
response.NextStatus = statusMappings.Where(s => s.StatusId == m.StatusId).Select(s => _mapper.Map<List<ExpensesStatusMasterMongoDB>>(s.NextStatus)).FirstOrDefault() ?? new List<ExpensesStatusMasterMongoDB>(); response.NextStatus = statusMappings.Where(s => s.StatusId == m.StatusId).Select(s => _mapper.Map<List<ExpensesStatusMasterMongoDB>>(s.NextStatus)).FirstOrDefault() ?? new List<ExpensesStatusMasterMongoDB>();
response.PaymentMode = paymentModes.Where(pm => pm.Id == m.PaymentModeId).Select(pm => _mapper.Map<PaymentModeMatserMongoDB>(pm)).FirstOrDefault() ?? new PaymentModeMatserMongoDB(); response.PaymentMode = paymentModes.Where(pm => pm.Id == m.PaymentModeId).Select(pm => _mapper.Map<PaymentModeMatserMongoDB>(pm)).FirstOrDefault() ?? new PaymentModeMatserMongoDB();
response.ExpensesType = expenseTypes.Where(et => et.Id == m.ExpensesTypeId).Select(et => _mapper.Map<ExpensesTypeMasterMongoDB>(et)).FirstOrDefault() ?? new ExpensesTypeMasterMongoDB(); response.ExpenseCategory = expenseTypes.Where(et => et.Id == m.ExpenseCategoryId).Select(et => _mapper.Map<ExpenseCategoryMasterMongoDB>(et)).FirstOrDefault() ?? new ExpenseCategoryMasterMongoDB();
response.Documents = billAttachments.Where(ba => ba.ExpensesId == m.Id).Select(ba => ba.Documents).FirstOrDefault() ?? new List<DocumentMongoDB>(); response.Documents = billAttachments.Where(ba => ba.ExpensesId == m.Id).Select(ba => ba.Documents).FirstOrDefault() ?? new List<DocumentMongoDB>();
return response; return response;
}).ToList(); }).ToList();
@ -1315,7 +1315,7 @@ namespace Marco.Pms.Services.Helpers
var expenseTypeTask = Task.Run(async () => var expenseTypeTask = Task.Run(async () =>
{ {
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
return await dbContext.ExpensesTypeMaster.AsNoTracking().FirstOrDefaultAsync(et => et.Id == model.ExpensesTypeId && et.TenantId == tenantId); return await dbContext.ExpensesTypeMaster.AsNoTracking().FirstOrDefaultAsync(et => et.Id == model.ExpenseCategoryId && et.TenantId == tenantId);
}); });
var paymentModeTask = Task.Run(async () => var paymentModeTask = Task.Run(async () =>
{ {
@ -1403,7 +1403,7 @@ namespace Marco.Pms.Services.Helpers
response.Status = _mapper.Map<ExpensesStatusMasterMongoDB>(status); response.Status = _mapper.Map<ExpensesStatusMasterMongoDB>(status);
} }
response.PaymentMode = _mapper.Map<PaymentModeMatserMongoDB>(paymentMode); response.PaymentMode = _mapper.Map<PaymentModeMatserMongoDB>(paymentMode);
response.ExpensesType = _mapper.Map<ExpensesTypeMasterMongoDB>(expenseType); response.ExpenseCategory = _mapper.Map<ExpenseCategoryMasterMongoDB>(expenseType);
if (billAttachment != null) response.Documents = billAttachment.Documents; if (billAttachment != null) response.Documents = billAttachment.Documents;
return response; return response;

View File

@ -257,9 +257,14 @@ namespace Marco.Pms.Services.MappingProfiles
.ForMember( .ForMember(
dest => dest.Id, dest => dest.Id,
opt => opt.MapFrom(src => Guid.Parse(src.Id))); opt => opt.MapFrom(src => Guid.Parse(src.Id)));
#region ======================================================= Payment Request ======================================================= #region ======================================================= Payment Request =======================================================
CreateMap<PaymentRequestDto, PaymentRequest>(); CreateMap<PaymentRequestDto, PaymentRequest>();
CreateMap<PaymentRequest, PaymentRequestVM>(); CreateMap<PaymentRequest, PaymentRequestVM>();
CreateMap<PaymentRequest, PaymentRequestDetailsVM>();
CreateMap<Document, PaymentRequestAttachmentVM>();
#endregion #endregion
CreateMap<AdvancePaymentTransaction, AdvancePaymentTransactionVM>(); CreateMap<AdvancePaymentTransaction, AdvancePaymentTransactionVM>();
@ -346,10 +351,10 @@ namespace Marco.Pms.Services.MappingProfiles
// Explicitly and safely convert nullable Guid to non-nullable Guid // Explicitly and safely convert nullable Guid to non-nullable Guid
opt => opt.MapFrom(src => src.Id ?? Guid.Empty) opt => opt.MapFrom(src => src.Id ?? Guid.Empty)
); );
CreateMap<ExpensesTypeMaster, ExpensesTypeMasterVM>(); CreateMap<ExpensesTypeMaster, ExpensesCategoryMasterVM>();
CreateMap<ExpenseCategoryMaster, ExpensesTypeMasterVM>(); CreateMap<ExpenseCategoryMaster, ExpensesCategoryMasterVM>();
CreateMap<ExpenseCategoryMaster, ExpensesTypeMasterMongoDB>() CreateMap<ExpenseCategoryMaster, ExpenseCategoryMasterMongoDB>()
.ForMember( .ForMember(
dest => dest.Id, dest => dest.Id,
opt => opt.MapFrom(src => src.Id.ToString())) opt => opt.MapFrom(src => src.Id.ToString()))
@ -357,7 +362,7 @@ namespace Marco.Pms.Services.MappingProfiles
dest => dest.TenantId, dest => dest.TenantId,
opt => opt.MapFrom(src => src.TenantId.ToString())); opt => opt.MapFrom(src => src.TenantId.ToString()));
CreateMap<ExpensesTypeMaster, ExpensesTypeMasterMongoDB>() CreateMap<ExpensesTypeMaster, ExpenseCategoryMasterMongoDB>()
.ForMember( .ForMember(
dest => dest.Id, dest => dest.Id,
opt => opt.MapFrom(src => src.Id.ToString())) opt => opt.MapFrom(src => src.Id.ToString()))
@ -365,7 +370,7 @@ namespace Marco.Pms.Services.MappingProfiles
dest => dest.TenantId, dest => dest.TenantId,
opt => opt.MapFrom(src => src.TenantId.ToString())); opt => opt.MapFrom(src => src.TenantId.ToString()));
CreateMap<ExpensesTypeMasterMongoDB, ExpensesTypeMasterVM>() CreateMap<ExpenseCategoryMasterMongoDB, ExpensesCategoryMasterVM>()
.ForMember( .ForMember(
dest => dest.Id, dest => dest.Id,
opt => opt.MapFrom(src => Guid.Parse(src.Id))); opt => opt.MapFrom(src => Guid.Parse(src.Id)));

View File

@ -141,7 +141,7 @@ namespace Marco.Pms.Services.Service
.Include(e => e.PaymentMode) .Include(e => e.PaymentMode)
.Include(e => e.Project) .Include(e => e.Project)
.Include(e => e.PaymentMode) .Include(e => e.PaymentMode)
.Include(e => e.ExpensesType) .Include(e => e.ExpenseCategory)
.Include(e => e.Status) .Include(e => e.Status)
.Where(e => e.TenantId == tenantId); // Always filter by TenantId first. .Where(e => e.TenantId == tenantId); // Always filter by TenantId first.
@ -190,9 +190,9 @@ namespace Marco.Pms.Services.Service
{ {
expensesQuery = expensesQuery.Where(e => expenseFilter.PaidById.Contains(e.PaidById)); expensesQuery = expensesQuery.Where(e => expenseFilter.PaidById.Contains(e.PaidById));
} }
if (expenseFilter.ExpenseTypeIds?.Any() == true) if (expenseFilter.ExpenseCategoryIds?.Any() == true)
{ {
expensesQuery = expensesQuery.Where(e => expenseFilter.ExpenseTypeIds.Contains(e.ExpensesTypeId)); expensesQuery = expensesQuery.Where(e => expenseFilter.ExpenseCategoryIds.Contains(e.ExpenseCategoryId));
} }
// Only allow filtering by 'CreatedBy' if the user has 'View All' permission. // Only allow filtering by 'CreatedBy' if the user has 'View All' permission.
@ -287,14 +287,14 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Error Occured", ExceptionMapper(ex), 500); return ApiResponse<object>.ErrorResponse("Error Occured", ExceptionMapper(ex), 500);
} }
} }
public async Task<ApiResponse<object>> GetExpenseDetailsAsync(Guid? id, string? financeUId, Employee loggedInEmployee, Guid tenantId) public async Task<ApiResponse<object>> GetExpenseDetailsAsync(Guid? id, string? expenseUId, Employee loggedInEmployee, Guid tenantId)
{ {
try try
{ {
if (!id.HasValue && string.IsNullOrWhiteSpace(financeUId)) if (!id.HasValue && string.IsNullOrWhiteSpace(expenseUId))
{ {
_logger.LogWarning("User do not provided id nor expenseUId"); _logger.LogWarning("Invalid parameters: Both Id and PaymentRequestUID are null or empty.");
return ApiResponse<object>.ErrorResponse("Id or ExpenseUId atleast one must be provided", "Id or ExpenseUId atleast one must be provided", 400); return ApiResponse<object>.ErrorResponse("At least one parameter (Id or expenseUId) must be provided.", "Invalid argument.", 400);
} }
ExpenseDetailsMongoDB? expenseDetails = null; ExpenseDetailsMongoDB? expenseDetails = null;
if (expenseDetails == null) if (expenseDetails == null)
@ -308,9 +308,9 @@ namespace Marco.Pms.Services.Service
.Include(e => e.PaymentMode) .Include(e => e.PaymentMode)
.Include(e => e.Project) .Include(e => e.Project)
.Include(e => e.PaymentMode) .Include(e => e.PaymentMode)
.Include(e => e.ExpensesType) .Include(e => e.ExpenseCategory)
.Include(e => e.Status) .Include(e => e.Status)
.AsNoTracking().FirstOrDefaultAsync(e => (e.Id == id || e.ExpenseUId == financeUId) && e.TenantId == tenantId); .AsNoTracking().FirstOrDefaultAsync(e => (e.Id == id || e.ExpenseUId == expenseUId) && e.TenantId == tenantId);
if (expense == null) if (expense == null)
{ {
@ -318,9 +318,9 @@ namespace Marco.Pms.Services.Service
{ {
_logger.LogWarning("User attempted to fetch expense details with ID {ExpenseId}, but not found in both database and cache", id); _logger.LogWarning("User attempted to fetch expense details with ID {ExpenseId}, but not found in both database and cache", id);
} }
else if (!string.IsNullOrWhiteSpace(financeUId)) else if (!string.IsNullOrWhiteSpace(expenseUId))
{ {
_logger.LogWarning("User attempted to fetch expense details with expenseUId {ExpenseUId}, but not found in both database and cache", financeUId); _logger.LogWarning("User attempted to fetch expense details with expenseUId {ExpenseUId}, but not found in both database and cache", expenseUId);
} }
return ApiResponse<object>.ErrorResponse("Expense Not Found", "Expense Not Found", 404); return ApiResponse<object>.ErrorResponse("Expense Not Found", "Expense Not Found", 404);
} }
@ -387,7 +387,7 @@ namespace Marco.Pms.Services.Service
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "An unhandled exception occurred while fetching an expense details {ExpenseId} or {ExpenseUId}.", id ?? Guid.Empty, financeUId ?? ""); _logger.LogError(ex, "An unhandled exception occurred while fetching an expense details {ExpenseId} or {ExpenseUId}.", id ?? Guid.Empty, expenseUId ?? "EX_00000");
return ApiResponse<object>.ErrorResponse("An internal server error occurred.", ExceptionMapper(ex), 500); return ApiResponse<object>.ErrorResponse("An internal server error occurred.", ExceptionMapper(ex), 500);
} }
} }
@ -414,7 +414,7 @@ namespace Marco.Pms.Services.Service
.Include(e => e.Project) .Include(e => e.Project)
.Include(e => e.CreatedBy) .Include(e => e.CreatedBy)
.Include(e => e.Status) .Include(e => e.Status)
.Include(e => e.ExpensesType) .Include(e => e.ExpenseCategory)
.Where(e => e.TenantId == tenantId) .Where(e => e.TenantId == tenantId)
.ToListAsync(); .ToListAsync();
@ -425,7 +425,7 @@ namespace Marco.Pms.Services.Service
PaidBy = expenses.Where(e => e.PaidBy != null).Select(e => new { Id = e.PaidBy!.Id, Name = $"{e.PaidBy.FirstName} {e.PaidBy.LastName}" }).Distinct().ToList(), PaidBy = expenses.Where(e => e.PaidBy != null).Select(e => new { Id = e.PaidBy!.Id, Name = $"{e.PaidBy.FirstName} {e.PaidBy.LastName}" }).Distinct().ToList(),
CreatedBy = expenses.Where(e => e.CreatedBy != null).Select(e => new { Id = e.CreatedBy!.Id, Name = $"{e.CreatedBy.FirstName} {e.CreatedBy.LastName}" }).Distinct().ToList(), CreatedBy = expenses.Where(e => e.CreatedBy != null).Select(e => new { Id = e.CreatedBy!.Id, Name = $"{e.CreatedBy.FirstName} {e.CreatedBy.LastName}" }).Distinct().ToList(),
Status = expenses.Where(e => e.Status != null).Select(e => new { Id = e.Status!.Id, Name = e.Status.Name }).Distinct().ToList(), Status = expenses.Where(e => e.Status != null).Select(e => new { Id = e.Status!.Id, Name = e.Status.Name }).Distinct().ToList(),
ExpensesType = expenses.Where(e => e.ExpensesType != null).Select(e => new { Id = e.ExpensesType!.Id, Name = e.ExpensesType.Name }).Distinct().ToList() ExpensesCategory = expenses.Where(e => e.ExpenseCategory != null).Select(e => new { Id = e.ExpenseCategory!.Id, Name = e.ExpenseCategory.Name }).Distinct().ToList()
}; };
return ApiResponse<object>.SuccessResponse(response, "Successfully fetched the filter list", 200); return ApiResponse<object>.SuccessResponse(response, "Successfully fetched the filter list", 200);
} }
@ -477,10 +477,10 @@ namespace Marco.Pms.Services.Service
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
return await dbContext.Employees.AsNoTracking().FirstOrDefaultAsync(e => e.Id == dto.PaidById); return await dbContext.Employees.AsNoTracking().FirstOrDefaultAsync(e => e.Id == dto.PaidById);
}); });
var expenseTypeTask = Task.Run(async () => var expenseCategoriesTask = Task.Run(async () =>
{ {
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
return await dbContext.ExpensesTypeMaster.AsNoTracking().FirstOrDefaultAsync(et => et.Id == dto.ExpensesTypeId); return await dbContext.ExpenseCategoryMasters.AsNoTracking().FirstOrDefaultAsync(et => et.Id == dto.ExpenseCategoryId);
}); });
var paymentModeTask = Task.Run(async () => var paymentModeTask = Task.Run(async () =>
{ {
@ -515,7 +515,7 @@ namespace Marco.Pms.Services.Service
// Await all prerequisite checks at once. // Await all prerequisite checks at once.
await Task.WhenAll(hasUploadPermissionTask, projectTask, expenseTypeTask, paymentModeTask, statusMappingTask, paidByTask, expenseUIdTask); await Task.WhenAll(hasUploadPermissionTask, projectTask, expenseCategoriesTask, paymentModeTask, statusMappingTask, paidByTask, expenseUIdTask);
// 2. Aggregate and Check Results // 2. Aggregate and Check Results
if (!await hasUploadPermissionTask) if (!await hasUploadPermissionTask)
@ -526,7 +526,7 @@ namespace Marco.Pms.Services.Service
var validationErrors = new List<string>(); var validationErrors = new List<string>();
var project = await projectTask; var project = await projectTask;
var expenseType = await expenseTypeTask; var expenseCategory = await expenseCategoriesTask;
var paymentMode = await paymentModeTask; var paymentMode = await paymentModeTask;
var statusMapping = await statusMappingTask; var statusMapping = await statusMappingTask;
var paidBy = await paidByTask; var paidBy = await paidByTask;
@ -534,10 +534,10 @@ namespace Marco.Pms.Services.Service
if (project == null) validationErrors.Add("Project not found."); if (project == null) validationErrors.Add("Project not found.");
if (paidBy == null) validationErrors.Add("Paid by employee not found"); if (paidBy == null) validationErrors.Add("Paid by employee not found");
if (expenseType == null) validationErrors.Add("Expense Type not found."); if (expenseCategory == null) validationErrors.Add("Expense Category not found.");
if (paymentMode == null) validationErrors.Add("Payment Mode not found."); if (paymentMode == null) validationErrors.Add("Payment Mode not found.");
if (statusMapping == null) validationErrors.Add("Default status 'Draft' not found."); if (statusMapping == null) validationErrors.Add("Default status 'Draft' not found.");
if ((expenseType?.IsAttachmentRequried ?? true) && !(dto.BillAttachments?.Any() ?? false)) validationErrors.Add("Bill Attachment is requried, but not found"); if ((expenseCategory?.IsAttachmentRequried ?? true) && !(dto.BillAttachments?.Any() ?? false)) validationErrors.Add("Bill Attachment is requried, but not found");
if (validationErrors.Any()) if (validationErrors.Any())
{ {
@ -580,7 +580,7 @@ namespace Marco.Pms.Services.Service
response.Status = _mapper.Map<ExpensesStatusMasterVM>(statusMapping!.Status); response.Status = _mapper.Map<ExpensesStatusMasterVM>(statusMapping!.Status);
response.NextStatus = _mapper.Map<List<ExpensesStatusMasterVM>>(statusMapping.NextStatus); response.NextStatus = _mapper.Map<List<ExpensesStatusMasterVM>>(statusMapping.NextStatus);
response.PaymentMode = _mapper.Map<PaymentModeMatserVM>(paymentMode); response.PaymentMode = _mapper.Map<PaymentModeMatserVM>(paymentMode);
response.ExpensesType = _mapper.Map<ExpensesTypeMasterVM>(expenseType); response.ExpensesCategory = _mapper.Map<ExpensesCategoryMasterVM>(expenseCategory);
_logger.LogInfo("Successfully created Expense {ExpenseId} for Project {ProjectId}.", expense.Id, expense.ProjectId); _logger.LogInfo("Successfully created Expense {ExpenseId} for Project {ProjectId}.", expense.Id, expense.ProjectId);
return ApiResponse<object>.SuccessResponse(response, "Expense created successfully.", 201); return ApiResponse<object>.SuccessResponse(response, "Expense created successfully.", 201);
@ -617,7 +617,7 @@ namespace Marco.Pms.Services.Service
// 1. Fetch Existing Expense with Related Entities (Single Query) // 1. Fetch Existing Expense with Related Entities (Single Query)
var expense = await _context.Expenses var expense = await _context.Expenses
.Include(e => e.ExpensesType) .Include(e => e.ExpenseCategory)
.Include(e => e.Project) .Include(e => e.Project)
.Include(e => e.PaidBy).ThenInclude(e => e!.JobRole) .Include(e => e.PaidBy).ThenInclude(e => e!.JobRole)
.Include(e => e.PaymentMode) .Include(e => e.PaymentMode)
@ -838,7 +838,7 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Invalid Parameters", "Invalid Parameters", 400); return ApiResponse<object>.ErrorResponse("Invalid Parameters", "Invalid Parameters", 400);
} }
var existingExpense = await _context.Expenses var existingExpense = await _context.Expenses
.Include(e => e.ExpensesType) .Include(e => e.ExpenseCategory)
.Include(e => e.Project) .Include(e => e.Project)
.Include(e => e.PaidBy) .Include(e => e.PaidBy)
.ThenInclude(e => e!.JobRole) .ThenInclude(e => e!.JobRole)
@ -1103,6 +1103,29 @@ namespace Marco.Pms.Services.Service
try try
{ {
var hasViewSelfPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseViewSelf, loggedInEmployee.Id);
});
var hasViewAllPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseViewAll, loggedInEmployee.Id);
});
await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask);
if (!hasViewAllPermissionTask.Result && !hasViewSelfPermissionTask.Result)
{
// User has neither required permission. Deny access.
_logger.LogWarning("Access DENIED for employee {EmployeeId} attempting to get payment request list.", loggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(new List<ExpenseList>(), "You do not have permission to view any payment request.", 200);
}
// Initial query including the necessary navigation properties and basic multi-tenant/security constraints // Initial query including the necessary navigation properties and basic multi-tenant/security constraints
var paymentRequestQuery = _context.PaymentRequests var paymentRequestQuery = _context.PaymentRequests
.Include(pr => pr.Currency) .Include(pr => pr.Currency)
@ -1120,6 +1143,11 @@ namespace Marco.Pms.Services.Service
pr.CreatedBy != null && pr.CreatedBy != null &&
pr.CreatedBy.JobRole != null); pr.CreatedBy.JobRole != null);
if (hasViewSelfPermissionTask.Result)
{
paymentRequestQuery = paymentRequestQuery.Where(pr => pr.CreatedById == loggedInEmployee.Id);
}
// Deserialize and apply advanced filter if provided // Deserialize and apply advanced filter if provided
PaymentRequestFilter? paymentRequestFilter = TryDeserializePaymentRequestFilter(filter); PaymentRequestFilter? paymentRequestFilter = TryDeserializePaymentRequestFilter(filter);
@ -1172,7 +1200,7 @@ namespace Marco.Pms.Services.Service
.Where(pr => .Where(pr =>
pr.Payee.Contains(searchString) || pr.Payee.Contains(searchString) ||
pr.Title.Contains(searchString) || pr.Title.Contains(searchString) ||
($"{pr.UIDPrefix}/{pr.UIDPostfix:D5}").Contains(searchString) (pr.UIDPrefix + "/" + pr.UIDPostfix.ToString().PadLeft(5, '0')).Contains(searchString)
); );
} }
@ -1219,6 +1247,342 @@ namespace Marco.Pms.Services.Service
} }
} }
public async Task<ApiResponse<object>> GetPaymentRequestDetails(Guid? id, string? paymentRequestUId, Employee loggedInEmployee, Guid tenantId)
{
if (!id.HasValue && string.IsNullOrWhiteSpace(paymentRequestUId))
{
return ApiResponse<object>.ErrorResponse("User must proivde atleast one parameter", "User must proivde atleast one parameter", 400);
}
var hasViewSelfPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseViewSelf, loggedInEmployee.Id);
});
var hasViewAllPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseViewAll, loggedInEmployee.Id);
});
var hasReviewPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseReview, loggedInEmployee.Id);
});
var hasApprovePermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseApprove, loggedInEmployee.Id);
});
var hasProcessPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseProcess, loggedInEmployee.Id);
});
await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask, hasReviewPermissionTask, hasApprovePermissionTask, hasProcessPermissionTask);
var hasViewSelfPermission = hasViewSelfPermissionTask.Result;
var hasViewAllPermission = hasViewAllPermissionTask.Result;
var hasReviewPermission = hasReviewPermissionTask.Result;
var hasApprovePermission = hasApprovePermissionTask.Result;
var hasProcessPermission = hasProcessPermissionTask.Result;
if (!hasViewSelfPermission && !hasViewAllPermission && !hasReviewPermission && !hasApprovePermission && !hasProcessPermission)
{
// User has neither required permission. Deny access.
_logger.LogWarning("Access DENIED for employee {EmployeeId} attempting to get payment request list.", loggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(new List<ExpenseList>(), "You do not have permission to view any payment request.", 200);
}
var paymentRequest = await _context.PaymentRequests
.Include(pr => pr.Currency)
.Include(pr => pr.Project)
.Include(pr => pr.RecurringPayment)
.Include(pr => pr.ExpenseCategory)
.Include(pr => pr.ExpenseStatus)
.Include(pr => pr.CreatedBy)
.ThenInclude(e => e!.JobRole)
.Include(pr => pr.UpdatedBy)
.ThenInclude(e => e!.JobRole)
.Where(pr => (pr.Id == id || (pr.UIDPrefix + "/" + pr.UIDPostfix.ToString().PadLeft(5, '0')) == paymentRequestUId) &&
pr.TenantId == tenantId &&
pr.Currency != null &&
pr.ExpenseCategory != null &&
pr.ExpenseStatus != null &&
pr.CreatedBy != null &&
pr.CreatedBy.JobRole != null).FirstOrDefaultAsync();
if (paymentRequest == null)
{
return ApiResponse<object>.ErrorResponse("Payment Request not found", "Payment Request not found", 404);
}
var selfCheck = hasViewSelfPermission && !hasViewAllPermission && !hasReviewPermission && !hasApprovePermission && !hasProcessPermission && paymentRequest.CreatedById != loggedInEmployee.Id;
if (selfCheck)
{
// User has neither required permission. Deny access.
_logger.LogWarning("Access DENIED for employee {EmployeeId} attempting to get payment request list.", loggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(new List<ExpenseList>(), "You do not have permission to view any payment request.", 200);
}
var nextStatusTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var nextStatus = await context.ExpensesStatusMapping
.Include(esm => esm.NextStatus)
.Where(esm => esm.StatusId == paymentRequest.ExpenseStatusId && esm.NextStatus != null)
.Select(esm => esm.NextStatus!)
.ToListAsync();
var nextStatusIds = nextStatus.Select(esm => esm.Id).ToList();
var permissionMapping = await context.StatusPermissionMapping.Where(spm => nextStatusIds.Contains(spm.StatusId)).ToListAsync();
List<ExpensesStatusMasterVM> results = new List<ExpensesStatusMasterVM>();
foreach (var status in nextStatus)
{
var permissionIds = permissionMapping.Where(spm => spm.StatusId == status.Id).Select(spm => spm.PermissionId).ToList();
var hasPermission = await permissionService.HasPermissionAny(permissionIds, loggedInEmployee.Id);
var hasStatusPermission = Review == status.Id && loggedInEmployee.Id == paymentRequest.CreatedById;
if (!hasPermission && !hasStatusPermission)
{
continue;
}
var result = _mapper.Map<ExpensesStatusMasterVM>(status);
result.PermissionIds = permissionIds;
results.Add(result);
}
return results;
});
var documnetTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
var documents = await context.PaymentRequestAttachments
.Include(pra => pra.Document)
.Where(pra => pra.PaymentRequestId == paymentRequest.Id && pra.Document != null)
.Select(pra => pra.Document!)
.ToListAsync();
return documents.Select(d =>
{
var result = _mapper.Map<PaymentRequestAttachmentVM>(d);
result.Url = _s3Service.GeneratePreSignedUrl(d.S3Key);
return result;
}).ToList();
});
await Task.WhenAll(nextStatusTask, documnetTask);
var nextStatus = nextStatusTask.Result;
var attachementVMs = documnetTask.Result;
var response = _mapper.Map<PaymentRequestDetailsVM>(paymentRequest);
response.PaymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}";
response.Attachments = attachementVMs;
response.NextStatus = nextStatus;
return ApiResponse<object>.SuccessResponse(response, "Payment request fetched successfully", 200);
}
public async Task<ApiResponse<object>> GetPaymentRequestDetailsAsync(Guid? id, string? paymentRequestUId, Employee loggedInEmployee, Guid tenantId)
{
_logger.LogInfo("Start GetPaymentRequestDetailsAsync called by EmployeeId: {EmployeeId} for TenantId: {TenantId} with Id: {Id}, UID: {UID}",
loggedInEmployee.Id, tenantId, id ?? Guid.Empty, paymentRequestUId ?? "PY/1125/00000");
try
{
// Validate input: at least one identifier must be provided
if (!id.HasValue && string.IsNullOrWhiteSpace(paymentRequestUId))
{
_logger.LogWarning("Invalid parameters: Both Id and PaymentRequestUID are null or empty.");
return ApiResponse<object>.ErrorResponse("At least one parameter (Id or PaymentRequestUID) must be provided.", "Invalid argument.", 400);
}
// Check user permissions concurrently
var hasViewSelfPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseViewSelf, loggedInEmployee.Id);
});
var hasViewAllPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseViewAll, loggedInEmployee.Id);
});
var hasReviewPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseReview, loggedInEmployee.Id);
});
var hasApprovePermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseApprove, loggedInEmployee.Id);
});
var hasProcessPermissionTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
return await permissionService.HasPermission(PermissionsMaster.ExpenseProcess, loggedInEmployee.Id);
});
await Task.WhenAll(hasViewSelfPermissionTask, hasViewAllPermissionTask, hasReviewPermissionTask, hasApprovePermissionTask, hasProcessPermissionTask);
bool hasViewSelfPermission = hasViewSelfPermissionTask.Result;
bool hasViewAllPermission = hasViewAllPermissionTask.Result;
bool hasReviewPermission = hasReviewPermissionTask.Result;
bool hasApprovePermission = hasApprovePermissionTask.Result;
bool hasProcessPermission = hasProcessPermissionTask.Result;
// Deny access if user has no relevant permissions
if (!hasViewSelfPermission && !hasViewAllPermission && !hasReviewPermission && !hasApprovePermission && !hasProcessPermission)
{
_logger.LogWarning("Access DENIED: Employee {EmployeeId} has no permission to view payment requests.", loggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(new List<ExpenseList>(), "You do not have permission to view any payment request.", 200);
}
// Query payment request with all necessary navigation properties and validation constraints
var paymentRequest = await _context.PaymentRequests
.Include(pr => pr.Currency)
.Include(pr => pr.Project)
.Include(pr => pr.RecurringPayment)
.Include(pr => pr.ExpenseCategory)
.Include(pr => pr.ExpenseStatus)
.Include(pr => pr.CreatedBy).ThenInclude(e => e!.JobRole)
.Include(pr => pr.UpdatedBy).ThenInclude(e => e!.JobRole)
.Where(pr =>
(pr.Id == id || (pr.UIDPrefix + "/" + pr.UIDPostfix.ToString().PadLeft(5, '0')) == paymentRequestUId) &&
pr.TenantId == tenantId &&
pr.Currency != null &&
pr.ExpenseCategory != null &&
pr.ExpenseStatus != null &&
pr.CreatedBy != null &&
pr.CreatedBy.JobRole != null)
.FirstOrDefaultAsync();
if (paymentRequest == null)
{
_logger.LogWarning("Payment Request not found: Id={Id}, UID={UID}, TenantId={TenantId}", id ?? Guid.Empty, paymentRequestUId ?? "PY/1125/00000", tenantId);
return ApiResponse<object>.ErrorResponse("Payment Request not found.", "Payment Request not found.", 404);
}
// Check if employee has only "view self" permission but the payment request is created by another employee => deny
bool selfCheck = hasViewSelfPermission && !hasViewAllPermission && !hasReviewPermission && !hasApprovePermission && !hasProcessPermission
&& paymentRequest.CreatedById != loggedInEmployee.Id;
if (selfCheck)
{
_logger.LogWarning("Access DENIED: Employee {EmployeeId} lacks permission to view PaymentRequest {PaymentRequestId} created by another employee.",
loggedInEmployee.Id, paymentRequest.Id);
return ApiResponse<object>.SuccessResponse(new List<ExpenseList>(), "You do not have permission to view this payment request.", 200);
}
// Concurrently fetch next possible statuses and related permissions
var nextStatusTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var nextStatuses = await context.ExpensesStatusMapping
.Include(esm => esm.NextStatus)
.Where(esm => esm.StatusId == paymentRequest.ExpenseStatusId && esm.NextStatus != null)
.Select(esm => esm.NextStatus!)
.ToListAsync();
var nextStatusIds = nextStatuses.Select(ns => ns.Id).ToList();
var permissionMappings = await context.StatusPermissionMapping.Where(spm => nextStatusIds.Contains(spm.StatusId)).ToListAsync();
var results = new List<ExpensesStatusMasterVM>();
foreach (var status in nextStatuses)
{
var permissionIds = permissionMappings.Where(spm => spm.StatusId == status.Id).Select(spm => spm.PermissionId).ToList();
bool hasPermission = await permissionService.HasPermissionAny(permissionIds, loggedInEmployee.Id);
// Special case: allow review status if creator is the logged-in user
bool hasStatusPermission = Review == status.Id && loggedInEmployee.Id == paymentRequest.CreatedById;
if (!hasPermission && !hasStatusPermission)
{
continue;
}
var mappedStatus = _mapper.Map<ExpensesStatusMasterVM>(status);
mappedStatus.PermissionIds = permissionIds;
results.Add(mappedStatus);
}
return results;
});
// Concurrently fetch attachments with pre-signed URLs
var documentTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
var documents = await context.PaymentRequestAttachments
.Include(pra => pra.Document)
.Where(pra => pra.PaymentRequestId == paymentRequest.Id && pra.Document != null)
.Select(pra => pra.Document!)
.ToListAsync();
return documents.Select(d =>
{
var attachmentVM = _mapper.Map<PaymentRequestAttachmentVM>(d);
attachmentVM.Url = _s3Service.GeneratePreSignedUrl(d.S3Key);
return attachmentVM;
}).ToList();
});
await Task.WhenAll(nextStatusTask, documentTask);
var nextStatuses = nextStatusTask.Result;
var attachmentVMs = documentTask.Result;
// Map main response model and populate additional fields
var response = _mapper.Map<PaymentRequestDetailsVM>(paymentRequest);
response.PaymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}";
response.Attachments = attachmentVMs;
response.NextStatus = nextStatuses;
_logger.LogInfo("Payment request details fetched successfully for PaymentRequestId: {PaymentRequestId}, EmployeeId: {EmployeeId}", paymentRequest.Id, loggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(response, "Payment request fetched successfully.", 200);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in GetPaymentRequestDetailsAsync for TenantId={TenantId}, EmployeeId={EmployeeId}: {Message}", tenantId, loggedInEmployee.Id, ex.Message);
return ApiResponse<object>.ErrorResponse("An error occurred while fetching the payment request details.", ex.Message, 500);
}
finally
{
_logger.LogInfo("End GetPaymentRequestDetailsAsync called by EmployeeId: {EmployeeId}", loggedInEmployee.Id);
}
}
public async Task<ApiResponse<object>> CreatePaymentRequestAsync(PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId) public async Task<ApiResponse<object>> CreatePaymentRequestAsync(PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId)
{ {
@ -1337,7 +1701,7 @@ namespace Marco.Pms.Services.Service
var response = _mapper.Map<PaymentRequestVM>(paymentRequest); var response = _mapper.Map<PaymentRequestVM>(paymentRequest);
response.PaymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}"; response.PaymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}";
response.Currency = currency; response.Currency = currency;
response.ExpenseCategory = _mapper.Map<ExpensesTypeMasterVM>(expenseCategory); response.ExpenseCategory = _mapper.Map<ExpensesCategoryMasterVM>(expenseCategory);
response.ExpenseStatus = _mapper.Map<ExpensesStatusMasterVM>(expenseStatus); response.ExpenseStatus = _mapper.Map<ExpensesStatusMasterVM>(expenseStatus);
response.Project = _mapper.Map<BasicProjectVM>(project); response.Project = _mapper.Map<BasicProjectVM>(project);
response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee); response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
@ -1360,7 +1724,6 @@ namespace Marco.Pms.Services.Service
_logger.LogInfo("End CreatePaymentRequestAsync for EmployeeId: {EmployeeId}", loggedInEmployee.Id); _logger.LogInfo("End CreatePaymentRequestAsync for EmployeeId: {EmployeeId}", loggedInEmployee.Id);
} }
} }
public async Task<ApiResponse<object>> EditPaymentRequestAsync(Guid id, PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId) public async Task<ApiResponse<object>> EditPaymentRequestAsync(Guid id, PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId)
{ {
_logger.LogInfo("Start EditPaymentRequestAsync for PaymentRequestId: {PaymentRequestId}, EmployeeId: {EmployeeId}", id, loggedInEmployee.Id); _logger.LogInfo("Start EditPaymentRequestAsync for PaymentRequestId: {PaymentRequestId}, EmployeeId: {EmployeeId}", id, loggedInEmployee.Id);
@ -1529,9 +1892,8 @@ namespace Marco.Pms.Services.Service
var response = _mapper.Map<PaymentRequestVM>(paymentRequest); var response = _mapper.Map<PaymentRequestVM>(paymentRequest);
response.PaymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}"; response.PaymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}";
response.Currency = currency; response.Currency = currency;
response.ExpenseCategory = _mapper.Map<ExpensesTypeMasterVM>(expenseCategory); response.ExpenseCategory = _mapper.Map<ExpensesCategoryMasterVM>(expenseCategory);
response.Project = _mapper.Map<BasicProjectVM>(project); response.Project = _mapper.Map<BasicProjectVM>(project);
response.UpdatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
_logger.LogInfo("PaymentRequest updated successfully with UID: {PaymentRequestUID}", response.PaymentRequestUID); _logger.LogInfo("PaymentRequest updated successfully with UID: {PaymentRequestUID}", response.PaymentRequestUID);
return ApiResponse<object>.SuccessResponse(response, "Payment Request updated successfully.", 200); return ApiResponse<object>.SuccessResponse(response, "Payment Request updated successfully.", 200);
@ -1618,7 +1980,7 @@ namespace Marco.Pms.Services.Service
List<ExpenseList> expenseList = new List<ExpenseList>(); List<ExpenseList> expenseList = new List<ExpenseList>();
var projectIds = model.Select(m => m.ProjectId).ToList(); var projectIds = model.Select(m => m.ProjectId).ToList();
var statusIds = model.Select(m => m.StatusId).ToList(); var statusIds = model.Select(m => m.StatusId).ToList();
var expensesTypeIds = model.Select(m => m.ExpensesTypeId).ToList(); var expenseCategoryIds = model.Select(m => m.ExpenseCategoryId).ToList();
var paymentModeIds = model.Select(m => m.PaymentModeId).ToList(); var paymentModeIds = model.Select(m => m.PaymentModeId).ToList();
var createdByIds = model.Select(m => m.CreatedById).ToList(); var createdByIds = model.Select(m => m.CreatedById).ToList();
var paidByIds = model.Select(m => m.PaidById).ToList(); var paidByIds = model.Select(m => m.PaidById).ToList();
@ -1638,10 +2000,10 @@ namespace Marco.Pms.Services.Service
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
return await dbContext.Employees.AsNoTracking().Where(e => createdByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync(); return await dbContext.Employees.AsNoTracking().Where(e => createdByIds.Contains(e.Id) && e.TenantId == tenantId).ToListAsync();
}); });
var expenseTypeTask = Task.Run(async () => var expenseCategoriesTask = Task.Run(async () =>
{ {
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
return await dbContext.ExpensesTypeMaster.AsNoTracking().Where(et => expensesTypeIds.Contains(et.Id) && et.TenantId == tenantId).ToListAsync(); return await dbContext.ExpenseCategoryMasters.AsNoTracking().Where(ec => expenseCategoryIds.Contains(ec.Id) && ec.TenantId == tenantId).ToListAsync();
}); });
var paymentModeTask = Task.Run(async () => var paymentModeTask = Task.Run(async () =>
{ {
@ -1685,10 +2047,10 @@ namespace Marco.Pms.Services.Service
}); });
// Await all prerequisite checks at once. // Await all prerequisite checks at once.
await Task.WhenAll(projectTask, expenseTypeTask, paymentModeTask, statusMappingTask, paidByTask, createdByTask, statusTask, permissionStatusMappingTask); await Task.WhenAll(projectTask, expenseCategoriesTask, paymentModeTask, statusMappingTask, paidByTask, createdByTask, statusTask, permissionStatusMappingTask);
var projects = projectTask.Result; var projects = projectTask.Result;
var expenseTypes = expenseTypeTask.Result; var expenseCategories = expenseCategoriesTask.Result;
var paymentModes = paymentModeTask.Result; var paymentModes = paymentModeTask.Result;
var statusMappings = statusMappingTask.Result; var statusMappings = statusMappingTask.Result;
var permissionStatusMappings = permissionStatusMappingTask.Result; var permissionStatusMappings = permissionStatusMappingTask.Result;
@ -1721,7 +2083,7 @@ namespace Marco.Pms.Services.Service
} }
} }
response.PaymentMode = paymentModes.Where(pm => pm.Id == m.PaymentModeId).Select(pm => _mapper.Map<PaymentModeMatserVM>(pm)).FirstOrDefault(); response.PaymentMode = paymentModes.Where(pm => pm.Id == m.PaymentModeId).Select(pm => _mapper.Map<PaymentModeMatserVM>(pm)).FirstOrDefault();
response.ExpensesType = expenseTypes.Where(et => et.Id == m.ExpensesTypeId).Select(et => _mapper.Map<ExpensesTypeMasterVM>(et)).FirstOrDefault(); response.ExpensesCategory = expenseCategories.Where(ec => ec.Id == m.ExpenseCategoryId).Select(ec => _mapper.Map<ExpensesCategoryMasterVM>(ec)).FirstOrDefault();
return response; return response;
}).ToList(); }).ToList();
@ -1802,7 +2164,7 @@ namespace Marco.Pms.Services.Service
response.Status = _mapper.Map<ExpensesStatusMasterMongoDB>(status); response.Status = _mapper.Map<ExpensesStatusMasterMongoDB>(status);
} }
response.PaymentMode = _mapper.Map<PaymentModeMatserMongoDB>(model.PaymentMode); response.PaymentMode = _mapper.Map<PaymentModeMatserMongoDB>(model.PaymentMode);
response.ExpensesType = _mapper.Map<ExpensesTypeMasterMongoDB>(model.ExpensesType); response.ExpenseCategory = _mapper.Map<ExpenseCategoryMasterMongoDB>(model.ExpenseCategory);
if (billAttachment != null) response.Documents = billAttachment.Documents; if (billAttachment != null) response.Documents = billAttachment.Documents;
return response; return response;

View File

@ -2077,7 +2077,7 @@ namespace Marco.Pms.Services.Service
{ {
// Featching the list of Expenses Type. // Featching the list of Expenses Type.
var typeList = await _context.ExpenseCategoryMasters.Where(et => et.TenantId == tenantId && et.IsActive == isActive).ToListAsync(); var typeList = await _context.ExpenseCategoryMasters.Where(et => et.TenantId == tenantId && et.IsActive == isActive).ToListAsync();
var response = _mapper.Map<List<ExpensesTypeMasterVM>>(typeList); var response = _mapper.Map<List<ExpensesCategoryMasterVM>>(typeList);
_logger.LogInfo("{Count} records of expense type have been fetched successfully by employee {EmployeeId}", response.Count, loggedInEmployee.Id); _logger.LogInfo("{Count} records of expense type have been fetched successfully by employee {EmployeeId}", response.Count, loggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(response, $"{response.Count} records of expense type have been fetched successfully.", 200); return ApiResponse<object>.SuccessResponse(response, $"{response.Count} records of expense type have been fetched successfully.", 200);
@ -2107,7 +2107,7 @@ namespace Marco.Pms.Services.Service
_logger.LogInfo("New Expense Type {ExpensesTypeId} was added by employee {EmployeeId}", expensesType.Id, loggedInEmployee.Id); _logger.LogInfo("New Expense Type {ExpensesTypeId} was added by employee {EmployeeId}", expensesType.Id, loggedInEmployee.Id);
var response = _mapper.Map<ExpensesTypeMasterVM>(expensesType); var response = _mapper.Map<ExpensesCategoryMasterVM>(expensesType);
return ApiResponse<object>.SuccessResponse(response, "Expense type craeted Successfully", 201); return ApiResponse<object>.SuccessResponse(response, "Expense type craeted Successfully", 201);
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
@ -2171,7 +2171,7 @@ namespace Marco.Pms.Services.Service
}, "ExpenseCategoryMasterModificationLog"); }, "ExpenseCategoryMasterModificationLog");
// Mapping ExpensesTypeMaster to ExpensesTypeMasterVM // Mapping ExpensesTypeMaster to ExpensesTypeMasterVM
var response = _mapper.Map<ExpensesTypeMasterVM>(expensesType); var response = _mapper.Map<ExpensesCategoryMasterVM>(expensesType);
return ApiResponse<object>.SuccessResponse(response, "Expense type updated Successfully", 200); return ApiResponse<object>.SuccessResponse(response, "Expense type updated Successfully", 200);
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
@ -2226,7 +2226,7 @@ namespace Marco.Pms.Services.Service
}, "ExpenseCategoryMasterModificationLog"); }, "ExpenseCategoryMasterModificationLog");
// Mapping ExpensesTypeMaster to ExpensesTypeMasterVM // Mapping ExpensesTypeMaster to ExpensesTypeMasterVM
var response = _mapper.Map<ExpensesTypeMasterVM>(expensesType); var response = _mapper.Map<ExpensesCategoryMasterVM>(expensesType);
return ApiResponse<object>.SuccessResponse(response, $"Expense type {action}d Successfully", 200); return ApiResponse<object>.SuccessResponse(response, $"Expense type {action}d Successfully", 200);
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)

View File

@ -8,7 +8,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
{ {
#region =================================================================== Expenses Functions =================================================================== #region =================================================================== Expenses Functions ===================================================================
Task<ApiResponse<object>> GetExpensesListAsync(Employee loggedInEmployee, Guid tenantId, string? searchString, string? filter, int pageSize, int pageNumber); Task<ApiResponse<object>> GetExpensesListAsync(Employee loggedInEmployee, Guid tenantId, string? searchString, string? filter, int pageSize, int pageNumber);
Task<ApiResponse<object>> GetExpenseDetailsAsync(Guid? id, string? financeUId, Employee loggedInEmployee, Guid tenantId); Task<ApiResponse<object>> GetExpenseDetailsAsync(Guid? id, string? expenseUId, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> GetSupplerNameListAsync(Employee loggedInEmployee, Guid tenantId); Task<ApiResponse<object>> GetSupplerNameListAsync(Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> GetFilterObjectAsync(Employee loggedInEmployee, Guid tenantId); Task<ApiResponse<object>> GetFilterObjectAsync(Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> CreateExpenseAsync(CreateExpensesDto dto, Employee loggedInEmployee, Guid tenantId); Task<ApiResponse<object>> CreateExpenseAsync(CreateExpensesDto dto, Employee loggedInEmployee, Guid tenantId);
@ -19,6 +19,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
#region =================================================================== Payment Request Functions =================================================================== #region =================================================================== Payment Request Functions ===================================================================
Task<ApiResponse<object>> GetPaymentRequestListAsync(string? searchString, string? filter, bool isActive, int pageSize, int pageNumber, Employee loggedInEmployee, Guid tenantId); Task<ApiResponse<object>> GetPaymentRequestListAsync(string? searchString, string? filter, bool isActive, int pageSize, int pageNumber, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> GetPaymentRequestDetailsAsync(Guid? id, string? paymentRequestUId, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> CreatePaymentRequestAsync(PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId); Task<ApiResponse<object>> CreatePaymentRequestAsync(PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> EditPaymentRequestAsync(Guid id, PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId); Task<ApiResponse<object>> EditPaymentRequestAsync(Guid id, PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId);
#endregion #endregion