Merge pull request 'Issues_June_3W' (#99) from Issues_June_3W into main

Reviewed-on: #99
This commit is contained in:
ashutosh.nehete 2025-07-01 10:25:44 +00:00
commit 65da812a97
19 changed files with 10838 additions and 63 deletions

View File

@ -511,14 +511,14 @@ namespace Marco.Pms.DataAccess.Data
modelBuilder.Entity<Feature>().HasData(
new Feature { Id = new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"), Description = "Manage Project", Name = "Manage Project", ModuleId = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"), IsActive = true },
new Feature { Id = new Guid("9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c"), Description = "Manage Infra", Name = "Manage Infra", ModuleId = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"), IsActive = true },
new Feature { Id = new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"), Description = "Manage Project", Name = "Project Management", ModuleId = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"), IsActive = true },
//new Feature { Id = new Guid("9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c"), Description = "Manage Infra", Name = "Manage Infra", ModuleId = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"), IsActive = true },
new Feature { Id = new Guid("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"), Description = "Manage Tasks", Name = "Task Management", ModuleId = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"), IsActive = true },
new Feature { Id = new Guid("81ab8a87-8ccd-4015-a917-0627cee6a100"), Description = "Manage Employee", Name = "Employee Management", ModuleId = new Guid("2a231490-bcb1-4bdd-91f1-f25fb7f25b23"), IsActive = true },
new Feature { Id = new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), Description = "Attendance", Name = "Attendance", ModuleId = new Guid("2a231490-bcb1-4bdd-91f1-f25fb7f25b23"), IsActive = true },
new Feature { Id = new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), Description = "Attendance", Name = "Attendance Management", ModuleId = new Guid("2a231490-bcb1-4bdd-91f1-f25fb7f25b23"), IsActive = true },
new Feature { Id = new Guid("be3b3afc-6ccf-4566-b9b6-aafcb65546be"), Description = "Global Masters", Name = "Global Masters", ModuleId = new Guid("c43db8c7-ab73-47f4-9d3b-f83e81357924"), IsActive = true },
new Feature { Id = new Guid("be3b3afc-6ccf-4566-b9b6-aafcb65546be"), Description = "Global Masters", Name = "Masters", ModuleId = new Guid("c43db8c7-ab73-47f4-9d3b-f83e81357924"), IsActive = true },
new Feature { Id = new Guid("39e66f81-efc6-446c-95bd-46bff6cfb606"), Description = "Managing all directory related rights", Name = "Directory Management", ModuleId = new Guid("c43db8c7-ab73-47f4-9d3b-f83e81357924"), IsActive = true }
//new Feature { Id = new Guid("660131a4-788c-4739-a082-cbbf7879cbf2"), Description = "Tenant Masters", Name = "Tenant Masters", ModuleId = new Guid("c43db8c7-ab73-47f4-9d3b-f83e81357924"), IsActive = true }
@ -528,9 +528,8 @@ namespace Marco.Pms.DataAccess.Data
new FeaturePermission { Id = new Guid("6ea44136-987e-44ba-9e5d-1cf8f5837ebc"), FeatureId = new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"), IsEnabled = true, Name = "View Project", Description = "Access all information related to the project." },
new FeaturePermission { Id = new Guid("172fc9b6-755b-4f62-ab26-55c34a330614"), FeatureId = new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"), IsEnabled = true, Name = "Manage Project", Description = "Potentially edit the project name, description, start/end dates, or status." },
new FeaturePermission { Id = new Guid("b94802ce-0689-4643-9e1d-11c86950c35b"), FeatureId = new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"), IsEnabled = true, Name = "Manage Team", Description = "The \"Manage Team\" feature allows authorized users to organize project personnel by adding, removing, and assigning employee to projects." },
new FeaturePermission { Id = new Guid("c7b68e33-72f0-474f-bd96-77636427ecc8"), FeatureId = new Guid("9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c"), IsEnabled = true, Name = "View Project Infra", Description = "Grants a user comprehensive read-only access to all details concerning the project's underlying systems, technologies, resources, and configurations" },
new FeaturePermission { Id = new Guid("f2aee20a-b754-4537-8166-f9507b44585b"), FeatureId = new Guid("9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c"), IsEnabled = true, Name = "Manage Project Infra", Description = "This allows them to create, modify, and manage all aspects of the supporting infrastructure." },
new FeaturePermission { Id = new Guid("8d7cc6e3-9147-41f7-aaa7-fa507e450bd4"), FeatureId = new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"), IsEnabled = true, Name = "View Project Infra", Description = "Grants a user comprehensive read-only access to all details concerning the project's underlying systems, technologies, resources, and configurations" },
new FeaturePermission { Id = new Guid("cf2825ad-453b-46aa-91d9-27c124d63373"), FeatureId = new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"), IsEnabled = true, Name = "Manage Project Infra", Description = "This allows them to create, modify, and manage all aspects of the supporting infrastructure." },
new FeaturePermission { Id = new Guid("9fcc5f87-25e3-4846-90ac-67a71ab92e3c"), FeatureId = new Guid("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"), IsEnabled = true, Name = "View Task", Description = "Grants a user comprehensive read-only access to all details associated with tasks within a project. This includes task descriptions, statuses, assignees, due dates, dependencies, progress, history, and any related attachments or discussions." },
@ -539,7 +538,8 @@ namespace Marco.Pms.DataAccess.Data
new FeaturePermission { Id = new Guid("db4e40c5-2ba9-4b6d-b8a6-a16a250ff99c"), FeatureId = new Guid("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"), IsEnabled = true, Name = "Approve Task", Description = "Grants a user the authority to officially confirm the completion or acceptance of a task, often signifying that it meets the required standards or criteria" },
new FeaturePermission { Id = new Guid("b82d2b7e-0d52-45f3-997b-c008ea460e7f"), FeatureId = new Guid("81ab8a87-8ccd-4015-a917-0627cee6a100"), IsEnabled = true, Name = "View Employee", Description = "Grants a user read-only access to details about the individuals within the system. This typically includes names, contact information, roles, departments, and potentially other relevant employee data" },
new FeaturePermission { Id = new Guid("60611762-7f8a-4fb5-b53f-b1139918796b"), FeatureId = new Guid("81ab8a87-8ccd-4015-a917-0627cee6a100"), IsEnabled = true, Name = "View All Employees", Description = "Grants a user read-only access to details about the all individuals within the system. This typically includes names, contact information, roles, departments, and potentially other relevant employee data" },
new FeaturePermission { Id = new Guid("b82d2b7e-0d52-45f3-997b-c008ea460e7f"), FeatureId = new Guid("81ab8a87-8ccd-4015-a917-0627cee6a100"), IsEnabled = true, Name = "View Team Members", Description = "Grants a user read-only access to details about the individuals within the system which are is assigned to same projects as user. This typically includes names, contact information, roles, departments, and potentially other relevant employee data" },
new FeaturePermission { Id = new Guid("a97d366a-c2bb-448d-be93-402bd2324566"), FeatureId = new Guid("81ab8a87-8ccd-4015-a917-0627cee6a100"), IsEnabled = true, Name = "Add/Edit Employee", Description = "Grants a user the authority to create new employee profiles and modify existing employee details within the system. This typically includes adding or updating information such as names, contact details, roles, departments, skills, and potentially other personal or professional data" },
new FeaturePermission { Id = new Guid("fbd213e0-0250-46f1-9f5f-4b2a1e6e76a3"), FeatureId = new Guid("81ab8a87-8ccd-4015-a917-0627cee6a100"), IsEnabled = true, Name = "Assign Roles", Description = "Grants a user the authority to manage employee application roles, enabling them to assign or revoke access privileges within the system." },

View File

@ -0,0 +1,101 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_UpdatedBy_In_Contacts_And_ContactNotes_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "UpdatedAt",
table: "Contacts",
type: "datetime(6)",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "UpdatedById",
table: "Contacts",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci");
migrationBuilder.AddColumn<DateTime>(
name: "UpdatedAt",
table: "ContactNotes",
type: "datetime(6)",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "UpdatedById",
table: "ContactNotes",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci");
migrationBuilder.CreateIndex(
name: "IX_Contacts_UpdatedById",
table: "Contacts",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_ContactNotes_UpdatedById",
table: "ContactNotes",
column: "UpdatedById");
migrationBuilder.AddForeignKey(
name: "FK_ContactNotes_Employees_UpdatedById",
table: "ContactNotes",
column: "UpdatedById",
principalTable: "Employees",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Contacts_Employees_UpdatedById",
table: "Contacts",
column: "UpdatedById",
principalTable: "Employees",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_ContactNotes_Employees_UpdatedById",
table: "ContactNotes");
migrationBuilder.DropForeignKey(
name: "FK_Contacts_Employees_UpdatedById",
table: "Contacts");
migrationBuilder.DropIndex(
name: "IX_Contacts_UpdatedById",
table: "Contacts");
migrationBuilder.DropIndex(
name: "IX_ContactNotes_UpdatedById",
table: "ContactNotes");
migrationBuilder.DropColumn(
name: "UpdatedAt",
table: "Contacts");
migrationBuilder.DropColumn(
name: "UpdatedById",
table: "Contacts");
migrationBuilder.DropColumn(
name: "UpdatedAt",
table: "ContactNotes");
migrationBuilder.DropColumn(
name: "UpdatedById",
table: "ContactNotes");
}
}
}

View File

@ -0,0 +1,131 @@
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_New_Feature_Permissiom_View_All_Employee : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("c7b68e33-72f0-474f-bd96-77636427ecc8"));
migrationBuilder.DeleteData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("f2aee20a-b754-4537-8166-f9507b44585b"));
migrationBuilder.DeleteData(
table: "Features",
keyColumn: "Id",
keyValue: new Guid("9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c"));
migrationBuilder.UpdateData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("b82d2b7e-0d52-45f3-997b-c008ea460e7f"),
column: "Description",
value: "Grants a user read-only access to details about the individuals within the system which are is assigned to same projects as user. This typically includes names, contact information, roles, departments, and potentially other relevant employee data");
migrationBuilder.InsertData(
table: "FeaturePermissions",
columns: new[] { "Id", "Description", "FeatureId", "IsEnabled", "Name" },
values: new object[,]
{
{ new Guid("60611762-7f8a-4fb5-b53f-b1139918796b"), "Grants a user read-only access to details about the all individuals within the system. This typically includes names, contact information, roles, departments, and potentially other relevant employee data", new Guid("81ab8a87-8ccd-4015-a917-0627cee6a100"), true, "View All Employee" },
{ new Guid("8d7cc6e3-9147-41f7-aaa7-fa507e450bd4"), "Grants a user comprehensive read-only access to all details concerning the project's underlying systems, technologies, resources, and configurations", new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"), true, "View Project Infra" },
{ new Guid("cf2825ad-453b-46aa-91d9-27c124d63373"), "This allows them to create, modify, and manage all aspects of the supporting infrastructure.", new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"), true, "Manage Project Infra" }
});
migrationBuilder.UpdateData(
table: "Features",
keyColumn: "Id",
keyValue: new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"),
column: "Name",
value: "Attendance Management");
migrationBuilder.UpdateData(
table: "Features",
keyColumn: "Id",
keyValue: new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"),
column: "Name",
value: "Project Management");
migrationBuilder.UpdateData(
table: "Features",
keyColumn: "Id",
keyValue: new Guid("be3b3afc-6ccf-4566-b9b6-aafcb65546be"),
column: "Name",
value: "Masters");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("60611762-7f8a-4fb5-b53f-b1139918796b"));
migrationBuilder.DeleteData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("8d7cc6e3-9147-41f7-aaa7-fa507e450bd4"));
migrationBuilder.DeleteData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("cf2825ad-453b-46aa-91d9-27c124d63373"));
migrationBuilder.UpdateData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("b82d2b7e-0d52-45f3-997b-c008ea460e7f"),
column: "Description",
value: "Grants a user read-only access to details about the individuals within the system. This typically includes names, contact information, roles, departments, and potentially other relevant employee data");
migrationBuilder.UpdateData(
table: "Features",
keyColumn: "Id",
keyValue: new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"),
column: "Name",
value: "Attendance");
migrationBuilder.UpdateData(
table: "Features",
keyColumn: "Id",
keyValue: new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"),
column: "Name",
value: "Manage Project");
migrationBuilder.UpdateData(
table: "Features",
keyColumn: "Id",
keyValue: new Guid("be3b3afc-6ccf-4566-b9b6-aafcb65546be"),
column: "Name",
value: "Global Masters");
migrationBuilder.InsertData(
table: "Features",
columns: new[] { "Id", "Description", "IsActive", "ModuleId", "Name" },
values: new object[] { new Guid("9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c"), "Manage Infra", true, new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"), "Manage Infra" });
migrationBuilder.InsertData(
table: "FeaturePermissions",
columns: new[] { "Id", "Description", "FeatureId", "IsEnabled", "Name" },
values: new object[,]
{
{ new Guid("c7b68e33-72f0-474f-bd96-77636427ecc8"), "Grants a user comprehensive read-only access to all details concerning the project's underlying systems, technologies, resources, and configurations", new Guid("9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c"), true, "View Project Infra" },
{ new Guid("f2aee20a-b754-4537-8166-f9507b44585b"), "This allows them to create, modify, and manage all aspects of the supporting infrastructure.", new Guid("9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c"), true, "Manage Project Infra" }
});
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Changed_Name_Of_Feature_Permission_To_ViewTeamMembers : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.UpdateData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("60611762-7f8a-4fb5-b53f-b1139918796b"),
column: "Name",
value: "View All Employees");
migrationBuilder.UpdateData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("b82d2b7e-0d52-45f3-997b-c008ea460e7f"),
column: "Name",
value: "View Team Members");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.UpdateData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("60611762-7f8a-4fb5-b53f-b1139918796b"),
column: "Name",
value: "View All Employee");
migrationBuilder.UpdateData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("b82d2b7e-0d52-45f3-997b-c008ea460e7f"),
column: "Name",
value: "View Employee");
}
}
}

