Compare commits

..

37 Commits

Author SHA1 Message Date
c6744f0556 Merge pull request 'Collection_Management' (#146) from Collection_Management into main
Reviewed-on: #146
2025-10-24 06:21:29 +00:00
031c34cb7c Added mark as complete check in get invoice list API 2025-10-17 11:28:26 +05:30
b3595ad6b6 Merge branch 'Collection_Management' of https://git.marcoaiot.com/admin/marco.pms.api into Collection_Management 2025-10-17 11:11:34 +05:30
34bd84ad73 Added audit while mark as complete ininvoice 2025-10-17 11:10:38 +05:30
c8d69e7c5b Merge branch 'Collection_Management' of https://git.marcoaiot.com/admin/marco.pms.api into Collection_Management 2025-10-17 10:56:46 +05:30
94f57e2852 Reactiving the employee without checking any condditions of attendance 2025-10-17 10:56:31 +05:30
843dc8edfc Addd the collection related permission in context and APIs 2025-10-16 13:04:49 +05:30
70aa5121fa Added the Finance in mobile menu 2025-10-15 17:43:58 +05:30
2e299c6152 Added the expense related dashboards 2025-10-15 13:00:17 +05:30
c13dec9052 Added the payement adjustment heads in master dropdown 2025-10-15 11:26:36 +05:30
8c305872a0 Added the CRUD API for Payment Adjustment heads 2025-10-15 11:24:48 +05:30
302185808e Added the Payment Adjustment Head table also new API of get Payment Adjustment Head lists as well added the forgin key in received payment table and API 2025-10-14 17:57:37 +05:30
4dc37cb0ac Added the projectID filter in get invoice list API 2025-10-14 16:55:46 +05:30
e80728805b Added the comment in Received Invoice Payment and added provide all project flag in get basic project 2025-10-14 15:34:38 +05:30
4434ea971f Added file name and content type in invoice attachment view models 2025-10-14 14:29:57 +05:30
5a9b06cca6 Added the update invoice API 2025-10-14 14:24:59 +05:30
47bb49fac6 Merge pull request 'Added the organization name in record response object' (#145) from Ashutosh_Bug#1498 into Collection_Management
Reviewed-on: #145
2025-10-14 06:07:09 +00:00
5ce95f7e23 Added the organization name in record response object 2025-10-14 11:36:32 +05:30
636dd48ad6 Change response for invoice number check 2025-10-14 11:29:04 +05:30
62688d508f Added the add comment API and get Details API 2025-10-14 11:24:35 +05:30
c92e71b292 Added the new API to markAsCompletedthe invoice 2025-10-13 21:08:11 +05:30
2b19890b53 Added the create payment received invoice API 2025-10-13 20:44:38 +05:30
5ff87cd870 Added the get list of invoices API 2025-10-13 19:59:18 +05:30
b30369baa5 Added the new API to create invoice 2025-10-13 18:35:57 +05:30
4684b438f6 Added the invoice related tables 2025-10-13 18:35:26 +05:30
7e20807325 corrected the distinct by error 2025-10-11 15:57:53 +05:30
186486d934 Added the new API to get organizations for dropdown 2025-10-11 15:36:52 +05:30
d07f0311ae Optmized the work status master APIs 2025-10-11 13:52:20 +05:30
2f6031e62c revert e02636b6b66287a22fcfb34c150aece1b136cc68
revert Optmized the work status master APIs
2025-10-11 08:19:59 +00:00
e02636b6b6 Optmized the work status master APIs 2025-10-11 13:48:36 +05:30
bad784e147 Optimized the contact related msater APIs 2025-10-11 12:59:31 +05:30
886d0bb3b1 Clearing the employee profile cache when selecting the tenant 2025-10-10 18:32:01 +05:30
74dd9eeb8d organization Tenant Mapping is already existed then return error message 2025-10-10 17:53:23 +05:30
1939a63d9a Assigning the appilcation role to root employee of the any organization 2025-10-10 17:05:43 +05:30
c07db9f94d removed employee project cache when adding the subscription 2025-10-10 15:59:42 +05:30
53da15416a Removing all employees profile from cache for certain tenant 2025-10-10 15:46:56 +05:30
a809bdd469 Merge pull request 'Sloved issues of permissions not been assigned when updating the subscription' (#144) from Ashutosh_Bug#1461 into main
Reviewed-on: #144
2025-10-10 08:49:19 +00:00
54 changed files with 44371 additions and 560 deletions

View File

@ -1,6 +1,7 @@
using Marco.Pms.Model.Activities; using Marco.Pms.Model.Activities;
using Marco.Pms.Model.AttendanceModule; using Marco.Pms.Model.AttendanceModule;
using Marco.Pms.Model.Authentication; using Marco.Pms.Model.Authentication;
using Marco.Pms.Model.Collection;
using Marco.Pms.Model.Directory; using Marco.Pms.Model.Directory;
using Marco.Pms.Model.DocumentManager; using Marco.Pms.Model.DocumentManager;
using Marco.Pms.Model.Employees; using Marco.Pms.Model.Employees;
@ -133,6 +134,13 @@ namespace Marco.Pms.DataAccess.Data
public DbSet<ProjectServiceMapping> ProjectServiceMappings { get; set; } public DbSet<ProjectServiceMapping> ProjectServiceMappings { get; set; }
public DbSet<ProjectOrgMapping> ProjectOrgMappings { get; set; } public DbSet<ProjectOrgMapping> ProjectOrgMappings { get; set; }
// Collection
public DbSet<Invoice> Invoices { get; set; }
public DbSet<InvoiceComment> InvoiceComments { get; set; }
public DbSet<InvoiceAttachment> InvoiceAttachments { get; set; }
public DbSet<ReceivedInvoicePayment> ReceivedInvoicePayments { get; set; }
public DbSet<PaymentAdjustmentHead> PaymentAdjustmentHeads { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
@ -775,6 +783,65 @@ namespace Marco.Pms.DataAccess.Data
} }
); );
modelBuilder.Entity<PaymentAdjustmentHead>().HasData(
new PaymentAdjustmentHead
{
Id = Guid.Parse("dbdc047f-a2d2-4db0-b0e6-b9d9f923a0f1"),
Name = "Advance payment",
Description = "An advance payment is a sum paid before receiving goods or services, often to secure a transaction or cover initial costs.",
IsActive = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new PaymentAdjustmentHead
{
Id = Guid.Parse("66c3c241-8b52-4327-a5ad-c1faf102583e"),
Name = "Base Amount",
Description = "The base amount refers to the principal sum or original value used as a reference in financial calculations, excluding taxes, fees, or additional charges.",
IsActive = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new PaymentAdjustmentHead
{
Id = Guid.Parse("0d70cb2e-827e-44fc-90a5-c2c55ba51ba9"),
Name = "Tax Deducted at Source (TDS)",
Description = "TDS, or Tax Deducted at Source, is a system under the Indian Income Tax Act where tax is deducted at the point of income generation—such as salary, interest, or rent—and remitted to the government to prevent tax evasion and ensure timely collection.",
IsActive = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new PaymentAdjustmentHead
{
Id = Guid.Parse("95f35acd-d979-4177-91ea-fd03a00e49ff"),
Name = "Retention",
Description = "Retention refers to a company's ability to keep customers, employees, or profits over time, commonly measured as a percentage and critical for long-term business sustainability and growth.",
IsActive = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new PaymentAdjustmentHead
{
Id = Guid.Parse("3f09b19a-8d45-4cf2-be27-f4f09b38b9f7"),
Name = "Tax",
Description = "Tax is a mandatory financial charge imposed by a government on individuals or entities to fund public services and government operations, without direct benefit to the taxpayer.",
IsActive = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new PaymentAdjustmentHead
{
Id = Guid.Parse("ec5e6a5f-ce62-44e5-8911-8426bbb4dde8"),
Name = "Penalty",
Description = "A penalty in the context of taxation is a financial sanction imposed by the government on individuals or entities for non-compliance with tax laws, such as late filing, underreporting income, or failure to pay taxes, and is typically calculated as a percentage of the tax due or a fixed amount.",
IsActive = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
},
new PaymentAdjustmentHead
{
Id = Guid.Parse("50584332-1cb7-4359-9721-c8ea35040881"),
Name = "Utility fees",
Description = "Utility fees are recurring charges for essential services such as electricity, water, gas, sewage, waste disposal, internet, and telecommunications, typically based on usage and necessary for operating a home or business.",
IsActive = true,
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
}
);
modelBuilder.Entity<EntityTypeMaster>().HasData( modelBuilder.Entity<EntityTypeMaster>().HasData(
new EntityTypeMaster new EntityTypeMaster
{ {
@ -1039,6 +1106,7 @@ namespace Marco.Pms.DataAccess.Data
// Project Module // Project Module
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("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("a4e25142-449b-4334-a6e5-22f70e4732d7"), Description = "Expense Management is the systematic process of tracking, controlling, and reporting business-related expenditures.", Name = "Expense Management", ModuleId = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"), IsActive = true }, new Feature { Id = new Guid("a4e25142-449b-4334-a6e5-22f70e4732d7"), Description = "Expense Management is the systematic process of tracking, controlling, and reporting business-related expenditures.", Name = "Expense Management", ModuleId = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"), IsActive = true },
new Feature { Id = new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"), Description = "Collection Management is a feature that enables organizations to track, organize, and manage the status and recovery of receivables or assets efficiently throughout their lifecycle, supporting systematic follow-up and resolution of outstanding accounts.", Name = "Collection Management", 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("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"), Description = "Manage Tasks", Name = "Task Management", ModuleId = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"), IsActive = true },
// Employee Module // Employee Module
@ -1110,6 +1178,13 @@ namespace Marco.Pms.DataAccess.Data
new FeaturePermission { Id = new Guid("ea5a1529-4ee8-4828-80ea-0e23c9d4dd11"), FeatureId = new Guid("a4e25142-449b-4334-a6e5-22f70e4732d7"), IsEnabled = true, Name = "Process", Description = "Allows a user to handle post-approval actions such as recording payments, updating financial records, or marking expenses as reimbursed or settled." }, new FeaturePermission { Id = new Guid("ea5a1529-4ee8-4828-80ea-0e23c9d4dd11"), FeatureId = new Guid("a4e25142-449b-4334-a6e5-22f70e4732d7"), IsEnabled = true, Name = "Process", Description = "Allows a user to handle post-approval actions such as recording payments, updating financial records, or marking expenses as reimbursed or settled." },
new FeaturePermission { Id = new Guid("bdee29a2-b73b-402d-8dd1-c4b1f81ccbc3"), FeatureId = new Guid("a4e25142-449b-4334-a6e5-22f70e4732d7"), IsEnabled = true, Name = "Manage", Description = "Allows a user to configure and control system settings, such as managing expense types, payment modes, permissions, and overall workflow rules." }, new FeaturePermission { Id = new Guid("bdee29a2-b73b-402d-8dd1-c4b1f81ccbc3"), FeatureId = new Guid("a4e25142-449b-4334-a6e5-22f70e4732d7"), IsEnabled = true, Name = "Manage", Description = "Allows a user to configure and control system settings, such as managing expense types, payment modes, permissions, and overall workflow rules." },
// Collection Management Feature
new FeaturePermission { Id = new Guid("dbf17591-09fe-4c93-9e1a-12db8f5cc5de"), FeatureId = new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"), IsEnabled = true, Name = "Collection Admin", Description = "Collection Admin is a permission that grants a user full administrative control over collections, including creating, editing, managing access, and deleting collections within a system." },
new FeaturePermission { Id = new Guid("c8d7eea5-4033-4aad-9ebe-76de49896830"), FeatureId = new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"), IsEnabled = true, Name = "View Collection", Description = "View Collection is a permission that allows users to see and browse assets or items within a collection without making any modifications or edits to its contents." },
new FeaturePermission { Id = new Guid("b93141fd-dbd3-4051-8f57-bf25d18e3555"), FeatureId = new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"), IsEnabled = true, Name = "Create Collection", Description = "Authorizes users to create new collections for organizing related resources and managing access" },
new FeaturePermission { Id = new Guid("455187b4-fef1-41f9-b3d0-025d0b6302c3"), FeatureId = new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"), IsEnabled = true, Name = "Edit Collection", Description = "Ability to modify collection properties, content, and access rights." },
new FeaturePermission { Id = new Guid("061d9ccd-85b4-4cb0-be06-2f9f32cebb72"), FeatureId = new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"), IsEnabled = true, Name = "Add Payment", Description = " Enables entry and processing of payment transactions." },
// Organization Management Feature // Organization Management Feature
new FeaturePermission { Id = new Guid("068cb3c1-49c5-4746-9f29-1fce16e820ac"), FeatureId = new Guid("6d4c82d6-dbce-48ab-b8b8-f785f4d8c914"), IsEnabled = true, Name = "Add Organization", Description = "Allow user to create new organization" }, new FeaturePermission { Id = new Guid("068cb3c1-49c5-4746-9f29-1fce16e820ac"), FeatureId = new Guid("6d4c82d6-dbce-48ab-b8b8-f785f4d8c914"), IsEnabled = true, Name = "Add Organization", Description = "Allow user to create new organization" },
new FeaturePermission { Id = new Guid("c1ae1363-ab8a-4bd9-a9d1-8c2c6083873a"), FeatureId = new Guid("6d4c82d6-dbce-48ab-b8b8-f785f4d8c914"), IsEnabled = true, Name = "Edit Organization", Description = "Allow the user to update the basic information of the organization" }, new FeaturePermission { Id = new Guid("c1ae1363-ab8a-4bd9-a9d1-8c2c6083873a"), FeatureId = new Guid("6d4c82d6-dbce-48ab-b8b8-f785f4d8c914"), IsEnabled = true, Name = "Edit Organization", Description = "Allow the user to update the basic information of the organization" },

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,256 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_Collection_Related_Tables : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Invoices",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Title = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Description = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
InvoiceNumber = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ProjectId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
InvoiceDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
ClientSubmitedDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
ExceptedPaymentDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
Amount = table.Column<double>(type: "double", nullable: false),
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_Invoices", x => x.Id);
table.ForeignKey(
name: "FK_Invoices_Employees_CreatedById",
column: x => x.CreatedById,
principalTable: "Employees",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Invoices_Employees_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Employees",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Invoices_Projects_ProjectId",
column: x => x.ProjectId,
principalTable: "Projects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Invoices_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "InvoiceAttachments",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
InvoiceId = 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_InvoiceAttachments", x => x.Id);
table.ForeignKey(
name: "FK_InvoiceAttachments_Documents_DocumentId",
column: x => x.DocumentId,
principalTable: "Documents",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_InvoiceAttachments_Invoices_InvoiceId",
column: x => x.InvoiceId,
principalTable: "Invoices",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_InvoiceAttachments_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "InvoiceComments",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Comment = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
InvoiceId = 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_InvoiceComments", x => x.Id);
table.ForeignKey(
name: "FK_InvoiceComments_Employees_CreatedById",
column: x => x.CreatedById,
principalTable: "Employees",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_InvoiceComments_Invoices_InvoiceId",
column: x => x.InvoiceId,
principalTable: "Invoices",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_InvoiceComments_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "ReceivedInvoicePayments",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
InvoiceId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
PaymentReceivedDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
TransactionId = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Amount = table.Column<double>(type: "double", nullable: false),
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = 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_ReceivedInvoicePayments", x => x.Id);
table.ForeignKey(
name: "FK_ReceivedInvoicePayments_Employees_CreatedById",
column: x => x.CreatedById,
principalTable: "Employees",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ReceivedInvoicePayments_Invoices_InvoiceId",
column: x => x.InvoiceId,
principalTable: "Invoices",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ReceivedInvoicePayments_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_InvoiceAttachments_DocumentId",
table: "InvoiceAttachments",
column: "DocumentId");
migrationBuilder.CreateIndex(
name: "IX_InvoiceAttachments_InvoiceId",
table: "InvoiceAttachments",
column: "InvoiceId");
migrationBuilder.CreateIndex(
name: "IX_InvoiceAttachments_TenantId",
table: "InvoiceAttachments",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_InvoiceComments_CreatedById",
table: "InvoiceComments",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_InvoiceComments_InvoiceId",
table: "InvoiceComments",
column: "InvoiceId");
migrationBuilder.CreateIndex(
name: "IX_InvoiceComments_TenantId",
table: "InvoiceComments",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_Invoices_CreatedById",
table: "Invoices",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_Invoices_ProjectId",
table: "Invoices",
column: "ProjectId");
migrationBuilder.CreateIndex(
name: "IX_Invoices_TenantId",
table: "Invoices",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_Invoices_UpdatedById",
table: "Invoices",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_ReceivedInvoicePayments_CreatedById",
table: "ReceivedInvoicePayments",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_ReceivedInvoicePayments_InvoiceId",
table: "ReceivedInvoicePayments",
column: "InvoiceId");
migrationBuilder.CreateIndex(
name: "IX_ReceivedInvoicePayments_TenantId",
table: "ReceivedInvoicePayments",
column: "TenantId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "InvoiceAttachments");
migrationBuilder.DropTable(
name: "InvoiceComments");
migrationBuilder.DropTable(
name: "ReceivedInvoicePayments");
migrationBuilder.DropTable(
name: "Invoices");
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,61 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_EInvoiceNumber_In_Invoice_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Amount",
table: "Invoices",
newName: "TaxAmount");
migrationBuilder.AddColumn<double>(
name: "BasicAmount",
table: "Invoices",
type: "double",
nullable: false,
defaultValue: 0.0);
migrationBuilder.AddColumn<string>(
name: "EInvoiceNumber",
table: "Invoices",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<bool>(
name: "MarkAsCompleted",
table: "Invoices",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "BasicAmount",
table: "Invoices");
migrationBuilder.DropColumn(
name: "EInvoiceNumber",
table: "Invoices");
migrationBuilder.DropColumn(
name: "MarkAsCompleted",
table: "Invoices");
migrationBuilder.RenameColumn(
name: "TaxAmount",
table: "Invoices",
newName: "Amount");
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_Comment_In_ReceivedInvoicePayment_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Comment",
table: "ReceivedInvoicePayments",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Comment",
table: "ReceivedInvoicePayments");
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,67 @@
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_PaymentAdjustmentHead_Master_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PaymentAdjustmentHeads",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Description = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false),
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_PaymentAdjustmentHeads", x => x.Id);
table.ForeignKey(
name: "FK_PaymentAdjustmentHeads_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.InsertData(
table: "PaymentAdjustmentHeads",
columns: new[] { "Id", "Description", "IsActive", "Name", "TenantId" },
values: new object[,]
{
{ new Guid("0d70cb2e-827e-44fc-90a5-c2c55ba51ba9"), "TDS, or Tax Deducted at Source, is a system under the Indian Income Tax Act where tax is deducted at the point of income generation—such as salary, interest, or rent—and remitted to the government to prevent tax evasion and ensure timely collection.", true, "Tax Deducted at Source (TDS)", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("3f09b19a-8d45-4cf2-be27-f4f09b38b9f7"), "Tax is a mandatory financial charge imposed by a government on individuals or entities to fund public services and government operations, without direct benefit to the taxpayer.", true, "Tax", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("50584332-1cb7-4359-9721-c8ea35040881"), "Utility fees are recurring charges for essential services such as electricity, water, gas, sewage, waste disposal, internet, and telecommunications, typically based on usage and necessary for operating a home or business.", true, "Utility fees", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("66c3c241-8b52-4327-a5ad-c1faf102583e"), "The base amount refers to the principal sum or original value used as a reference in financial calculations, excluding taxes, fees, or additional charges.", true, "Base Amount", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("95f35acd-d979-4177-91ea-fd03a00e49ff"), "Retention refers to a company's ability to keep customers, employees, or profits over time, commonly measured as a percentage and critical for long-term business sustainability and growth.", true, "Retention", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("dbdc047f-a2d2-4db0-b0e6-b9d9f923a0f1"), "An advance payment is a sum paid before receiving goods or services, often to secure a transaction or cover initial costs.", true, "Advance payment", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") },
{ new Guid("ec5e6a5f-ce62-44e5-8911-8426bbb4dde8"), "A penalty in the context of taxation is a financial sanction imposed by the government on individuals or entities for non-compliance with tax laws, such as late filing, underreporting income, or failure to pay taxes, and is typically calculated as a percentage of the tax due or a fixed amount.", true, "Penalty", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") }
});
migrationBuilder.CreateIndex(
name: "IX_PaymentAdjustmentHeads_TenantId",
table: "PaymentAdjustmentHeads",
column: "TenantId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PaymentAdjustmentHeads");
}
}
}

View File

@ -0,0 +1,51 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_PaymentAdjustmentHead_ForignKey_In_ReceivedInvoicePayment_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "PaymentAdjustmentHeadId",
table: "ReceivedInvoicePayments",
type: "char(36)",
nullable: false,
defaultValue: new Guid("66c3c241-8b52-4327-a5ad-c1faf102583e"),
collation: "ascii_general_ci");
migrationBuilder.CreateIndex(
name: "IX_ReceivedInvoicePayments_PaymentAdjustmentHeadId",
table: "ReceivedInvoicePayments",
column: "PaymentAdjustmentHeadId");
migrationBuilder.AddForeignKey(
name: "FK_ReceivedInvoicePayments_PaymentAdjustmentHeads_PaymentAdjust~",
table: "ReceivedInvoicePayments",
column: "PaymentAdjustmentHeadId",
principalTable: "PaymentAdjustmentHeads",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_ReceivedInvoicePayments_PaymentAdjustmentHeads_PaymentAdjust~",
table: "ReceivedInvoicePayments");
migrationBuilder.DropIndex(
name: "IX_ReceivedInvoicePayments_PaymentAdjustmentHeadId",
table: "ReceivedInvoicePayments");
migrationBuilder.DropColumn(
name: "PaymentAdjustmentHeadId",
table: "ReceivedInvoicePayments");
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,68 @@
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_Collection_Related_Permissions : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "Features",
columns: new[] { "Id", "Description", "IsActive", "ModuleId", "Name" },
values: new object[] { new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"), "Collection Management is a feature that enables organizations to track, organize, and manage the status and recovery of receivables or assets efficiently throughout their lifecycle, supporting systematic follow-up and resolution of outstanding accounts.", true, new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"), "Collection Management" });
migrationBuilder.InsertData(
table: "FeaturePermissions",
columns: new[] { "Id", "Description", "FeatureId", "IsEnabled", "Name" },
values: new object[,]
{
{ new Guid("061d9ccd-85b4-4cb0-be06-2f9f32cebb72"), " Enables entry and processing of payment transactions.", new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"), true, "Add Payment" },
{ new Guid("455187b4-fef1-41f9-b3d0-025d0b6302c3"), "Ability to modify collection properties, content, and access rights.", new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"), true, "Edit Collection" },
{ new Guid("b93141fd-dbd3-4051-8f57-bf25d18e3555"), "Authorizes users to create new collections for organizing related resources and managing access", new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"), true, "Create Collection" },
{ new Guid("c8d7eea5-4033-4aad-9ebe-76de49896830"), "View Collection is a permission that allows users to see and browse assets or items within a collection without making any modifications or edits to its contents.", new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"), true, "View Collection" },
{ new Guid("dbf17591-09fe-4c93-9e1a-12db8f5cc5de"), "Collection Admin is a permission that grants a user full administrative control over collections, including creating, editing, managing access, and deleting collections within a system.", new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"), true, "Collection Admin" }
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("061d9ccd-85b4-4cb0-be06-2f9f32cebb72"));
migrationBuilder.DeleteData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("455187b4-fef1-41f9-b3d0-025d0b6302c3"));
migrationBuilder.DeleteData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("b93141fd-dbd3-4051-8f57-bf25d18e3555"));
migrationBuilder.DeleteData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("c8d7eea5-4033-4aad-9ebe-76de49896830"));
migrationBuilder.DeleteData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("dbf17591-09fe-4c93-9e1a-12db8f5cc5de"));
migrationBuilder.DeleteData(
table: "Features",
keyColumn: "Id",
keyValue: new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"));
}
}
}

View File

@ -369,6 +369,273 @@ namespace Marco.Pms.DataAccess.Migrations
b.ToTable("RefreshTokens"); b.ToTable("RefreshTokens");
}); });
modelBuilder.Entity("Marco.Pms.Model.Collection.Invoice", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<double>("BasicAmount")
.HasColumnType("double");
b.Property<DateTime>("ClientSubmitedDate")
.HasColumnType("datetime(6)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid>("CreatedById")
.HasColumnType("char(36)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("EInvoiceNumber")
.HasColumnType("longtext");
b.Property<DateTime>("ExceptedPaymentDate")
.HasColumnType("datetime(6)");
b.Property<DateTime>("InvoiceDate")
.HasColumnType("datetime(6)");
b.Property<string>("InvoiceNumber")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<bool>("MarkAsCompleted")
.HasColumnType("tinyint(1)");
b.Property<Guid>("ProjectId")
.HasColumnType("char(36)");
b.Property<double>("TaxAmount")
.HasColumnType("double");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid?>("UpdatedById")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.HasIndex("ProjectId");
b.HasIndex("TenantId");
b.HasIndex("UpdatedById");
b.ToTable("Invoices");
});
modelBuilder.Entity("Marco.Pms.Model.Collection.InvoiceAttachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid>("DocumentId")
.HasColumnType("char(36)");
b.Property<Guid>("InvoiceId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("DocumentId");
b.HasIndex("InvoiceId");
b.HasIndex("TenantId");
b.ToTable("InvoiceAttachments");
});
modelBuilder.Entity("Marco.Pms.Model.Collection.InvoiceComment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Comment")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid>("CreatedById")
.HasColumnType("char(36)");
b.Property<Guid>("InvoiceId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.HasIndex("InvoiceId");
b.HasIndex("TenantId");
b.ToTable("InvoiceComments");
});
modelBuilder.Entity("Marco.Pms.Model.Collection.PaymentAdjustmentHead", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Description")
.HasColumnType("longtext");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("TenantId");
b.ToTable("PaymentAdjustmentHeads");
b.HasData(
new
{
Id = new Guid("dbdc047f-a2d2-4db0-b0e6-b9d9f923a0f1"),
Description = "An advance payment is a sum paid before receiving goods or services, often to secure a transaction or cover initial costs.",
IsActive = true,
Name = "Advance payment",
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("66c3c241-8b52-4327-a5ad-c1faf102583e"),
Description = "The base amount refers to the principal sum or original value used as a reference in financial calculations, excluding taxes, fees, or additional charges.",
IsActive = true,
Name = "Base Amount",
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("0d70cb2e-827e-44fc-90a5-c2c55ba51ba9"),
Description = "TDS, or Tax Deducted at Source, is a system under the Indian Income Tax Act where tax is deducted at the point of income generation—such as salary, interest, or rent—and remitted to the government to prevent tax evasion and ensure timely collection.",
IsActive = true,
Name = "Tax Deducted at Source (TDS)",
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("95f35acd-d979-4177-91ea-fd03a00e49ff"),
Description = "Retention refers to a company's ability to keep customers, employees, or profits over time, commonly measured as a percentage and critical for long-term business sustainability and growth.",
IsActive = true,
Name = "Retention",
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("3f09b19a-8d45-4cf2-be27-f4f09b38b9f7"),
Description = "Tax is a mandatory financial charge imposed by a government on individuals or entities to fund public services and government operations, without direct benefit to the taxpayer.",
IsActive = true,
Name = "Tax",
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("ec5e6a5f-ce62-44e5-8911-8426bbb4dde8"),
Description = "A penalty in the context of taxation is a financial sanction imposed by the government on individuals or entities for non-compliance with tax laws, such as late filing, underreporting income, or failure to pay taxes, and is typically calculated as a percentage of the tax due or a fixed amount.",
IsActive = true,
Name = "Penalty",
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{
Id = new Guid("50584332-1cb7-4359-9721-c8ea35040881"),
Description = "Utility fees are recurring charges for essential services such as electricity, water, gas, sewage, waste disposal, internet, and telecommunications, typically based on usage and necessary for operating a home or business.",
IsActive = true,
Name = "Utility fees",
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
});
});
modelBuilder.Entity("Marco.Pms.Model.Collection.ReceivedInvoicePayment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<double>("Amount")
.HasColumnType("double");
b.Property<string>("Comment")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid>("CreatedById")
.HasColumnType("char(36)");
b.Property<Guid>("InvoiceId")
.HasColumnType("char(36)");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<Guid>("PaymentAdjustmentHeadId")
.HasColumnType("char(36)");
b.Property<DateTime>("PaymentReceivedDate")
.HasColumnType("datetime(6)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.Property<string>("TransactionId")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.HasIndex("InvoiceId");
b.HasIndex("PaymentAdjustmentHeadId");
b.HasIndex("TenantId");
b.ToTable("ReceivedInvoicePayments");
});
modelBuilder.Entity("Marco.Pms.Model.Directory.Bucket", b => modelBuilder.Entity("Marco.Pms.Model.Directory.Bucket", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -1688,6 +1955,46 @@ namespace Marco.Pms.DataAccess.Migrations
Name = "Manage" Name = "Manage"
}, },
new new
{
Id = new Guid("dbf17591-09fe-4c93-9e1a-12db8f5cc5de"),
Description = "Collection Admin is a permission that grants a user full administrative control over collections, including creating, editing, managing access, and deleting collections within a system.",
FeatureId = new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"),
IsEnabled = true,
Name = "Collection Admin"
},
new
{
Id = new Guid("c8d7eea5-4033-4aad-9ebe-76de49896830"),
Description = "View Collection is a permission that allows users to see and browse assets or items within a collection without making any modifications or edits to its contents.",
FeatureId = new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"),
IsEnabled = true,
Name = "View Collection"
},
new
{
Id = new Guid("b93141fd-dbd3-4051-8f57-bf25d18e3555"),
Description = "Authorizes users to create new collections for organizing related resources and managing access",
FeatureId = new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"),
IsEnabled = true,
Name = "Create Collection"
},
new
{
Id = new Guid("455187b4-fef1-41f9-b3d0-025d0b6302c3"),
Description = "Ability to modify collection properties, content, and access rights.",
FeatureId = new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"),
IsEnabled = true,
Name = "Edit Collection"
},
new
{
Id = new Guid("061d9ccd-85b4-4cb0-be06-2f9f32cebb72"),
Description = " Enables entry and processing of payment transactions.",
FeatureId = new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"),
IsEnabled = true,
Name = "Add Payment"
},
new
{ {
Id = new Guid("068cb3c1-49c5-4746-9f29-1fce16e820ac"), Id = new Guid("068cb3c1-49c5-4746-9f29-1fce16e820ac"),
Description = "Allow user to create new organization", Description = "Allow user to create new organization",
@ -2829,6 +3136,14 @@ namespace Marco.Pms.DataAccess.Migrations
Name = "Expense Management" Name = "Expense Management"
}, },
new new
{
Id = new Guid("fc586e7d-ed1a-45e5-bb51-9f34af98ec13"),
Description = "Collection Management is a feature that enables organizations to track, organize, and manage the status and recovery of receivables or assets efficiently throughout their lifecycle, supporting systematic follow-up and resolution of outstanding accounts.",
IsActive = true,
ModuleId = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"),
Name = "Collection Management"
},
new
{ {
Id = new Guid("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"), Id = new Guid("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"),
Description = "Manage Tasks", Description = "Manage Tasks",
@ -4794,6 +5109,139 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("User"); b.Navigation("User");
}); });
modelBuilder.Entity("Marco.Pms.Model.Collection.Invoice", b =>
{
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
.WithMany()
.HasForeignKey("UpdatedById");
b.Navigation("CreatedBy");
b.Navigation("Project");
b.Navigation("Tenant");
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("Marco.Pms.Model.Collection.InvoiceAttachment", b =>
{
b.HasOne("Marco.Pms.Model.DocumentManager.Document", "Document")
.WithMany()
.HasForeignKey("DocumentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Collection.Invoice", "Invoice")
.WithMany()
.HasForeignKey("InvoiceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Document");
b.Navigation("Invoice");
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.Collection.InvoiceComment", b =>
{
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Collection.Invoice", "Invoice")
.WithMany()
.HasForeignKey("InvoiceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CreatedBy");
b.Navigation("Invoice");
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.Collection.PaymentAdjustmentHead", b =>
{
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.Collection.ReceivedInvoicePayment", b =>
{
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Collection.Invoice", "Invoice")
.WithMany()
.HasForeignKey("InvoiceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Collection.PaymentAdjustmentHead", "PaymentAdjustmentHead")
.WithMany()
.HasForeignKey("PaymentAdjustmentHeadId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CreatedBy");
b.Navigation("Invoice");
b.Navigation("PaymentAdjustmentHead");
b.Navigation("Tenant");
});
modelBuilder.Entity("Marco.Pms.Model.Directory.Bucket", b => modelBuilder.Entity("Marco.Pms.Model.Directory.Bucket", b =>
{ {
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy") b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")

View File

@ -212,6 +212,48 @@ namespace Marco.Pms.Helpers.CacheHelper
return true; return true;
} }
public async Task<bool> ClearAllEmployeesFromCacheByOnlyEmployeeId(Guid employeeId)
{
var employeeIdString = employeeId.ToString();
try
{
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeIdString);
var result = await _collection.DeleteManyAsync(filter);
if (result.DeletedCount == 0)
return false;
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occured while deleting employee profile");
return false;
}
}
public async Task<bool> ClearAllEmployeesFromCacheByTenantId(Guid tenantId)
{
var tenantIdString = tenantId.ToString();
try
{
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
var result = await _collection.DeleteManyAsync(filter);
if (result.DeletedCount == 0)
return false;
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occured while deleting employee profile");
return false;
}
}
public async Task<bool> ClearAllEmployeesFromCacheByEmployeeIds(List<string> employeeIds, Guid tenantId) public async Task<bool> ClearAllEmployeesFromCacheByEmployeeIds(List<string> employeeIds, Guid tenantId)
{ {
var tenantIdString = tenantId.ToString(); var tenantIdString = tenantId.ToString();

View File

@ -0,0 +1,41 @@
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.Collection
{
public class Invoice : TenantRelation
{
public Guid Id { get; set; }
public string Title { get; set; } = default!;
public string Description { get; set; } = default!;
public string InvoiceNumber { get; set; } = default!;
public string? EInvoiceNumber { get; set; }
public Guid ProjectId { get; set; }
[ValidateNever]
[ForeignKey("ProjectId")]
public Project? Project { get; set; }
public DateTime InvoiceDate { get; set; }
public DateTime ClientSubmitedDate { get; set; }
public DateTime ExceptedPaymentDate { get; set; }
public double BasicAmount { get; set; }
public double TaxAmount { get; set; }
public bool IsActive { get; set; } = true;
public bool MarkAsCompleted { get; set; } = true;
public DateTime CreatedAt { get; set; }
public Guid CreatedById { get; set; }
[ValidateNever]
[ForeignKey("CreatedById")]
public Employee? CreatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }
public Guid? UpdatedById { get; set; }
[ValidateNever]
[ForeignKey("UpdatedById")]
public Employee? UpdatedBy { get; set; }
}
}

View File

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

View File

@ -0,0 +1,24 @@
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.Collection
{
public class InvoiceComment : TenantRelation
{
public Guid Id { get; set; }
public string Comment { get; set; } = default!;
public DateTime CreatedAt { get; set; }
public Guid CreatedById { get; set; }
[ValidateNever]
[ForeignKey("CreatedById")]
public Employee? CreatedBy { get; set; }
public Guid InvoiceId { get; set; }
[ValidateNever]
[ForeignKey("InvoiceId")]
public Invoice? Invoice { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using Marco.Pms.Model.Utilities;
namespace Marco.Pms.Model.Collection
{
public class PaymentAdjustmentHead : TenantRelation
{
public Guid Id { get; set; }
public string Name { get; set; } = default!;
public string? Description { get; set; }
public bool IsActive { get; set; } = true;
}
}

View File

@ -0,0 +1,33 @@
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.Collection
{
public class ReceivedInvoicePayment : TenantRelation
{
public Guid Id { get; set; }
public Guid InvoiceId { get; set; }
[ValidateNever]
[ForeignKey("InvoiceId")]
public Invoice? Invoice { get; set; }
public DateTime PaymentReceivedDate { get; set; }
public string TransactionId { get; set; } = default!;
public double Amount { get; set; }
public string Comment { get; set; } = default!;
public bool IsActive { get; set; } = true;
public Guid PaymentAdjustmentHeadId { get; set; }
[ValidateNever]
[ForeignKey("PaymentAdjustmentHeadId")]
public PaymentAdjustmentHead? PaymentAdjustmentHead { get; set; }
public DateTime CreatedAt { get; set; }
public Guid CreatedById { get; set; }
[ValidateNever]
[ForeignKey("CreatedById")]
public Employee? CreatedBy { get; set; }
}
}

View File

@ -2,7 +2,7 @@
{ {
public class CreateWorkStatusMasterDto public class CreateWorkStatusMasterDto
{ {
public string? Name { get; set; } public required string Name { get; set; }
public string? Description { get; set; } public required string Description { get; set; }
} }
} }

View File

@ -3,7 +3,7 @@
public class UpdateWorkStatusMasterDto public class UpdateWorkStatusMasterDto
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public string? Name { get; set; } public required string Name { get; set; }
public string? Description { get; set; } public required string Description { get; set; }
} }
} }

View File

@ -0,0 +1,8 @@
namespace Marco.Pms.Model.Dtos.Collection
{
public class InvoiceCommentDto
{
public required string Comment { get; set; }
public required Guid InvoiceId { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using Marco.Pms.Model.Utilities;
namespace Marco.Pms.Model.Dtos.Collection
{
public class InvoiceDto
{
public Guid? Id { get; set; }
public required string Title { get; set; }
public string? Description { get; set; }
public required string InvoiceNumber { get; set; }
public string? EInvoiceNumber { get; set; }
public required Guid ProjectId { get; set; }
public required DateTime InvoiceDate { get; set; }
public required DateTime ClientSubmitedDate { get; set; }
public required DateTime ExceptedPaymentDate { get; set; }
public double BasicAmount { get; set; }
public double TaxAmount { get; set; }
public List<FileUploadModel>? Attachments { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace Marco.Pms.Model.Dtos.Collection
{
public class PaymentAdjustmentHeadDto
{
public Guid? Id { get; set; }
public required string Name { get; set; }
public string? Description { get; set; }
}
}

View File

@ -0,0 +1,13 @@
namespace Marco.Pms.Model.Dtos.Collection
{
public class ReceivedInvoicePaymentDto
{
public Guid? Id { get; set; }
public required Guid InvoiceId { get; set; }
public required DateTime PaymentReceivedDate { get; set; }
public required string TransactionId { get; set; }
public required Guid PaymentAdjustmentHeadId { get; set; }
public required double Amount { get; set; }
public required string Comment { get; set; }
}
}

View File

@ -2,7 +2,7 @@
{ {
public class CreateContactCategoryDto public class CreateContactCategoryDto
{ {
public string? Name { get; set; } public required string Name { get; set; }
public string? Description { get; set; } public required string Description { get; set; }
} }
} }

View File

@ -2,7 +2,7 @@
{ {
public class CreateContactTagDto public class CreateContactTagDto
{ {
public string? Name { get; set; } public required string Name { get; set; }
public string? Description { get; set; } public required string Description { get; set; }
} }
} }

View File

@ -2,8 +2,8 @@
{ {
public class UpdateContactCategoryDto public class UpdateContactCategoryDto
{ {
public Guid Id { get; set; } public required Guid Id { get; set; }
public string? Name { get; set; } public required string Name { get; set; }
public string? Description { get; set; } public required string Description { get; set; }
} }
} }

View File

@ -3,7 +3,7 @@
public class UpdateContactTagDto public class UpdateContactTagDto
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public string? Name { get; set; } public required string Name { get; set; }
public string? Description { get; set; } public required string Description { get; set; }
} }
} }

View File

@ -47,6 +47,12 @@
public static readonly Guid DownloadDocument = Guid.Parse("404373d0-860f-490e-a575-1c086ffbce1d"); public static readonly Guid DownloadDocument = Guid.Parse("404373d0-860f-490e-a575-1c086ffbce1d");
public static readonly Guid VerifyDocument = Guid.Parse("13a1f30f-38d1-41bf-8e7a-b75189aab8e0"); public static readonly Guid VerifyDocument = Guid.Parse("13a1f30f-38d1-41bf-8e7a-b75189aab8e0");
public static readonly Guid CollectionAdmin = Guid.Parse("dbf17591-09fe-4c93-9e1a-12db8f5cc5de");
public static readonly Guid ViewCollection = Guid.Parse("c8d7eea5-4033-4aad-9ebe-76de49896830");
public static readonly Guid CreateCollection = Guid.Parse("b93141fd-dbd3-4051-8f57-bf25d18e3555");
public static readonly Guid EditCollection = Guid.Parse("455187b4-fef1-41f9-b3d0-025d0b6302c3");
public static readonly Guid AddPayment = Guid.Parse("061d9ccd-85b4-4cb0-be06-2f9f32cebb72");
public static readonly Guid AddOrganization = Guid.Parse("068cb3c1-49c5-4746-9f29-1fce16e820ac"); public static readonly Guid AddOrganization = Guid.Parse("068cb3c1-49c5-4746-9f29-1fce16e820ac");
public static readonly Guid EditOrganization = Guid.Parse("c1ae1363-ab8a-4bd9-a9d1-8c2c6083873a"); public static readonly Guid EditOrganization = Guid.Parse("c1ae1363-ab8a-4bd9-a9d1-8c2c6083873a");
public static readonly Guid ViewOrganization = Guid.Parse("7a6cf830-0008-4e03-b31d-0d050cb634f4"); public static readonly Guid ViewOrganization = Guid.Parse("7a6cf830-0008-4e03-b31d-0d050cb634f4");

View File

@ -0,0 +1,16 @@
using Marco.Pms.Model.ViewModels.Activities;
namespace Marco.Pms.Model.ViewModels.Collection
{
public class InvoiceAttachmentVM
{
public Guid Id { get; set; }
public Guid InvoiceId { get; set; }
public Guid DocumentId { get; set; }
public string? FileName { get; set; }
public string? ContentType { get; set; }
public string? PreSignedUrl { get; set; }
public BasicEmployeeVM? UploadedBy { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using Marco.Pms.Model.ViewModels.Activities;
namespace Marco.Pms.Model.ViewModels.Collection
{
public class InvoiceCommentVM
{
public Guid Id { get; set; }
public string Comment { get; set; } = default!;
public DateTime CreatedAt { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; }
public Guid InvoiceId { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.Projects;
namespace Marco.Pms.Model.ViewModels.Collection
{
public class InvoiceDetailsVM
{
public Guid Id { get; set; }
public string Title { get; set; } = default!;
public string Description { get; set; } = default!;
public string InvoiceNumber { get; set; } = default!;
public string? EInvoiceNumber { get; set; }
public BasicProjectVM? Project { get; set; }
public DateTime InvoiceDate { get; set; }
public DateTime ClientSubmitedDate { get; set; }
public DateTime ExceptedPaymentDate { get; set; }
public double BasicAmount { get; set; }
public double TaxAmount { get; set; }
public double BalanceAmount { get; set; }
public bool IsActive { get; set; } = true;
public bool MarkAsCompleted { get; set; } = true;
public DateTime CreatedAt { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }
public BasicEmployeeVM? UpdatedBy { get; set; }
public List<InvoiceAttachmentVM>? Attachments { get; set; }
public List<ReceivedInvoicePaymentVM>? ReceivedInvoicePayments { get; set; }
public List<InvoiceCommentVM>? Comments { get; set; }
}
}

View File

@ -0,0 +1,27 @@
using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.Projects;
namespace Marco.Pms.Model.ViewModels.Collection
{
public class InvoiceListVM
{
public Guid Id { get; set; }
public string Title { get; set; } = default!;
public string Description { get; set; } = default!;
public string InvoiceNumber { get; set; } = default!;
public string? EInvoiceNumber { get; set; }
public BasicProjectVM? Project { get; set; }
public DateTime InvoiceDate { get; set; }
public DateTime ClientSubmitedDate { get; set; }
public DateTime ExceptedPaymentDate { get; set; }
public double BasicAmount { get; set; }
public double TaxAmount { get; set; }
public double BalanceAmount { get; set; }
public bool IsActive { get; set; }
public bool MarkAsCompleted { get; set; }
public DateTime CreatedAt { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }
public BasicEmployeeVM? UpdatedBy { get; set; }
}
}

View File

@ -0,0 +1,10 @@
namespace Marco.Pms.Model.ViewModels.Collection
{
public class PaymentAdjustmentHeadVM
{
public Guid Id { get; set; }
public string? Name { get; set; }
public string? Description { get; set; }
public bool IsActive { get; set; } = true;
}
}

View File

@ -0,0 +1,18 @@
using Marco.Pms.Model.ViewModels.Activities;
namespace Marco.Pms.Model.ViewModels.Collection
{
public class ReceivedInvoicePaymentVM
{
public Guid Id { get; set; }
public Guid InvoiceId { get; set; }
public DateTime PaymentReceivedDate { get; set; }
public PaymentAdjustmentHeadVM? PaymentAdjustmentHead { get; set; }
public string TransactionId { get; set; } = default!;
public double Amount { get; set; }
public string? Comment { get; set; }
public bool IsActive { get; set; } = true;
public DateTime CreatedAt { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; }
}
}

View File

@ -577,7 +577,6 @@ namespace Marco.Pms.Services.Controllers
{ {
new MasterMenuVM { Id = 3, Name = "Work Category" }, new MasterMenuVM { Id = 3, Name = "Work Category" },
new MasterMenuVM { Id = 8, Name = "Services" } new MasterMenuVM { Id = 8, Name = "Services" }
//new MasterMenuVM { Id = 10, Name = "Payment Mode" }
} }
}, },
{ {
@ -591,7 +590,8 @@ namespace Marco.Pms.Services.Controllers
ExpenseManagement, new List<MasterMenuVM> ExpenseManagement, new List<MasterMenuVM>
{ {
new MasterMenuVM { Id = 6, Name = "Expense Type" }, new MasterMenuVM { Id = 6, Name = "Expense Type" },
new MasterMenuVM { Id = 7, Name = "Payment Mode" } new MasterMenuVM { Id = 7, Name = "Payment Mode" },
new MasterMenuVM { Id = 10, Name = "Payment Adjustment Head" }
} }
} }
}; };
@ -665,6 +665,46 @@ namespace Marco.Pms.Services.Controllers
Available = true Available = true
}; };
response.Add(menuVM); response.Add(menuVM);
if (item.Submenu?.Any() == true)
{
var allowedSubmenus = new List<SubMenuItem>();
foreach (var subItem in item.Submenu)
{
if (!subItem.PermissionIds.Any())
{
MenuSectionApplicationVM subMenuVM = new MenuSectionApplicationVM
{
Id = subItem.Id,
Name = subItem.Text,
Available = true
};
response.Add(subMenuVM);
continue;
}
var subMenuPermissionIds = subItem.PermissionIds
.Select(Guid.Parse)
.ToList();
bool isSubItemAllowed = await _permissions.HasPermissionAny(subMenuPermissionIds, employeeId);
if (isSubItemAllowed)
{
MenuSectionApplicationVM subMenuVM = new MenuSectionApplicationVM
{
Id = subItem.Id,
Name = subItem.Text,
Available = true
};
response.Add(subMenuVM);
}
}
// Replace with filtered submenus
item.Submenu = allowedSubmenus;
}
} }
else else
{ {

View File

@ -321,7 +321,6 @@ namespace MarcoBMS.Services.Controllers
return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200)); return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
} }
[HttpGet("project/team")]
/// <summary> /// <summary>
/// Retrieves employee attendance records for a specified project and date. /// Retrieves employee attendance records for a specified project and date.
/// The result is filtered based on the logged-in employee's permissions (Team or Self). /// The result is filtered based on the logged-in employee's permissions (Team or Self).
@ -331,6 +330,8 @@ namespace MarcoBMS.Services.Controllers
/// <param name="includeInactive">Optional. Includes inactive employees in the team list if true.</param> /// <param name="includeInactive">Optional. Includes inactive employees in the team list if true.</param>
/// <param name="date">Optional. The date for which to fetch attendance, in "yyyy-MM-dd" format. Defaults to the current UTC date.</param> /// <param name="date">Optional. The date for which to fetch attendance, in "yyyy-MM-dd" format. Defaults to the current UTC date.</param>
/// <returns>An IActionResult containing a list of employee attendance records or an error response.</returns> /// <returns>An IActionResult containing a list of employee attendance records or an error response.</returns>
[HttpGet("project/team")]
public async Task<IActionResult> EmployeeAttendanceByProjectAsync([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive, [FromQuery] string? date = null) public async Task<IActionResult> EmployeeAttendanceByProjectAsync([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive, [FromQuery] string? date = null)
{ {
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
@ -390,7 +391,6 @@ namespace MarcoBMS.Services.Controllers
} }
} }
[HttpGet("regularize")] [HttpGet("regularize")]
public async Task<IActionResult> GetRequestRegularizeAttendance([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool IncludeInActive) public async Task<IActionResult> GetRequestRegularizeAttendance([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool IncludeInActive)
{ {
@ -456,8 +456,7 @@ namespace MarcoBMS.Services.Controllers
return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200)); return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
} }
[HttpPost] [HttpPost("record")]
[Route("record")]
public async Task<IActionResult> RecordAttendance([FromBody] RecordAttendanceDot recordAttendanceDot) public async Task<IActionResult> RecordAttendance([FromBody] RecordAttendanceDot recordAttendanceDot)
{ {
if (!ModelState.IsValid) if (!ModelState.IsValid)
@ -514,7 +513,7 @@ namespace MarcoBMS.Services.Controllers
{ {
DateTime date = attendance.AttendanceDate; DateTime date = attendance.AttendanceDate;
finalDateTime = GetDateFromTimeStamp(date.Date, recordAttendanceDot.MarkTime); finalDateTime = GetDateFromTimeStamp(date.Date, recordAttendanceDot.MarkTime);
if (attendance.InTime < finalDateTime) if (attendance.InTime <= finalDateTime)
{ {
attendance.OutTime = finalDateTime; attendance.OutTime = finalDateTime;
attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE; attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE;
@ -612,7 +611,8 @@ namespace MarcoBMS.Services.Controllers
LastName = employee.LastName, LastName = employee.LastName,
Id = attendance.Id, Id = attendance.Id,
Activity = attendance.Activity, Activity = attendance.Activity,
JobRoleName = employee.JobRole.Name JobRoleName = employee.JobRole.Name,
OrganizationName = employee.Organization?.Name
}; };
var sendActivity = 0; var sendActivity = 0;
if (recordAttendanceDot.Id == Guid.Empty) if (recordAttendanceDot.Id == Guid.Empty)

View File

@ -7,6 +7,7 @@ using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Tenant; using Marco.Pms.Model.ViewModels.Tenant;
using Marco.Pms.Services.Helpers;
using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@ -1469,6 +1470,9 @@ namespace MarcoBMS.Services.Controllers
// Generate and store refresh token // Generate and store refresh token
var refreshToken = await _refreshTokenService.CreateRefreshToken(loggedInEmployee.ApplicationUserId, tenantId.ToString(), loggedInEmployee.OrganizationId, _jwtSettings); var refreshToken = await _refreshTokenService.CreateRefreshToken(loggedInEmployee.ApplicationUserId, tenantId.ToString(), loggedInEmployee.OrganizationId, _jwtSettings);
var _cache = scope.ServiceProvider.GetRequiredService<CacheUpdateHelper>();
await _cache.ClearAllEmployeesFromCacheByOnlyEmployeeId(loggedInEmployee.Id);
_logger.LogInfo("Tenant selected and tokens generated for TenantId: {TenantId} and Employee: {EmployeeEmail}", tenantId, loggedInEmployee.Email ?? string.Empty); _logger.LogInfo("Tenant selected and tokens generated for TenantId: {TenantId} and Employee: {EmployeeEmail}", tenantId, loggedInEmployee.Email ?? string.Empty);
// Return success response including tokens // Return success response including tokens

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -206,7 +206,6 @@ namespace MarcoBMS.Services.Controllers
} }
} }
[HttpGet("list/{projectId?}")] [HttpGet("list/{projectId?}")]
public async Task<IActionResult> GetEmployeesByProjectAsync(Guid? projectId, [FromQuery] bool showInactive = false) public async Task<IActionResult> GetEmployeesByProjectAsync(Guid? projectId, [FromQuery] bool showInactive = false)
{ {
@ -327,7 +326,6 @@ namespace MarcoBMS.Services.Controllers
} }
} }
[HttpGet("basic")] [HttpGet("basic")]
public async Task<IActionResult> GetEmployeesByProjectBasic(Guid? projectId, [FromQuery] string? searchString, [FromQuery] bool allEmployee) public async Task<IActionResult> GetEmployeesByProjectBasic(Guid? projectId, [FromQuery] string? searchString, [FromQuery] bool allEmployee)
{ {
@ -447,7 +445,6 @@ namespace MarcoBMS.Services.Controllers
200)); 200));
} }
[HttpGet] [HttpGet]
[Route("profile/get/{employeeId}")] [Route("profile/get/{employeeId}")]
public async Task<IActionResult> GetEmployeeProfileById(Guid employeeId) public async Task<IActionResult> GetEmployeeProfileById(Guid employeeId)
@ -472,7 +469,6 @@ namespace MarcoBMS.Services.Controllers
return _userHelper.GetTenantId(); return _userHelper.GetTenantId();
} }
[HttpPost("old/manage")] [HttpPost("old/manage")]
public async Task<IActionResult> CreateUser([FromBody] CreateUserDto model) public async Task<IActionResult> CreateUser([FromBody] CreateUserDto model)
{ {
@ -802,7 +798,6 @@ namespace MarcoBMS.Services.Controllers
} }
} }
[HttpPost("manage-mobile")] [HttpPost("manage-mobile")]
public async Task<IActionResult> CreateUserMoblie([FromBody] MobileUserManageDto model) public async Task<IActionResult> CreateUserMoblie([FromBody] MobileUserManageDto model)
{ {
@ -1076,15 +1071,15 @@ namespace MarcoBMS.Services.Controllers
} }
} }
[HttpDelete("{id}")] [HttpDelete("{id}")]
public async Task<IActionResult> SuspendEmployee(Guid id, [FromQuery] bool active = false) public async Task<IActionResult> SuspendEmployee(Guid id, [FromQuery] bool active = false)
{ {
using var scope = _serviceScopeFactory.CreateScope(); using var scope = _serviceScopeFactory.CreateScope();
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
var LoggedEmployee = await _userHelper.GetCurrentEmployeeAsync(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
Employee? employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == id && e.TenantId == tenantId);
Employee? employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == id && e.OrganizationId == organizationId);
if (employee == null) if (employee == null)
{ {
_logger.LogWarning("Employee with ID {EmploueeId} not found in database", id); _logger.LogWarning("Employee with ID {EmploueeId} not found in database", id);
@ -1092,7 +1087,7 @@ namespace MarcoBMS.Services.Controllers
} }
if (employee.IsSystem) if (employee.IsSystem)
{ {
_logger.LogWarning("Employee with ID {LoggedEmployeeId} tries to suspend system-defined employee with ID {EmployeeId}", LoggedEmployee.Id, employee.Id); _logger.LogWarning("Employee with ID {LoggedEmployeeId} tries to suspend system-defined employee with ID {EmployeeId}", loggedInEmployee.Id, employee.Id);
return BadRequest(ApiResponse<object>.ErrorResponse("System-defined employees cannot be suspended.", "System-defined employees cannot be suspended.", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("System-defined employees cannot be suspended.", "System-defined employees cannot be suspended.", 400));
} }
var assignedToTasks = await _context.TaskMembers.Where(t => t.EmployeeId == employee.Id).ToListAsync(); var assignedToTasks = await _context.TaskMembers.Where(t => t.EmployeeId == employee.Id).ToListAsync();
@ -1112,11 +1107,12 @@ namespace MarcoBMS.Services.Controllers
} }
} }
var attendance = await _context.Attendes.Where(a => a.EmployeeId == employee.Id && (a.OutTime == null || a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)).ToListAsync(); var attendance = await _context.Attendes.Where(a => a.EmployeeId == employee.Id && (a.OutTime == null || a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)).ToListAsync();
if (attendance.Count != 0) if (attendance.Count != 0 && !active)
{ {
_logger.LogWarning("Employee with ID {EmployeeId} have any pending check-out or regularization requests", employee.Id); _logger.LogWarning("Employee with ID {EmployeeId} have any pending check-out or regularization requests", employee.Id);
return BadRequest(ApiResponse<object>.ErrorResponse("Employee have any pending check-out or regularization requests", "Employee have any pending check-out or regularization requests", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Employee have any pending check-out or regularization requests", "Employee have any pending check-out or regularization requests", 400));
} }
EmployeeVM employeeVM = new EmployeeVM();
if (active) if (active)
{ {
employee.IsActive = true; employee.IsActive = true;
@ -1128,6 +1124,7 @@ namespace MarcoBMS.Services.Controllers
} }
_logger.LogInfo("Employee with ID {EmployeId} Actived successfully", employee.Id); _logger.LogInfo("Employee with ID {EmployeId} Actived successfully", employee.Id);
employeeVM = _mapper.Map<EmployeeVM>(employee);
} }
else else
{ {
@ -1166,6 +1163,20 @@ namespace MarcoBMS.Services.Controllers
} }
_logger.LogInfo("Employee with ID {EmployeId} Deleted successfully", employee.Id); _logger.LogInfo("Employee with ID {EmployeId} Deleted successfully", employee.Id);
}
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_logger.LogError(ex, "Exception Occured While activting/deactivting employee {EmployeeId}", employee.Id);
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error Occured", "Error occured while saving the entity", 500));
}
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Employee", EmployeeId = employee.Id };
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>(); var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
_ = Task.Run(async () => _ = Task.Run(async () =>
@ -1177,21 +1188,7 @@ namespace MarcoBMS.Services.Controllers
await _firebase.SendEmployeeSuspendMessageAsync(employee.Id, tenantId); await _firebase.SendEmployeeSuspendMessageAsync(employee.Id, tenantId);
}); });
return Ok(ApiResponse<object>.SuccessResponse(employeeVM, "Employee Suspended successfully", 200));
}
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_logger.LogError(ex, "Exception Occured While activting/deactivting employee {EmployeeId}", employee.Id);
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error Occured", "Error occured while saving the entity", 500));
}
var notification = new { LoggedInUserId = LoggedEmployee.Id, Keyword = "Employee", EmployeeId = employee.Id };
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
return Ok(ApiResponse<object>.SuccessResponse(new { }, "Employee Suspended successfully", 200));
} }
private static Employee GetNewEmployeeModel(CreateUserDto model, Guid TenantId, string ApplicationUserId) private static Employee GetNewEmployeeModel(CreateUserDto model, Guid TenantId, string ApplicationUserId)
{ {

View File

@ -1,5 +1,6 @@
using Marco.Pms.DataAccess.Data; using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Dtos.Activities; using Marco.Pms.Model.Dtos.Activities;
using Marco.Pms.Model.Dtos.Collection;
using Marco.Pms.Model.Dtos.DocumentManager; using Marco.Pms.Model.Dtos.DocumentManager;
using Marco.Pms.Model.Dtos.Master; using Marco.Pms.Model.Dtos.Master;
using Marco.Pms.Model.Forum; using Marco.Pms.Model.Forum;
@ -975,5 +976,38 @@ namespace Marco.Pms.Services.Controllers
} }
#endregion #endregion
#region =================================================================== Payment Adjustment Head APIs ===================================================================
[HttpGet("payment-adjustment-head/list")]
public async Task<IActionResult> GetpaymentAdjustmentHeadsList([FromQuery] bool isActive = true)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _masterService.GetPaymentAdjustmentHeadListAsync(isActive, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}
[HttpPost("payment-adjustment-head")]
public async Task<IActionResult> CreatePaymentAdjustmentHead([FromBody] PaymentAdjustmentHeadDto dto)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _masterService.CreatePaymentAdjustmentHeadAsync(dto, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}
[HttpPut("payment-adjustment-head/edit/{id}")]
public async Task<IActionResult> UpdatePaymentAdjustmentHead(Guid id, [FromBody] PaymentAdjustmentHeadDto dto)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _masterService.UpdatePaymentAdjustmentHeadAsync(id, dto, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}
[HttpDelete("payment-adjustment-head/delete/{id}")]
public async Task<IActionResult> DeletePaymentAdjustmentHead(Guid id, [FromQuery] bool isActive = false)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _masterService.DeletePaymentAdjustmentHeadAsync(id, isActive, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}
#endregion
} }
} }

