Added the payment Request status Change API
This commit is contained in:
parent
a204efb133
commit
18480b94cd
File diff suppressed because one or more lines are too long
@ -0,0 +1,83 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Marco.Pms.DataAccess.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Added_Payment_Infromation_In_Payment_request_Table : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "NextStatusId",
|
||||||
|
table: "StatusUpdateLogs",
|
||||||
|
type: "char(36)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||||
|
collation: "ascii_general_ci");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "PaidAt",
|
||||||
|
table: "PaymentRequests",
|
||||||
|
type: "datetime(6)",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "PaidById",
|
||||||
|
table: "PaymentRequests",
|
||||||
|
type: "char(36)",
|
||||||
|
nullable: true,
|
||||||
|
collation: "ascii_general_ci");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "PaidTransactionId",
|
||||||
|
table: "PaymentRequests",
|
||||||
|
type: "longtext",
|
||||||
|
nullable: true)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PaymentRequests_PaidById",
|
||||||
|
table: "PaymentRequests",
|
||||||
|
column: "PaidById");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_PaymentRequests_Employees_PaidById",
|
||||||
|
table: "PaymentRequests",
|
||||||
|
column: "PaidById",
|
||||||
|
principalTable: "Employees",
|
||||||
|
principalColumn: "Id");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_PaymentRequests_Employees_PaidById",
|
||||||
|
table: "PaymentRequests");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_PaymentRequests_PaidById",
|
||||||
|
table: "PaymentRequests");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "NextStatusId",
|
||||||
|
table: "StatusUpdateLogs");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "PaidAt",
|
||||||
|
table: "PaymentRequests");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "PaidById",
|
||||||
|
table: "PaymentRequests");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "PaidTransactionId",
|
||||||
|
table: "PaymentRequests");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7359
Marco.Pms.DataAccess/Migrations/20251103133736_Added_UIDPerfix_In_Expense_Table.Designer.cs
generated
Normal file
7359
Marco.Pms.DataAccess/Migrations/20251103133736_Added_UIDPerfix_In_Expense_Table.Designer.cs
generated
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,86 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Marco.Pms.DataAccess.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Added_UIDPerfix_In_Expense_Table : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_AdvancePaymentTransactions_Projects_ProjectId",
|
||||||
|
table: "AdvancePaymentTransactions");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "UIDPostfix",
|
||||||
|
table: "Expenses",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "UIDPrefix",
|
||||||
|
table: "Expenses",
|
||||||
|
type: "longtext",
|
||||||
|
nullable: false)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "ProjectId",
|
||||||
|
table: "AdvancePaymentTransactions",
|
||||||
|
type: "char(36)",
|
||||||
|
nullable: true,
|
||||||
|
collation: "ascii_general_ci",
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "char(36)")
|
||||||
|
.OldAnnotation("Relational:Collation", "ascii_general_ci");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_AdvancePaymentTransactions_Projects_ProjectId",
|
||||||
|
table: "AdvancePaymentTransactions",
|
||||||
|
column: "ProjectId",
|
||||||
|
principalTable: "Projects",
|
||||||
|
principalColumn: "Id");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_AdvancePaymentTransactions_Projects_ProjectId",
|
||||||
|
table: "AdvancePaymentTransactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UIDPostfix",
|
||||||
|
table: "Expenses");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UIDPrefix",
|
||||||
|
table: "Expenses");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "ProjectId",
|
||||||
|
table: "AdvancePaymentTransactions",
|
||||||
|
type: "char(36)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||||
|
collation: "ascii_general_ci",
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "char(36)",
|
||||||
|
oldNullable: true)
|
||||||
|
.OldAnnotation("Relational:Collation", "ascii_general_ci");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_AdvancePaymentTransactions_Projects_ProjectId",
|
||||||
|
table: "AdvancePaymentTransactions",
|
||||||
|
column: "ProjectId",
|
||||||
|
principalTable: "Projects",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2097,7 +2097,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
b.Property<bool>("IsActive")
|
b.Property<bool>("IsActive")
|
||||||
.HasColumnType("tinyint(1)");
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
b.Property<Guid>("ProjectId")
|
b.Property<Guid?>("ProjectId")
|
||||||
.HasColumnType("char(36)");
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
b.Property<Guid>("TenantId")
|
b.Property<Guid>("TenantId")
|
||||||
@ -2372,6 +2372,13 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
b.Property<string>("TransactionId")
|
b.Property<string>("TransactionId")
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("UIDPostfix")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("UIDPrefix")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("ApprovedById");
|
b.HasIndex("ApprovedById");
|
||||||
@ -2563,6 +2570,15 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
b.Property<bool>("IsAdvancePayment")
|
b.Property<bool>("IsAdvancePayment")
|
||||||
.HasColumnType("tinyint(1)");
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("PaidAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("PaidById")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("PaidTransactionId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<string>("Payee")
|
b.Property<string>("Payee")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
@ -2603,6 +2619,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
|
|
||||||
b.HasIndex("ExpenseStatusId");
|
b.HasIndex("ExpenseStatusId");
|
||||||
|
|
||||||
|
b.HasIndex("PaidById");
|
||||||
|
|
||||||
b.HasIndex("ProjectId");
|
b.HasIndex("ProjectId");
|
||||||
|
|
||||||
b.HasIndex("RecurringPaymentId");
|
b.HasIndex("RecurringPaymentId");
|
||||||
@ -3853,6 +3871,9 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
b.Property<Guid>("EntityId")
|
b.Property<Guid>("EntityId")
|
||||||
.HasColumnType("char(36)");
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("NextStatusId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
b.Property<Guid>("StatusId")
|
b.Property<Guid>("StatusId")
|
||||||
.HasColumnType("char(36)");
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
@ -6219,9 +6240,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
|
|
||||||
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
|
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("ProjectId")
|
.HasForeignKey("ProjectId");
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
@ -6469,6 +6488,10 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Marco.Pms.Model.Employees.Employee", "PaidBy")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PaidById");
|
||||||
|
|
||||||
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
|
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("ProjectId");
|
.HasForeignKey("ProjectId");
|
||||||
@ -6495,6 +6518,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
|
|
||||||
b.Navigation("ExpenseStatus");
|
b.Navigation("ExpenseStatus");
|
||||||
|
|
||||||
|
b.Navigation("PaidBy");
|
||||||
|
|
||||||
b.Navigation("Project");
|
b.Navigation("Project");
|
||||||
|
|
||||||
b.Navigation("RecurringPayment");
|
b.Navigation("RecurringPayment");
|
||||||
|
|||||||
12
Marco.Pms.Model/Dtos/Expenses/PaymentRequestRecordDto.cs
Normal file
12
Marco.Pms.Model/Dtos/Expenses/PaymentRequestRecordDto.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Marco.Pms.Model.Dtos.Expenses
|
||||||
|
{
|
||||||
|
public class PaymentRequestRecordDto
|
||||||
|
{
|
||||||
|
public Guid PaymentRequestId { get; set; }
|
||||||
|
public Guid StatusId { get; set; }
|
||||||
|
public string? Comment { get; set; }
|
||||||
|
public string? PaidTransactionId { get; set; }
|
||||||
|
public DateTime? PaidAt { get; set; }
|
||||||
|
public Guid? PaidById { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,7 +12,7 @@ namespace Marco.Pms.Model.Expenses
|
|||||||
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 string Title { get; set; } = default!;
|
||||||
public Guid ProjectId { get; set; }
|
public Guid? ProjectId { get; set; }
|
||||||
|
|
||||||
[ValidateNever]
|
[ValidateNever]
|
||||||
[ForeignKey("ProjectId")]
|
[ForeignKey("ProjectId")]
|
||||||
|
|||||||
@ -10,6 +10,8 @@ namespace Marco.Pms.Model.Expenses
|
|||||||
public class Expenses : TenantRelation
|
public class Expenses : TenantRelation
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
public string UIDPrefix { get; set; } = default!;
|
||||||
|
public int UIDPostfix { get; set; }
|
||||||
public Guid ProjectId { get; set; }
|
public Guid ProjectId { get; set; }
|
||||||
|
|
||||||
[ValidateNever]
|
[ValidateNever]
|
||||||
|
|||||||
@ -43,6 +43,13 @@ namespace Marco.Pms.Model.Expenses
|
|||||||
[ValidateNever]
|
[ValidateNever]
|
||||||
[ForeignKey("ExpenseStatusId")]
|
[ForeignKey("ExpenseStatusId")]
|
||||||
public ExpensesStatusMaster? ExpenseStatus { get; set; }
|
public ExpensesStatusMaster? ExpenseStatus { get; set; }
|
||||||
|
public string? PaidTransactionId { get; set; }
|
||||||
|
public DateTime? PaidAt { get; set; }
|
||||||
|
public Guid? PaidById { get; set; }
|
||||||
|
|
||||||
|
[ValidateNever]
|
||||||
|
[ForeignKey("PaidById")]
|
||||||
|
public Employee? PaidBy { get; set; }
|
||||||
public bool IsActive { get; set; }
|
public bool IsActive { get; set; }
|
||||||
public DateTime CreatedAt { get; set; }
|
public DateTime CreatedAt { get; set; }
|
||||||
public Guid CreatedById { get; set; }
|
public Guid CreatedById { get; set; }
|
||||||
|
|||||||
@ -9,6 +9,7 @@ namespace Marco.Pms.Model.Master
|
|||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public Guid StatusId { get; set; }
|
public Guid StatusId { get; set; }
|
||||||
|
public Guid NextStatusId { get; set; }
|
||||||
public Guid EntityId { get; set; }
|
public Guid EntityId { get; set; }
|
||||||
public string? Comment { get; set; }
|
public string? Comment { get; set; }
|
||||||
public DateTime UpdatedAt { get; set; }
|
public DateTime UpdatedAt { get; set; }
|
||||||
|
|||||||
@ -7,5 +7,6 @@
|
|||||||
public string ContentType { get; set; } = string.Empty;
|
public string ContentType { get; set; } = string.Empty;
|
||||||
public string S3Key { get; set; } = string.Empty;
|
public string S3Key { get; set; } = string.Empty;
|
||||||
public string ThumbS3Key { get; set; } = string.Empty;
|
public string ThumbS3Key { get; set; } = string.Empty;
|
||||||
|
public long FileSize { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,12 +20,16 @@ namespace Marco.Pms.Model.ViewModels.Expenses
|
|||||||
public RecurringPayment? RecurringPayment { get; set; }
|
public RecurringPayment? RecurringPayment { get; set; }
|
||||||
public ExpensesCategoryMasterVM? ExpenseCategory { get; set; }
|
public ExpensesCategoryMasterVM? ExpenseCategory { get; set; }
|
||||||
public ExpensesStatusMasterVM? ExpenseStatus { get; set; }
|
public ExpensesStatusMasterVM? ExpenseStatus { get; set; }
|
||||||
|
public string? PaidTransactionId { get; set; }
|
||||||
|
public DateTime? PaidAt { get; set; }
|
||||||
|
public BasicEmployeeVM? PaidBy { get; set; }
|
||||||
public bool IsAdvancePayment { get; set; }
|
public bool IsAdvancePayment { 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 DateTime UpdatedAt { get; set; }
|
||||||
public BasicEmployeeVM? UpdatedBy { get; set; }
|
public BasicEmployeeVM? UpdatedBy { get; set; }
|
||||||
public List<ExpensesStatusMasterVM>? NextStatus { get; set; }
|
public List<ExpensesStatusMasterVM>? NextStatus { get; set; }
|
||||||
|
public List<PaymentRequestUpdateLog>? UpdateLogs { get; set; }
|
||||||
public List<PaymentRequestAttachmentVM>? Attachments { get; set; }
|
public List<PaymentRequestAttachmentVM>? Attachments { get; set; }
|
||||||
public bool IsActive { get; set; }
|
public bool IsActive { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
using Marco.Pms.Model.ViewModels.Activities;
|
||||||
|
using Marco.Pms.Model.ViewModels.Master;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.ViewModels.Expenses
|
||||||
|
{
|
||||||
|
public class PaymentRequestUpdateLog
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public ExpensesStatusMasterVM? Status { get; set; }
|
||||||
|
public ExpensesStatusMasterVM? NextStatus { get; set; }
|
||||||
|
public string? Comment { get; set; }
|
||||||
|
public DateTime UpdatedAt { get; set; }
|
||||||
|
public BasicEmployeeVM? UpdatedBy { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -172,6 +172,19 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
return StatusCode(response.StatusCode, response);
|
return StatusCode(response.StatusCode, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("payment-request/action")]
|
||||||
|
public async Task<IActionResult> ChangePaymentRequestStatus([FromBody] PaymentRequestRecordDto model)
|
||||||
|
{
|
||||||
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
var response = await _expensesService.ChangePaymentRequestStatusAsync(model, loggedInEmployee, tenantId);
|
||||||
|
if (response.Success)
|
||||||
|
{
|
||||||
|
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Payment_Request", Response = response.Data };
|
||||||
|
await _signalR.SendNotificationAsync(notification);
|
||||||
|
}
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -6,6 +6,7 @@ using Marco.Pms.Model.Employees;
|
|||||||
using Marco.Pms.Model.Entitlements;
|
using Marco.Pms.Model.Entitlements;
|
||||||
using Marco.Pms.Model.Expenses;
|
using Marco.Pms.Model.Expenses;
|
||||||
using Marco.Pms.Model.Filters;
|
using Marco.Pms.Model.Filters;
|
||||||
|
using Marco.Pms.Model.Master;
|
||||||
using Marco.Pms.Model.MongoDBModels;
|
using Marco.Pms.Model.MongoDBModels;
|
||||||
using Marco.Pms.Model.MongoDBModels.Employees;
|
using Marco.Pms.Model.MongoDBModels.Employees;
|
||||||
using Marco.Pms.Model.MongoDBModels.Expenses;
|
using Marco.Pms.Model.MongoDBModels.Expenses;
|
||||||
@ -46,6 +47,7 @@ namespace Marco.Pms.Services.Service
|
|||||||
private static readonly Guid RejectedByApprover = Guid.Parse("d1ee5eec-24b6-4364-8673-a8f859c60729");
|
private static readonly Guid RejectedByApprover = Guid.Parse("d1ee5eec-24b6-4364-8673-a8f859c60729");
|
||||||
private static readonly Guid ProcessPending = Guid.Parse("f18c5cfd-7815-4341-8da2-2c2d65778e27");
|
private static readonly Guid ProcessPending = Guid.Parse("f18c5cfd-7815-4341-8da2-2c2d65778e27");
|
||||||
private static readonly Guid Processed = Guid.Parse("61578360-3a49-4c34-8604-7b35a3787b95");
|
private static readonly Guid Processed = Guid.Parse("61578360-3a49-4c34-8604-7b35a3787b95");
|
||||||
|
private static readonly Guid AdvancePayment = Guid.Parse("f67beee6-6763-4108-922c-03bd86b9178d");
|
||||||
private static readonly string Collection = "ExpensesModificationLog";
|
private static readonly string Collection = "ExpensesModificationLog";
|
||||||
public ExpensesService(
|
public ExpensesService(
|
||||||
IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||||
@ -549,9 +551,19 @@ namespace Marco.Pms.Services.Service
|
|||||||
|
|
||||||
var currentexpenseUId = (lastExpenseUId + 1).ToString("D5");
|
var currentexpenseUId = (lastExpenseUId + 1).ToString("D5");
|
||||||
|
|
||||||
|
string uIDPrefix = $"EX/{DateTime.Now:MMyy}";
|
||||||
|
|
||||||
|
// Generate unique UID postfix based on existing requests for the current prefix
|
||||||
|
var lastPR = await _context.Expenses.Where(pr => pr.UIDPrefix == uIDPrefix)
|
||||||
|
.OrderByDescending(pr => pr.UIDPostfix)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
int uIDPostfix = lastPR == null ? 1 : (lastPR.UIDPostfix + 1);
|
||||||
|
|
||||||
// 3. Entity Creation
|
// 3. Entity Creation
|
||||||
var expense = _mapper.Map<Expenses>(dto);
|
var expense = _mapper.Map<Expenses>(dto);
|
||||||
expense.ExpenseUId = $"EX-{currentexpenseUId}";
|
expense.ExpenseUId = $"EX-{currentexpenseUId}";
|
||||||
|
expense.UIDPostfix = uIDPostfix;
|
||||||
|
expense.UIDPrefix = uIDPrefix;
|
||||||
expense.CreatedById = loggedInEmployee.Id;
|
expense.CreatedById = loggedInEmployee.Id;
|
||||||
expense.CreatedAt = DateTime.UtcNow;
|
expense.CreatedAt = DateTime.UtcNow;
|
||||||
expense.TenantId = tenantId;
|
expense.TenantId = tenantId;
|
||||||
@ -642,6 +654,12 @@ namespace Marco.Pms.Services.Service
|
|||||||
expense.Id, expense.StatusId, model.StatusId);
|
expense.Id, expense.StatusId, model.StatusId);
|
||||||
|
|
||||||
// 2. Run Prerequisite Checks in Parallel (Status transition + Permissions)
|
// 2. Run Prerequisite Checks in Parallel (Status transition + Permissions)
|
||||||
|
var processedStatusTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.ExpensesStatusMaster
|
||||||
|
.FirstOrDefaultAsync(es => es.Id == Processed);
|
||||||
|
});
|
||||||
var statusTransitionTask = Task.Run(async () =>
|
var statusTransitionTask = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
@ -658,9 +676,10 @@ namespace Marco.Pms.Services.Service
|
|||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
await Task.WhenAll(statusTransitionTask, targetStatusPermissionsTask);
|
await Task.WhenAll(statusTransitionTask, targetStatusPermissionsTask, processedStatusTask);
|
||||||
var statusTransition = await statusTransitionTask;
|
var statusTransition = statusTransitionTask.Result;
|
||||||
var requiredPermissions = await targetStatusPermissionsTask;
|
var requiredPermissions = targetStatusPermissionsTask.Result;
|
||||||
|
var processedStatus = processedStatusTask.Result;
|
||||||
|
|
||||||
// 3. Validate Transition and Required Fields
|
// 3. Validate Transition and Required Fields
|
||||||
if (statusTransition == null)
|
if (statusTransition == null)
|
||||||
@ -710,10 +729,65 @@ namespace Marco.Pms.Services.Service
|
|||||||
// 5. Prepare for update (Audit snapshot)
|
// 5. Prepare for update (Audit snapshot)
|
||||||
var expenseStateBeforeChange = _updateLogHelper.EntityToBsonDocument(expense);
|
var expenseStateBeforeChange = _updateLogHelper.EntityToBsonDocument(expense);
|
||||||
|
|
||||||
|
var expenseLogs = new List<ExpenseLog>
|
||||||
|
{
|
||||||
|
new ExpenseLog
|
||||||
|
{
|
||||||
|
ExpenseId = expense.Id,
|
||||||
|
Action = $"Status changed to '{statusTransition.NextStatus?.Name}'",
|
||||||
|
UpdatedById = loggedInEmployee.Id,
|
||||||
|
UpdateAt = DateTime.UtcNow,
|
||||||
|
Comment = model.Comment,
|
||||||
|
TenantId = tenantId
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// 6. Apply Status Transition
|
// 6. Apply Status Transition
|
||||||
|
if (model.StatusId == ProcessPending && expense.PaymentModeId == AdvancePayment)
|
||||||
|
{
|
||||||
|
expense.StatusId = Processed;
|
||||||
|
expense.Status = processedStatus;
|
||||||
|
expense.ProcessedById = loggedInEmployee.Id;
|
||||||
|
|
||||||
|
var lastTransaction = await _context.AdvancePaymentTransactions.OrderByDescending(apt => apt.CreatedAt).FirstOrDefaultAsync(apt => apt.TenantId == tenantId);
|
||||||
|
double lastBalance = 0;
|
||||||
|
if (lastTransaction != null)
|
||||||
|
{
|
||||||
|
lastBalance = lastTransaction.CurrentBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.AdvancePaymentTransactions.Add(new AdvancePaymentTransaction
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
FinanceUIdPostfix = expense.UIDPostfix,
|
||||||
|
FinanceUIdPrefix = expense.UIDPrefix,
|
||||||
|
Title = expense.Description,
|
||||||
|
ProjectId = expense.ProjectId,
|
||||||
|
EmployeeId = expense.PaidById,
|
||||||
|
Amount = 0 - expense.Amount,
|
||||||
|
CurrentBalance = lastBalance - expense.Amount,
|
||||||
|
CreatedAt = expense.TransactionDate,
|
||||||
|
CreatedById = loggedInEmployee.Id,
|
||||||
|
IsActive = true,
|
||||||
|
TenantId = tenantId
|
||||||
|
});
|
||||||
|
|
||||||
|
var expenseLog = new ExpenseLog
|
||||||
|
{
|
||||||
|
ExpenseId = expense.Id,
|
||||||
|
Action = $"Status changed to '{processedStatus?.Name}'",
|
||||||
|
UpdatedById = loggedInEmployee.Id,
|
||||||
|
UpdateAt = DateTime.UtcNow,
|
||||||
|
Comment = model.Comment,
|
||||||
|
TenantId = tenantId
|
||||||
|
};
|
||||||
|
expenseLogs.Add(expenseLog);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
expense.StatusId = statusTransition.NextStatusId;
|
expense.StatusId = statusTransition.NextStatusId;
|
||||||
expense.Status = statusTransition.NextStatus;
|
expense.Status = statusTransition.NextStatus;
|
||||||
|
}
|
||||||
// Handle reviewer/approver/processor fields based on target StatusId (Guid)
|
// Handle reviewer/approver/processor fields based on target StatusId (Guid)
|
||||||
if (model.StatusId == Approve || model.StatusId == RejectedByReviewer)
|
if (model.StatusId == Approve || model.StatusId == RejectedByReviewer)
|
||||||
{
|
{
|
||||||
@ -749,15 +823,7 @@ namespace Marco.Pms.Services.Service
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 8. Add Expense Log Entry
|
// 8. Add Expense Log Entry
|
||||||
_context.ExpenseLogs.Add(new ExpenseLog
|
_context.ExpenseLogs.AddRange(expenseLogs);
|
||||||
{
|
|
||||||
ExpenseId = expense.Id,
|
|
||||||
Action = $"Status changed to '{statusTransition.NextStatus?.Name}'",
|
|
||||||
UpdatedById = loggedInEmployee.Id,
|
|
||||||
UpdateAt = DateTime.UtcNow,
|
|
||||||
Comment = model.Comment,
|
|
||||||
TenantId = tenantId
|
|
||||||
});
|
|
||||||
|
|
||||||
// 9. Commit database transaction
|
// 9. Commit database transaction
|
||||||
try
|
try
|
||||||
@ -1383,6 +1449,14 @@ namespace Marco.Pms.Services.Service
|
|||||||
results.Add(mappedStatus);
|
results.Add(mappedStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int index = results.FindIndex(ns => ns.DisplayName == "Reject");
|
||||||
|
if (index > -1)
|
||||||
|
{
|
||||||
|
var item = results[index];
|
||||||
|
results.RemoveAt(index);
|
||||||
|
results.Insert(0, item);
|
||||||
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1404,16 +1478,45 @@ namespace Marco.Pms.Services.Service
|
|||||||
}).ToList();
|
}).ToList();
|
||||||
});
|
});
|
||||||
|
|
||||||
await Task.WhenAll(nextStatusTask, documentTask);
|
var updateLogsTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await context.StatusUpdateLogs.Include(sul => sul.UpdatedBy).Where(sul => sul.EntityId == paymentRequest.Id && sul.TenantId == tenantId).ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
await Task.WhenAll(nextStatusTask, documentTask, updateLogsTask);
|
||||||
|
|
||||||
var nextStatuses = nextStatusTask.Result;
|
var nextStatuses = nextStatusTask.Result;
|
||||||
var attachmentVMs = documentTask.Result;
|
var attachmentVMs = documentTask.Result;
|
||||||
|
var updateLogs = updateLogsTask.Result;
|
||||||
|
|
||||||
|
var statusIds = updateLogs.Select(sul => sul.StatusId).ToList();
|
||||||
|
statusIds.AddRange(updateLogs.Select(sul => sul.NextStatusId).ToList());
|
||||||
|
|
||||||
|
statusIds = statusIds.Distinct().ToList();
|
||||||
|
|
||||||
|
var status = await _context.ExpensesStatusMaster.Where(es => statusIds.Contains(es.Id)).ToListAsync();
|
||||||
|
|
||||||
// Map main response model and populate additional fields
|
// Map main response model and populate additional fields
|
||||||
var response = _mapper.Map<PaymentRequestDetailsVM>(paymentRequest);
|
var response = _mapper.Map<PaymentRequestDetailsVM>(paymentRequest);
|
||||||
response.PaymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}";
|
response.PaymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}";
|
||||||
response.Attachments = attachmentVMs;
|
response.Attachments = attachmentVMs;
|
||||||
response.NextStatus = nextStatuses;
|
response.NextStatus = nextStatuses;
|
||||||
|
response.UpdateLogs = updateLogs.Select(ul =>
|
||||||
|
{
|
||||||
|
var statusVm = status.FirstOrDefault(es => es.Id == ul.StatusId);
|
||||||
|
var nextStatusVm = status.FirstOrDefault(es => es.Id == ul.NextStatusId);
|
||||||
|
|
||||||
|
return new PaymentRequestUpdateLog
|
||||||
|
{
|
||||||
|
Id = ul.Id,
|
||||||
|
Comment = ul.Comment,
|
||||||
|
Status = _mapper.Map<ExpensesStatusMasterVM>(statusVm),
|
||||||
|
NextStatus = _mapper.Map<ExpensesStatusMasterVM>(nextStatusVm),
|
||||||
|
UpdatedAt = ul.UpdatedAt,
|
||||||
|
UpdatedBy = _mapper.Map<BasicEmployeeVM>(ul.UpdatedBy)
|
||||||
|
};
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
_logger.LogInfo("Payment request details fetched successfully for PaymentRequestId: {PaymentRequestId}, EmployeeId: {EmployeeId}", paymentRequest.Id, loggedInEmployee.Id);
|
_logger.LogInfo("Payment request details fetched successfully for PaymentRequestId: {PaymentRequestId}, EmployeeId: {EmployeeId}", paymentRequest.Id, loggedInEmployee.Id);
|
||||||
|
|
||||||
@ -1474,12 +1577,11 @@ namespace Marco.Pms.Services.Service
|
|||||||
return ApiResponse<object>.ErrorResponse("Internal Exception Occured", ExceptionMapper(ex), 500);
|
return ApiResponse<object>.ErrorResponse("Internal Exception Occured", ExceptionMapper(ex), 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ApiResponse<object>> CreatePaymentRequestAsync(PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId)
|
public async Task<ApiResponse<object>> CreatePaymentRequestAsync(PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId)
|
||||||
{
|
{
|
||||||
_logger.LogInfo("Start CreatePaymentRequestAsync for EmployeeId: {EmployeeId} TenantId: {TenantId}", loggedInEmployee.Id, tenantId);
|
_logger.LogInfo("Start CreatePaymentRequestAsync for EmployeeId: {EmployeeId} TenantId: {TenantId}", loggedInEmployee.Id, tenantId);
|
||||||
|
|
||||||
string uIDPrefix = $"PY/{DateTime.Now:MMyy}";
|
string uIDPrefix = $"PR/{DateTime.Now:MMyy}";
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -1615,6 +1717,194 @@ 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>> ChangePaymentRequestStatusAsync(PaymentRequestRecordDto model, Employee loggedInEmployee, Guid tenantId)
|
||||||
|
{
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
|
||||||
|
|
||||||
|
// 1. Fetch Existing Payment Request with Related Entities (Single Query)
|
||||||
|
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)
|
||||||
|
.FirstOrDefaultAsync(pr =>
|
||||||
|
pr.Id == model.PaymentRequestId &&
|
||||||
|
pr.ExpenseStatusId != model.StatusId &&
|
||||||
|
pr.TenantId == tenantId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (paymentRequest == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ChangeStatus: Payment Request not found or already at target status. payment RequestId={PaymentRequestId}, TenantId={TenantId}", model.PaymentRequestId, tenantId);
|
||||||
|
return ApiResponse<object>.ErrorResponse("payment Request not found or status is already set.", "payment Request not found", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInfo("ChangeStatus: Requested status change. PaymentRequestId={PaymentRequestId} FromStatus={FromStatusId} ToStatus={ToStatusId}",
|
||||||
|
paymentRequest.Id, paymentRequest.ExpenseStatusId, model.StatusId);
|
||||||
|
|
||||||
|
// 2. Run Prerequisite Checks in Parallel (Status transition + Permissions)
|
||||||
|
var statusTransitionTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.ExpensesStatusMapping
|
||||||
|
.Include(m => m.NextStatus)
|
||||||
|
.FirstOrDefaultAsync(m => m.StatusId == paymentRequest.ExpenseStatusId && m.NextStatusId == model.StatusId);
|
||||||
|
});
|
||||||
|
|
||||||
|
var targetStatusPermissionsTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.StatusPermissionMapping
|
||||||
|
.Where(spm => spm.StatusId == paymentRequest.ExpenseStatusId)
|
||||||
|
.ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
await Task.WhenAll(statusTransitionTask, targetStatusPermissionsTask);
|
||||||
|
var statusTransition = await statusTransitionTask;
|
||||||
|
var requiredPermissions = await targetStatusPermissionsTask;
|
||||||
|
|
||||||
|
// 3. Validate Transition and Required Fields
|
||||||
|
if (statusTransition == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ChangeStatus: Invalid status transition. PaymentRequestId={PaymentRequestId}, FromStatus={FromStatus}, ToStatus={ToStatus}",
|
||||||
|
paymentRequest.Id, paymentRequest.ExpenseStatusId, model.StatusId);
|
||||||
|
return ApiResponse<object>.ErrorResponse("Status change is not permitted.", "Invalid Transition", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate special logic for "Processed"
|
||||||
|
if (statusTransition.NextStatusId == Processed &&
|
||||||
|
(string.IsNullOrWhiteSpace(model.PaidTransactionId) ||
|
||||||
|
!model.PaidAt.HasValue ||
|
||||||
|
model.PaidById == null ||
|
||||||
|
model.PaidById == Guid.Empty))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ChangeStatus: Missing payment fields for 'Processed'. PaymentRequestId={PaymentRequestId}", paymentRequest.Id);
|
||||||
|
return ApiResponse<object>.ErrorResponse("payment details are missing or invalid.", "Invalid Payment", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Permission Check (CreatedBy -> Reviewer bypass, else required permissions)
|
||||||
|
bool hasPermission = false;
|
||||||
|
if (model.StatusId == Review && paymentRequest.CreatedById == loggedInEmployee.Id)
|
||||||
|
{
|
||||||
|
hasPermission = true;
|
||||||
|
}
|
||||||
|
else if (requiredPermissions.Any())
|
||||||
|
{
|
||||||
|
var permissionIds = requiredPermissions.Select(p => p.PermissionId).ToList();
|
||||||
|
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||||
|
hasPermission = await permissionService.HasPermissionAny(permissionIds, loggedInEmployee.Id) && model.StatusId != Review;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasPermission)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ChangeStatus: Permission denied. EmployeeId={EmployeeId} PaymentRequestId={PaymentRequestId} ToStatus={ToStatusId}",
|
||||||
|
loggedInEmployee.Id, paymentRequest.Id, model.StatusId);
|
||||||
|
return ApiResponse<object>.ErrorResponse("You do not have permission for this action.", "Access Denied", 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Prepare for update (Audit snapshot)
|
||||||
|
var paymentRequestStateBeforeChange = _updateLogHelper.EntityToBsonDocument(paymentRequest);
|
||||||
|
|
||||||
|
// 6. Apply Status Transition
|
||||||
|
paymentRequest.ExpenseStatusId = statusTransition.NextStatusId;
|
||||||
|
paymentRequest.ExpenseStatus = statusTransition.NextStatus;
|
||||||
|
|
||||||
|
|
||||||
|
// 7. Add Reimbursement if applicable
|
||||||
|
if (model.StatusId == Processed)
|
||||||
|
{
|
||||||
|
paymentRequest.PaidAt = model.PaidAt;
|
||||||
|
paymentRequest.PaidById = model.PaidById;
|
||||||
|
paymentRequest.PaidTransactionId = model.PaidTransactionId;
|
||||||
|
|
||||||
|
var lastTransaction = await _context.AdvancePaymentTransactions.OrderByDescending(apt => apt.CreatedAt).FirstOrDefaultAsync(apt => apt.TenantId == tenantId);
|
||||||
|
double lastBalance = 0;
|
||||||
|
if (lastTransaction != null)
|
||||||
|
{
|
||||||
|
lastBalance = lastTransaction.CurrentBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.AdvancePaymentTransactions.Add(new AdvancePaymentTransaction
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
FinanceUIdPostfix = paymentRequest.UIDPostfix,
|
||||||
|
FinanceUIdPrefix = paymentRequest.UIDPrefix,
|
||||||
|
Title = paymentRequest.Title,
|
||||||
|
ProjectId = paymentRequest.ProjectId,
|
||||||
|
EmployeeId = paymentRequest.CreatedById,
|
||||||
|
Amount = paymentRequest.Amount,
|
||||||
|
CurrentBalance = lastBalance + paymentRequest.Amount,
|
||||||
|
CreatedAt = model.PaidAt!.Value,
|
||||||
|
CreatedById = loggedInEmployee.Id,
|
||||||
|
IsActive = true,
|
||||||
|
TenantId = tenantId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Add paymentRequest Log Entry
|
||||||
|
_context.StatusUpdateLogs.Add(new StatusUpdateLog
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
EntityId = paymentRequest.Id,
|
||||||
|
StatusId = statusTransition.StatusId,
|
||||||
|
NextStatusId = statusTransition.NextStatusId,
|
||||||
|
UpdatedById = loggedInEmployee.Id,
|
||||||
|
UpdatedAt = DateTime.UtcNow,
|
||||||
|
Comment = model.Comment,
|
||||||
|
TenantId = tenantId
|
||||||
|
});
|
||||||
|
|
||||||
|
// 9. Commit database transaction
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
_logger.LogInfo("ChangeStatus: Status updated successfully. PaymentRequestId={PaymentRequestId} NewStatus={NewStatusId}", paymentRequest.Id, paymentRequest.ExpenseStatusId);
|
||||||
|
}
|
||||||
|
catch (DbUpdateConcurrencyException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "ChangeStatus: Concurrency error. PaymentRequestId={PaymentRequestId}", paymentRequest.Id);
|
||||||
|
return ApiResponse<object>.ErrorResponse("Payment Request was modified by another user. Please refresh and try again.", "Concurrency Error", 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
//_ = Task.Run(async () =>
|
||||||
|
//{
|
||||||
|
// // --- Push Notification Section ---
|
||||||
|
// // This section attempts to send a test push notification to the user's device.
|
||||||
|
// // It's designed to fail gracefully and handle invalid Firebase Cloud Messaging (FCM) tokens.
|
||||||
|
|
||||||
|
// var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}";
|
||||||
|
|
||||||
|
// await _firebase.SendExpenseMessageAsync(paymentRequest, name, tenantId);
|
||||||
|
|
||||||
|
//});
|
||||||
|
|
||||||
|
// 10. Post-processing (audit log, cache, fetch next states)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
|
||||||
|
{
|
||||||
|
EntityId = paymentRequest.Id.ToString(),
|
||||||
|
UpdatedById = loggedInEmployee.Id.ToString(),
|
||||||
|
OldObject = paymentRequestStateBeforeChange,
|
||||||
|
UpdatedAt = DateTime.UtcNow
|
||||||
|
}, "PaymentRequestModificationLog");
|
||||||
|
|
||||||
|
// Prepare response
|
||||||
|
var responseDto = _mapper.Map<PaymentRequestVM>(paymentRequest);
|
||||||
|
|
||||||
|
return ApiResponse<object>.SuccessResponse(responseDto, "Payment Request status chnaged successfully", 200);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "ChangeStatus: Post-operation error (e.g. audit logging). PaymentRequestId={PaymentRequestId}", paymentRequest.Id);
|
||||||
|
var responseDto = _mapper.Map<PaymentRequestVM>(paymentRequest);
|
||||||
|
return ApiResponse<object>.SuccessResponse(responseDto, "Status updated, but audit logging or cache update failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
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);
|
||||||
@ -2023,7 +2313,8 @@ namespace Marco.Pms.Services.Service
|
|||||||
FileName = ba.Document.FileName,
|
FileName = ba.Document.FileName,
|
||||||
ContentType = ba.Document.ContentType,
|
ContentType = ba.Document.ContentType,
|
||||||
S3Key = ba.Document.S3Key,
|
S3Key = ba.Document.S3Key,
|
||||||
ThumbS3Key = ba.Document.ThumbS3Key ?? ba.Document.S3Key
|
ThumbS3Key = ba.Document.ThumbS3Key ?? ba.Document.S3Key,
|
||||||
|
FileSize = ba.Document.FileSize,
|
||||||
}).ToList()
|
}).ToList()
|
||||||
})
|
})
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|||||||
@ -23,6 +23,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
|||||||
Task<ApiResponse<object>> GetPayeeNameListAsync(Employee loggedInEmployee, Guid tenantId);
|
Task<ApiResponse<object>> GetPayeeNameListAsync(Employee loggedInEmployee, Guid tenantId);
|
||||||
Task<ApiResponse<object>> GetPaymentRequestFilterObjectAsync(Employee loggedInEmployee, Guid tenantId);
|
Task<ApiResponse<object>> GetPaymentRequestFilterObjectAsync(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>> ChangePaymentRequestStatusAsync(PaymentRequestRecordDto 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
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user