View File

@ -434,6 +434,12 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid?>("UpdatedById")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("ContactCategoryId");
@ -442,6 +448,8 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasIndex("TenantId");
b.HasIndex("UpdatedById");
b.ToTable("Contacts");
});
@ -545,6 +553,12 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid?>("UpdatedById")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("ContactId");
@ -553,6 +567,8 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasIndex("TenantId");
b.HasIndex("UpdatedById");
b.ToTable("ContactNotes");
});
@ -971,17 +987,17 @@ namespace Marco.Pms.DataAccess.Migrations
},
new
{
Id = new Guid("c7b68e33-72f0-474f-bd96-77636427ecc8"),
Id = new Guid("8d7cc6e3-9147-41f7-aaa7-fa507e450bd4"),
Description = "Grants a user comprehensive read-only access to all details concerning the project's underlying systems, technologies, resources, and configurations",
FeatureId = new Guid("9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c"),
FeatureId = new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"),
IsEnabled = true,
Name = "View Project Infra"
},
new
{
Id = new Guid("f2aee20a-b754-4537-8166-f9507b44585b"),
Id = new Guid("cf2825ad-453b-46aa-91d9-27c124d63373"),
Description = "This allows them to create, modify, and manage all aspects of the supporting infrastructure.",
FeatureId = new Guid("9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c"),
FeatureId = new Guid("53176ebf-c75d-42e5-839f-4508ffac3def"),
IsEnabled = true,
Name = "Manage Project Infra"
},
@ -1019,11 +1035,19 @@ namespace Marco.Pms.DataAccess.Migrations
},
new
{
Id = new Guid("b82d2b7e-0d52-45f3-997b-c008ea460e7f"),
Description = "Grants a user read-only access to details about the individuals within the system. This typically includes names, contact information, roles, departments, and potentially other relevant employee data",
Id = new Guid("60611762-7f8a-4fb5-b53f-b1139918796b"),
Description = "Grants a user read-only access to details about the all individuals within the system. This typically includes names, contact information, roles, departments, and potentially other relevant employee data",
FeatureId = new Guid("81ab8a87-8ccd-4015-a917-0627cee6a100"),
IsEnabled = true,
Name = "View Employee"
Name = "View All Employees"
},
new
{
Id = new Guid("b82d2b7e-0d52-45f3-997b-c008ea460e7f"),
Description = "Grants a user read-only access to details about the individuals within the system which are is assigned to same projects as user. This typically includes names, contact information, roles, departments, and potentially other relevant employee data",
FeatureId = new Guid("81ab8a87-8ccd-4015-a917-0627cee6a100"),
IsEnabled = true,
Name = "View Team Members"
},
new
{
@ -1498,15 +1522,7 @@ namespace Marco.Pms.DataAccess.Migrations
Description = "Manage Project",
IsActive = true,
ModuleId = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"),
Name = "Manage Project"
},
new
{
Id = new Guid("9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c"),
Description = "Manage Infra",
IsActive = true,
ModuleId = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"),
Name = "Manage Infra"
Name = "Project Management"
},
new
{
@ -1530,7 +1546,7 @@ namespace Marco.Pms.DataAccess.Migrations
Description = "Attendance",
IsActive = true,
ModuleId = new Guid("2a231490-bcb1-4bdd-91f1-f25fb7f25b23"),
Name = "Attendance"
Name = "Attendance Management"
},
new
{
@ -1538,7 +1554,7 @@ namespace Marco.Pms.DataAccess.Migrations
Description = "Global Masters",
IsActive = true,
ModuleId = new Guid("c43db8c7-ab73-47f4-9d3b-f83e81357924"),
Name = "Global Masters"
Name = "Masters"
},
new
{
@ -2742,11 +2758,17 @@ namespace Marco.Pms.DataAccess.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
.WithMany()
.HasForeignKey("UpdatedById");
b.Navigation("ContactCategory");
b.Navigation("CreatedBy");
b.Navigation("Tenant");
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("Marco.Pms.Model.Directory.ContactBucketMapping", b =>
@ -2810,11 +2832,17 @@ namespace Marco.Pms.DataAccess.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
.WithMany()
.HasForeignKey("UpdatedById");
b.Navigation("Contact");
b.Navigation("Createdby");
b.Navigation("Tenant");
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("Marco.Pms.Model.Directory.ContactPhone", b =>

View File

@ -20,6 +20,11 @@ namespace Marco.Pms.Model.Directory
[ValidateNever]
[ForeignKey("CreatedById")]
public Employee? CreatedBy { get; set; }
public Guid? UpdatedById { get; set; }
[ValidateNever]
[ForeignKey("UpdatedById")]
public Employee? UpdatedBy { get; set; }
[DisplayName("ContactCategoryId")]
public Guid? ContactCategoryId { get; set; }
@ -27,5 +32,6 @@ namespace Marco.Pms.Model.Directory
[ForeignKey(nameof(ContactCategoryId))]
public ContactCategoryMaster? ContactCategory { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
}
}

View File

@ -14,7 +14,13 @@ namespace Marco.Pms.Model.Directory
[ValidateNever]
[ForeignKey("CreatedById")]
public Employee? Createdby { get; set; }
public Guid? UpdatedById { get; set; }
[ValidateNever]
[ForeignKey("UpdatedById")]
public Employee? UpdatedBy { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; set; }
public Guid ContactId { get; set; }
[ValidateNever]

View File

@ -232,11 +232,15 @@ namespace Marco.Pms.Model.Mapper
{
Id = note.Id,
Note = note.Note,
ContactName = note.Contact?.Name,
OrganizationName = note.Contact?.Organization,
ContactId = note.ContactId,
CreatedAt = note.CreatedAt,
UpdatedAt = note.UpdatedAt,
CreatedBy = note.Createdby != null ? note.Createdby.ToBasicEmployeeVMFromEmployee() : null,
UpdatedBy = note.UpdatedBy != null ? note.UpdatedBy.ToBasicEmployeeVMFromEmployee() : null,
IsActive = note.IsActive
};
}
}
}
}