View File

@ -9,6 +9,7 @@ using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.Master; using Marco.Pms.Model.ViewModels.Master;
using Marco.Pms.Model.ViewModels.Organization; using Marco.Pms.Model.ViewModels.Organization;
using Marco.Pms.Model.ViewModels.Projects; using Marco.Pms.Model.ViewModels.Projects;
using Marco.Pms.Services.Helpers;
using Marco.Pms.Services.Service; using Marco.Pms.Services.Service;
using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
@ -30,6 +31,7 @@ namespace Marco.Pms.Services.Controllers
private readonly UserHelper _userHelper; private readonly UserHelper _userHelper;
private readonly Guid tenantId; private readonly Guid tenantId;
private readonly IMapper _mapper; private readonly IMapper _mapper;
private readonly Guid loggedOrganizationId;
private readonly ILoggingService _logger; private readonly ILoggingService _logger;
private static readonly Guid PMCProvider = Guid.Parse("b1877a3b-8832-47b1-bbe3-dc7e98672f49"); private static readonly Guid PMCProvider = Guid.Parse("b1877a3b-8832-47b1-bbe3-dc7e98672f49");
@ -47,6 +49,7 @@ namespace Marco.Pms.Services.Controllers
_userHelper = userHelper ?? throw new ArgumentNullException(nameof(userHelper)); _userHelper = userHelper ?? throw new ArgumentNullException(nameof(userHelper));
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
loggedOrganizationId = _userHelper.GetCurrentOrganizationId();
tenantId = userHelper.GetTenantId(); tenantId = userHelper.GetTenantId();
} }
#region =================================================================== Get Functions =================================================================== #region =================================================================== Get Functions ===================================================================
@ -669,6 +672,8 @@ namespace Marco.Pms.Services.Controllers
Service = _mapper.Map<ServiceMasterVM>(s) Service = _mapper.Map<ServiceMasterVM>(s)
}).ToList(); }).ToList();
await AssignApplicationRoleToOrganization(organization.Id, project.TenantId);
return Ok(ApiResponse<object>.SuccessResponse(response, "Organization successfully assigned to the project", 200)); return Ok(ApiResponse<object>.SuccessResponse(response, "Organization successfully assigned to the project", 200));
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
@ -717,8 +722,14 @@ namespace Marco.Pms.Services.Controllers
return NotFound(ApiResponse<object>.ErrorResponse("Organization not found", "Organization not found in database", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Organization not found", "Organization not found in database", 404));
} }
if (organizationTenantMapping == null) if (organizationTenantMapping != null)
{ {
_logger.LogInfo("Organization {OrganizationId} is already assigned to tenant {TenantId}. No action taken.", organizationId, tenantId);
// Commit transaction anyway to complete scope cleanly (optional)
await transaction.RollbackAsync();
return StatusCode(409, ApiResponse<object>.ErrorResponse("Organization is already assigned to tenant", "Organization is already assigned to tenant", 409));
}
// Create new tenant-organization mapping if none exists // Create new tenant-organization mapping if none exists
var newMapping = new TenantOrgMapping var newMapping = new TenantOrgMapping
{ {
@ -734,17 +745,13 @@ namespace Marco.Pms.Services.Controllers
await transaction.CommitAsync(); await transaction.CommitAsync();
_logger.LogInfo("Assigned organization {OrganizationId} to tenant {TenantId} successfully.", organizationId, tenantId); _logger.LogInfo("Assigned organization {OrganizationId} to tenant {TenantId} successfully.", organizationId, tenantId);
}
else
{
_logger.LogInfo("Organization {OrganizationId} is already assigned to tenant {TenantId}. No action taken.", organizationId, tenantId);
// Commit transaction anyway to complete scope cleanly (optional)
await transaction.CommitAsync();
}
// Prepare response view model // Prepare response view model
var response = _mapper.Map<BasicOrganizationVm>(organization); var response = _mapper.Map<BasicOrganizationVm>(organization);
await AssignApplicationRoleToOrganization(organization.Id, tenantId);
return Ok(ApiResponse<object>.SuccessResponse(response, "Organization has been assigned to tenant", 200)); return Ok(ApiResponse<object>.SuccessResponse(response, "Organization has been assigned to tenant", 200));
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
@ -938,45 +945,98 @@ namespace Marco.Pms.Services.Controllers
#endregion #endregion
#region =================================================================== Helper Functions =================================================================== #region =================================================================== Helper Functions ===================================================================
//private ServicesProviderFilter? TryDeserializeServicesProviderFilter(string? filter)
//{
// if (string.IsNullOrWhiteSpace(filter))
// {
// return null;
// }
// var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; private async Task AssignApplicationRoleToOrganization(Guid organizationId, Guid tenantId)
// ServicesProviderFilter? documentFilter = null; {
if (loggedOrganizationId == organizationId)
{
return;
}
await using var _context = await _dbContextFactory.CreateDbContextAsync();
using var scope = _serviceScope.CreateScope();
// try var rootEmployee = await _context.Employees
// { .Include(e => e.ApplicationUser)
// // First, try to deserialize directly. This is the expected case (e.g., from a web client). .FirstOrDefaultAsync(e => e.ApplicationUser != null && e.ApplicationUser.IsRootUser.HasValue && e.ApplicationUser.IsRootUser.Value && e.OrganizationId == organizationId && e.IsPrimary);
// documentFilter = JsonSerializer.Deserialize<ServicesProviderFilter>(filter, options); if (rootEmployee == null)
// } {
// catch (JsonException ex) return;
// { }
// _logger.LogError(ex, "[{MethodName}] Failed to directly deserialize filter. Attempting to unescape and re-parse. Filter: {Filter}", nameof(TryDeserializeServicesProviderFilter), filter); string serviceProviderRoleName = "Service Provider Role";
// // If direct deserialization fails, it might be an escaped string (common with tools like Postman or some mobile clients). var serviceProviderRole = await _context.ApplicationRoles.FirstOrDefaultAsync(ar => ar.Role == serviceProviderRoleName && ar.TenantId == tenantId);
// try if (serviceProviderRole == null)
// { {
// // Unescape the string first, then deserialize the result. serviceProviderRole = new Model.Roles.ApplicationRole
// string unescapedJsonString = JsonSerializer.Deserialize<string>(filter, options) ?? ""; {
// if (!string.IsNullOrWhiteSpace(unescapedJsonString)) Id = Guid.NewGuid(),
// { Role = serviceProviderRoleName,
// documentFilter = JsonSerializer.Deserialize<ServicesProviderFilter>(unescapedJsonString, options); Description = serviceProviderRoleName,
// } IsSystem = true,
// } TenantId = tenantId
// catch (JsonException ex1) };
// { _context.ApplicationRoles.Add(serviceProviderRole);
// // 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(TryDeserializeServicesProviderFilter), filter);
// return null;
// }
// }
// return documentFilter;
//}
var rolePermissionMappigs = new List<RolePermissionMappings> {
new RolePermissionMappings
{
ApplicationRoleId = serviceProviderRole.Id,
FeaturePermissionId = PermissionsMaster.ViewProject
},
new RolePermissionMappings
{
ApplicationRoleId = serviceProviderRole.Id,
FeaturePermissionId = PermissionsMaster.ViewProjectInfra
},
new RolePermissionMappings
{
ApplicationRoleId = serviceProviderRole.Id,
FeaturePermissionId = PermissionsMaster.ViewTask
},
new RolePermissionMappings
{
ApplicationRoleId = serviceProviderRole.Id,
FeaturePermissionId = PermissionsMaster.ViewAllEmployees
},
new RolePermissionMappings
{
ApplicationRoleId = serviceProviderRole.Id,
FeaturePermissionId = PermissionsMaster.TeamAttendance
},
new RolePermissionMappings
{
ApplicationRoleId = serviceProviderRole.Id,
FeaturePermissionId = PermissionsMaster.AssignRoles
},
new RolePermissionMappings
{
ApplicationRoleId = serviceProviderRole.Id,
FeaturePermissionId = PermissionsMaster.ManageProjectInfra
},
new RolePermissionMappings
{
ApplicationRoleId = serviceProviderRole.Id,
FeaturePermissionId = PermissionsMaster.AssignAndReportProgress
},
new RolePermissionMappings
{
ApplicationRoleId = serviceProviderRole.Id,
FeaturePermissionId = PermissionsMaster.AddAndEditTask
}
};
_context.RolePermissionMappings.AddRange(rolePermissionMappigs);
}
_context.EmployeeRoleMappings.Add(new EmployeeRoleMapping
{
EmployeeId = rootEmployee.Id,
RoleId = serviceProviderRole.Id,
IsEnabled = true,
TenantId = tenantId
});
var _cache = scope.ServiceProvider.GetRequiredService<CacheUpdateHelper>();
await _cache.ClearAllPermissionIdsByEmployeeID(rootEmployee.Id, tenantId);
}
#endregion #endregion
} }
} }

