resolver conflict and merged- AdvancePaymentTranaction

This commit is contained in:
pramod.mahajan 2025-11-01 16:43:24 +05:30
commit bdeeb673c7
20 changed files with 22689 additions and 121 deletions

View File

@ -106,6 +106,7 @@ namespace Marco.Pms.DataAccess.Data
public DbSet<Expenses> Expenses { get; set; }
public DbSet<ExpenseLog> ExpenseLogs { get; set; }
public DbSet<ExpensesTypeMaster> ExpensesTypeMaster { get; set; }
public DbSet<ExpenseCategoryMaster> ExpenseCategoryMasters { get; set; }
public DbSet<PaymentModeMatser> PaymentModeMatser { get; set; }
public DbSet<ExpensesStatusMaster> ExpensesStatusMaster { get; set; }
public DbSet<BillAttachments> BillAttachments { get; set; }
@ -114,6 +115,7 @@ namespace Marco.Pms.DataAccess.Data
public DbSet<StatusPermissionMapping> StatusPermissionMapping { get; set; }
public DbSet<ExpensesStatusMapping> ExpensesStatusMapping { get; set; }
public DbSet<PaymentRequest> PaymentRequests { get; set; }
public DbSet<PaymentRequestAttachment> PaymentRequestAttachments { get; set; }
public DbSet<RecurringPayment> RecurringPayments { get; set; }
public DbSet<AdvancePaymentTransaction> AdvancePaymentTransactions { get; set; }
@ -674,8 +676,8 @@ namespace Marco.Pms.DataAccess.Data
StatusId = Guid.Parse("61578360-3a49-4c34-8604-7b35a3787b95")
});
modelBuilder.Entity<ExpensesTypeMaster>().HasData(
new ExpensesTypeMaster
modelBuilder.Entity<ExpenseCategoryMaster>().HasData(
new ExpenseCategoryMaster
{
Id = Guid.Parse("5e0c6227-d49d-41ff-9f1f-781f0aee2469"),
Name = "Procurement",
@ -685,7 +687,7 @@ namespace Marco.Pms.DataAccess.Data
IsAttachmentRequried = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new ExpensesTypeMaster
new ExpenseCategoryMaster
{
Id = Guid.Parse("2de53163-0dbd-404b-8e60-1b02e6b4886a"),
Name = "Transport",
@ -695,7 +697,7 @@ namespace Marco.Pms.DataAccess.Data
IsAttachmentRequried = false,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new ExpensesTypeMaster
new ExpenseCategoryMaster
{
Id = Guid.Parse("dd120bc4-ab0a-45ba-8450-5cd45ff221ca"),
Name = "Travelling",
@ -705,7 +707,7 @@ namespace Marco.Pms.DataAccess.Data
IsAttachmentRequried = false,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new ExpensesTypeMaster
new ExpenseCategoryMaster
{
Id = Guid.Parse("52484820-1b54-4865-8f0f-baa2b1d339b9"),
Name = "Mobilization",
@ -715,7 +717,7 @@ namespace Marco.Pms.DataAccess.Data
IsAttachmentRequried = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new ExpensesTypeMaster
new ExpenseCategoryMaster
{
Id = Guid.Parse("fc59eb90-98ea-481c-b421-54bfa9e42d8f"),
Name = "Employee Welfare",
@ -725,7 +727,7 @@ namespace Marco.Pms.DataAccess.Data
IsAttachmentRequried = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new ExpensesTypeMaster
new ExpenseCategoryMaster
{
Id = Guid.Parse("77013784-9324-4d8b-bd36-d6f928e68942"),
Name = "Maintenance & Utilities",
@ -735,7 +737,7 @@ namespace Marco.Pms.DataAccess.Data
IsAttachmentRequried = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new ExpensesTypeMaster
new ExpenseCategoryMaster
{
Id = Guid.Parse("1e2d697a-76b4-4be8-bc66-87144561a1a0"),
Name = "Vendor/Supplier Payments",
@ -745,7 +747,7 @@ namespace Marco.Pms.DataAccess.Data
IsAttachmentRequried = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new ExpensesTypeMaster
new ExpenseCategoryMaster
{
Id = Guid.Parse("4842fa61-64eb-4241-aebd-8282065af9f9"),
Name = "Compliance & Safety",

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_CurrentBalance_In_AdvancePayment_Tables : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<double>(
name: "CurrentBalance",
table: "AdvancePaymentTransactions",
type: "double",
nullable: false,
defaultValue: 0.0);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CurrentBalance",
table: "AdvancePaymentTransactions");
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,145 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Made_UpdatedBy_And_UpdateAt_As_Nullable : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_PaymentRequests_Employees_UpdatedById",
table: "PaymentRequests");
migrationBuilder.DropForeignKey(
name: "FK_RecurringPayments_Employees_UpdatedById",
table: "RecurringPayments");
migrationBuilder.AlterColumn<Guid>(
name: "UpdatedById",
table: "RecurringPayments",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci",
oldClrType: typeof(Guid),
oldType: "char(36)")
.OldAnnotation("Relational:Collation", "ascii_general_ci");
migrationBuilder.AlterColumn<DateTime>(
name: "UpdatedAt",
table: "RecurringPayments",
type: "datetime(6)",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "datetime(6)");
migrationBuilder.AlterColumn<Guid>(
name: "UpdatedById",
table: "PaymentRequests",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci",
oldClrType: typeof(Guid),
oldType: "char(36)")
.OldAnnotation("Relational:Collation", "ascii_general_ci");
migrationBuilder.AlterColumn<DateTime>(
name: "UpdatedAt",
table: "PaymentRequests",
type: "datetime(6)",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "datetime(6)");
migrationBuilder.AddForeignKey(
name: "FK_PaymentRequests_Employees_UpdatedById",
table: "PaymentRequests",
column: "UpdatedById",
principalTable: "Employees",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_RecurringPayments_Employees_UpdatedById",
table: "RecurringPayments",
column: "UpdatedById",
principalTable: "Employees",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_PaymentRequests_Employees_UpdatedById",
table: "PaymentRequests");
migrationBuilder.DropForeignKey(
name: "FK_RecurringPayments_Employees_UpdatedById",
table: "RecurringPayments");
migrationBuilder.AlterColumn<Guid>(
name: "UpdatedById",
table: "RecurringPayments",
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.AlterColumn<DateTime>(
name: "UpdatedAt",
table: "RecurringPayments",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
oldClrType: typeof(DateTime),
oldType: "datetime(6)",
oldNullable: true);
migrationBuilder.AlterColumn<Guid>(
name: "UpdatedById",
table: "PaymentRequests",
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.AlterColumn<DateTime>(
name: "UpdatedAt",
table: "PaymentRequests",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
oldClrType: typeof(DateTime),
oldType: "datetime(6)",
oldNullable: true);
migrationBuilder.AddForeignKey(
name: "FK_PaymentRequests_Employees_UpdatedById",
table: "PaymentRequests",
column: "UpdatedById",
principalTable: "Employees",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_RecurringPayments_Employees_UpdatedById",
table: "RecurringPayments",
column: "UpdatedById",
principalTable: "Employees",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,209 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_PaymentRequestAttachmnets_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_PaymentRequests_ExpensesTypeMaster_ExpenseCategoryId",
table: "PaymentRequests");
migrationBuilder.DeleteData(
table: "ExpensesTypeMaster",
keyColumn: "Id",
keyValue: new Guid("1e2d697a-76b4-4be8-bc66-87144561a1a0"));
migrationBuilder.DeleteData(
table: "ExpensesTypeMaster",
keyColumn: "Id",
keyValue: new Guid("2de53163-0dbd-404b-8e60-1b02e6b4886a"));
migrationBuilder.DeleteData(
table: "ExpensesTypeMaster",
keyColumn: "Id",
keyValue: new Guid("4842fa61-64eb-4241-aebd-8282065af9f9"));
migrationBuilder.DeleteData(
table: "ExpensesTypeMaster",
keyColumn: "Id",
keyValue: new Guid("52484820-1b54-4865-8f0f-baa2b1d339b9"));
migrationBuilder.DeleteData(
table: "ExpensesTypeMaster",
keyColumn: "Id",
keyValue: new Guid("5e0c6227-d49d-41ff-9f1f-781f0aee2469"));
migrationBuilder.DeleteData(
table: "ExpensesTypeMaster",
keyColumn: "Id",
keyValue: new Guid("77013784-9324-4d8b-bd36-d6f928e68942"));
migrationBuilder.DeleteData(
table: "ExpensesTypeMaster",
keyColumn: "Id",
keyValue: new Guid("dd120bc4-ab0a-45ba-8450-5cd45ff221ca"));
migrationBuilder.DeleteData(
table: "ExpensesTypeMaster",
keyColumn: "Id",
keyValue: new Guid("fc59eb90-98ea-481c-b421-54bfa9e42d8f"));
migrationBuilder.AddColumn<bool>(
name: "IsAdvancePayment",
table: "PaymentRequests",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.CreateTable(
name: "ExpenseCategoryMasters",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
NoOfPersonsRequired = table.Column<bool>(type: "tinyint(1)", nullable: false),
Description = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false),
IsAttachmentRequried = table.Column<bool>(type: "tinyint(1)", nullable: false),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_ExpenseCategoryMasters", x => x.Id);
table.ForeignKey(
name: "FK_ExpenseCategoryMasters_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "PaymentRequestAttachments",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
PaymentRequestId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
DocumentId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_PaymentRequestAttachments", x => x.Id);
table.ForeignKey(
name: "FK_PaymentRequestAttachments_Documents_DocumentId",
column: x => x.DocumentId,
principalTable: "Documents",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_PaymentRequestAttachments_PaymentRequests_PaymentRequestId",
column: x => x.PaymentRequestId,
principalTable: "PaymentRequests",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_PaymentRequestAttachments_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.InsertData(
table: "ExpenseCategoryMasters",
columns: new[] { "Id", "Description", "IsActive", "IsAttachmentRequried", "Name", "NoOfPersonsRequired", "TenantId" },
values: new object[,]
{
{ new Guid("1e2d697a-76b4-4be8-bc66-87144561a1a0"), "Scheduled payments for external services or goods.", true, true, "Vendor/Supplier Payments", false, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("2de53163-0dbd-404b-8e60-1b02e6b4886a"), "Vehicle fuel, logistics services and delivery of goods or personnel.", true, false, "Transport", false, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("4842fa61-64eb-4241-aebd-8282065af9f9"), "Government fees, insurance, inspections and safety-related expenditures.", true, true, "Compliance & Safety", false, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("52484820-1b54-4865-8f0f-baa2b1d339b9"), "Site setup costs including equipment deployment and temporary infrastructure.", true, true, "Mobilization", false, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("5e0c6227-d49d-41ff-9f1f-781f0aee2469"), "Materials, equipment and supplies purchased for site operations.", true, true, "Procurement", false, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("77013784-9324-4d8b-bd36-d6f928e68942"), "Machinery servicing, electricity, water, and temporary office needs.", true, true, "Maintenance & Utilities", false, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("dd120bc4-ab0a-45ba-8450-5cd45ff221ca"), "Delivery of personnel.", true, false, "Travelling", true, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("fc59eb90-98ea-481c-b421-54bfa9e42d8f"), " Worker amenities like snacks, meals, safety gear, accommodation, medical support etc.", true, true, "Employee Welfare", true, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") }
});
migrationBuilder.CreateIndex(
name: "IX_ExpenseCategoryMasters_TenantId",
table: "ExpenseCategoryMasters",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_PaymentRequestAttachments_DocumentId",
table: "PaymentRequestAttachments",
column: "DocumentId");
migrationBuilder.CreateIndex(
name: "IX_PaymentRequestAttachments_PaymentRequestId",
table: "PaymentRequestAttachments",
column: "PaymentRequestId");
migrationBuilder.CreateIndex(
name: "IX_PaymentRequestAttachments_TenantId",
table: "PaymentRequestAttachments",
column: "TenantId");
migrationBuilder.AddForeignKey(
name: "FK_PaymentRequests_ExpenseCategoryMasters_ExpenseCategoryId",
table: "PaymentRequests",
column: "ExpenseCategoryId",
principalTable: "ExpenseCategoryMasters",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_PaymentRequests_ExpenseCategoryMasters_ExpenseCategoryId",
table: "PaymentRequests");
migrationBuilder.DropTable(
name: "ExpenseCategoryMasters");
migrationBuilder.DropTable(
name: "PaymentRequestAttachments");
migrationBuilder.DropColumn(
name: "IsAdvancePayment",
table: "PaymentRequests");
migrationBuilder.InsertData(
table: "ExpensesTypeMaster",
columns: new[] { "Id", "Description", "IsActive", "IsAttachmentRequried", "Name", "NoOfPersonsRequired", "TenantId" },
values: new object[,]
{
{ new Guid("1e2d697a-76b4-4be8-bc66-87144561a1a0"), "Scheduled payments for external services or goods.", true, true, "Vendor/Supplier Payments", false, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("2de53163-0dbd-404b-8e60-1b02e6b4886a"), "Vehicle fuel, logistics services and delivery of goods or personnel.", true, false, "Transport", false, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("4842fa61-64eb-4241-aebd-8282065af9f9"), "Government fees, insurance, inspections and safety-related expenditures.", true, true, "Compliance & Safety", false, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("52484820-1b54-4865-8f0f-baa2b1d339b9"), "Site setup costs including equipment deployment and temporary infrastructure.", true, true, "Mobilization", false, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("5e0c6227-d49d-41ff-9f1f-781f0aee2469"), "Materials, equipment and supplies purchased for site operations.", true, true, "Procurement", false, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("77013784-9324-4d8b-bd36-d6f928e68942"), "Machinery servicing, electricity, water, and temporary office needs.", true, true, "Maintenance & Utilities", false, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("dd120bc4-ab0a-45ba-8450-5cd45ff221ca"), "Delivery of personnel.", true, false, "Travelling", true, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("fc59eb90-98ea-481c-b421-54bfa9e42d8f"), " Worker amenities like snacks, meals, safety gear, accommodation, medical support etc.", true, true, "Employee Welfare", true, new Guid("b3466e83-7e11-464c-b93a-daf047838b26") }
});
migrationBuilder.AddForeignKey(
name: "FK_PaymentRequests_ExpensesTypeMaster_ExpenseCategoryId",
table: "PaymentRequests",
column: "ExpenseCategoryId",
principalTable: "ExpensesTypeMaster",
principalColumn: "Id");
}
}
}

View File

@ -2081,6 +2081,9 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<Guid>("CreatedById")
.HasColumnType("char(36)");
b.Property<double>("CurrentBalance")
.HasColumnType("double");
b.Property<Guid>("EmployeeId")
.HasColumnType("char(36)");
@ -2134,6 +2137,121 @@ namespace Marco.Pms.DataAccess.Migrations
b.ToTable("BillAttachments");
});
modelBuilder.Entity("Marco.Pms.Model.Expenses.ExpenseCategoryMaster", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsAttachmentRequried")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("NoOfPersonsRequired")
.HasColumnType("tinyint(1)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("TenantId");
b.ToTable("ExpenseCategoryMasters");
b.HasData(
new
{
Id = new Guid("5e0c6227-d49d-41ff-9f1f-781f0aee2469"),
Description = "Materials, equipment and supplies purchased for site operations.",
IsActive = true,
IsAttachmentRequried = true,
Name = "Procurement",
NoOfPersonsRequired = false,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("2de53163-0dbd-404b-8e60-1b02e6b4886a"),
Description = "Vehicle fuel, logistics services and delivery of goods or personnel.",
IsActive = true,
IsAttachmentRequried = false,
Name = "Transport",
NoOfPersonsRequired = false,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("dd120bc4-ab0a-45ba-8450-5cd45ff221ca"),
Description = "Delivery of personnel.",
IsActive = true,
IsAttachmentRequried = false,
Name = "Travelling",
NoOfPersonsRequired = true,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("52484820-1b54-4865-8f0f-baa2b1d339b9"),
Description = "Site setup costs including equipment deployment and temporary infrastructure.",
IsActive = true,
IsAttachmentRequried = true,
Name = "Mobilization",
NoOfPersonsRequired = false,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("fc59eb90-98ea-481c-b421-54bfa9e42d8f"),
Description = " Worker amenities like snacks, meals, safety gear, accommodation, medical support etc.",
IsActive = true,
IsAttachmentRequried = true,
Name = "Employee Welfare",
NoOfPersonsRequired = true,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("77013784-9324-4d8b-bd36-d6f928e68942"),
Description = "Machinery servicing, electricity, water, and temporary office needs.",
IsActive = true,
IsAttachmentRequried = true,
Name = "Maintenance & Utilities",
NoOfPersonsRequired = false,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("1e2d697a-76b4-4be8-bc66-87144561a1a0"),
Description = "Scheduled payments for external services or goods.",
IsActive = true,
IsAttachmentRequried = true,
Name = "Vendor/Supplier Payments",
NoOfPersonsRequired = false,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("4842fa61-64eb-4241-aebd-8282065af9f9"),
Description = "Government fees, insurance, inspections and safety-related expenditures.",
IsActive = true,
IsAttachmentRequried = true,
Name = "Compliance & Safety",
NoOfPersonsRequired = false,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
});
});
modelBuilder.Entity("Marco.Pms.Model.Expenses.ExpenseLog", b =>
{
b.Property<Guid>("Id")
@ -2433,6 +2551,9 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsAdvancePayment")
.HasColumnType("tinyint(1)");
b.Property<string>("Payee")
.IsRequired()
.HasColumnType("longtext");
@ -2457,10 +2578,10 @@ namespace Marco.Pms.DataAccess.Migrations
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("UpdatedAt")
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid>("UpdatedById")
b.Property<Guid?>("UpdatedById")
.HasColumnType("char(36)");
b.HasKey("Id");
@ -2484,6 +2605,32 @@ namespace Marco.Pms.DataAccess.Migrations
b.ToTable("PaymentRequests");
});
modelBuilder.Entity("Marco.Pms.Model.Expenses.PaymentRequestAttachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid>("DocumentId")
.HasColumnType("char(36)");
b.Property<Guid>("PaymentRequestId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("DocumentId");
b.HasIndex("PaymentRequestId");
b.HasIndex("TenantId");
b.ToTable("PaymentRequestAttachments");
});
modelBuilder.Entity("Marco.Pms.Model.Expenses.RecurringPayment", b =>
{
b.Property<Guid>("Id")
@ -2561,10 +2708,10 @@ namespace Marco.Pms.DataAccess.Migrations
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("UpdatedAt")
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid>("UpdatedById")
b.Property<Guid?>("UpdatedById")
.HasColumnType("char(36)");
b.HasKey("Id");
@ -3241,88 +3388,6 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasIndex("TenantId");
b.ToTable("ExpensesTypeMaster");
b.HasData(
new
{
Id = new Guid("5e0c6227-d49d-41ff-9f1f-781f0aee2469"),
Description = "Materials, equipment and supplies purchased for site operations.",
IsActive = true,
IsAttachmentRequried = true,
Name = "Procurement",
NoOfPersonsRequired = false,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("2de53163-0dbd-404b-8e60-1b02e6b4886a"),
Description = "Vehicle fuel, logistics services and delivery of goods or personnel.",
IsActive = true,
IsAttachmentRequried = false,
Name = "Transport",
NoOfPersonsRequired = false,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("dd120bc4-ab0a-45ba-8450-5cd45ff221ca"),
Description = "Delivery of personnel.",
IsActive = true,
IsAttachmentRequried = false,
Name = "Travelling",
NoOfPersonsRequired = true,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("52484820-1b54-4865-8f0f-baa2b1d339b9"),
Description = "Site setup costs including equipment deployment and temporary infrastructure.",
IsActive = true,
IsAttachmentRequried = true,
Name = "Mobilization",
NoOfPersonsRequired = false,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("fc59eb90-98ea-481c-b421-54bfa9e42d8f"),
Description = " Worker amenities like snacks, meals, safety gear, accommodation, medical support etc.",
IsActive = true,
IsAttachmentRequried = true,
Name = "Employee Welfare",
NoOfPersonsRequired = true,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("77013784-9324-4d8b-bd36-d6f928e68942"),
Description = "Machinery servicing, electricity, water, and temporary office needs.",
IsActive = true,
IsAttachmentRequried = true,
Name = "Maintenance & Utilities",
NoOfPersonsRequired = false,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("1e2d697a-76b4-4be8-bc66-87144561a1a0"),
Description = "Scheduled payments for external services or goods.",
IsActive = true,
IsAttachmentRequried = true,
Name = "Vendor/Supplier Payments",
NoOfPersonsRequired = false,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("4842fa61-64eb-4241-aebd-8282065af9f9"),
Description = "Government fees, insurance, inspections and safety-related expenditures.",
IsActive = true,
IsAttachmentRequried = true,
Name = "Compliance & Safety",
NoOfPersonsRequired = false,
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
});
});
modelBuilder.Entity("Marco.Pms.Model.Master.Feature", b =>
@ -6183,6 +6248,17 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.Expenses.ExpenseCategoryMaster", b =>
{
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.Expenses.ExpenseLog", b =>
{
b.HasOne("Marco.Pms.Model.Expenses.Expenses", "Expense")
@ -6366,7 +6442,7 @@ namespace Marco.Pms.DataAccess.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Master.ExpensesTypeMaster", "ExpenseCategory")
b.HasOne("Marco.Pms.Model.Expenses.ExpenseCategoryMaster", "ExpenseCategory")
.WithMany()
.HasForeignKey("ExpenseCategoryId");
@ -6392,9 +6468,7 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
.WithMany()
.HasForeignKey("UpdatedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
.HasForeignKey("UpdatedById");
b.Navigation("CreatedBy");
@ -6413,6 +6487,33 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("Marco.Pms.Model.Expenses.PaymentRequestAttachment", b =>
{
b.HasOne("Marco.Pms.Model.DocumentManager.Document", "Document")
.WithMany()
.HasForeignKey("DocumentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Expenses.PaymentRequest", "PaymentRequest")
.WithMany()
.HasForeignKey("PaymentRequestId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Document");
b.Navigation("PaymentRequest");
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.Expenses.RecurringPayment", b =>
{
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
@ -6455,9 +6556,7 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
.WithMany()
.HasForeignKey("UpdatedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
.HasForeignKey("UpdatedById");
b.Navigation("CreatedBy");

View File

@ -0,0 +1,19 @@
using Marco.Pms.Model.Utilities;
namespace Marco.Pms.Model.Dtos.Expenses
{
public class PaymentRequestDto
{
public Guid? Id { get; set; }
public required string Title { get; set; }
public required string Description { get; set; }
public required string Payee { get; set; }
public required Guid CurrencyId { get; set; }
public required double Amount { get; set; }
public required DateTime DueDate { get; set; }
public Guid? ProjectId { get; set; }
public required Guid ExpenseCategoryId { get; set; }
public bool IsAdvancePayment { get; set; }
public List<FileUploadModel>? BillAttachments { get; set; }
}
}

View File

@ -16,6 +16,7 @@ namespace Marco.Pms.Model.Expenses
[ForeignKey("EmployeeId")]
public Employee? Employee { get; set; }
public double Amount { get; set; }
public double CurrentBalance { get; set; }
public DateTime CreatedAt { get; set; }
public Guid CreatedById { get; set; }

View File

@ -0,0 +1,14 @@
using Marco.Pms.Model.Utilities;
namespace Marco.Pms.Model.Expenses
{
public class ExpenseCategoryMaster : TenantRelation
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public bool NoOfPersonsRequired { get; set; }
public string Description { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
public bool IsAttachmentRequried { get; set; } = true;
}
}

View File

@ -15,6 +15,7 @@ namespace Marco.Pms.Model.Expenses
public string UIDPrefix { get; set; } = default!;
public int UIDPostfix { get; set; }
public string Payee { get; set; } = default!;
public bool IsAdvancePayment { get; set; }
public Guid CurrencyId { get; set; }
[ValidateNever]
@ -36,7 +37,7 @@ namespace Marco.Pms.Model.Expenses
[ValidateNever]
[ForeignKey("ExpenseCategoryId")]
public ExpensesTypeMaster? ExpenseCategory { get; set; }
public ExpenseCategoryMaster? ExpenseCategory { get; set; }
public Guid ExpenseStatusId { get; set; }
[ValidateNever]
@ -49,8 +50,8 @@ namespace Marco.Pms.Model.Expenses
[ValidateNever]
[ForeignKey("CreatedById")]
public Employee? CreatedBy { get; set; }
public DateTime UpdatedAt { get; set; }
public Guid UpdatedById { get; set; }
public DateTime? UpdatedAt { get; set; }
public Guid? UpdatedById { get; set; }
[ValidateNever]
[ForeignKey("UpdatedById")]

View File

@ -0,0 +1,22 @@
using Marco.Pms.Model.DocumentManager;
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.Expenses
{
public class PaymentRequestAttachment : TenantRelation
{
public Guid Id { get; set; }
public Guid PaymentRequestId { get; set; }
[ValidateNever]
[ForeignKey("PaymentRequestId")]
public PaymentRequest? PaymentRequest { get; set; }
public Guid DocumentId { get; set; }
[ValidateNever]
[ForeignKey("DocumentId")]
public Document? Document { get; set; }
}
}

View File

@ -56,8 +56,8 @@ namespace Marco.Pms.Model.Expenses
[ValidateNever]
[ForeignKey("CreatedById")]
public Employee? CreatedBy { get; set; }
public DateTime UpdatedAt { get; set; }
public Guid UpdatedById { get; set; }
public DateTime? UpdatedAt { get; set; }
public Guid? UpdatedById { get; set; }
[ValidateNever]
[ForeignKey("UpdatedById")]

View File

@ -0,0 +1,31 @@
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 PaymentRequestVM
{
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 ExpensesTypeMasterVM? 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; }
}
}

View File

@ -29,6 +29,7 @@ namespace Marco.Pms.Services.Controllers
tenantId = userHelper.GetTenantId();
}
#region =================================================================== Expense Functions ===================================================================
/// <summary>
/// Retrieves a paginated list of expenses based on user permissions and optional filters.
@ -131,5 +132,21 @@ namespace Marco.Pms.Services.Controllers
return StatusCode(response.StatusCode, response);
}
#endregion
#region =================================================================== Payment Request Functions ===================================================================
[HttpPost("payment-request/create")]
public async Task<IActionResult> CreatePaymentRequest([FromBody] PaymentRequestDto model)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _expensesService.CreatePaymentRequestAsync(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);
}
#endregion
}
}

View File

@ -257,6 +257,10 @@ 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>();
#endregion
CreateMap<AdvancePaymentTransaction, AdvancePaymentTransactionVM>();
@ -336,7 +340,22 @@ namespace Marco.Pms.Services.MappingProfiles
// Explicitly and safely convert nullable Guid to non-nullable Guid
opt => opt.MapFrom(src => src.Id ?? Guid.Empty)
);
CreateMap<ExpensesTypeMasterDto, ExpenseCategoryMaster>()
.ForMember(
dest => dest.Id,
// 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<ExpenseCategoryMaster, ExpensesTypeMasterMongoDB>()
.ForMember(
dest => dest.Id,
opt => opt.MapFrom(src => src.Id.ToString()))
.ForMember(
dest => dest.TenantId,
opt => opt.MapFrom(src => src.TenantId.ToString()));
CreateMap<ExpensesTypeMaster, ExpensesTypeMasterMongoDB>()
.ForMember(

View File

@ -66,6 +66,8 @@ namespace Marco.Pms.Services.Service
_mapper = mapper;
}
#region =================================================================== Expense Functions ===================================================================
#region =================================================================== Get Functions ===================================================================
/// <summary>
@ -1145,6 +1147,159 @@ namespace Marco.Pms.Services.Service
#endregion
#endregion
#region =================================================================== Payment Request Functions ===================================================================
public async Task<ApiResponse<object>> CreatePaymentRequestAsync(PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId)
{
_logger.LogInfo("Start CreatePaymentRequestAsync for EmployeeId: {EmployeeId} TenantId: {TenantId}", loggedInEmployee.Id, tenantId);
string uIDPrefix = $"PY/{DateTime.Now:MMyy}";
try
{
// Execute database lookups concurrently
var expenseCategoryTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ExpenseCategoryMasters.FirstOrDefaultAsync(et => et.Id == model.ExpenseCategoryId && et.IsActive);
});
var currencyTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId);
});
var expenseStatusTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ExpensesStatusMaster.FirstOrDefaultAsync(es => es.Id == Draft && es.IsActive);
});
var projectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Projects.FirstOrDefaultAsync(P => model.ProjectId.HasValue && P.Id == model.ProjectId.Value);
});
await Task.WhenAll(expenseCategoryTask, currencyTask, expenseStatusTask, projectTask);
var expenseCategory = await expenseCategoryTask;
if (expenseCategory == null)
{
_logger.LogWarning("Expense Category not found with Id: {ExpenseCategoryId}", model.ExpenseCategoryId);
return ApiResponse<object>.ErrorResponse("Expense Category not found.", "Expense Category not found.", 404);
}
var currency = await currencyTask;
if (currency == null)
{
_logger.LogWarning("Currency not found with Id: {CurrencyId}", model.CurrencyId);
return ApiResponse<object>.ErrorResponse("Currency not found.", "Currency not found.", 404);
}
var expenseStatus = await expenseStatusTask;
if (expenseStatus == null)
{
_logger.LogWarning("Expense Status not found for Draft.");
return ApiResponse<object>.ErrorResponse("Expense Status (Draft) not found.", "Expense Status not found.", 404);
}
var project = await projectTask;
// Project is optional so no error if not found
// Generate unique UID postfix based on existing requests for the current prefix
var lastPR = await _context.PaymentRequests.Where(pr => pr.UIDPrefix == uIDPrefix)
.OrderByDescending(pr => pr.UIDPostfix)
.FirstOrDefaultAsync();
int uIDPostfix = lastPR == null ? 1 : (lastPR.UIDPostfix + 1);
// Map DTO to PaymentRequest entity and set additional mandatory fields
var paymentRequest = _mapper.Map<PaymentRequest>(model);
paymentRequest.ExpenseStatusId = Draft;
paymentRequest.UIDPrefix = uIDPrefix;
paymentRequest.UIDPostfix = uIDPostfix;
paymentRequest.IsActive = true;
paymentRequest.CreatedAt = DateTime.UtcNow;
paymentRequest.CreatedById = loggedInEmployee.Id;
paymentRequest.TenantId = tenantId;
await _context.PaymentRequests.AddAsync(paymentRequest);
await _context.SaveChangesAsync();
// Process bill attachments if any
if (model.BillAttachments?.Any() == true)
{
_logger.LogInfo("Processing {AttachmentCount} attachments for PaymentRequest Id: {PaymentRequestId}",
model.BillAttachments.Count, paymentRequest.Id);
// Validate base64 attachments data before processing
foreach (var attachment in model.BillAttachments)
{
if (string.IsNullOrWhiteSpace(attachment.Base64Data) || !_s3Service.IsBase64String(attachment.Base64Data))
{
_logger.LogWarning("Invalid or missing Base64 data for attachment: {FileName}", attachment.FileName ?? "N/A");
throw new ArgumentException($"Invalid or missing Base64 data for attachment: {attachment.FileName ?? "N/A"}");
}
}
var batchId = Guid.NewGuid();
// Process all attachments concurrently
var processingTasks = model.BillAttachments.Select(attachment =>
ProcessSinglePaymentRequestAttachmentAsync(attachment, paymentRequest, loggedInEmployee.Id, tenantId, batchId)
).ToList();
var results = await Task.WhenAll(processingTasks);
// Add documents and payment request attachments after concurrent processing
foreach (var (document, paymentRequestAttachment) in results)
{
_context.Documents.Add(document);
_context.PaymentRequestAttachments.Add(paymentRequestAttachment);
}
await _context.SaveChangesAsync();
_logger.LogInfo("{AttachmentCount} attachments processed and saved for PaymentRequest Id: {PaymentRequestId}",
results.Length, paymentRequest.Id);
}
// Prepare response VM
var response = _mapper.Map<PaymentRequestVM>(paymentRequest);
response.PaymentRequestUID = $"{paymentRequest.UIDPrefix}/{paymentRequest.UIDPostfix:D5}";
response.Currency = currency;
response.ExpenseCategory = _mapper.Map<ExpensesTypeMasterVM>(expenseCategory);
response.ExpenseStatus = _mapper.Map<ExpensesStatusMasterVM>(expenseStatus);
response.Project = _mapper.Map<BasicProjectVM>(project);
response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
_logger.LogInfo("Payment request created successfully with UID: {PaymentRequestUID}", response.PaymentRequestUID);
return ApiResponse<object>.SuccessResponse(response, "Created the Payment Request Successfully.", 201);
}
catch (ArgumentException ex)
{
_logger.LogError(ex, "Argument error in CreatePaymentRequestAsync: {Message}", ex.Message);
return ApiResponse<object>.ErrorResponse(ex.Message, "Invalid data.", 400);
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error in CreatePaymentRequestAsync: {Message}", ex.Message);
return ApiResponse<object>.ErrorResponse("An error occurred while creating the payment request.", ex.Message, 500);
}
finally
{
_logger.LogInfo("End CreatePaymentRequestAsync for EmployeeId: {EmployeeId}", loggedInEmployee.Id);
}
}
#endregion
#region =================================================================== Payment Request Functions ===================================================================
#endregion
#region =================================================================== Payment Request Functions ===================================================================
#endregion
#region =================================================================== Helper Functions ===================================================================
private int ExtractNumber(string id)
@ -1284,7 +1439,6 @@ namespace Marco.Pms.Services.Service
return expenseList;
}
private async Task<ExpenseDetailsMongoDB> GetAllExpnesRelatedTablesForSingle(Expenses model, Guid tenantId)
{
var statusMappingTask = Task.Run(async () =>
@ -1366,7 +1520,6 @@ namespace Marco.Pms.Services.Service
}
/// <summary>
/// Deserializes the filter string, handling multiple potential formats (e.g., direct JSON vs. escaped JSON string).
/// </summary>
@ -1429,7 +1582,7 @@ namespace Marco.Pms.Services.Service
// Create a list of tasks to be executed concurrently.
var processingTasks = attachments.Select(attachment =>
ProcessSingleAttachmentAsync(attachment, expense, employeeId, tenantId, batchId)
ProcessSingleExpenseAttachmentAsync(attachment, expense, employeeId, tenantId, batchId)
).ToList();
var results = await Task.WhenAll(processingTasks);
@ -1446,7 +1599,7 @@ namespace Marco.Pms.Services.Service
/// <summary>
/// Handles the logic for a single attachment: upload to S3 and create corresponding entities.
/// </summary>
private async Task<(Document document, BillAttachments billAttachment)> ProcessSingleAttachmentAsync(
private async Task<(Document document, BillAttachments billAttachment)> ProcessSingleExpenseAttachmentAsync(
FileUploadModel attachment, Expenses expense, Guid employeeId, Guid tenantId, Guid batchId)
{
var base64Data = attachment.Base64Data!.Contains(',') ? attachment.Base64Data[(attachment.Base64Data.IndexOf(",") + 1)..] : attachment.Base64Data;
@ -1458,7 +1611,7 @@ namespace Marco.Pms.Services.Service
await _s3Service.UploadFileAsync(base64Data, fileType, objectKey);
_logger.LogInfo("Uploaded file to S3 with key: {ObjectKey}", objectKey);
return CreateAttachmentEntities(batchId, expense.Id, employeeId, tenantId, objectKey, attachment);
return CreateExpenseAttachmentEntities(batchId, expense.Id, employeeId, tenantId, objectKey, attachment);
}
@ -1466,7 +1619,7 @@ namespace Marco.Pms.Services.Service
/// A private static helper method to create Document and BillAttachment entities.
/// This remains unchanged as it's a pure data-shaping method.
/// </summary>
private static (Document document, BillAttachments billAttachment) CreateAttachmentEntities(
private static (Document document, BillAttachments billAttachment) CreateExpenseAttachmentEntities(
Guid batchId, Guid expenseId, Guid uploadedById, Guid tenantId, string s3Key, FileUploadModel attachmentDto)
{
var document = new Document
@ -1484,6 +1637,54 @@ namespace Marco.Pms.Services.Service
return (document, billAttachment);
}
/// <summary>
/// Handles the logic for a single attachment: upload to S3 and create corresponding entities.
/// </summary>
private async Task<(Document document, PaymentRequestAttachment billAttachment)> ProcessSinglePaymentRequestAttachmentAsync(
FileUploadModel attachment, PaymentRequest paymentRequest, Guid employeeId, Guid tenantId, Guid batchId)
{
var base64Data = attachment.Base64Data!.Contains(',') ? attachment.Base64Data[(attachment.Base64Data.IndexOf(",") + 1)..] : attachment.Base64Data;
var fileType = _s3Service.GetContentTypeFromBase64(base64Data);
var fileName = _s3Service.GenerateFileName(fileType, paymentRequest.Id, "PaymentRequest");
string objectKey;
if (paymentRequest.ProjectId.HasValue)
{
objectKey = $"tenant-{tenantId}/project-{paymentRequest.ProjectId}/PaymentRequest/{fileName}";
}
else
{
objectKey = $"tenant-{tenantId}/PaymentRequest/{fileName}";
}
// Await the I/O-bound upload operation directly.
await _s3Service.UploadFileAsync(base64Data, fileType, objectKey);
_logger.LogInfo("Uploaded file to S3 with key: {ObjectKey}", objectKey);
return CreatePaymentRequestAttachmentEntities(batchId, paymentRequest.Id, employeeId, tenantId, objectKey, attachment);
}
/// <summary>
/// A private static helper method to create Document and BillAttachment entities.
/// This remains unchanged as it's a pure data-shaping method.
/// </summary>
private static (Document document, PaymentRequestAttachment paymentRequestAttachment) CreatePaymentRequestAttachmentEntities(
Guid batchId, Guid paymentRequestId, Guid uploadedById, Guid tenantId, string s3Key, FileUploadModel attachmentDto)
{
var document = new Document
{
BatchId = batchId,
UploadedById = uploadedById,
FileName = attachmentDto.FileName ?? "",
ContentType = attachmentDto.ContentType ?? "",
S3Key = s3Key,
FileSize = attachmentDto.FileSize,
UploadedAt = DateTime.UtcNow,
TenantId = tenantId
};
var paymentRequestAttachment = new PaymentRequestAttachment { Document = document, PaymentRequestId = paymentRequestId, TenantId = tenantId };
return (document, paymentRequestAttachment);
}
private async Task DeleteAttachemnts(List<Guid> documentIds)
{
var attachmentTask = Task.Run(async () =>

View File

@ -10,6 +10,7 @@ using Marco.Pms.Model.Dtos.DocumentManager;
using Marco.Pms.Model.Dtos.Master;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Expenses;
using Marco.Pms.Model.Master;
using Marco.Pms.Model.MongoDBModels.Utility;
using Marco.Pms.Model.Utilities;
@ -2051,7 +2052,7 @@ namespace Marco.Pms.Services.Service
try
{
// Featching the list of Expenses Type.
var typeList = await _context.ExpensesTypeMaster.Where(et => et.TenantId == tenantId && et.IsActive == isActive).ToListAsync();
var typeList = await _context.ExpenseCategoryMasters.Where(et => et.TenantId == tenantId && et.IsActive == isActive).ToListAsync();
var response = _mapper.Map<List<ExpensesTypeMasterVM>>(typeList);
_logger.LogInfo("{Count} records of expense type have been fetched successfully by employee {EmployeeId}", response.Count, loggedInEmployee.Id);
@ -2074,10 +2075,10 @@ namespace Marco.Pms.Services.Service
_logger.LogWarning("Access DENIED for employee {EmployeeId} for managing EXPANSES TYPE MASTER.", loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
}
var expensesType = _mapper.Map<ExpensesTypeMaster>(model);
var expensesType = _mapper.Map<ExpenseCategoryMaster>(model);
expensesType.TenantId = tenantId;
_context.ExpensesTypeMaster.Add(expensesType);
_context.ExpenseCategoryMasters.Add(expensesType);
await _context.SaveChangesAsync();
_logger.LogInfo("New Expense Type {ExpensesTypeId} was added by employee {EmployeeId}", expensesType.Id, loggedInEmployee.Id);
@ -2115,7 +2116,7 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Invalid Data", "User has send invalid payload", 400);
}
var expensesType = await _context.ExpensesTypeMaster.AsNoTracking().FirstOrDefaultAsync(et => et.Id == model.Id.Value && et.TenantId == tenantId);
var expensesType = await _context.ExpenseCategoryMasters.AsNoTracking().FirstOrDefaultAsync(et => et.Id == model.Id.Value && et.TenantId == tenantId);
// Checking if expense type exists
if (expensesType == null)
@ -2130,7 +2131,7 @@ namespace Marco.Pms.Services.Service
// Mapping ExpensesTypeMasterDto to ExpensesTypeMaster
_mapper.Map(model, expensesType);
_context.ExpensesTypeMaster.Update(expensesType);
_context.ExpenseCategoryMasters.Update(expensesType);
await _context.SaveChangesAsync();
_logger.LogInfo("Expense Type {ExpensesTypeId} was updated by employee {EmployeeId}", expensesType.Id, loggedInEmployee.Id);
@ -2143,7 +2144,7 @@ namespace Marco.Pms.Services.Service
UpdatedById = loggedInEmployee.Id.ToString(),
OldObject = existingEntityBson,
UpdatedAt = DateTime.UtcNow
}, "ExpensesTypeMasterModificationLog");
}, "ExpenseCategoryMasterModificationLog");
// Mapping ExpensesTypeMaster to ExpensesTypeMasterVM
var response = _mapper.Map<ExpensesTypeMasterVM>(expensesType);
@ -2173,7 +2174,7 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
}
var expensesType = await _context.ExpensesTypeMaster.FirstOrDefaultAsync(et => et.Id == id && et.TenantId == tenantId);
var expensesType = await _context.ExpenseCategoryMasters.FirstOrDefaultAsync(et => et.Id == id && et.TenantId == tenantId);
// Checking if expense type exists
if (expensesType == null)
@ -2198,7 +2199,7 @@ namespace Marco.Pms.Services.Service
UpdatedById = loggedInEmployee.Id.ToString(),
OldObject = existingEntityBson,
UpdatedAt = DateTime.UtcNow
}, "ExpensesTypeMasterModificationLog");
}, "ExpenseCategoryMasterModificationLog");
// Mapping ExpensesTypeMaster to ExpensesTypeMasterVM
var response = _mapper.Map<ExpensesTypeMasterVM>(expensesType);

View File

@ -16,5 +16,8 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
Task<ApiResponse<object>> DeleteExpanseAsync(Guid id, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> GetAdvancePaymentTransactionAsync(Guid id);
Task<ApiResponse<object>> CreatePaymentRequestAsync(PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId);
}
}