Merge pull request 'Added the upload document API' (#118) from Ashutosh_Task#1009 into Document_Manager
Reviewed-on: #118
This commit is contained in:
commit
d57395efcd
@ -113,7 +113,6 @@ namespace Marco.Pms.DataAccess.Data
|
||||
public DbSet<DocumentCategoryMaster> DocumentCategoryMasters { get; set; }
|
||||
public DbSet<DocumentTagMaster> DocumentTagMasters { get; set; }
|
||||
public DbSet<DocumentAttachment> DocumentAttachments { get; set; }
|
||||
public DbSet<DocumentAttachmentMapping> DocumentAttachmentMappings { get; set; }
|
||||
public DbSet<AttachmentVersionMapping> AttachmentVersionMappings { get; set; }
|
||||
public DbSet<AttachmentTagMapping> AttachmentTagMappings { get; set; }
|
||||
|
||||
@ -770,7 +769,7 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Id = Guid.Parse("336225ac-67f3-4e14-ba7a-8fad03cf2832"),
|
||||
Name = "Aadhaar card",
|
||||
RegexExpression = "^[2-9][0-9]{11}$",
|
||||
AllowedContentType = "application/pdf",
|
||||
AllowedContentType = "application/pdf,image/jpeg",
|
||||
MaxSizeAllowedInMB = 2,
|
||||
IsValidationRequired = true,
|
||||
IsMandatory = true,
|
||||
@ -785,7 +784,7 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Id = Guid.Parse("6344393b-9bb1-45f8-b620-9f6e279d012c"),
|
||||
Name = "Pan Card",
|
||||
RegexExpression = "^[A-Z]{5}[0-9]{4}[A-Z]{1}$",
|
||||
AllowedContentType = "application/pdf",
|
||||
AllowedContentType = "application/pdf,image/jpeg",
|
||||
MaxSizeAllowedInMB = 2,
|
||||
IsValidationRequired = true,
|
||||
IsMandatory = true,
|
||||
@ -800,7 +799,7 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Id = Guid.Parse("2d1d7441-46a8-425e-9395-94d0956f8e91"),
|
||||
Name = "Voter Card",
|
||||
RegexExpression = "^[A-Z]{3}[0-9]{7}$",
|
||||
AllowedContentType = "application/pdf",
|
||||
AllowedContentType = "application/pdf,image/jpeg",
|
||||
MaxSizeAllowedInMB = 2,
|
||||
IsValidationRequired = true,
|
||||
IsMandatory = true,
|
||||
@ -815,7 +814,7 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Id = Guid.Parse("16c40b80-c207-4a0c-a4d3-381414afe35a"),
|
||||
Name = "Passport",
|
||||
RegexExpression = "^[A-PR-WY][1-9]\\d\\s?\\d{4}[1-9]$",
|
||||
AllowedContentType = "application/pdf",
|
||||
AllowedContentType = "application/pdf,image/jpeg",
|
||||
MaxSizeAllowedInMB = 2,
|
||||
IsValidationRequired = true,
|
||||
IsMandatory = true,
|
||||
@ -830,7 +829,7 @@ namespace Marco.Pms.DataAccess.Data
|
||||
Id = Guid.Parse("f76d8215-d399-4f0e-b414-12e427f50be3"),
|
||||
Name = "Bank Passbook",
|
||||
RegexExpression = "^\\d{9,18}$",
|
||||
AllowedContentType = "application/pdf",
|
||||
AllowedContentType = "application/pdf,image/jpeg",
|
||||
MaxSizeAllowedInMB = 2,
|
||||
IsValidationRequired = true,
|
||||
IsMandatory = true,
|
||||
|
5444
Marco.Pms.DataAccess/Migrations/20250828092249_Removed_DocumentAttachmentyMapping_And_Added.Designer.cs
generated
Normal file
5444
Marco.Pms.DataAccess/Migrations/20250828092249_Removed_DocumentAttachmentyMapping_And_Added.Designer.cs
generated
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,285 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Removed_DocumentAttachmentyMapping_And_Added : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "DocumentAttachmentMappings");
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "DocumentDataId",
|
||||
table: "DocumentAttachments",
|
||||
type: "char(36)",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
collation: "ascii_general_ci");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentCategoryMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("2d9fb9cf-db53-476b-a452-492e88e2b51f"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7895));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentCategoryMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("cfbff269-072b-477a-b48b-72cdc57dd1d3"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7887));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("07ca7182-9ac0-4407-b988-59901170cb86"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7995));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("16c40b80-c207-4a0c-a4d3-381414afe35a"),
|
||||
columns: new[] { "AllowedContentType", "CreatedAt" },
|
||||
values: new object[] { "application/pdf,image/jpeg", new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7975) });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("260abd7e-c96d-4ae4-a29b-9b5bb5d24ebd"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7984));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("2d1d7441-46a8-425e-9395-94d0956f8e91"),
|
||||
columns: new[] { "AllowedContentType", "CreatedAt" },
|
||||
values: new object[] { "application/pdf,image/jpeg", new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7971) });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("336225ac-67f3-4e14-ba7a-8fad03cf2832"),
|
||||
columns: new[] { "AllowedContentType", "CreatedAt" },
|
||||
values: new object[] { "application/pdf,image/jpeg", new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7958) });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("5668de00-5d84-47f7-b9b5-7fefd1219f05"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(8008));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("6344393b-9bb1-45f8-b620-9f6e279d012c"),
|
||||
columns: new[] { "AllowedContentType", "CreatedAt" },
|
||||
values: new object[] { "application/pdf,image/jpeg", new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7966) });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("7cc41c91-23cb-442b-badd-f932138d149f"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(8004));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("846e89a9-5735-45ec-a21d-c97f85a94ada"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(8000));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("a1a190ba-c4a8-432f-b26d-1231ca1d44bc"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7991));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("f76d8215-d399-4f0e-b414-12e427f50be3"),
|
||||
columns: new[] { "AllowedContentType", "CreatedAt" },
|
||||
values: new object[] { "application/pdf,image/jpeg", new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7980) });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DocumentAttachments_DocumentDataId",
|
||||
table: "DocumentAttachments",
|
||||
column: "DocumentDataId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_DocumentAttachments_Documents_DocumentDataId",
|
||||
table: "DocumentAttachments",
|
||||
column: "DocumentDataId",
|
||||
principalTable: "Documents",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_DocumentAttachments_Documents_DocumentDataId",
|
||||
table: "DocumentAttachments");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_DocumentAttachments_DocumentDataId",
|
||||
table: "DocumentAttachments");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DocumentDataId",
|
||||
table: "DocumentAttachments");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DocumentAttachmentMappings",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||
AttachmentId = 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_DocumentAttachmentMappings", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_DocumentAttachmentMappings_DocumentAttachments_AttachmentId",
|
||||
column: x => x.AttachmentId,
|
||||
principalTable: "DocumentAttachments",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_DocumentAttachmentMappings_Documents_DocumentId",
|
||||
column: x => x.DocumentId,
|
||||
principalTable: "Documents",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_DocumentAttachmentMappings_Tenants_TenantId",
|
||||
column: x => x.TenantId,
|
||||
principalTable: "Tenants",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentCategoryMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("2d9fb9cf-db53-476b-a452-492e88e2b51f"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8157));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentCategoryMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("cfbff269-072b-477a-b48b-72cdc57dd1d3"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8150));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("07ca7182-9ac0-4407-b988-59901170cb86"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8229));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("16c40b80-c207-4a0c-a4d3-381414afe35a"),
|
||||
columns: new[] { "AllowedContentType", "CreatedAt" },
|
||||
values: new object[] { "application/pdf", new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8216) });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("260abd7e-c96d-4ae4-a29b-9b5bb5d24ebd"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8222));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("2d1d7441-46a8-425e-9395-94d0956f8e91"),
|
||||
columns: new[] { "AllowedContentType", "CreatedAt" },
|
||||
values: new object[] { "application/pdf", new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8212) });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("336225ac-67f3-4e14-ba7a-8fad03cf2832"),
|
||||
columns: new[] { "AllowedContentType", "CreatedAt" },
|
||||
values: new object[] { "application/pdf", new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8204) });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("5668de00-5d84-47f7-b9b5-7fefd1219f05"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8238));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("6344393b-9bb1-45f8-b620-9f6e279d012c"),
|
||||
columns: new[] { "AllowedContentType", "CreatedAt" },
|
||||
values: new object[] { "application/pdf", new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8209) });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("7cc41c91-23cb-442b-badd-f932138d149f"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8235));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("846e89a9-5735-45ec-a21d-c97f85a94ada"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8232));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("a1a190ba-c4a8-432f-b26d-1231ca1d44bc"),
|
||||
column: "CreatedAt",
|
||||
value: new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8226));
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "DocumentTypeMasters",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("f76d8215-d399-4f0e-b414-12e427f50be3"),
|
||||
columns: new[] { "AllowedContentType", "CreatedAt" },
|
||||
values: new object[] { "application/pdf", new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8219) });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DocumentAttachmentMappings_AttachmentId",
|
||||
table: "DocumentAttachmentMappings",
|
||||
column: "AttachmentId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DocumentAttachmentMappings_DocumentId",
|
||||
table: "DocumentAttachmentMappings",
|
||||
column: "DocumentId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DocumentAttachmentMappings_TenantId",
|
||||
table: "DocumentAttachmentMappings",
|
||||
column: "TenantId");
|
||||
}
|
||||
}
|
||||
}
|
@ -833,6 +833,9 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("DocumentDataId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("DocumentId")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
@ -875,6 +878,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DocumentDataId");
|
||||
|
||||
b.HasIndex("DocumentTypeId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
@ -888,32 +893,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.ToTable("DocumentAttachments");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.DocumentManager.DocumentAttachmentMapping", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("AttachmentId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("DocumentId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AttachmentId");
|
||||
|
||||
b.HasIndex("DocumentId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("DocumentAttachmentMappings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.DocumentManager.DocumentCategoryMaster", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@ -949,7 +928,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
new
|
||||
{
|
||||
Id = new Guid("cfbff269-072b-477a-b48b-72cdc57dd1d3"),
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8150),
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7887),
|
||||
Description = "Project documents are formal records that outline the plans, progress, and details necessary to execute and manage a project effectively.",
|
||||
EntityTypeId = new Guid("c8fe7115-aa27-43bc-99f4-7b05fabe436e"),
|
||||
Name = "Project Documents",
|
||||
@ -958,7 +937,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
new
|
||||
{
|
||||
Id = new Guid("2d9fb9cf-db53-476b-a452-492e88e2b51f"),
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8157),
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7895),
|
||||
Description = "Employment details along with legal IDs like passports or driver’s licenses to verify identity and work authorization.",
|
||||
EntityTypeId = new Guid("dbb9555a-7a0c-40f2-a9ed-f0463f1ceed7"),
|
||||
Name = "Employee Documents",
|
||||
@ -1043,8 +1022,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
new
|
||||
{
|
||||
Id = new Guid("336225ac-67f3-4e14-ba7a-8fad03cf2832"),
|
||||
AllowedContentType = "application/pdf",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8204),
|
||||
AllowedContentType = "application/pdf,image/jpeg",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7958),
|
||||
DocumentCategoryId = new Guid("2d9fb9cf-db53-476b-a452-492e88e2b51f"),
|
||||
IsActive = true,
|
||||
IsMandatory = true,
|
||||
@ -1058,8 +1037,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
new
|
||||
{
|
||||
Id = new Guid("6344393b-9bb1-45f8-b620-9f6e279d012c"),
|
||||
AllowedContentType = "application/pdf",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8209),
|
||||
AllowedContentType = "application/pdf,image/jpeg",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7966),
|
||||
DocumentCategoryId = new Guid("2d9fb9cf-db53-476b-a452-492e88e2b51f"),
|
||||
IsActive = true,
|
||||
IsMandatory = true,
|
||||
@ -1073,8 +1052,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
new
|
||||
{
|
||||
Id = new Guid("2d1d7441-46a8-425e-9395-94d0956f8e91"),
|
||||
AllowedContentType = "application/pdf",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8212),
|
||||
AllowedContentType = "application/pdf,image/jpeg",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7971),
|
||||
DocumentCategoryId = new Guid("2d9fb9cf-db53-476b-a452-492e88e2b51f"),
|
||||
IsActive = true,
|
||||
IsMandatory = true,
|
||||
@ -1088,8 +1067,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
new
|
||||
{
|
||||
Id = new Guid("16c40b80-c207-4a0c-a4d3-381414afe35a"),
|
||||
AllowedContentType = "application/pdf",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8216),
|
||||
AllowedContentType = "application/pdf,image/jpeg",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7975),
|
||||
DocumentCategoryId = new Guid("2d9fb9cf-db53-476b-a452-492e88e2b51f"),
|
||||
IsActive = true,
|
||||
IsMandatory = true,
|
||||
@ -1103,8 +1082,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
new
|
||||
{
|
||||
Id = new Guid("f76d8215-d399-4f0e-b414-12e427f50be3"),
|
||||
AllowedContentType = "application/pdf",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8219),
|
||||
AllowedContentType = "application/pdf,image/jpeg",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7980),
|
||||
DocumentCategoryId = new Guid("2d9fb9cf-db53-476b-a452-492e88e2b51f"),
|
||||
IsActive = true,
|
||||
IsMandatory = true,
|
||||
@ -1119,7 +1098,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
Id = new Guid("260abd7e-c96d-4ae4-a29b-9b5bb5d24ebd"),
|
||||
AllowedContentType = "application/pdf,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8222),
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7984),
|
||||
DocumentCategoryId = new Guid("cfbff269-072b-477a-b48b-72cdc57dd1d3"),
|
||||
IsActive = true,
|
||||
IsMandatory = false,
|
||||
@ -1133,7 +1112,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
Id = new Guid("a1a190ba-c4a8-432f-b26d-1231ca1d44bc"),
|
||||
AllowedContentType = "application/pdf,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8226),
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7991),
|
||||
DocumentCategoryId = new Guid("cfbff269-072b-477a-b48b-72cdc57dd1d3"),
|
||||
IsActive = true,
|
||||
IsMandatory = false,
|
||||
@ -1147,7 +1126,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
Id = new Guid("07ca7182-9ac0-4407-b988-59901170cb86"),
|
||||
AllowedContentType = "application/pdf,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8229),
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(7995),
|
||||
DocumentCategoryId = new Guid("cfbff269-072b-477a-b48b-72cdc57dd1d3"),
|
||||
IsActive = true,
|
||||
IsMandatory = false,
|
||||
@ -1161,7 +1140,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
Id = new Guid("846e89a9-5735-45ec-a21d-c97f85a94ada"),
|
||||
AllowedContentType = "application/pdf,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8232),
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(8000),
|
||||
DocumentCategoryId = new Guid("cfbff269-072b-477a-b48b-72cdc57dd1d3"),
|
||||
IsActive = true,
|
||||
IsMandatory = false,
|
||||
@ -1175,7 +1154,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
Id = new Guid("7cc41c91-23cb-442b-badd-f932138d149f"),
|
||||
AllowedContentType = "application/pdf,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8235),
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(8004),
|
||||
DocumentCategoryId = new Guid("cfbff269-072b-477a-b48b-72cdc57dd1d3"),
|
||||
IsActive = true,
|
||||
IsMandatory = false,
|
||||
@ -1189,7 +1168,7 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
Id = new Guid("5668de00-5d84-47f7-b9b5-7fefd1219f05"),
|
||||
AllowedContentType = "application/pdf,image/vnd.dwg,application/acad",
|
||||
CreatedAt = new DateTime(2025, 8, 28, 5, 50, 2, 39, DateTimeKind.Utc).AddTicks(8238),
|
||||
CreatedAt = new DateTime(2025, 8, 28, 9, 22, 46, 902, DateTimeKind.Utc).AddTicks(8008),
|
||||
DocumentCategoryId = new Guid("cfbff269-072b-477a-b48b-72cdc57dd1d3"),
|
||||
IsActive = true,
|
||||
IsMandatory = false,
|
||||
@ -4594,6 +4573,12 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.DocumentManager.DocumentAttachment", b =>
|
||||
{
|
||||
b.HasOne("Marco.Pms.Model.DocumentManager.Document", "Document")
|
||||
.WithMany()
|
||||
.HasForeignKey("DocumentDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.DocumentManager.DocumentTypeMaster", "DocumentType")
|
||||
.WithMany()
|
||||
.HasForeignKey("DocumentTypeId")
|
||||
@ -4620,6 +4605,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.WithMany()
|
||||
.HasForeignKey("VerifiedById");
|
||||
|
||||
b.Navigation("Document");
|
||||
|
||||
b.Navigation("DocumentType");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
@ -4631,33 +4618,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Navigation("VerifiedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.DocumentManager.DocumentAttachmentMapping", b =>
|
||||
{
|
||||
b.HasOne("Marco.Pms.Model.DocumentManager.DocumentAttachment", "Attachment")
|
||||
.WithMany()
|
||||
.HasForeignKey("AttachmentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.DocumentManager.Document", "Document")
|
||||
.WithMany()
|
||||
.HasForeignKey("DocumentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Attachment");
|
||||
|
||||
b.Navigation("Document");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.DocumentManager.DocumentCategoryMaster", b =>
|
||||
{
|
||||
b.HasOne("Marco.Pms.Model.Master.EntityTypeMaster", "EntityTypeMaster")
|
||||
|
@ -36,6 +36,12 @@ namespace Marco.Pms.Model.DocumentManager
|
||||
[ValidateNever]
|
||||
[ForeignKey("DocumentTypeId")]
|
||||
public DocumentTypeMaster? DocumentType { get; set; }
|
||||
|
||||
public Guid DocumentDataId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("DocumentDataId")]
|
||||
public Document? Document { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,7 @@ namespace Marco.Pms.Model.DocumentManager
|
||||
public class DocumentAttachmentMapping : TenantRelation
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid DocumentId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("DocumentId")]
|
||||
public Document? Document { get; set; }
|
||||
|
||||
public Guid AttachmentId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
|
@ -39,6 +39,12 @@
|
||||
public static readonly Guid ExpenseApprove = Guid.Parse("eaafdd76-8aac-45f9-a530-315589c6deca");
|
||||
public static readonly Guid ExpenseProcess = Guid.Parse("ea5a1529-4ee8-4828-80ea-0e23c9d4dd11");
|
||||
public static readonly Guid ExpenseManage = Guid.Parse("bdee29a2-b73b-402d-8dd1-c4b1f81ccbc3");
|
||||
|
||||
public static readonly Guid ViewDocument = Guid.Parse("71189504-f1c8-4ca5-8db6-810497be2854");
|
||||
public static readonly Guid UploadDocument = Guid.Parse("3f6d1f67-6fa5-4b7c-b17b-018d4fe4aab8");
|
||||
public static readonly Guid MofifyDocument = Guid.Parse("c423fd81-6273-4b9d-bb5e-76a0fb343833");
|
||||
public static readonly Guid DeleteDocument = Guid.Parse("40863a13-5a66-469d-9b48-135bc5dbf486");
|
||||
public static readonly Guid VerifyDocument = Guid.Parse("13a1f30f-38d1-41bf-8e7a-b75189aab8e0");
|
||||
}
|
||||
}
|
||||
|
||||
|
557
Marco.Pms.Services/Controllers/DocumentController.cs
Normal file
557
Marco.Pms.Services/Controllers/DocumentController.cs
Normal file
@ -0,0 +1,557 @@
|
||||
using AutoMapper;
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.Model.DocumentManager;
|
||||
using Marco.Pms.Model.Dtos.DocumentManager;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Model.Filters;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.DocumentManager;
|
||||
using Marco.Pms.Services.Service;
|
||||
using MarcoBMS.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using Document = Marco.Pms.Model.DocumentManager.Document;
|
||||
|
||||
namespace Marco.Pms.Services.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
public class DocumentController : ControllerBase
|
||||
{
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
|
||||
private readonly IServiceScopeFactory _serviceScope;
|
||||
private readonly UserHelper _userHelper;
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly Guid tenantId;
|
||||
|
||||
private static readonly Guid ProjectEntity = Guid.Parse("c8fe7115-aa27-43bc-99f4-7b05fabe436e");
|
||||
private static readonly Guid EmployeeEntity = Guid.Parse("dbb9555a-7a0c-40f2-a9ed-f0463f1ceed7");
|
||||
|
||||
public DocumentController(IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||
IServiceScopeFactory serviceScope,
|
||||
UserHelper userHelper,
|
||||
ILoggingService logger,
|
||||
IMapper mapper)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
|
||||
_serviceScope = serviceScope ?? throw new ArgumentNullException(nameof(serviceScope));
|
||||
_userHelper = userHelper ?? throw new ArgumentNullException(nameof(userHelper));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
||||
tenantId = userHelper.GetTenantId();
|
||||
}
|
||||
// GET: api/<DocumentController>
|
||||
[HttpGet("list/{entityTypeId}")]
|
||||
public async Task<IActionResult> Get(Guid entityTypeId, [FromQuery] Guid entityId, [FromQuery] string filter,
|
||||
[FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20)
|
||||
{
|
||||
using var scope = _serviceScope.CreateScope();
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
var isViewPermission = await _permission.HasPermission(PermissionsMaster.ViewDocument, loggedInEmployee.Id);
|
||||
|
||||
if (!isViewPermission)
|
||||
{
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to upload document", 403));
|
||||
}
|
||||
if (ProjectEntity != entityTypeId && EmployeeEntity != entityTypeId)
|
||||
{
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Entity type not found", "Entity Type not found in database", 404));
|
||||
}
|
||||
if (ProjectEntity == entityTypeId)
|
||||
{
|
||||
var isHasProjectPermission = await _permission.HasProjectPermission(loggedInEmployee, entityId);
|
||||
if (!isHasProjectPermission)
|
||||
{
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to upload document to this project", 403));
|
||||
}
|
||||
}
|
||||
else if (EmployeeEntity == entityTypeId)
|
||||
{
|
||||
var isEmployeeExists = await _context.Employees.AnyAsync(e => e.Id == entityId && e.TenantId == tenantId);
|
||||
if (!isEmployeeExists)
|
||||
{
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Employee not found", "Employee not found in database", 404));
|
||||
}
|
||||
}
|
||||
|
||||
var documentQuery = _context.DocumentAttachments
|
||||
.Include(da => da.DocumentType)
|
||||
.ThenInclude(dt => dt!.DocumentCategory)
|
||||
.Where(da => da.DocumentType != null &&
|
||||
da.DocumentType.DocumentCategory != null &&
|
||||
da.DocumentType.DocumentCategory.EntityTypeId == entityTypeId);
|
||||
|
||||
var documents = await documentQuery
|
||||
.OrderByDescending(t => t.UploadedAt)
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(documents, "Document list fetched successfully", 200));
|
||||
}
|
||||
|
||||
// GET api/<DocumentController>/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task Get(int id)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
//string preSignedUrl = _s3Service.GeneratePreSignedUrl(objectKey);
|
||||
}
|
||||
|
||||
// POST api/<DocumentController>
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Post([FromBody] DocumentAttachmentDto model)
|
||||
{
|
||||
using var scope = _serviceScope.CreateScope();
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
var isUploadPermission = await _permission.HasPermission(PermissionsMaster.UploadDocument, loggedInEmployee.Id);
|
||||
|
||||
if (!isUploadPermission || loggedInEmployee.Id != model.EntityId)
|
||||
{
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to upload document", 403));
|
||||
}
|
||||
|
||||
var documentType = await _context.DocumentTypeMasters
|
||||
.Include(dt => dt.DocumentCategory)
|
||||
.FirstOrDefaultAsync(dt => dt.Id == model.DocumentTypeId && dt.TenantId == tenantId && dt.DocumentCategory != null);
|
||||
|
||||
if (documentType == null)
|
||||
{
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Document Type not found in database", "Document Type not found in database", 404));
|
||||
}
|
||||
if (documentType.IsMandatory && string.IsNullOrWhiteSpace(model.DocumentId))
|
||||
{
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Document ID is missing", "User must provide the document ID fro this document", 400));
|
||||
}
|
||||
if (documentType.IsValidationRequired && !string.IsNullOrWhiteSpace(model.DocumentId))
|
||||
{
|
||||
bool isValid = Regex.IsMatch(model.DocumentId, documentType.RegexExpression ?? "");
|
||||
if (!isValid)
|
||||
{
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invaid Document ID", "Provided document ID is not valid", 400));
|
||||
}
|
||||
}
|
||||
|
||||
var employeeExistTask = Task.Run(async () =>
|
||||
{
|
||||
if (documentType.DocumentCategory!.EntityTypeId == EmployeeEntity)
|
||||
{
|
||||
await using var _dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await _dbContext.Employees.AnyAsync(e => e.Id == model.EntityId && e.TenantId == tenantId);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
var projectExistTask = Task.Run(async () =>
|
||||
{
|
||||
if (documentType.DocumentCategory!.EntityTypeId == ProjectEntity)
|
||||
{
|
||||
await using var _dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
return await _dbContext.Projects.AnyAsync(p => p.Id == model.EntityId && p.TenantId == tenantId);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
await Task.WhenAll(employeeExistTask, projectExistTask);
|
||||
|
||||
var employeeExist = employeeExistTask.Result;
|
||||
var projectExist = projectExistTask.Result;
|
||||
if (documentType.DocumentCategory!.EntityTypeId == EmployeeEntity && !employeeExist)
|
||||
{
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Employee Not Found", "Employee not found in database", 404));
|
||||
}
|
||||
if (documentType.DocumentCategory.EntityTypeId == ProjectEntity && !projectExist)
|
||||
{
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Project Not Found", "Project not found in database", 404));
|
||||
}
|
||||
|
||||
var documentDetails = _mapper.Map<DocumentAttachment>(model);
|
||||
documentDetails.UploadedAt = DateTime.UtcNow;
|
||||
documentDetails.UploadedById = loggedInEmployee.Id;
|
||||
documentDetails.TenantId = tenantId;
|
||||
|
||||
var _s3Service = scope.ServiceProvider.GetRequiredService<S3UploadService>();
|
||||
var batchId = Guid.NewGuid();
|
||||
List<Document> documents = new List<Document>();
|
||||
if (model.Attachment.FileSize > documentType.MaxSizeAllowedInMB)
|
||||
{
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("File size limit exceeded",
|
||||
$"File size exceeded. Maximum allowed is {documentType.MaxSizeAllowedInMB} MB.", 400));
|
||||
}
|
||||
|
||||
|
||||
string base64 = model.Attachment.Base64Data?.Split(',').LastOrDefault() ?? "";
|
||||
if (string.IsNullOrWhiteSpace(base64))
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Image data missing", 400));
|
||||
|
||||
var fileType = _s3Service.GetContentTypeFromBase64(base64);
|
||||
|
||||
var validContentType = documentType.AllowedContentType.Split(',').ToList();
|
||||
if (!validContentType.Contains(fileType))
|
||||
{
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Unsupported file type.",
|
||||
$"Unsupported file type. {fileType}", 400));
|
||||
}
|
||||
|
||||
string? fileName = null;
|
||||
string? objectKey = null;
|
||||
if (documentType.DocumentCategory!.EntityTypeId == EmployeeEntity)
|
||||
{
|
||||
fileName = _s3Service.GenerateFileName(fileType, tenantId, "EmployeeDocuments");
|
||||
objectKey = $"tenant-{tenantId}/Employee/{model.EntityId}/EmployeeDocuments/{fileName}";
|
||||
}
|
||||
else if (documentType.DocumentCategory!.EntityTypeId == ProjectEntity)
|
||||
{
|
||||
fileName = _s3Service.GenerateFileName(fileType, tenantId, "ProjectDocuments");
|
||||
objectKey = $"tenant-{tenantId}/project-{model.EntityId}/ProjectDocuments/{fileName}";
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(objectKey) && !string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
var _s3UploadService = scope.ServiceProvider.GetRequiredService<S3UploadService>();
|
||||
var _threadLogger = scope.ServiceProvider.GetRequiredService<ILoggingService>();
|
||||
|
||||
await _s3UploadService.UploadFileAsync(base64, fileType, objectKey!);
|
||||
|
||||
_threadLogger.LogInfo("File stored successfully {ObjectKey}", objectKey!);
|
||||
});
|
||||
|
||||
Document document = new Document
|
||||
{
|
||||
BatchId = batchId,
|
||||
UploadedById = loggedInEmployee.Id,
|
||||
FileName = model.Attachment.FileName ?? fileName,
|
||||
ContentType = model.Attachment.ContentType,
|
||||
S3Key = objectKey,
|
||||
FileSize = model.Attachment.FileSize,
|
||||
UploadedAt = DateTime.UtcNow,
|
||||
TenantId = tenantId
|
||||
};
|
||||
_context.Documents.Add(document);
|
||||
|
||||
documentDetails.DocumentDataId = document.Id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
_context.DocumentAttachments.Add(documentDetails);
|
||||
|
||||
if (model.Tags != null && model.Tags.Any())
|
||||
{
|
||||
var names = model.Tags.Select(t => t.Name).ToList();
|
||||
var existingTags = await _context.DocumentTagMasters.Where(t => names.Contains(t.Name) && t.TenantId == tenantId).ToListAsync();
|
||||
List<AttachmentTagMapping> attachmentTagMappings = new List<AttachmentTagMapping>();
|
||||
foreach (var tag in model.Tags)
|
||||
{
|
||||
var existingTag = existingTags.FirstOrDefault(t => t.Name == tag.Name);
|
||||
if (existingTag != null && tag.IsActive)
|
||||
{
|
||||
AttachmentTagMapping attachmentTagMapping = new AttachmentTagMapping
|
||||
{
|
||||
DocumentTagId = existingTag.Id,
|
||||
AttachmentId = documentDetails.Id,
|
||||
TenantId = tenantId
|
||||
};
|
||||
attachmentTagMappings.Add(attachmentTagMapping);
|
||||
}
|
||||
else if (existingTag == null && tag.IsActive)
|
||||
{
|
||||
var newTag = new DocumentTagMaster
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = tag.Name,
|
||||
Description = tag.Name,
|
||||
TenantId = tenantId
|
||||
};
|
||||
_context.DocumentTagMasters.Add(newTag);
|
||||
AttachmentTagMapping attachmentTagMapping = new AttachmentTagMapping
|
||||
{
|
||||
DocumentTagId = newTag.Id,
|
||||
AttachmentId = documentDetails.Id,
|
||||
TenantId = tenantId
|
||||
};
|
||||
attachmentTagMappings.Add(attachmentTagMapping);
|
||||
}
|
||||
}
|
||||
_context.AttachmentTagMappings.AddRange(attachmentTagMappings);
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
var response = _mapper.Map<DocumentListVM>(documentDetails);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Document Added Successfully", 200));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uploads a document attachment for an Employee or Project.
|
||||
/// Validates permissions, document type, entity existence, tags, and uploads to S3.
|
||||
/// </summary>
|
||||
[HttpPost("upload")]
|
||||
public async Task<IActionResult> UploadDocument([FromBody] DocumentAttachmentDto model)
|
||||
{
|
||||
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScope.CreateScope();
|
||||
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILoggingService>();
|
||||
logger.LogInfo("Document upload initiated for EntityId: {EntityId}, DocumentTypeId: {DocumentTypeId}", model.EntityId, model.DocumentTypeId);
|
||||
|
||||
try
|
||||
{
|
||||
// Get logged in user
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
// Permission check
|
||||
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
var hasUploadPermission = await permissionService.HasPermission(PermissionsMaster.UploadDocument, loggedInEmployee.Id);
|
||||
|
||||
if (!hasUploadPermission && loggedInEmployee.Id != model.EntityId)
|
||||
{
|
||||
logger.LogWarning("Access Denied. User {UserId} tried to upload document for {EntityId}", loggedInEmployee.Id, model.EntityId);
|
||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to upload this document", 403));
|
||||
}
|
||||
|
||||
// Validate Document Type
|
||||
var documentType = await dbContext.DocumentTypeMasters
|
||||
.Include(dt => dt.DocumentCategory)
|
||||
.FirstOrDefaultAsync(dt => dt.Id == model.DocumentTypeId && dt.TenantId == tenantId && dt.DocumentCategory != null);
|
||||
|
||||
if (documentType == null)
|
||||
{
|
||||
logger.LogWarning("DocumentTypeId {DocumentTypeId} not found for Tenant {TenantId}", model.DocumentTypeId, tenantId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Document Type not found", "Document Type not found in database", 404));
|
||||
}
|
||||
|
||||
// Document ID validation
|
||||
if (documentType.IsMandatory && string.IsNullOrWhiteSpace(model.DocumentId))
|
||||
{
|
||||
logger.LogWarning("Mandatory DocumentId missing for DocumentTypeId: {DocumentTypeId}", documentType.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Document ID missing", "User must provide the document ID for this document", 400));
|
||||
}
|
||||
|
||||
if (documentType.IsValidationRequired && !string.IsNullOrWhiteSpace(model.DocumentId))
|
||||
{
|
||||
if (!Regex.IsMatch(model.DocumentId, documentType.RegexExpression ?? ""))
|
||||
{
|
||||
logger.LogWarning("Invalid DocumentId format for DocumentTypeId: {DocumentTypeId}, Provided: {DocumentId}", documentType.Id, model.DocumentId);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Document ID", "Provided document ID is not valid", 400));
|
||||
}
|
||||
}
|
||||
|
||||
// Verify if Employee/Project exists
|
||||
var entityType = documentType.DocumentCategory!.EntityTypeId;
|
||||
|
||||
bool entityExists = false;
|
||||
if (entityType.Equals(EmployeeEntity))
|
||||
{
|
||||
entityExists = await dbContext.Employees.AnyAsync(e => e.Id == model.EntityId && e.TenantId == tenantId);
|
||||
}
|
||||
else if (entityType.Equals(ProjectEntity))
|
||||
{
|
||||
entityExists = await dbContext.Projects.AnyAsync(p => p.Id == model.EntityId && p.TenantId == tenantId);
|
||||
}
|
||||
|
||||
if (!entityExists)
|
||||
{
|
||||
logger.LogWarning("Entity not found. EntityType: {EntityType}, EntityId: {EntityId}", entityType, model.EntityId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse($"{(entityType == EmployeeEntity ? "Employee" : "Project")} Not Found", "Entity not found in database", 404));
|
||||
}
|
||||
|
||||
// Map DTO to DB entity
|
||||
var attachment = _mapper.Map<DocumentAttachment>(model);
|
||||
attachment.UploadedAt = DateTime.UtcNow;
|
||||
attachment.UploadedById = loggedInEmployee.Id;
|
||||
attachment.TenantId = tenantId;
|
||||
|
||||
// Validate Attachment
|
||||
if (model.Attachment.FileSize > documentType.MaxSizeAllowedInMB)
|
||||
{
|
||||
logger.LogWarning("File size {FileSize} exceeded max allowed {MaxSize}MB", model.Attachment.FileSize, documentType.MaxSizeAllowedInMB);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("File size limit exceeded", $"Max allowed {documentType.MaxSizeAllowedInMB} MB.", 400));
|
||||
}
|
||||
|
||||
string base64 = model.Attachment.Base64Data?.Split(',').LastOrDefault() ?? "";
|
||||
if (string.IsNullOrWhiteSpace(base64))
|
||||
{
|
||||
logger.LogWarning("Missing Base64 data for attachment.");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data missing", "File data required", 400));
|
||||
}
|
||||
|
||||
var s3Service = scope.ServiceProvider.GetRequiredService<S3UploadService>();
|
||||
var fileType = s3Service.GetContentTypeFromBase64(base64);
|
||||
|
||||
var validContentTypes = documentType.AllowedContentType.Split(',').ToList();
|
||||
if (!validContentTypes.Contains(fileType))
|
||||
{
|
||||
logger.LogWarning("Unsupported file type {FileType} for DocumentType {DocumentTypeId}", fileType, documentType.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Unsupported file type", $"Unsupported file type: {fileType}", 400));
|
||||
}
|
||||
|
||||
// Generate S3 ObjectKey/FileName
|
||||
string folderName = entityType == EmployeeEntity ? "EmployeeDocuments" : "ProjectDocuments";
|
||||
string fileName = s3Service.GenerateFileName(fileType, tenantId, folderName);
|
||||
string objectKey = entityType == EmployeeEntity
|
||||
? $"tenant-{tenantId}/Employee/{model.EntityId}/{folderName}/{fileName}"
|
||||
: $"tenant-{tenantId}/project-{model.EntityId}/{folderName}/{fileName}";
|
||||
|
||||
// Fire-and-forget upload
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await s3Service.UploadFileAsync(base64, fileType, objectKey);
|
||||
logger.LogInfo("File uploaded successfully to S3: {ObjectKey}", objectKey);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "S3 upload failed for {ObjectKey}.", objectKey);
|
||||
}
|
||||
});
|
||||
|
||||
// Create Document record
|
||||
var document = new Document
|
||||
{
|
||||
BatchId = Guid.NewGuid(),
|
||||
UploadedById = loggedInEmployee.Id,
|
||||
FileName = model.Attachment.FileName ?? fileName,
|
||||
ContentType = model.Attachment.ContentType ?? fileType,
|
||||
S3Key = objectKey,
|
||||
FileSize = model.Attachment.FileSize,
|
||||
UploadedAt = DateTime.UtcNow,
|
||||
TenantId = tenantId
|
||||
};
|
||||
|
||||
dbContext.Documents.Add(document);
|
||||
|
||||
attachment.DocumentDataId = document.Id;
|
||||
|
||||
dbContext.DocumentAttachments.Add(attachment);
|
||||
|
||||
// Process Tags
|
||||
if (model.Tags?.Any() == true)
|
||||
{
|
||||
var names = model.Tags.Select(t => t.Name).ToList();
|
||||
var existingTags = await dbContext.DocumentTagMasters
|
||||
.Where(t => names.Contains(t.Name) && t.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
|
||||
var attachmentTagMappings = new List<AttachmentTagMapping>();
|
||||
|
||||
foreach (var tag in model.Tags.Where(t => t.IsActive))
|
||||
{
|
||||
var existingTag = existingTags.FirstOrDefault(t => t.Name == tag.Name);
|
||||
var tagEntity = existingTag ?? new DocumentTagMaster
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = tag.Name,
|
||||
Description = tag.Name,
|
||||
TenantId = tenantId
|
||||
};
|
||||
|
||||
if (existingTag == null)
|
||||
{
|
||||
dbContext.DocumentTagMasters.Add(tagEntity);
|
||||
}
|
||||
|
||||
attachmentTagMappings.Add(new AttachmentTagMapping
|
||||
{
|
||||
DocumentTagId = tagEntity.Id,
|
||||
AttachmentId = attachment.Id,
|
||||
TenantId = tenantId
|
||||
});
|
||||
}
|
||||
|
||||
dbContext.AttachmentTagMappings.AddRange(attachmentTagMappings);
|
||||
}
|
||||
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
logger.LogInfo("Document uploaded successfully. AttachmentId: {AttachmentId}, DocumentId: {DocumentId}", attachment.Id, document.Id);
|
||||
|
||||
var response = _mapper.Map<DocumentListVM>(attachment);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Document added successfully", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Unexpected error during document upload.");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An error occurred while uploading the document", 500));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// PUT api/<DocumentController>/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task Put(int id, [FromBody] string value)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
}
|
||||
|
||||
// DELETE api/<DocumentController>/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task Delete(int id)
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the filter string, handling multiple potential formats (e.g., direct JSON vs. escaped JSON string).
|
||||
/// </summary>
|
||||
/// <param name="filter">The JSON filter string from the request.</param>
|
||||
/// <returns>An <see cref="TenantFilter"/> object or null if deserialization fails.</returns>
|
||||
|
||||
private DocumentFilter? TryDeserializeFilter(string? filter)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filter))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
||||
DocumentFilter? documentFilter = null;
|
||||
|
||||
try
|
||||
{
|
||||
// First, try to deserialize directly. This is the expected case (e.g., from a web client).
|
||||
documentFilter = JsonSerializer.Deserialize<DocumentFilter>(filter, options);
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
_logger.LogError(ex, "[{MethodName}] Failed to directly deserialize filter. Attempting to unescape and re-parse. Filter: {Filter}", nameof(TryDeserializeFilter), filter);
|
||||
|
||||
// If direct deserialization fails, it might be an escaped string (common with tools like Postman or some mobile clients).
|
||||
try
|
||||
{
|
||||
// Unescape the string first, then deserialize the result.
|
||||
string unescapedJsonString = JsonSerializer.Deserialize<string>(filter, options) ?? "";
|
||||
if (!string.IsNullOrWhiteSpace(unescapedJsonString))
|
||||
{
|
||||
documentFilter = JsonSerializer.Deserialize<DocumentFilter>(unescapedJsonString, options);
|
||||
}
|
||||
}
|
||||
catch (JsonException ex1)
|
||||
{
|
||||
// If both attempts fail, log the final error and return null.
|
||||
_logger.LogError(ex1, "[{MethodName}] All attempts to deserialize the filter failed. Filter will be ignored. Filter: {Filter}", nameof(TryDeserializeFilter), filter);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return documentFilter;
|
||||
}
|
||||
}
|
||||
}
|
@ -309,7 +309,7 @@ namespace Marco.Pms.Services.MappingProfiles
|
||||
opt => opt.MapFrom(src => Guid.Parse(src.DocumentId)));
|
||||
|
||||
CreateMap<DocumentAttachmentDto, DocumentAttachment>();
|
||||
CreateMap<DocumentAttachmentDto, DocumentAttachment>();
|
||||
CreateMap<DocumentAttachment, DocumentListVM>();
|
||||
CreateMap<DocumentAttachmentDto, DocumentAttachment>();
|
||||
CreateMap<DocumentAttachmentDto, DocumentAttachment>();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user