View File

@ -41,11 +41,11 @@ namespace MarcoBMS.Services.Controllers
#region =================================================================== Project Get APIs =================================================================== #region =================================================================== Project Get APIs ===================================================================
[HttpGet("list/basic")] [HttpGet("list/basic")]
public async Task<IActionResult> GetAllProjectsBasic() public async Task<IActionResult> GetAllProjectsBasic([FromQuery] bool provideAll = false)
{ {
// Get the current user // Get the current user
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _projectServices.GetAllProjectsBasicAsync(tenantId, loggedInEmployee); var response = await _projectServices.GetAllProjectsBasicAsync(provideAll, tenantId, loggedInEmployee);
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
@ -560,6 +560,13 @@ namespace MarcoBMS.Services.Controllers
var response = await _projectServices.GetAssignedOrganizationsToProjectAsync(projectId, tenantId, loggedInEmployee); var response = await _projectServices.GetAssignedOrganizationsToProjectAsync(projectId, tenantId, loggedInEmployee);
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
[HttpGet("get/assigned/organization/dropdown/{projectId}")]
public async Task<IActionResult> GetAssignedOrganizationsToProjectForDropdownAsync(Guid projectId)
{
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _projectServices.GetAssignedOrganizationsToProjectForDropdownAsync(projectId, tenantId, loggedInEmployee);
return StatusCode(response.StatusCode, response);
}
#endregion #endregion
} }