View File

@ -6,6 +6,8 @@ namespace Marco.Pms.Model.ViewModels.Directory
{
public Guid Id { get; set; }
public string Note { get; set; } = string.Empty;
public string? ContactName { get; set; }
public string? OrganizationName { get; set; }
public DateTime CreatedAt { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }

View File

@ -158,6 +158,13 @@ namespace Marco.Pms.Services.Controllers
// -------------------------------- Contact Notes --------------------------------
[HttpGet("notes")]
public async Task<IActionResult> GetListOFAllNotes([FromQuery] Guid? projectId, [FromQuery] int? pageSize, [FromQuery] int pageNumber)
{
var response = await _directoryHelper.GetListOFAllNotes(projectId, pageSize ?? 25, pageNumber);
return StatusCode(response.StatusCode, response);
}
[HttpPost("note")]
public async Task<IActionResult> CreateContactNote([FromBody] CreateContactNoteDto noteDto)
{
@ -338,4 +345,4 @@ namespace Marco.Pms.Services.Controllers
}
}
}
}
}

View File

@ -10,6 +10,7 @@ using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Employee;
using Marco.Pms.Services.Hubs;
using Marco.Pms.Services.Service;
using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization;
@ -35,10 +36,16 @@ namespace MarcoBMS.Services.Controllers
private readonly IConfiguration _configuration;
private readonly ILoggingService _logger;
private readonly IHubContext<MarcoHub> _signalR;
private readonly PermissionServices _permission;
private readonly ProjectsHelper _projectsHelper;
private readonly Guid ViewAllEmployees;
private readonly Guid ViewTeamMembers;
private readonly Guid tenantId;
public EmployeeController(UserManager<ApplicationUser> userManager, IEmailSender emailSender,
ApplicationDbContext context, EmployeeHelper employeeHelper, UserHelper userHelper, IConfiguration configuration, ILoggingService logger,
IHubContext<MarcoHub> signalR)
IHubContext<MarcoHub> signalR, PermissionServices permission, ProjectsHelper projectsHelper)
{
_context = context;
_userManager = userManager;
@ -48,6 +55,11 @@ namespace MarcoBMS.Services.Controllers
_configuration = configuration;
_logger = logger;
_signalR = signalR;
_permission = permission;
ViewAllEmployees = Guid.Parse("60611762-7f8a-4fb5-b53f-b1139918796b");
ViewTeamMembers = Guid.Parse("b82d2b7e-0d52-45f3-997b-c008ea460e7f");
_projectsHelper = projectsHelper;
tenantId = _userHelper.GetTenantId();
}
[HttpGet]
@ -93,18 +105,65 @@ namespace MarcoBMS.Services.Controllers
[Route("list/{projectid?}")]
public async Task<IActionResult> GetEmployeesByProject(Guid? projectid, [FromQuery] bool ShowInactive)
{
// Step 1: Validate incoming request model state
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
_logger.LogWarning("Invalid model state in GetEmployeesByProject. Errors: {@Errors}", errors);
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
}
var result = await _employeeHelper.GetEmployeeByProjectId(GetTenantId(), projectid, ShowInactive);
// Step 2: Get logged-in employee
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
_logger.LogInfo("GetEmployeesByProject called by EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, ShowInactive: {ShowInactive}",
loggedInEmployee.Id, projectid ?? Guid.Empty, ShowInactive);
// Step 3: Fetch project access and permissions
List<Project> projects = await _projectsHelper.GetMyProjects(tenantId, loggedInEmployee);
var projectIds = projects.Select(p => p.Id).ToList();
var hasViewAllEmployeesPermission = await _permission.HasPermission(ViewAllEmployees, loggedInEmployee.Id);
var hasViewTeamMembersPermission = await _permission.HasPermission(ViewTeamMembers, loggedInEmployee.Id);
List<EmployeeVM> result = new();
// Step 4: Determine access level and fetch employees accordingly
if (hasViewAllEmployeesPermission || projectid != null)
{
result = await _employeeHelper.GetEmployeeByProjectId(tenantId, projectid, ShowInactive);
_logger.LogInfo("Employee list fetched using full access or specific project.");
}
else if (hasViewTeamMembersPermission && !ShowInactive)
{
var employeeIds = await _context.ProjectAllocations
.Where(pa => projectIds.Contains(pa.ProjectId) && pa.IsActive)
.Select(pa => pa.EmployeeId)
.Distinct()
.ToListAsync();
result = await _context.Employees
.Include(fp => fp.JobRole)
.Where(e => employeeIds.Contains(e.Id) && e.IsActive)
.Select(e => e.ToEmployeeVMFromEmployee())
.ToListAsync();
_logger.LogInfo("Employee list fetched using limited access (active only).");
}
else
{
_logger.LogWarning("Access denied for EmployeeId: {EmployeeId} - insufficient permissions.", loggedInEmployee.Id);
return Ok(ApiResponse<object>.SuccessResponse(result, "Filter applied.", 200));
}
// Step 5: Log and return results
_logger.LogInfo("Employees fetched successfully by EmployeeId: {EmployeeId} for ProjectId: {ProjectId}. Count: {Count}",
loggedInEmployee.Id, projectid ?? Guid.Empty, result.Count);
return Ok(ApiResponse<object>.SuccessResponse(result, "Filter applied.", 200));
}
[HttpGet]

