Added the payment request details API
This commit is contained in:
parent
cf24f3af32
commit
4c0ef1f585
7334
Marco.Pms.DataAccess/Migrations/20251103100115_Added_Changed_ExpensesType_To_ExpenseCategory.Designer.cs
generated
Normal file
7334
Marco.Pms.DataAccess/Migrations/20251103100115_Added_Changed_ExpensesType_To_ExpenseCategory.Designer.cs
generated
Normal file
File diff suppressed because one or more lines are too long
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2097,15 +2097,24 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("EmployeeId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("AdvancePaymentTransactions");
|
||||
@ -2310,13 +2319,13 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("ExpenseCategoryId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("ExpenseUId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("ExpensesTypeId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("GSTNumber")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
@ -2369,7 +2378,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("ExpensesTypeId");
|
||||
b.HasIndex("ExpenseCategoryId");
|
||||
|
||||
b.HasIndex("PaidById");
|
||||
|
||||
@ -6208,6 +6217,12 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
@ -6218,6 +6233,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.Navigation("Employee");
|
||||
|
||||
b.Navigation("Project");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
@ -6298,9 +6315,9 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Master.ExpensesTypeMaster", "ExpensesType")
|
||||
b.HasOne("Marco.Pms.Model.Expenses.ExpenseCategoryMaster", "ExpenseCategory")
|
||||
.WithMany()
|
||||
.HasForeignKey("ExpensesTypeId")
|
||||
.HasForeignKey("ExpenseCategoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
@ -6346,7 +6363,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("ExpensesType");
|
||||
b.Navigation("ExpenseCategory");
|
||||
|
||||
b.Navigation("PaidBy");
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ namespace Marco.Pms.Model.Dtos.Expenses
|
||||
public class CreateExpensesDto
|
||||
{
|
||||
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 PaidById { get; set; }
|
||||
public DateTime TransactionDate { get; set; } = DateTime.Now;
|
||||
|
||||
@ -6,7 +6,7 @@ namespace Marco.Pms.Model.Dtos.Expenses
|
||||
{
|
||||
public required Guid Id { 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 PaidById { get; set; }
|
||||
public DateTime TransactionDate { get; set; } = DateTime.Now;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
@ -10,6 +11,12 @@ namespace Marco.Pms.Model.Expenses
|
||||
public Guid Id { get; set; }
|
||||
public string FinanceUIdPrefix { get; set; } = default!;
|
||||
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; }
|
||||
|
||||
[ValidateNever]
|
||||
|
||||
@ -15,11 +15,11 @@ namespace Marco.Pms.Model.Expenses
|
||||
[ValidateNever]
|
||||
[ForeignKey("ProjectId")]
|
||||
public Project? Project { get; set; }
|
||||
public Guid ExpensesTypeId { get; set; }
|
||||
public Guid ExpenseCategoryId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("ExpensesTypeId")]
|
||||
public ExpensesTypeMaster? ExpensesType { get; set; }
|
||||
[ForeignKey("ExpenseCategoryId")]
|
||||
public ExpenseCategoryMaster? ExpenseCategory { get; set; }
|
||||
public Guid PaymentModeId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
public List<Guid>? StatusIds { get; set; }
|
||||
public List<Guid>? CreatedByIds { 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 DateTime? StartDate { get; set; }
|
||||
public DateTime? EndDate { get; set; }
|
||||
|
||||
@ -8,7 +8,7 @@ namespace Marco.Pms.Model.MongoDBModels.Expenses
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
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 BasicEmployeeMongoDB PaidBy { get; set; } = new BasicEmployeeMongoDB();
|
||||
public BasicEmployeeMongoDB CreatedBy { get; set; } = new BasicEmployeeMongoDB();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ namespace Marco.Pms.Model.ViewModels.Expenses
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public ProjectInfoVM? Project { get; set; }
|
||||
public ExpensesTypeMasterVM? ExpensesType { get; set; }
|
||||
public ExpensesCategoryMasterVM? ExpensesType { get; set; }
|
||||
public PaymentModeMatserVM? PaymentMode { get; set; }
|
||||
public BasicEmployeeVM? PaidBy { get; set; }
|
||||
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||
|
||||
@ -8,7 +8,7 @@ namespace Marco.Pms.Model.ViewModels.Expanses
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public ProjectInfoVM? Project { get; set; }
|
||||
public ExpensesTypeMasterVM? ExpensesType { get; set; }
|
||||
public ExpensesCategoryMasterVM? ExpensesCategory { get; set; }
|
||||
public PaymentModeMatserVM? PaymentMode { get; set; }
|
||||
public BasicEmployeeVM? PaidBy { get; set; }
|
||||
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -18,14 +18,11 @@ namespace Marco.Pms.Model.ViewModels.Expenses
|
||||
public DateTime DueDate { get; set; }
|
||||
public BasicProjectVM? Project { get; set; }
|
||||
public RecurringPayment? RecurringPayment { get; set; }
|
||||
public ExpensesTypeMasterVM? ExpenseCategory { get; set; }
|
||||
public ExpensesCategoryMasterVM? ExpenseCategory { get; set; }
|
||||
public ExpensesStatusMasterVM? ExpenseStatus { get; set; }
|
||||
public bool IsAdvancePayment { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public Guid? UpdatedById { get; set; }
|
||||
public BasicEmployeeVM? UpdatedBy { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
namespace Marco.Pms.Model.ViewModels.Master
|
||||
{
|
||||
public class ExpensesTypeMasterVM
|
||||
public class ExpensesCategoryMasterVM
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
@ -642,7 +642,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
baseQuery = baseQuery.Where(e => e.ProjectId == projectId);
|
||||
|
||||
if (categoryId.HasValue)
|
||||
baseQuery = baseQuery.Where(e => e.ExpensesTypeId == categoryId);
|
||||
baseQuery = baseQuery.Where(e => e.ExpenseCategoryId == categoryId);
|
||||
|
||||
// Single server-side group/aggregate by project
|
||||
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
|
||||
// Group by expense type name; adjust to the correct key if ExpensesCategory is an enum or navigation
|
||||
var query = baseQuery
|
||||
.Where(e => e.ExpensesType != null)
|
||||
.Where(e => e.ExpenseCategory != null)
|
||||
.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,
|
||||
StatusId = e.StatusId
|
||||
})
|
||||
|
||||
@ -48,10 +48,10 @@ namespace Marco.Pms.Services.Controllers
|
||||
}
|
||||
|
||||
[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 response = await _expensesService.GetExpenseDetailsAsync(id, financeUId, loggedInEmployee, tenantId);
|
||||
var response = await _expensesService.GetExpenseDetailsAsync(id, expenseUId, loggedInEmployee, tenantId);
|
||||
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);
|
||||
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")]
|
||||
public async Task<IActionResult> CreatePaymentRequest([FromBody] PaymentRequestDto model)
|
||||
{
|
||||
@ -146,6 +155,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
}
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpPut("payment-request/edit/{id}")]
|
||||
public async Task<IActionResult> EditPaymentRequest(Guid id, [FromBody] PaymentRequestDto model)
|
||||
{
|
||||
|
||||
@ -1144,7 +1144,7 @@ namespace Marco.Pms.Services.Helpers
|
||||
var expenseIds = model.Select(m => m.Id).ToList();
|
||||
var projectIds = model.Select(m => m.ProjectId).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 createdByIds = model.Select(m => m.CreatedById).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.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>();
|
||||
return response;
|
||||
}).ToList();
|
||||
@ -1315,7 +1315,7 @@ namespace Marco.Pms.Services.Helpers
|
||||
var expenseTypeTask = Task.Run(async () =>
|
||||
{
|
||||
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 () =>
|
||||
{
|
||||
@ -1403,7 +1403,7 @@ namespace Marco.Pms.Services.Helpers
|
||||
response.Status = _mapper.Map<ExpensesStatusMasterMongoDB>(status);
|
||||
}
|
||||
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;
|
||||
|
||||
return response;
|
||||
|
||||
@ -257,9 +257,14 @@ namespace Marco.Pms.Services.MappingProfiles
|
||||
.ForMember(
|
||||
dest => dest.Id,
|
||||
opt => opt.MapFrom(src => Guid.Parse(src.Id)));
|
||||
|
||||
#region ======================================================= Payment Request =======================================================
|
||||
|
||||
CreateMap<PaymentRequestDto, PaymentRequest>();
|
||||
CreateMap<PaymentRequest, PaymentRequestVM>();
|
||||
CreateMap<PaymentRequest, PaymentRequestDetailsVM>();
|
||||
CreateMap<Document, PaymentRequestAttachmentVM>();
|
||||
|
||||
#endregion
|
||||
|
||||
CreateMap<AdvancePaymentTransaction, AdvancePaymentTransactionVM>();
|
||||
@ -346,10 +351,10 @@ namespace Marco.Pms.Services.MappingProfiles
|
||||
// Explicitly and safely convert nullable Guid to non-nullable Guid
|
||||
opt => opt.MapFrom(src => src.Id ?? Guid.Empty)
|
||||
);
|
||||
CreateMap<ExpensesTypeMaster, ExpensesTypeMasterVM>();
|
||||
CreateMap<ExpenseCategoryMaster, ExpensesTypeMasterVM>();
|
||||
CreateMap<ExpensesTypeMaster, ExpensesCategoryMasterVM>();
|
||||
CreateMap<ExpenseCategoryMaster, ExpensesCategoryMasterVM>();
|
||||
|
||||
CreateMap<ExpenseCategoryMaster, ExpensesTypeMasterMongoDB>()
|
||||
CreateMap<ExpenseCategoryMaster, ExpenseCategoryMasterMongoDB>()
|
||||
.ForMember(
|
||||
dest => dest.Id,
|
||||
opt => opt.MapFrom(src => src.Id.ToString()))
|
||||
@ -357,7 +362,7 @@ namespace Marco.Pms.Services.MappingProfiles
|
||||
dest => dest.TenantId,
|
||||
opt => opt.MapFrom(src => src.TenantId.ToString()));
|
||||
|
||||
CreateMap<ExpensesTypeMaster, ExpensesTypeMasterMongoDB>()
|
||||
CreateMap<ExpensesTypeMaster, ExpenseCategoryMasterMongoDB>()
|
||||
.ForMember(
|
||||
dest => dest.Id,
|
||||
opt => opt.MapFrom(src => src.Id.ToString()))
|
||||
@ -365,7 +370,7 @@ namespace Marco.Pms.Services.MappingProfiles
|
||||
dest => dest.TenantId,
|
||||
opt => opt.MapFrom(src => src.TenantId.ToString()));
|
||||
|
||||
CreateMap<ExpensesTypeMasterMongoDB, ExpensesTypeMasterVM>()
|
||||
CreateMap<ExpenseCategoryMasterMongoDB, ExpensesCategoryMasterVM>()
|
||||
.ForMember(
|
||||
dest => dest.Id,
|
||||
opt => opt.MapFrom(src => Guid.Parse(src.Id)));
|
||||
|
||||
@ -141,7 +141,7 @@ namespace Marco.Pms.Services.Service
|
||||
.Include(e => e.PaymentMode)
|
||||
.Include(e => e.Project)
|
||||
.Include(e => e.PaymentMode)
|
||||
.Include(e => e.ExpensesType)
|
||||
.Include(e => e.ExpenseCategory)
|
||||
.Include(e => e.Status)
|
||||
.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));
|
||||
}
|
||||
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.
|
||||
@ -287,14 +287,14 @@ namespace Marco.Pms.Services.Service
|
||||
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
|
||||
{
|
||||
if (!id.HasValue && string.IsNullOrWhiteSpace(financeUId))
|
||||
if (!id.HasValue && string.IsNullOrWhiteSpace(expenseUId))
|
||||
{
|
||||
_logger.LogWarning("User do not provided id nor expenseUId");
|
||||
return ApiResponse<object>.ErrorResponse("Id or ExpenseUId atleast one must be provided", "Id or ExpenseUId atleast one must be provided", 400);
|
||||
_logger.LogWarning("Invalid parameters: Both Id and PaymentRequestUID are null or empty.");
|
||||
return ApiResponse<object>.ErrorResponse("At least one parameter (Id or expenseUId) must be provided.", "Invalid argument.", 400);
|
||||
}
|
||||
ExpenseDetailsMongoDB? expenseDetails = null;
|
||||
if (expenseDetails == null)
|
||||
@ -308,9 +308,9 @@ namespace Marco.Pms.Services.Service
|
||||
.Include(e => e.PaymentMode)
|
||||
.Include(e => e.Project)
|
||||
.Include(e => e.PaymentMode)
|
||||
.Include(e => e.ExpensesType)
|
||||
.Include(e => e.ExpenseCategory)
|
||||
.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)
|
||||
{
|
||||
@ -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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
@ -387,7 +387,7 @@ namespace Marco.Pms.Services.Service
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -414,7 +414,7 @@ namespace Marco.Pms.Services.Service
|
||||
.Include(e => e.Project)
|
||||
.Include(e => e.CreatedBy)
|
||||
.Include(e => e.Status)
|
||||
.Include(e => e.ExpensesType)
|
||||
.Include(e => e.ExpenseCategory)
|
||||
.Where(e => e.TenantId == tenantId)
|
||||
.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(),
|
||||
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(),
|
||||
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);
|
||||
}
|
||||
@ -477,10 +477,10 @@ namespace Marco.Pms.Services.Service
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
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();
|
||||
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 () =>
|
||||
{
|
||||
@ -515,7 +515,7 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
|
||||
// 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
|
||||
if (!await hasUploadPermissionTask)
|
||||
@ -526,7 +526,7 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
var validationErrors = new List<string>();
|
||||
var project = await projectTask;
|
||||
var expenseType = await expenseTypeTask;
|
||||
var expenseCategory = await expenseCategoriesTask;
|
||||
var paymentMode = await paymentModeTask;
|
||||
var statusMapping = await statusMappingTask;
|
||||
var paidBy = await paidByTask;
|
||||
@ -534,10 +534,10 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
if (project == null) validationErrors.Add("Project 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 (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())
|
||||
{
|
||||
@ -580,7 +580,7 @@ namespace Marco.Pms.Services.Service
|
||||
response.Status = _mapper.Map<ExpensesStatusMasterVM>(statusMapping!.Status);
|
||||
response.NextStatus = _mapper.Map<List<ExpensesStatusMasterVM>>(statusMapping.NextStatus);
|
||||
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);
|
||||
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)
|
||||
var expense = await _context.Expenses
|
||||
.Include(e => e.ExpensesType)
|
||||
.Include(e => e.ExpenseCategory)
|
||||
.Include(e => e.Project)
|
||||
.Include(e => e.PaidBy).ThenInclude(e => e!.JobRole)
|
||||
.Include(e => e.PaymentMode)
|
||||
@ -838,7 +838,7 @@ namespace Marco.Pms.Services.Service
|
||||
return ApiResponse<object>.ErrorResponse("Invalid Parameters", "Invalid Parameters", 400);
|
||||
}
|
||||
var existingExpense = await _context.Expenses
|
||||
.Include(e => e.ExpensesType)
|
||||
.Include(e => e.ExpenseCategory)
|
||||
.Include(e => e.Project)
|
||||
.Include(e => e.PaidBy)
|
||||
.ThenInclude(e => e!.JobRole)
|
||||
@ -1103,6 +1103,29 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
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
|
||||
var paymentRequestQuery = _context.PaymentRequests
|
||||
.Include(pr => pr.Currency)
|
||||
@ -1120,6 +1143,11 @@ namespace Marco.Pms.Services.Service
|
||||
pr.CreatedBy != null &&
|
||||
pr.CreatedBy.JobRole != null);
|
||||
|
||||
if (hasViewSelfPermissionTask.Result)
|
||||
{
|
||||
paymentRequestQuery = paymentRequestQuery.Where(pr => pr.CreatedById == loggedInEmployee.Id);
|
||||
}
|
||||
|
||||
// Deserialize and apply advanced filter if provided
|
||||
PaymentRequestFilter? paymentRequestFilter = TryDeserializePaymentRequestFilter(filter);
|
||||
|
||||
@ -1172,7 +1200,7 @@ namespace Marco.Pms.Services.Service
|
||||
.Where(pr =>
|
||||
pr.Payee.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)
|
||||
{
|
||||
@ -1337,7 +1701,7 @@ namespace Marco.Pms.Services.Service
|
||||
var response = _mapper.Map<PaymentRequestVM>(paymentRequest);
|
||||
response.PaymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}";
|
||||
response.Currency = currency;
|
||||
response.ExpenseCategory = _mapper.Map<ExpensesTypeMasterVM>(expenseCategory);
|
||||
response.ExpenseCategory = _mapper.Map<ExpensesCategoryMasterVM>(expenseCategory);
|
||||
response.ExpenseStatus = _mapper.Map<ExpensesStatusMasterVM>(expenseStatus);
|
||||
response.Project = _mapper.Map<BasicProjectVM>(project);
|
||||
response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
|
||||
@ -1360,7 +1724,6 @@ namespace Marco.Pms.Services.Service
|
||||
_logger.LogInfo("End CreatePaymentRequestAsync for EmployeeId: {EmployeeId}", loggedInEmployee.Id);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@ -1529,9 +1892,8 @@ namespace Marco.Pms.Services.Service
|
||||
var response = _mapper.Map<PaymentRequestVM>(paymentRequest);
|
||||
response.PaymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}";
|
||||
response.Currency = currency;
|
||||
response.ExpenseCategory = _mapper.Map<ExpensesTypeMasterVM>(expenseCategory);
|
||||
response.ExpenseCategory = _mapper.Map<ExpensesCategoryMasterVM>(expenseCategory);
|
||||
response.Project = _mapper.Map<BasicProjectVM>(project);
|
||||
response.UpdatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
|
||||
|
||||
_logger.LogInfo("PaymentRequest updated successfully with UID: {PaymentRequestUID}", response.PaymentRequestUID);
|
||||
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>();
|
||||
var projectIds = model.Select(m => m.ProjectId).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 createdByIds = model.Select(m => m.CreatedById).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();
|
||||
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();
|
||||
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 () =>
|
||||
{
|
||||
@ -1685,10 +2047,10 @@ namespace Marco.Pms.Services.Service
|
||||
});
|
||||
|
||||
// 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 expenseTypes = expenseTypeTask.Result;
|
||||
var expenseCategories = expenseCategoriesTask.Result;
|
||||
var paymentModes = paymentModeTask.Result;
|
||||
var statusMappings = statusMappingTask.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.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;
|
||||
}).ToList();
|
||||
@ -1802,7 +2164,7 @@ namespace Marco.Pms.Services.Service
|
||||
response.Status = _mapper.Map<ExpensesStatusMasterMongoDB>(status);
|
||||
}
|
||||
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;
|
||||
|
||||
return response;
|
||||
|
||||
@ -2077,7 +2077,7 @@ namespace Marco.Pms.Services.Service
|
||||
{
|
||||
// Featching the list of Expenses Type.
|
||||
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);
|
||||
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);
|
||||
|
||||
var response = _mapper.Map<ExpensesTypeMasterVM>(expensesType);
|
||||
var response = _mapper.Map<ExpensesCategoryMasterVM>(expensesType);
|
||||
return ApiResponse<object>.SuccessResponse(response, "Expense type craeted Successfully", 201);
|
||||
}
|
||||
catch (DbUpdateException dbEx)
|
||||
@ -2171,7 +2171,7 @@ namespace Marco.Pms.Services.Service
|
||||
}, "ExpenseCategoryMasterModificationLog");
|
||||
|
||||
// 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);
|
||||
}
|
||||
catch (DbUpdateException dbEx)
|
||||
@ -2226,7 +2226,7 @@ namespace Marco.Pms.Services.Service
|
||||
}, "ExpenseCategoryMasterModificationLog");
|
||||
|
||||
// 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);
|
||||
}
|
||||
catch (DbUpdateException dbEx)
|
||||
|
||||
@ -8,7 +8,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
||||
{
|
||||
#region =================================================================== Expenses Functions ===================================================================
|
||||
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>> GetFilterObjectAsync(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 ===================================================================
|
||||
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>> EditPaymentRequestAsync(Guid id, PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId);
|
||||
#endregion
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user