View File

@ -1013,10 +1013,6 @@ namespace Marco.Pms.Services.Controllers
try try
{ {
_ = Task.Run(async () =>
{
await ClearPermissionForTenant();
});
var features = await _featureDetailsHelper.GetFeatureDetails(subscriptionPlan.FeaturesId); var features = await _featureDetailsHelper.GetFeatureDetails(subscriptionPlan.FeaturesId);
if (features == null) if (features == null)
{ {
@ -1127,6 +1123,9 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo("Removed {Count} role permission mappings for role {RoleId}", deleteMappings.Count, roleId); _logger.LogInfo("Removed {Count} role permission mappings for role {RoleId}", deleteMappings.Count, roleId);
} }
var _cache = scope.ServiceProvider.GetRequiredService<CacheUpdateHelper>();
await _cache.ClearAllEmployeesFromCacheByTenantId(tenant.Id);
var _masteData = scope.ServiceProvider.GetRequiredService<MasterDataService>(); var _masteData = scope.ServiceProvider.GetRequiredService<MasterDataService>();
if (features.Modules?.ProjectManagement?.Enabled ?? false) if (features.Modules?.ProjectManagement?.Enabled ?? false)
@ -1325,10 +1324,6 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo("Subscription plan changed: Tenant={TenantId}, NewPlan={PlanId}", _logger.LogInfo("Subscription plan changed: Tenant={TenantId}, NewPlan={PlanId}",
model.TenantId, model.PlanId); model.TenantId, model.PlanId);
_ = Task.Run(async () =>
{
await ClearPermissionForTenant();
});
// 8. Update tenant permissions based on subscription features. // 8. Update tenant permissions based on subscription features.
var features = await _featureDetailsHelper.GetFeatureDetails(subscriptionPlan.FeaturesId); var features = await _featureDetailsHelper.GetFeatureDetails(subscriptionPlan.FeaturesId);
@ -1374,7 +1369,8 @@ namespace Marco.Pms.Services.Controllers
var rootRoleId = await context.EmployeeRoleMappings var rootRoleId = await context.EmployeeRoleMappings
.AsNoTracking() .AsNoTracking()
.Where(er => er.EmployeeId == rootEmployee.Id && er.TenantId == model.TenantId) .Include(er => er.Role)
.Where(er => er.EmployeeId == rootEmployee.Id && er.TenantId == model.TenantId && er.Role != null && er.Role.Role == "Super User")
.Select(er => er.RoleId) .Select(er => er.RoleId)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
@ -1439,6 +1435,9 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo("Permissions revoked: {Count} for Role={RoleId}", mappingsToRemove.Count, rootRoleId); _logger.LogInfo("Permissions revoked: {Count} for Role={RoleId}", mappingsToRemove.Count, rootRoleId);
} }
var _cache = scope.ServiceProvider.GetRequiredService<CacheUpdateHelper>();
await _cache.ClearAllEmployeesFromCacheByTenantId(tenant.Id);
var _masteData = scope.ServiceProvider.GetRequiredService<MasterDataService>(); var _masteData = scope.ServiceProvider.GetRequiredService<MasterDataService>();
if (features.Modules?.ProjectManagement?.Enabled ?? false) if (features.Modules?.ProjectManagement?.Enabled ?? false)
@ -1823,19 +1822,6 @@ namespace Marco.Pms.Services.Controllers
return ApiResponse<SubscriptionPlanVM>.SuccessResponse(VM, "Success", 200); return ApiResponse<SubscriptionPlanVM>.SuccessResponse(VM, "Success", 200);
} }
private async Task ClearPermissionForTenant()
{
await using var _context = await _dbContextFactory.CreateDbContextAsync();
using var scope = _serviceScopeFactory.CreateScope();
var _cache = scope.ServiceProvider.GetRequiredService<CacheUpdateHelper>();
var _cacheLogger = scope.ServiceProvider.GetRequiredService<ILoggingService>();
var employeeIds = await _context.Employees.Where(e => e.TenantId == tenantId).Select(e => e.Id).ToListAsync();
await _cache.ClearAllEmployeesFromCacheByEmployeeIds(employeeIds, tenantId);
_cacheLogger.LogInfo("{EmployeeCount} number of employee deleted", employeeIds.Count);
}
#endregion #endregion
} }
} }