View File

@ -33,7 +33,7 @@ namespace MarcoBMS.Services.Controllers
features.Add(item);
}
}
return features;
return features.OrderBy(f => f.Name).ToList();
}
[HttpGet]
@ -50,7 +50,7 @@ namespace MarcoBMS.Services.Controllers
ModuleId = c.ModuleId,
ModuleName = c.Module != null ? c.Module.Name : string.Empty,
IsActive = c.IsActive
});
}).OrderBy(f => f.Name).ToList();
return Ok(ApiResponse<object>.SuccessResponse(rolesVM, "Success.", 200));
}
}

View File

@ -156,6 +156,7 @@ namespace MarcoBMS.Services.Controllers
RoleName = x.Role.Role,
FeaturePermission = x.FeaturePermission
})
.OrderByDescending(r => r.RoleName)
.ToListAsync();
List<ApplicationRolesVM> applicationRoles = new List<ApplicationRolesVM>();

View File

@ -7,6 +7,7 @@ using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Directory;
using Marco.Pms.Model.ViewModels.Master;
using Marco.Pms.Model.ViewModels.Projects;
using Marco.Pms.Services.Service;
using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service;
using Microsoft.EntityFrameworkCore;
@ -18,15 +19,17 @@ namespace Marco.Pms.Services.Helpers
private readonly ApplicationDbContext _context;
private readonly ILoggingService _logger;
private readonly UserHelper _userHelper;
private readonly PermissionServices _permissionServices;
private readonly Guid directoryAdmin;
private readonly Guid directoryManager;
private readonly Guid directoryUser;
public DirectoryHelper(ApplicationDbContext context, ILoggingService logger, UserHelper userHelper)
public DirectoryHelper(ApplicationDbContext context, ILoggingService logger, UserHelper userHelper, PermissionServices permissionServices)
{
_context = context;
_logger = logger;
_userHelper = userHelper;
_permissionServices = permissionServices;
directoryAdmin = Guid.Parse("4286a13b-bb40-4879-8c6d-18e9e393beda");
directoryManager = Guid.Parse("62668630-13ce-4f52-a0f0-db38af2230c5");
directoryUser = Guid.Parse("0f919170-92d4-4337-abd3-49b66fc871bb");
@ -504,6 +507,8 @@ namespace Marco.Pms.Services.Helpers
var newContact = updateContact.ToContactFromUpdateContactDto(tenantId, contact);
newContact.UpdatedById = LoggedInEmployee.Id;
newContact.UpdatedAt = DateTime.UtcNow;
_context.Contacts.Update(newContact);
await _context.SaveChangesAsync();
@ -893,6 +898,138 @@ namespace Marco.Pms.Services.Helpers
// -------------------------------- Contact Notes --------------------------------
/// <summary>
/// Retrieves a paginated list of contact notes based on user permissions.
/// </summary>
/// <param name="pageSize">The number of items per page.</param>
/// <param name="pageNumber">The current page number.</param>
/// <returns>An ApiResponse containing the paginated notes or an error message.</returns>
public async Task<ApiResponse<object>> GetListOFAllNotes(Guid? projectId, int pageSize, int pageNumber)
{
_logger.LogInfo("Attempting to fetch list of all notes. PageSize: {PageSize}, PageNumber: {PageNumber}", pageSize, pageNumber);
Guid tenantId = _userHelper.GetTenantId();
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
List<Guid>? projectContactIds = null;
if (loggedInEmployee == null)
{
_logger.LogWarning("GetListOFAllNotes: LoggedInEmployee is null. Cannot proceed.");
return ApiResponse<object>.ErrorResponse("Unauthorized", "Employee not found.", 401);
}
// --- Permission Checks ---
var hasAdminPermission = await _permissionServices.HasPermission(directoryAdmin, loggedInEmployee.Id);
var hasManagerPermission = await _permissionServices.HasPermission(directoryManager, loggedInEmployee.Id);
var hasUserPermission = await _permissionServices.HasPermission(directoryUser, loggedInEmployee.Id);
IQueryable<ContactNote> notesQuery = _context.ContactNotes
.Include(cn => cn.UpdatedBy)
.Include(cn => cn.Createdby) // Assuming 'CreatedBy' (PascalCase)
.Include(cn => cn.Contact)
.Where(cn => cn.TenantId == tenantId)
.AsQueryable(); // Start building the query
if (!hasAdminPermission && !(hasManagerPermission || hasUserPermission))
{
_logger.LogWarning("GetListOFAllNotes: User {EmployeeId} does not have required permissions to access notes for TenantId: {TenantId}", loggedInEmployee.Id, tenantId);
return ApiResponse<object>.ErrorResponse("Access Denied", "You don't have access to view notes.", 403);
}
if (projectId != null)
{
projectContactIds = await _context.ContactProjectMappings
.Where(pc => pc.ProjectId == projectId)
.Select(pc => pc.ContactId)
.ToListAsync();
}
if (!hasAdminPermission) // If not an admin, apply additional filtering
{
_logger.LogInfo("GetListOFAllNotes: User {EmployeeId} is not an admin. Applying manager/user specific filters.", loggedInEmployee.Id);
var assignedBucketIds = await _context.EmployeeBucketMappings
.Where(eb => eb.EmployeeId == loggedInEmployee.Id)
.Select(eb => eb.BucketId)
.ToListAsync();
if (!assignedBucketIds.Any())
{
_logger.LogInfo("GetListOFAllNotes: User {EmployeeId} has no assigned buckets. Returning empty list.", loggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(new { CurrentPage = pageNumber, TotalPages = 0, Data = new List<ContactNoteVM>() }, "No notes found based on assigned buckets.", 200);
}
List<Guid>? contactIds = null;
if (projectContactIds == null)
{
contactIds = await _context.ContactBucketMappings
.Where(cb => assignedBucketIds.Contains(cb.BucketId))
.Select(cb => cb.ContactId)
.ToListAsync();
}
else
{
contactIds = await _context.ContactBucketMappings
.Where(cb => assignedBucketIds.Contains(cb.BucketId) && projectContactIds.Contains(cb.ContactId))
.Select(cb => cb.ContactId)
.ToListAsync();
}
if (!contactIds.Any())
{
_logger.LogInfo("GetListOFAllNotes: No contacts found for assigned buckets for user {EmployeeId}. Returning empty list.", loggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(new { CurrentPage = pageNumber, TotalPages = 0, Data = new List<ContactNoteVM>() }, "No notes found for associated contacts.", 200);
}
notesQuery = notesQuery.Where(cn => contactIds.Contains(cn.ContactId));
}
else
{
if (projectContactIds != null)
{
notesQuery = notesQuery.Where(cn => projectContactIds.Contains(cn.ContactId));
}
}
// --- Pagination Logic ---
// Ensure pageSize and pageNumber are valid
pageSize = pageSize < 1 ? 25 : pageSize; // Default to 25 if less than 1
pageNumber = pageNumber < 1 ? 1 : pageNumber; // Default to 1 if less than 1
// Get total count BEFORE applying Skip/Take for accurate pagination metadata
int totalRecords = await notesQuery.CountAsync();
int totalPages = (int)Math.Ceiling((double)totalRecords / pageSize);
int skip = (pageNumber - 1) * pageSize;
// --- Apply Ordering and Pagination in the database ---
List<ContactNote> notes = await notesQuery
.OrderByDescending(cn => (cn.UpdatedAt != null ? cn.UpdatedAt : cn.CreatedAt)) // Order by updated date or created date
.Skip(skip)
.Take(pageSize)
.ToListAsync();
_logger.LogInfo("GetListOFAllNotes: Fetched {Count} notes for page {PageNumber} of {TotalPages} total pages. Total records: {TotalRecords}.",
notes.Count, pageNumber, totalPages, totalRecords);
// --- Map to ViewModel (in-memory) ---
// This mapping is done in memory because ToBasicEmployeeVMFromEmployee() is likely a C# method
// that cannot be translated to SQL by Entity Framework.
List<ContactNoteVM> noteVMS = notes
.Select(cn => cn.ToContactNoteVMFromContactNote())
.ToList();
var response = new
{
CurrentPage = pageNumber,
PageSize = pageSize, // Include pageSize in response for client clarity
TotalPages = totalPages,
TotalRecords = totalRecords, // Add total records for client
Data = noteVMS
};
_logger.LogInfo("GetListOFAllNotes: Successfully retrieved notes and mapped to ViewModel for TenantId: {TenantId}.", tenantId);
return ApiResponse<object>.SuccessResponse(response, $"{noteVMS.Count} notes fetched successfully.", 200);
}
public async Task<ApiResponse<object>> GetNoteListByContactId(Guid id, bool active)
{
Guid tenantId = _userHelper.GetTenantId();
@ -903,32 +1040,25 @@ namespace Marco.Pms.Services.Helpers
List<ContactNote> notes = new List<ContactNote>();
if (active)
{
notes = await _context.ContactNotes.Include(n => n.Createdby).Where(n => n.ContactId == contact.Id && n.IsActive && n.TenantId == tenantId).ToListAsync();
notes = await _context.ContactNotes
.Include(n => n.Createdby)
.Include(n => n.UpdatedBy)
.Where(n => n.ContactId == contact.Id && n.IsActive && n.TenantId == tenantId)
.ToListAsync();
}
else
{
notes = await _context.ContactNotes.Include(n => n.Createdby).Where(n => n.ContactId == contact.Id && n.TenantId == tenantId).ToListAsync();
notes = await _context.ContactNotes
.Include(n => n.Createdby)
.Include(n => n.UpdatedBy)
.Where(n => n.ContactId == contact.Id && n.TenantId == tenantId)
.ToListAsync();
}
var noteIds = notes.Select(n => n.Id).ToList();
List<DirectoryUpdateLog>? updateLogs = await _context.DirectoryUpdateLogs.Include(l => l.Employee).Where(l => noteIds.Contains(l.RefereanceId)).ToListAsync();
List<ContactNoteVM>? noteVMs = new List<ContactNoteVM>();
foreach (var note in notes)
{
ContactNoteVM noteVM = note.ToContactNoteVMFromContactNote();
DirectoryUpdateLog? updateLog = updateLogs.Where(l => l.RefereanceId == note.Id).OrderByDescending(l => l.UpdateAt).FirstOrDefault();
if (updateLog != null)
{
noteVM.UpdatedAt = updateLog.UpdateAt;
noteVM.UpdatedBy = updateLog.Employee != null ? updateLog.Employee.ToBasicEmployeeVMFromEmployee() : null;
}
else
{
noteVM.UpdatedAt = note.CreatedAt;
noteVM.UpdatedBy = note.Createdby != null ? note.Createdby.ToBasicEmployeeVMFromEmployee() : null;
}
noteVMs.Add(noteVM);
//List<ContactNoteVM>? noteVMs = new List<ContactNoteVM>();
List<ContactNoteVM>? noteVMs = notes.Select(n => n.ToContactNoteVMFromContactNote()).ToList();
}
_logger.LogInfo("{count} contact-notes record from contact {ContactId} fetched by Employee {EmployeeId}", noteVMs.Count, id, LoggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(noteVMs, $"{noteVMs.Count} contact-notes record fetched successfully", 200);
}
@ -966,10 +1096,12 @@ namespace Marco.Pms.Services.Helpers
Contact? contact = await _context.Contacts.FirstOrDefaultAsync(c => c.Id == noteDto.ContactId && c.IsActive && c.TenantId == tenantId);
if (contact != null)
{
ContactNote? contactNote = await _context.ContactNotes.FirstOrDefaultAsync(n => n.Id == noteDto.Id && n.ContactId == contact.Id && n.IsActive);
ContactNote? contactNote = await _context.ContactNotes.Include(cn => cn.Createdby).Include(cn => cn.Contact).FirstOrDefaultAsync(n => n.Id == noteDto.Id && n.ContactId == contact.Id && n.IsActive);
if (contactNote != null)
{
contactNote.Note = noteDto.Note;
contactNote.UpdatedById = LoggedInEmployee.Id;
contactNote.UpdatedAt = DateTime.UtcNow;
_context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
{
@ -1004,6 +1136,9 @@ namespace Marco.Pms.Services.Helpers
if (note != null)
{
note.IsActive = active;
note.UpdatedById = LoggedInEmployee.Id;
note.UpdatedAt = DateTime.UtcNow;
_context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
{
RefereanceId = id,
@ -1335,6 +1470,9 @@ namespace Marco.Pms.Services.Helpers
_logger.LogWarning("Employee {EmployeeId} tries to delete bucket {BucketId} but not found in database", LoggedInEmployee.Id, id);
return ApiResponse<object>.SuccessResponse(new { }, "Bucket deleted successfully", 200);
}
// -------------------------------- Helper --------------------------------
private bool Compare(string sentence, string search)
{
sentence = sentence.Trim().ToLower();
@ -1345,4 +1483,4 @@ namespace Marco.Pms.Services.Helpers
return result;
}
}
}
}

View File

@ -218,11 +218,11 @@ namespace Marco.Pms.Services.Helpers
_logger.LogInfo("Work category master {ConatctTagId} updated successfully by employee {EmployeeId}", contactTagVm.Id, LoggedInEmployee.Id);
ApiResponse<object>.SuccessResponse(contactTagVm, "Contact Tag master updated successfully", 200);
_logger.LogInfo("Contact tag master {ConatctTagId} updated successfully by employee {EmployeeId}", contactTagVm.Id, LoggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(contactTagVm, "Contact Tag master updated successfully", 200);
}
_logger.LogError("Contact Tag master {ContactTagId} not found in database", id);
ApiResponse<object>.ErrorResponse("Contact Tag master not found", "Contact tag master not found", 404);
return ApiResponse<object>.ErrorResponse("Contact Tag master not found", "Contact tag master not found", 404);
}
_logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", LoggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400);

View File

@ -98,23 +98,23 @@ namespace Marco.Pms.Services.Service
IsSystem = true,
JoiningDate = Convert.ToDateTime("2000-04-20 10:11:17.588000"),
};
if ((!await dbContext.Employees.Where(e => e.FirstName == "Admin").AnyAsync()) && (jobRole != null ? jobRole.Id : Guid.Empty) != Guid.Empty)
if ((!await dbContext.Employees.Where(e => e.Email == "admin@marcoaiot.com").AnyAsync()) && jobRole?.Id != Guid.Empty)
{
await dbContext.Employees.AddAsync(employee);
}
else
{
employee = await dbContext.Employees.Where(e => e.FirstName == "Admin").FirstOrDefaultAsync();
employee = await dbContext.Employees.Where(e => e.Email == "admin@marcoaiot.com").FirstOrDefaultAsync();
}
await dbContext.SaveChangesAsync();
if (!await dbContext.EmployeeRoleMappings.AnyAsync())
{
await dbContext.EmployeeRoleMappings.AddAsync(new EmployeeRoleMapping
{
EmployeeId = employee != null ? employee.Id : Guid.Empty,
EmployeeId = employee?.Id ?? Guid.Empty,
IsEnabled = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26"),
RoleId = role != null ? role.Id : Guid.Empty
RoleId = role?.Id ?? Guid.Empty
});
}
if (!await dbContext.RolePermissionMappings.AnyAsync())
@ -136,4 +136,4 @@ namespace Marco.Pms.Services.Service
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
}
}