View File

@ -956,6 +956,28 @@ namespace Marco.Pms.Services.Helpers
_logger.LogError(ex, "Error occured while deleting all employees from Cache"); _logger.LogError(ex, "Error occured while deleting all employees from Cache");
} }
} }
public async Task ClearAllEmployeesFromCacheByOnlyEmployeeId(Guid employeeId)
{
try
{
var response = await _employeeCache.ClearAllEmployeesFromCacheByOnlyEmployeeId(employeeId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occured while deleting all employees from Cache");
}
}
public async Task ClearAllEmployeesFromCacheByTenantId(Guid tenantId)
{
try
{
var response = await _employeeCache.ClearAllEmployeesFromCacheByTenantId(tenantId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occured while deleting all employees from Cache");
}
}
public async Task ClearAllEmployees() public async Task ClearAllEmployees()
{ {
try try

View File

@ -24,7 +24,7 @@ namespace MarcoBMS.Services.Helpers
public async Task<Employee> GetEmployeeByID(Guid EmployeeID) public async Task<Employee> GetEmployeeByID(Guid EmployeeID)
{ {
return await _context.Employees.Include(e => e.JobRole).FirstOrDefaultAsync(e => e.Id == EmployeeID) ?? new Employee { }; return await _context.Employees.Include(e => e.JobRole).Include(e => e.Organization).FirstOrDefaultAsync(e => e.Id == EmployeeID) ?? new Employee { };
} }
public async Task<Employee> GetEmployeeByApplicationUserID(string ApplicationUserID) public async Task<Employee> GetEmployeeByApplicationUserID(string ApplicationUserID)

View File

@ -1,9 +1,11 @@
using AutoMapper; using AutoMapper;
using Marco.Pms.Model.AppMenu; using Marco.Pms.Model.AppMenu;
using Marco.Pms.Model.Collection;
using Marco.Pms.Model.Directory; using Marco.Pms.Model.Directory;
using Marco.Pms.Model.DocumentManager; using Marco.Pms.Model.DocumentManager;
using Marco.Pms.Model.Dtos.Activities; using Marco.Pms.Model.Dtos.Activities;
using Marco.Pms.Model.Dtos.AppMenu; using Marco.Pms.Model.Dtos.AppMenu;
using Marco.Pms.Model.Dtos.Collection;
using Marco.Pms.Model.Dtos.Directory; using Marco.Pms.Model.Dtos.Directory;
using Marco.Pms.Model.Dtos.DocumentManager; using Marco.Pms.Model.Dtos.DocumentManager;
using Marco.Pms.Model.Dtos.Employees; using Marco.Pms.Model.Dtos.Employees;
@ -26,6 +28,7 @@ using Marco.Pms.Model.Projects;
using Marco.Pms.Model.TenantModels; using Marco.Pms.Model.TenantModels;
using Marco.Pms.Model.TenantModels.MongoDBModel; using Marco.Pms.Model.TenantModels.MongoDBModel;
using Marco.Pms.Model.ViewModels.Activities; using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.Collection;
using Marco.Pms.Model.ViewModels.Directory; using Marco.Pms.Model.ViewModels.Directory;
using Marco.Pms.Model.ViewModels.DocumentManager; using Marco.Pms.Model.ViewModels.DocumentManager;
using Marco.Pms.Model.ViewModels.Employee; using Marco.Pms.Model.ViewModels.Employee;
@ -255,6 +258,19 @@ namespace Marco.Pms.Services.MappingProfiles
#endregion #endregion
#region ======================================================= Collection =======================================================
CreateMap<InvoiceDto, Invoice>();
CreateMap<Invoice, InvoiceListVM>();
CreateMap<Invoice, InvoiceDetailsVM>();
CreateMap<ReceivedInvoicePaymentDto, ReceivedInvoicePayment>();
CreateMap<ReceivedInvoicePayment, ReceivedInvoicePaymentVM>();
CreateMap<InvoiceComment, InvoiceCommentVM>();
CreateMap<InvoiceAttachment, InvoiceAttachmentVM>();
#endregion
#region ======================================================= Master ======================================================= #region ======================================================= Master =======================================================
CreateMap<FeaturePermission, FeaturePermissionVM>(); CreateMap<FeaturePermission, FeaturePermissionVM>();
@ -379,6 +395,35 @@ namespace Marco.Pms.Services.MappingProfiles
#endregion #endregion
#region ======================================================= Contact Category Master =======================================================
CreateMap<CreateContactCategoryDto, ContactCategoryMaster>();
CreateMap<UpdateContactCategoryDto, ContactCategoryMaster>();
CreateMap<ContactCategoryMaster, ContactCategoryVM>();
#endregion
#region ======================================================= Contact Tag Master =======================================================
CreateMap<CreateContactTagDto, ContactTagMaster>();
CreateMap<UpdateContactTagDto, ContactTagMaster>();
CreateMap<ContactTagMaster, ContactTagVM>();
#endregion
#region ======================================================= Payment Adjustment Head Master =======================================================
CreateMap<PaymentAdjustmentHeadDto, PaymentAdjustmentHead>();
CreateMap<PaymentAdjustmentHead, PaymentAdjustmentHeadVM>();
#endregion
#region ======================================================= Expenses Status Master =======================================================
#endregion
#region ======================================================= Expenses Status Master =======================================================
#endregion
#region ======================================================= Expenses Status Master =======================================================
#endregion
#region ======================================================= Expenses Status Master =======================================================
#endregion
#endregion #endregion
#region ======================================================= Document ======================================================= #region ======================================================= Document =======================================================

File diff suppressed because it is too large Load Diff

View File

@ -54,7 +54,7 @@ namespace Marco.Pms.Services.Service
#region =================================================================== Project Get APIs =================================================================== #region =================================================================== Project Get APIs ===================================================================
public async Task<ApiResponse<object>> GetAllProjectsBasicAsync(Guid tenantId, Employee loggedInEmployee) public async Task<ApiResponse<object>> GetAllProjectsBasicAsync(bool provideAll, Guid tenantId, Employee loggedInEmployee)
{ {
try try
{ {
@ -67,7 +67,15 @@ namespace Marco.Pms.Services.Service
_logger.LogInfo("Basic project list requested by EmployeeId {EmployeeId}", loggedInEmployee.Id); _logger.LogInfo("Basic project list requested by EmployeeId {EmployeeId}", loggedInEmployee.Id);
// Step 2: Get the list of project IDs the user has access to // Step 2: Get the list of project IDs the user has access to
List<Guid> accessibleProjectIds = await GetMyProjects(tenantId, loggedInEmployee); List<Guid> accessibleProjectIds = new List<Guid>();
if (provideAll)
{
accessibleProjectIds = await _context.Projects.Where(p => p.TenantId == tenantId).Select(p => p.Id).ToListAsync();
}
else
{
accessibleProjectIds = await GetMyProjects(tenantId, loggedInEmployee);
}
if (accessibleProjectIds == null || !accessibleProjectIds.Any()) if (accessibleProjectIds == null || !accessibleProjectIds.Any())
{ {
@ -2805,6 +2813,128 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Internal error", "An internal exception occurred", 500); return ApiResponse<object>.ErrorResponse("Internal error", "An internal exception occurred", 500);
} }
} }
public async Task<ApiResponse<object>> GetAssignedOrganizationsToProjectForDropdownAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee)
{
_logger.LogDebug("Started fetching assigned organizations for ProjectId: {ProjectId} and TenantId: {TenantId} by user {UserId}",
projectId, tenantId, loggedInEmployee.Id);
try
{
// Create a scoped PermissionServices instance for permission checks
using var scope = _serviceScopeFactory.CreateScope();
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// Retrieve the project by projectId and tenantId
var projectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Projects.AsNoTracking().Include(p => p.Promoter).Include(p => p.PMC).FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId);
});
var tenantTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Tenants.AsNoTracking().Include(t => t.Organization).FirstOrDefaultAsync(t => t.Id == tenantId);
});
var projectServiceTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.ProjectServiceMappings
.AsNoTracking()
.Include(ps => ps!.Service)
.Where(ps => ps.ProjectId == projectId && ps.TenantId == tenantId).ToListAsync();
});
await Task.WhenAll(projectTask, tenantTask, projectServiceTask);
var project = projectTask.Result;
var tenant = tenantTask.Result;
var projectService = projectServiceTask.Result;
if (project == null || tenant == null)
{
_logger.LogWarning("Project {ProjectId} not found in database for tenant {TenantId}", projectId, tenantId);
return ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 404);
}
// Check if the logged in employee has permission to access the project
var hasPermission = await permissionService.HasProjectPermission(loggedInEmployee, projectId);
if (!hasPermission)
{
_logger.LogWarning("Access denied for user {UserId} on project {ProjectId}", loggedInEmployee.Id, projectId);
return ApiResponse<object>.ErrorResponse("Access Denied", "You do not have permission to access this project.", 403);
}
// Fetch all project-organization mappings with related service and organization data
var projectOrgMappingsQuery = _context.ProjectOrgMappings
.AsNoTracking()
.Include(po => po.ProjectService)
.ThenInclude(ps => ps!.Service)
.Include(po => po.AssignedBy)
.Include(po => po.OrganizationType)
.Include(po => po.Organization)
.Where(po => po.ProjectService != null
&& po.ProjectService.ProjectId == projectId
&& po.TenantId == tenantId);
if (loggedInEmployee.OrganizationId != project.PMCId && loggedInEmployee.OrganizationId != project.PromoterId && loggedInEmployee.OrganizationId != tenant.OrganizationId)
{
projectOrgMappingsQuery = projectOrgMappingsQuery.Where(po => po.ParentOrganizationId == loggedInEmployee.OrganizationId || po.OrganizationId == loggedInEmployee.OrganizationId);
}
var projectOrgMappings = await projectOrgMappingsQuery
.Distinct()
.ToListAsync();
var organizations = projectOrgMappings.Select(po => po.Organization!).ToList();
if (loggedInEmployee.OrganizationId == project.PMCId || loggedInEmployee.OrganizationId == project.PromoterId || loggedInEmployee.OrganizationId == tenant.OrganizationId)
{
var pmc = project.PMC;
var promoter = project.Promoter;
var organization = tenant.Organization;
if (!organizations.Any(r => r.Id == project.PMCId) && pmc != null)
{
organizations.Add(pmc);
}
if (!organizations.Any(r => r.Id == project.PromoterId) && promoter != null)
{
organizations.Add(promoter);
}
if (!organizations.Any(r => r.Id == tenant.OrganizationId) && organization != null)
{
organizations.Add(organization);
}
}
organizations = organizations.DistinctBy(o => o.Id).ToList();
// Filter and map the data to the desired view model
var response = organizations
.Select(o => new ProjectOrganizationVM
{
Id = o.Id,
Name = o.Name,
SPRID = 0
})
.ToList();
_logger.LogInfo("Fetched {Count} assigned organizations for ProjectId: {ProjectId}", response.Count, projectId);
return ApiResponse<object>.SuccessResponse(response, "Successfully fetched the list of organizations assigned to the project", 200);
}
catch (DbUpdateException dbEx)
{
_logger.LogError(dbEx, "Database exception while fetching assigned organizations for ProjectId: {ProjectId}", projectId);
return ApiResponse<object>.ErrorResponse("Internal error", "A database exception occurred", 500);
}
catch (Exception ex)
{
_logger.LogError(ex, "Unhandled exception while fetching assigned organizations for ProjectId: {ProjectId}", projectId);
return ApiResponse<object>.ErrorResponse("Internal error", "An internal exception occurred", 500);
}
}
#endregion #endregion

View File

@ -1,4 +1,5 @@
using Marco.Pms.Model.Dtos.Activities; using Marco.Pms.Model.Dtos.Activities;
using Marco.Pms.Model.Dtos.Collection;
using Marco.Pms.Model.Dtos.DocumentManager; using Marco.Pms.Model.Dtos.DocumentManager;
using Marco.Pms.Model.Dtos.Master; using Marco.Pms.Model.Dtos.Master;
using Marco.Pms.Model.Employees; using Marco.Pms.Model.Employees;
@ -46,6 +47,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
Task<ApiResponse<object>> DeleteActivityGroupAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId); Task<ApiResponse<object>> DeleteActivityGroupAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId);
#endregion #endregion
#region =================================================================== Contact Category APIs =================================================================== #region =================================================================== Contact Category APIs ===================================================================
Task<ApiResponse<object>> CreateContactCategory(CreateContactCategoryDto contactCategoryDto, Employee loggedInEmployee, Guid tenantId); Task<ApiResponse<object>> CreateContactCategory(CreateContactCategoryDto contactCategoryDto, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> UpdateContactCategory(Guid id, UpdateContactCategoryDto contactCategoryDto, Employee loggedInEmployee, Guid tenantId); Task<ApiResponse<object>> UpdateContactCategory(Guid id, UpdateContactCategoryDto contactCategoryDto, Employee loggedInEmployee, Guid tenantId);
@ -104,5 +106,12 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
Task<ApiResponse<object>> UpdateDocumentTypeMasterAsync(Guid id, CreateDocumentTypeDto model, Employee loggedInEmployee, Guid tenantId); Task<ApiResponse<object>> UpdateDocumentTypeMasterAsync(Guid id, CreateDocumentTypeDto model, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> DeleteDocumentTypeMasterAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId); Task<ApiResponse<object>> DeleteDocumentTypeMasterAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId);
#endregion #endregion
#region =================================================================== Payment Adjustment Head APIs ===================================================================
Task<ApiResponse<object>> GetPaymentAdjustmentHeadListAsync(bool isActive, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> CreatePaymentAdjustmentHeadAsync(PaymentAdjustmentHeadDto model, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> UpdatePaymentAdjustmentHeadAsync(Guid id, PaymentAdjustmentHeadDto model, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> DeletePaymentAdjustmentHeadAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId);
#endregion
} }
} }

View File

@ -10,7 +10,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
{ {
public interface IProjectServices public interface IProjectServices
{ {
Task<ApiResponse<object>> GetAllProjectsBasicAsync(Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetAllProjectsBasicAsync(bool provideAll, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetAllProjectsAsync(Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetAllProjectsAsync(Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetProjectAsync(Guid id, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetProjectAsync(Guid id, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetProjectDetailsAsync(Guid id, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetProjectDetailsAsync(Guid id, Guid tenantId, Employee loggedInEmployee);
@ -49,6 +49,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
Task<ApiResponse<object>> DeassignServiceToProjectAsync(DeassignServiceDto model, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> DeassignServiceToProjectAsync(DeassignServiceDto model, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetAssignedOrganizationsToProjectAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetAssignedOrganizationsToProjectAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetAssignedOrganizationsToProjectForDropdownAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee);
} }
} }