Added API to get plan List and add subscription to specific tenant

This commit is contained in:
ashutosh.nehete 2025-08-04 12:10:52 +05:30
parent 001bb6447d
commit 53a2c5d87c
36 changed files with 9061 additions and 100 deletions

View File

@ -0,0 +1,53 @@
using Marco.Pms.Model.TenantModels.MongoDBModel;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
namespace Marco.Pms.CacheHelper
{
public class FeatureDetailsHelper
{
private readonly IMongoCollection<FeatureDetails> _collection;
private readonly ILogger<FeatureDetailsHelper> _logger;
public FeatureDetailsHelper(IConfiguration configuration, ILogger<FeatureDetailsHelper> logger)
{
_logger = logger;
var connectionString = configuration["MongoDB:ModificationConnectionString"];
var mongoUrl = new MongoUrl(connectionString);
var client = new MongoClient(mongoUrl); // Your MongoDB connection string
var mongoDB = client.GetDatabase(mongoUrl.DatabaseName); // Your MongoDB Database name
_collection = mongoDB.GetCollection<FeatureDetails>("FeatureDetails");
}
public async Task<FeatureDetails?> GetFeatureDetails(Guid Id)
{
try
{
var filter = Builders<FeatureDetails>.Filter.Eq(e => e.Id, Id);
var result = await _collection
.Find(filter)
.FirstOrDefaultAsync();
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception occured while fetchig features for subscription plan");
return null;
}
}
public async Task<bool> AddFeatureDetails(FeatureDetails featureDetails)
{
try
{
await _collection.InsertOneAsync(featureDetails);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception occured while fetchig features for subscription plan");
return false;
}
}
}
}

View File

@ -11,6 +11,7 @@ using Marco.Pms.Model.Master;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Roles;
using Marco.Pms.Model.TenantModel;
using Marco.Pms.Model.TenantModels;
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
@ -32,7 +33,10 @@ namespace Marco.Pms.DataAccess.Data
public DbSet<RefreshToken> RefreshTokens { get; set; }
public DbSet<TenantStatus> TenantStatus { get; set; }
public DbSet<SubscriptionStatus> SubscriptionStatus { get; set; }
public DbSet<Tenant> Tenants { get; set; }
public DbSet<SubscriptionPlan> SubscriptionPlans { get; set; }
public DbSet<TenantSubscriptions> TenantSubscriptions { get; set; }
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<ActivityMaster> ActivityMasters { get; set; }
public DbSet<Project> Projects { get; set; }
@ -54,6 +58,7 @@ namespace Marco.Pms.DataAccess.Data
public DbSet<Module> Modules { get; set; }
public DbSet<Feature> Features { get; set; }
public DbSet<FeaturePermission> FeaturePermissions { get; set; }
public DbSet<CurrencyMaster> CurrencyMaster { get; set; }
public DbSet<ApplicationRole> ApplicationRoles { get; set; }
public DbSet<JobRole> JobRoles { get; set; }
public DbSet<RolePermissionMappings> RolePermissionMappings { get; set; }
@ -191,79 +196,11 @@ namespace Marco.Pms.DataAccess.Data
ProjectStatusId = new Guid("b74da4c2-d07e-46f2-9919-e75e49b12731"),
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
}
//, new Project
//{
// Id = new Guid("3ef56a12-f5e5-4193-87d6-9e110ed10b86"),
// Name = "Project 2",
// ProjectAddress = "Project 2 Address",
// ContactPerson = "Project 2 Contact Person",
// StartDate = DateTime.ParseExact("2025-04-20 10:11:17.588000", "yyyy-MM-dd HH:mm:ss.ffffff", CultureInfo.InvariantCulture),
// EndDate = DateTime.ParseExact("2026-04-20 10:11:17.588000", "yyyy-MM-dd HH:mm:ss.ffffff", CultureInfo.InvariantCulture),
// ProjectStatusId = new Guid("ef1c356e-0fe0-42df-a5d3-8daee355492d"),
// TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
//}, new Project
//{
// Id = new Guid("54d013e3-0a2b-48be-85c7-5ef03492a18c"),
// Name = "Project 3",
// ProjectAddress = "Project 3 Address",
// ContactPerson = "Project 3 Contact Person",
// StartDate = DateTime.ParseExact("2025-04-20 10:11:17.588000", "yyyy-MM-dd HH:mm:ss.ffffff", CultureInfo.InvariantCulture),
// EndDate = DateTime.ParseExact("2026-04-20 10:11:17.588000", "yyyy-MM-dd HH:mm:ss.ffffff", CultureInfo.InvariantCulture),
// ProjectStatusId = new Guid("33deaef9-9af1-4f2a-b443-681ea0d04f81"),
// TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
//}
);
var tenantId = _httpContextAccessor.HttpContext?.Items["TenantId"]?.ToString();
//modelBuilder.Entity<ActivityMaster>()
// .HasData(
// new ActivityMaster
// {
// Id = new Guid("4117b7de-ef6c-461f-a2c2-64eaac5f9a11"),
// ActivityName = "Core Cutting",
// UnitOfMeasurement = UnitOfMeasurement.Number.ToString(),
// TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
// }, new ActivityMaster
// {
// Id = new Guid("1714f64d-7591-4419-bee5-118d21bb2855"),
// ActivityName = "Fabrication",
// UnitOfMeasurement = UnitOfMeasurement.Meter.ToString(),
// TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
// }, new ActivityMaster
// {
// Id = new Guid("b3f51a93-dde6-45f9-8b22-f1bf017a640b"),
// ActivityName = "Welding",
// UnitOfMeasurement = UnitOfMeasurement.Meter.ToString(),
// TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
// }, new ActivityMaster
// {
// Id = new Guid("53eedf44-4076-445f-be93-fedef17117e7"),
// ActivityName = "MS Support Fabrication",
// UnitOfMeasurement = UnitOfMeasurement.Number.ToString(),
// TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
// }, new ActivityMaster
// {
// Id = new Guid("715b9ddb-d9e2-4afa-8987-d9918905cea4"),
// ActivityName = "MS Support Hanging",
// UnitOfMeasurement = UnitOfMeasurement.Number.ToString(),
// TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
// }, new ActivityMaster
// {
// Id = new Guid("a3d191a7-a5aa-4dd8-a525-12c99263bbd6"),
// ActivityName = "Hydrant Volve",
// UnitOfMeasurement = UnitOfMeasurement.Number.ToString(),
// TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
// }, new ActivityMaster
// {
// Id = new Guid("c138a7de-713a-4bd4-8292-b0b265be77a3"),
// ActivityName = "Sprinkler Installation",
// UnitOfMeasurement = UnitOfMeasurement.Number.ToString(),
// TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
// }
// );
modelBuilder.Entity<Industry>().HasData(
new Industry { Id = Guid.Parse("15436ee3-a650-469e-bfc2-59993f7514bb"), Name = "Information Technology (IT) Services" },
new Industry { Id = Guid.Parse("0a63e657-2c5f-49b5-854b-42c978293154"), Name = "Manufacturing & Production" },
@ -483,34 +420,52 @@ namespace Marco.Pms.DataAccess.Data
modelBuilder.Entity<Module>().HasData(new Module
{
Id = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"),
Name = "Project",
Description = "Project Module",
Key = "b04da7e9-0406-409c-ac7f-b97256e6ea02"
},
new Module
{
Id = new Guid("2a231490-bcb1-4bdd-91f1-f25fb7f25b23"),
Name = "Employee",
Description = "Employee Module",
Key = "0971c7fb-6ce1-458a-ae3f-8d3205893637"
},
new Module
{
Id = new Guid("c43db8c7-ab73-47f4-9d3b-f83e81357924"),
Name = "Masters",
Description = "Masters Module",
Key = "504ec132-e6a9-422f-8f85-050602cfce05"
},
new Module
{
Id = new Guid("f482a079-4dec-4f2d-9867-6baf2a4f23d9"),
Name = "Tenant",
Description = "Tenant Module",
Key = "504ec132-e6a9-422f-8f85-050602cfce05"
});
modelBuilder.Entity<SubscriptionStatus>().HasData(
new SubscriptionStatus
{
Id = Guid.Parse("cd3a68ea-41fd-42f0-bd0c-c871c7337727"),
Name = "Active"
},
new SubscriptionStatus
{
Id = Guid.Parse("4ed487b1-af22-4e25-aecd-b63fd850cf2d"),
Name = "InActive"
},
new SubscriptionStatus
{
Id = Guid.Parse("1c0e422e-01b6-412f-b72a-1db004cc8a7f"),
Name = "Suspended"
}
);
modelBuilder.Entity<Module>().HasData(
new Module
{
Id = new Guid("bf59fd88-b57a-4d67-bf01-3780f385896b"),
Name = "Project",
Description = "Project Module",
Key = "b04da7e9-0406-409c-ac7f-b97256e6ea02"
},
new Module
{
Id = new Guid("2a231490-bcb1-4bdd-91f1-f25fb7f25b23"),
Name = "Employee",
Description = "Employee Module",
Key = "0971c7fb-6ce1-458a-ae3f-8d3205893637"
},
new Module
{
Id = new Guid("c43db8c7-ab73-47f4-9d3b-f83e81357924"),
Name = "Masters",
Description = "Masters Module",
Key = "504ec132-e6a9-422f-8f85-050602cfce05"
},
new Module
{
Id = new Guid("f482a079-4dec-4f2d-9867-6baf2a4f23d9"),
Name = "Tenant",
Description = "Tenant Module",
Key = "504ec132-e6a9-422f-8f85-050602cfce05"
});
@ -565,6 +520,65 @@ namespace Marco.Pms.DataAccess.Data
//new FeaturePermission { Id = new Guid("6b1a6d97-a951-4de5-9b19-709bac7c4f18"), FeatureId = new Guid("660131a4-788c-4739-a082-cbbf7879cbf2"), IsEnabled = true, Name = "Manage Masters", Description = "" }
);
modelBuilder.Entity<CurrencyMaster>().HasData(
new CurrencyMaster
{
Id = Guid.Parse("78e96e4a-7ce0-4164-ae3a-c833ad45ec2c"),
CurrencyCode = "INR",
CurrencyName = "Indian Rupee",
Symbol = "₹",
IsActive = true
},
new CurrencyMaster
{
Id = Guid.Parse("2f672568-a67b-4961-acb2-a8c7834e1762"),
CurrencyCode = "USD",
CurrencyName = "US Dollar",
Symbol = "$",
IsActive = true
},
new CurrencyMaster
{
Id = Guid.Parse("4d1155bb-1448-4d97-a732-96c92eb99c45"),
CurrencyCode = "EUR",
CurrencyName = "Euro",
Symbol = "€",
IsActive = true
},
new CurrencyMaster
{
Id = Guid.Parse("3e456237-ef06-4ea1-a261-188c9b0c6df6"),
CurrencyCode = "GBP",
CurrencyName = "Pound Sterling",
Symbol = "£",
IsActive = true
},
new CurrencyMaster
{
Id = Guid.Parse("297e237a-56d3-48f6-b39d-ec3991dea8bf"),
CurrencyCode = "JPY",
CurrencyName = "Japanese Yen",
Symbol = "¥",
IsActive = true
},
new CurrencyMaster
{
Id = Guid.Parse("efe9b4f6-64d6-446e-a42d-1c7aaf6dd70d"),
CurrencyCode = "RUB",
CurrencyName = "Russian Ruble",
Symbol = "₽",
IsActive = true
},
new CurrencyMaster
{
Id = Guid.Parse("b960166a-f7e9-49e3-bb4b-28511f126c08"),
CurrencyCode = "CNY",
CurrencyName = "Chinese Yuan (Renminbi)",
Symbol = "¥",
IsActive = true
}
);
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,243 @@
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_Subscription_Related_Tables : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "CurrencyMaster",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
CurrencyCode = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
CurrencyName = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Symbol = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CurrencyMaster", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "SubscriptionStatus",
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")
},
constraints: table =>
{
table.PrimaryKey("PK_SubscriptionStatus", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "SubscriptionPlans",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
PlanName = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Description = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
PriceQuarterly = table.Column<double>(type: "double", nullable: false),
PriceMonthly = table.Column<double>(type: "double", nullable: false),
PriceHalfMonthly = table.Column<double>(type: "double", nullable: false),
PriceYearly = table.Column<double>(type: "double", nullable: false),
TrialDays = table.Column<int>(type: "int", nullable: false),
MaxUser = table.Column<double>(type: "double", nullable: false),
MaxStorage = table.Column<double>(type: "double", nullable: false),
FeaturesId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
CreateAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdateAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
CurrencyId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_SubscriptionPlans", x => x.Id);
table.ForeignKey(
name: "FK_SubscriptionPlans_CurrencyMaster_CurrencyId",
column: x => x.CurrencyId,
principalTable: "CurrencyMaster",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_SubscriptionPlans_Employees_CreatedById",
column: x => x.CreatedById,
principalTable: "Employees",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_SubscriptionPlans_Employees_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Employees",
principalColumn: "Id");
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "TenantSubscriptions",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
PlanId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
StartDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
EndDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
IsTrial = table.Column<bool>(type: "tinyint(1)", nullable: false),
StatusId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
CurrencyId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
NextBillingDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CancellationDate = table.Column<DateTime>(type: "datetime(6)", nullable: true),
AutoRemew = table.Column<bool>(type: "tinyint(1)", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdateAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
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_TenantSubscriptions", x => x.Id);
table.ForeignKey(
name: "FK_TenantSubscriptions_CurrencyMaster_CurrencyId",
column: x => x.CurrencyId,
principalTable: "CurrencyMaster",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_TenantSubscriptions_Employees_CreatedById",
column: x => x.CreatedById,
principalTable: "Employees",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_TenantSubscriptions_Employees_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Employees",
principalColumn: "Id");
table.ForeignKey(
name: "FK_TenantSubscriptions_SubscriptionPlans_PlanId",
column: x => x.PlanId,
principalTable: "SubscriptionPlans",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_TenantSubscriptions_SubscriptionStatus_StatusId",
column: x => x.StatusId,
principalTable: "SubscriptionStatus",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_TenantSubscriptions_Tenants_TenantId",
column: x => x.TenantId,
principalTable: "Tenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.InsertData(
table: "CurrencyMaster",
columns: new[] { "Id", "CurrencyCode", "CurrencyName", "IsActive", "Symbol" },
values: new object[,]
{
{ new Guid("297e237a-56d3-48f6-b39d-ec3991dea8bf"), "JPY", "Japanese Yen", true, "¥" },
{ new Guid("2f672568-a67b-4961-acb2-a8c7834e1762"), "USD", "US Dollar", true, "$" },
{ new Guid("3e456237-ef06-4ea1-a261-188c9b0c6df6"), "GBP", "Pound Sterling", true, "£" },
{ new Guid("4d1155bb-1448-4d97-a732-96c92eb99c45"), "EUR", "Euro", true, "€" },
{ new Guid("78e96e4a-7ce0-4164-ae3a-c833ad45ec2c"), "INR", "Indian Rupee", true, "₹" },
{ new Guid("b960166a-f7e9-49e3-bb4b-28511f126c08"), "CNY", "Chinese Yuan (Renminbi)", true, "¥" },
{ new Guid("efe9b4f6-64d6-446e-a42d-1c7aaf6dd70d"), "RUB", "Russian Ruble", true, "₽" }
});
migrationBuilder.InsertData(
table: "SubscriptionStatus",
columns: new[] { "Id", "Name" },
values: new object[,]
{
{ new Guid("1c0e422e-01b6-412f-b72a-1db004cc8a7f"), "Suspended" },
{ new Guid("4ed487b1-af22-4e25-aecd-b63fd850cf2d"), "InActive" },
{ new Guid("cd3a68ea-41fd-42f0-bd0c-c871c7337727"), "Active" }
});
migrationBuilder.CreateIndex(
name: "IX_SubscriptionPlans_CreatedById",
table: "SubscriptionPlans",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_SubscriptionPlans_CurrencyId",
table: "SubscriptionPlans",
column: "CurrencyId");
migrationBuilder.CreateIndex(
name: "IX_SubscriptionPlans_UpdatedById",
table: "SubscriptionPlans",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_TenantSubscriptions_CreatedById",
table: "TenantSubscriptions",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_TenantSubscriptions_CurrencyId",
table: "TenantSubscriptions",
column: "CurrencyId");
migrationBuilder.CreateIndex(
name: "IX_TenantSubscriptions_PlanId",
table: "TenantSubscriptions",
column: "PlanId");
migrationBuilder.CreateIndex(
name: "IX_TenantSubscriptions_StatusId",
table: "TenantSubscriptions",
column: "StatusId");
migrationBuilder.CreateIndex(
name: "IX_TenantSubscriptions_TenantId",
table: "TenantSubscriptions",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_TenantSubscriptions_UpdatedById",
table: "TenantSubscriptions",
column: "UpdatedById");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "TenantSubscriptions");
migrationBuilder.DropTable(
name: "SubscriptionPlans");
migrationBuilder.DropTable(
name: "SubscriptionStatus");
migrationBuilder.DropTable(
name: "CurrencyMaster");
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Corrected_Typo_In_Subscription_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "AutoRemew",
table: "TenantSubscriptions",
newName: "AutoRenew");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "AutoRenew",
table: "TenantSubscriptions",
newName: "AutoRemew");
}
}
}

View File

@ -1469,6 +1469,90 @@ namespace Marco.Pms.DataAccess.Migrations
b.ToTable("ActivityMasters");
});
modelBuilder.Entity("Marco.Pms.Model.Master.CurrencyMaster", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("CurrencyCode")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("CurrencyName")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<string>("Symbol")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("CurrencyMaster");
b.HasData(
new
{
Id = new Guid("78e96e4a-7ce0-4164-ae3a-c833ad45ec2c"),
CurrencyCode = "INR",
CurrencyName = "Indian Rupee",
IsActive = true,
Symbol = "₹"
},
new
{
Id = new Guid("2f672568-a67b-4961-acb2-a8c7834e1762"),
CurrencyCode = "USD",
CurrencyName = "US Dollar",
IsActive = true,
Symbol = "$"
},
new
{
Id = new Guid("4d1155bb-1448-4d97-a732-96c92eb99c45"),
CurrencyCode = "EUR",
CurrencyName = "Euro",
IsActive = true,
Symbol = "€"
},
new
{
Id = new Guid("3e456237-ef06-4ea1-a261-188c9b0c6df6"),
CurrencyCode = "GBP",
CurrencyName = "Pound Sterling",
IsActive = true,
Symbol = "£"
},
new
{
Id = new Guid("297e237a-56d3-48f6-b39d-ec3991dea8bf"),
CurrencyCode = "JPY",
CurrencyName = "Japanese Yen",
IsActive = true,
Symbol = "¥"
},
new
{
Id = new Guid("efe9b4f6-64d6-446e-a42d-1c7aaf6dd70d"),
CurrencyCode = "RUB",
CurrencyName = "Russian Ruble",
IsActive = true,
Symbol = "₽"
},
new
{
Id = new Guid("b960166a-f7e9-49e3-bb4b-28511f126c08"),
CurrencyCode = "CNY",
CurrencyName = "Chinese Yuan (Renminbi)",
IsActive = true,
Symbol = "¥"
});
});
modelBuilder.Entity("Marco.Pms.Model.Master.Feature", b =>
{
b.Property<Guid>("Id")
@ -1716,6 +1800,38 @@ namespace Marco.Pms.DataAccess.Migrations
});
});
modelBuilder.Entity("Marco.Pms.Model.Master.SubscriptionStatus", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("SubscriptionStatus");
b.HasData(
new
{
Id = new Guid("cd3a68ea-41fd-42f0-bd0c-c871c7337727"),
Name = "Active"
},
new
{
Id = new Guid("4ed487b1-af22-4e25-aecd-b63fd850cf2d"),
Name = "InActive"
},
new
{
Id = new Guid("1c0e422e-01b6-412f-b72a-1db004cc8a7f"),
Name = "Suspended"
});
});
modelBuilder.Entity("Marco.Pms.Model.Master.TenantStatus", b =>
{
b.Property<Guid>("Id")
@ -2400,6 +2516,138 @@ namespace Marco.Pms.DataAccess.Migrations
});
});
modelBuilder.Entity("Marco.Pms.Model.TenantModels.SubscriptionPlan", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<DateTime>("CreateAt")
.HasColumnType("datetime(6)");
b.Property<Guid>("CreatedById")
.HasColumnType("char(36)");
b.Property<Guid>("CurrencyId")
.HasColumnType("char(36)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<Guid>("FeaturesId")
.HasColumnType("char(36)");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<double>("MaxStorage")
.HasColumnType("double");
b.Property<double>("MaxUser")
.HasColumnType("double");
b.Property<string>("PlanName")
.IsRequired()
.HasColumnType("longtext");
b.Property<double>("PriceHalfMonthly")
.HasColumnType("double");
b.Property<double>("PriceMonthly")
.HasColumnType("double");
b.Property<double>("PriceQuarterly")
.HasColumnType("double");
b.Property<double>("PriceYearly")
.HasColumnType("double");
b.Property<int>("TrialDays")
.HasColumnType("int");
b.Property<DateTime?>("UpdateAt")
.HasColumnType("datetime(6)");
b.Property<Guid?>("UpdatedById")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.HasIndex("CurrencyId");
b.HasIndex("UpdatedById");
b.ToTable("SubscriptionPlans");
});
modelBuilder.Entity("Marco.Pms.Model.TenantModels.TenantSubscriptions", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<bool>("AutoRenew")
.HasColumnType("tinyint(1)");
b.Property<DateTime?>("CancellationDate")
.HasColumnType("datetime(6)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid>("CreatedById")
.HasColumnType("char(36)");
b.Property<Guid>("CurrencyId")
.HasColumnType("char(36)");
b.Property<DateTime>("EndDate")
.HasColumnType("datetime(6)");
b.Property<bool>("IsTrial")
.HasColumnType("tinyint(1)");
b.Property<DateTime>("NextBillingDate")
.HasColumnType("datetime(6)");
b.Property<Guid>("PlanId")
.HasColumnType("char(36)");
b.Property<DateTime>("StartDate")
.HasColumnType("datetime(6)");
b.Property<Guid>("StatusId")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.Property<DateTime?>("UpdateAt")
.HasColumnType("datetime(6)");
b.Property<Guid?>("UpdatedById")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.HasIndex("CurrencyId");
b.HasIndex("PlanId");
b.HasIndex("StatusId");
b.HasIndex("TenantId");
b.HasIndex("UpdatedById");
b.ToTable("TenantSubscriptions");
});
modelBuilder.Entity("Marco.Pms.Model.Utilities.Inquiries", b =>
{
b.Property<Guid>("Id")
@ -3481,6 +3729,80 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("TenantStatus");
});
modelBuilder.Entity("Marco.Pms.Model.TenantModels.SubscriptionPlan", b =>
{
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Master.CurrencyMaster", "Currency")
.WithMany()
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
.WithMany()
.HasForeignKey("UpdatedById");
b.Navigation("CreatedBy");
b.Navigation("Currency");
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("Marco.Pms.Model.TenantModels.TenantSubscriptions", b =>
{
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Master.CurrencyMaster", "Currency")
.WithMany()
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModels.SubscriptionPlan", "Plan")
.WithMany()
.HasForeignKey("PlanId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.Master.SubscriptionStatus", "Status")
.WithMany()
.HasForeignKey("StatusId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Marco.Pms.Model.TenantModel.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("Currency");
b.Navigation("Plan");
b.Navigation("Status");
b.Navigation("Tenant");
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)

View File

@ -0,0 +1,15 @@
using Marco.Pms.Model.TenantModels;
namespace Marco.Pms.Model.Dtos.Tenant
{
public class AddSubscriptionDto
{
public Guid TenantId { get; set; }
public Guid PlanId { get; set; }
public Guid CurrencyId { get; set; }
public int MaxUsers { get; set; }
public PLAN_FREQUENCY Frequency { get; set; }
public bool IsTrial { get; set; } = false;
public bool AutoRenew { get; set; } = true;
}
}

View File

@ -0,0 +1,10 @@
namespace Marco.Pms.Model.Dtos.Tenant
{
public class AttendanceDetailsDto
{
public bool Enabled { get; set; } = false;
public bool ManualEntry { get; set; } = true;
public bool LocationTracking { get; set; } = true;
public bool ShiftManagement { get; set; } = false;
}
}

View File

@ -0,0 +1,9 @@
namespace Marco.Pms.Model.Dtos.Tenant
{
public class DirectoryDetailsDto
{
public bool Enabled { get; set; } = false;
public int BucketLimit { get; set; } = 25;
public bool OrganizationChart { get; set; } = false;
}
}

View File

@ -0,0 +1,7 @@
namespace Marco.Pms.Model.Dtos.Tenant
{
public class ExpenseModuleDetailsDto
{
public bool Enabled { get; set; } = false;
}
}

View File

@ -0,0 +1,10 @@
namespace Marco.Pms.Model.Dtos.Tenant
{
public class FeatureDetailsDto
{
public ModulesDetailsDto? Modules { get; set; }
public ReportDetailsDto? Reports { get; set; }
public SupportDetailsDto? Supports { get; set; }
public List<SubscriptionCheckListDto> SubscriptionCheckList { get; set; } = new List<SubscriptionCheckListDto>();
}
}

View File

@ -0,0 +1,10 @@
namespace Marco.Pms.Model.Dtos.Tenant
{
public class ModulesDetailsDto
{
public ProjectManagementDetailsDto? ProjectManagement { get; set; }
public AttendanceDetailsDto? Attendance { get; set; }
public DirectoryDetailsDto? Directory { get; set; }
public ExpenseModuleDetailsDto? Expense { get; set; }
}
}

View File

@ -0,0 +1,11 @@
namespace Marco.Pms.Model.Dtos.Tenant
{
public class ProjectManagementDetailsDto
{
public bool Enabled { get; set; } = false;
public int MaxProject { get; set; } = 10;
public double MaxTaskPerProject { get; set; } = 100000000;
public bool GanttChart { get; set; } = false;
public bool ResourceAllocation { get; set; } = false;
}
}

View File

@ -0,0 +1,9 @@
namespace Marco.Pms.Model.Dtos.Tenant
{
public class ReportDetailsDto
{
public bool BasicReports { get; set; } = true;
public bool CustomReports { get; set; } = false;
public List<string> ExportData { get; set; } = new List<string>();
}
}

View File

@ -0,0 +1,8 @@
namespace Marco.Pms.Model.Dtos.Tenant
{
public class SubscriptionCheckListDto
{
public string Name { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
}
}

View File

@ -0,0 +1,19 @@
namespace Marco.Pms.Model.Dtos.Tenant
{
public class SubscriptionPlanDto
{
public Guid? Id { get; set; }
public required string PlanName { get; set; }
public required string Description { get; set; }
public double PriceQuarterly { get; set; }
public double PriceMonthly { get; set; }
public double PriceHalfMonthly { get; set; }
public double PriceYearly { get; set; }
public required int TrialDays { get; set; }
public required double MaxUser { get; set; }
public double MaxStorage { get; set; }
public required FeatureDetailsDto Features { get; set; }
public Guid CurrencyId { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace Marco.Pms.Model.Dtos.Tenant
{
public class SupportDetailsDto
{
public bool EmailSupport { get; set; } = true;
public bool PhoneSupport { get; set; } = false;
public bool PrioritySupport { get; set; } = false;
}
}

View File

@ -0,0 +1,11 @@
namespace Marco.Pms.Model.Master
{
public class CurrencyMaster
{
public Guid Id { get; set; }
public string CurrencyCode { get; set; } = string.Empty;
public string CurrencyName { get; set; } = string.Empty;
public string Symbol { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
}
}

View File

@ -0,0 +1,8 @@
namespace Marco.Pms.Model.Master
{
public class SubscriptionStatus
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,16 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.TenantModels.MongoDBModel
{
public class AttendanceDetails
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; } = Guid.NewGuid();
public bool Enabled { get; set; } = false;
public bool ManualEntry { get; set; } = true;
public bool LocationTracking { get; set; } = true;
public bool ShiftManagement { get; set; } = false;
}
}

View File

@ -0,0 +1,15 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.TenantModels.MongoDBModel
{
public class DirectoryDetails
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; } = Guid.NewGuid();
public bool Enabled { get; set; } = false;
public int BucketLimit { get; set; } = 25;
public bool OrganizationChart { get; set; } = false;
}
}

View File

@ -0,0 +1,13 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.TenantModels.MongoDBModel
{
public class ExpenseModuleDetails
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; } = Guid.NewGuid();
public bool Enabled { get; set; } = false;
}
}

View File

@ -0,0 +1,18 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.TenantModels.MongoDBModel
{
public class FeatureDetails
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; } = Guid.NewGuid();
public ModulesDetails? Modules { get; set; }
public ReportDetails? Reports { get; set; }
public SupportDetails? Supports { get; set; }
public List<SubscriptionCheckList> SubscriptionCheckList { get; set; } = new List<SubscriptionCheckList>();
}
}

View File

@ -0,0 +1,16 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.TenantModels.MongoDBModel
{
public class ModulesDetails
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; } = Guid.NewGuid();
public ProjectManagementDetails? ProjectManagement { get; set; }
public AttendanceDetails? Attendance { get; set; }
public DirectoryDetails? Directory { get; set; }
public ExpenseModuleDetails? Expense { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.TenantModels.MongoDBModel
{
public class ProjectManagementDetails
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; } = Guid.NewGuid();
public bool Enabled { get; set; } = false;
public int MaxProject { get; set; } = 10;
public double MaxTaskPerProject { get; set; } = 100000000;
public bool GanttChart { get; set; } = false;
public bool ResourceAllocation { get; set; } = false;
}
}

View File

@ -0,0 +1,15 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.TenantModels.MongoDBModel
{
public class ReportDetails
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; } = Guid.NewGuid();
public bool BasicReports { get; set; } = true;
public bool CustomReports { get; set; } = false;
public List<string> ExportData { get; set; } = new List<string>();
}
}

View File

@ -0,0 +1,14 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.TenantModels.MongoDBModel
{
public class SubscriptionCheckList
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; } = Guid.NewGuid();
public string Name { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
}
}

View File

@ -0,0 +1,15 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.TenantModels.MongoDBModel
{
public class SupportDetails
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; } = Guid.NewGuid();
public bool EmailSupport { get; set; } = true;
public bool PhoneSupport { get; set; } = false;
public bool PrioritySupport { get; set; } = false;
}
}

View File

@ -0,0 +1,45 @@
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Master;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.TenantModels
{
public class SubscriptionPlan
{
public Guid Id { get; set; }
public string PlanName { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public double PriceQuarterly { get; set; }
public double PriceMonthly { get; set; }
public double PriceHalfMonthly { get; set; }
public double PriceYearly { get; set; }
public int TrialDays { get; set; } = 30;
public double MaxUser { get; set; } = 10;
public double MaxStorage { get; set; }
public Guid FeaturesId { get; set; }
public DateTime CreateAt { get; set; }
public DateTime? UpdateAt { get; set; }
public Guid CurrencyId { get; set; }
[ForeignKey("CurrencyId")]
[ValidateNever]
public CurrencyMaster? Currency { get; set; }
public Guid CreatedById { get; set; }
[ForeignKey("CreatedById")]
[ValidateNever]
public Employee? CreatedBy { get; set; }
public Guid? UpdatedById { get; set; }
[ForeignKey("UpdatedById")]
[ValidateNever]
public Employee? UpdatedBy { get; set; }
public bool IsActive { get; set; } = true;
}
public enum PLAN_FREQUENCY
{
MONTHLY = 0, QUARTERLY = 1, HALF_MONTHLY = 2, YEARLY = 3
}
}

View File

@ -0,0 +1,46 @@
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Master;
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations.Schema;
namespace Marco.Pms.Model.TenantModels
{
public class TenantSubscriptions : TenantRelation
{
public Guid Id { get; set; }
public Guid PlanId { get; set; }
[ForeignKey("PlanId")]
[ValidateNever]
public SubscriptionPlan? Plan { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public bool IsTrial { get; set; }
public Guid StatusId { get; set; }
[ForeignKey("StatusId")]
[ValidateNever]
public SubscriptionStatus? Status { get; set; }
public Guid CurrencyId { get; set; }
[ForeignKey("CurrencyId")]
[ValidateNever]
public CurrencyMaster? Currency { get; set; }
public DateTime NextBillingDate { get; set; }
public DateTime? CancellationDate { get; set; }
public bool AutoRenew { get; set; } = true;
public DateTime CreatedAt { get; set; }
public DateTime? UpdateAt { get; set; }
public Guid CreatedById { get; set; }
[ForeignKey("CreatedById")]
[ValidateNever]
public Employee? CreatedBy { get; set; }
public Guid? UpdatedById { get; set; }
[ForeignKey("UpdatedById")]
[ValidateNever]
public Employee? UpdatedBy { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using Marco.Pms.Model.Master;
using Marco.Pms.Model.TenantModels.MongoDBModel;
namespace Marco.Pms.Model.ViewModels.Tenant
{
public class SubscriptionPlanVM
{
public Guid Id { get; set; }
public string? PlanName { get; set; }
public string? Description { get; set; }
public double? Price { get; set; }
public int TrialDays { get; set; }
public double MaxUser { get; set; }
public double MaxStorage { get; set; }
public FeatureDetails? Features { get; set; }
public CurrencyMaster? Currency { get; set; }
}
}

View File

@ -11,7 +11,7 @@ namespace Marco.Pms.Model.ViewModels.Tenant
public string ContactName { get; set; } = string.Empty;
public string ContactNumber { get; set; } = string.Empty;
public string? logoImage { get; set; } // Base64
public string? OragnizationSize { get; set; }
public string? OrganizationSize { get; set; }
public Industry? Industry { get; set; }
public TenantStatus? TenantStatus { get; set; }
}

View File

@ -1,4 +1,5 @@
using AutoMapper;
using Marco.Pms.CacheHelper;
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Dtos.Tenant;
using Marco.Pms.Model.Employees;
@ -6,6 +7,8 @@ using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Roles;
using Marco.Pms.Model.TenantModel;
using Marco.Pms.Model.TenantModels;
using Marco.Pms.Model.TenantModels.MongoDBModel;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.Tenant;
@ -34,14 +37,18 @@ namespace Marco.Pms.Services.Controllers
private readonly UserManager<ApplicationUser> _userManager;
private readonly IMapper _mapper;
private readonly UserHelper _userHelper;
private readonly FeatureDetailsHelper _featureDetailsHelper;
private readonly static Guid activeStatus = Guid.Parse("62b05792-5115-4f99-8ff5-e8374859b191");
private readonly static Guid activePlanStatus = Guid.Parse("cd3a68ea-41fd-42f0-bd0c-c871c7337727");
private readonly static string AdminRoleName = "Admin";
public TenantController(IDbContextFactory<ApplicationDbContext> dbContextFactory,
IServiceScopeFactory serviceScopeFactory,
ILoggingService logger,
UserManager<ApplicationUser> userManager,
IMapper mapper,
UserHelper userHelper)
UserHelper userHelper,
FeatureDetailsHelper featureDetailsHelper)
{
_dbContextFactory = dbContextFactory;
_serviceScopeFactory = serviceScopeFactory;
@ -49,6 +56,7 @@ namespace Marco.Pms.Services.Controllers
_userManager = userManager;
_mapper = mapper;
_userHelper = userHelper;
_featureDetailsHelper = featureDetailsHelper;
}
#region =================================================================== Tenant APIs ===================================================================
@ -85,7 +93,7 @@ namespace Marco.Pms.Services.Controllers
var isRootUser = loggedInEmployee.ApplicationUser?.IsRootUser ?? false;
var hasPermission = await _permissionService.HasPermission(PermissionsMaster.ManageTenants, loggedInEmployee.Id);
if (!hasPermission && !isRootUser)
if (!hasPermission || !isRootUser)
{
_logger.LogWarning("Permission denied: User {EmployeeId} attempted to list tenants without 'ManageTenants' permission or root access.", loggedInEmployee.Id);
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access denied", "User does not have the required permissions for this action.", 403));
@ -336,6 +344,7 @@ namespace Marco.Pms.Services.Controllers
LastName = model.LastName,
Email = model.Email,
PhoneNumber = model.ContactNumber,
JoiningDate = model.OnBoardingDate,
ApplicationUserId = applicationUser.Id,
JobRole = adminJobRole, // Link to the newly created role
CurrentAddress = model.BillingAddress,
@ -387,9 +396,11 @@ namespace Marco.Pms.Services.Controllers
// Create a default project for the new tenant
var project = new Project
{
Name = $"{model.OrganizationName} - Default Project",
Name = "Default Project",
ProjectStatusId = Guid.Parse("b74da4c2-d07e-46f2-9919-e75e49b12731"), // Consider using a constant for this GUID
ProjectAddress = model.BillingAddress,
StartDate = model.OnBoardingDate,
EndDate = DateTime.MaxValue,
ContactPerson = tenant.ContactName,
TenantId = tenant.Id
};
@ -449,10 +460,156 @@ namespace Marco.Pms.Services.Controllers
#region =================================================================== Subscription APIs ===================================================================
[HttpPost("add-subscription")]
public async Task<IActionResult> AddSubscription(AddSubscriptionDto model)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
await using var _context = await _dbContextFactory.CreateDbContextAsync();
using var scope = _serviceScopeFactory.CreateScope();
var _permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// A root user should have access regardless of the specific permission.
var isRootUser = loggedInEmployee.ApplicationUser?.IsRootUser ?? false;
var hasPermission = await _permissionService.HasPermission(PermissionsMaster.ManageTenants, loggedInEmployee.Id);
if (!hasPermission || !isRootUser)
{
_logger.LogWarning("Permission denied: User {EmployeeId} attempted to list tenants without 'ManageTenants' permission or root access.", loggedInEmployee.Id);
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access denied", "User does not have the required permissions for this action.", 403));
}
var tenantSubscription = new TenantSubscriptions
{
TenantId = model.TenantId,
PlanId = model.PlanId,
StatusId = activePlanStatus,
CreatedAt = DateTime.UtcNow,
CreatedById = loggedInEmployee.Id,
CurrencyId = model.CurrencyId,
IsTrial = model.IsTrial,
StartDate = DateTime.UtcNow,
AutoRenew = model.AutoRenew
};
switch (model.Frequency)
{
case PLAN_FREQUENCY.MONTHLY:
tenantSubscription.EndDate = DateTime.UtcNow.AddMonths(1);
tenantSubscription.NextBillingDate = DateTime.UtcNow.AddMonths(1);
break;
case PLAN_FREQUENCY.QUARTERLY:
tenantSubscription.EndDate = DateTime.UtcNow.AddMonths(3);
tenantSubscription.NextBillingDate = DateTime.UtcNow.AddMonths(3);
break;
case PLAN_FREQUENCY.HALF_MONTHLY:
tenantSubscription.EndDate = DateTime.UtcNow.AddMonths(6);
tenantSubscription.NextBillingDate = DateTime.UtcNow.AddMonths(6);
break;
case PLAN_FREQUENCY.YEARLY:
tenantSubscription.EndDate = DateTime.UtcNow.AddMonths(12);
tenantSubscription.NextBillingDate = DateTime.UtcNow.AddMonths(12);
break;
}
_context.TenantSubscriptions.Add(tenantSubscription);
await _context.SaveChangesAsync();
return Ok(ApiResponse<object>.SuccessResponse(tenantSubscription, "Tenant Subscription Successfully", 200));
}
#endregion
#region =================================================================== Subscription Plan APIs ===================================================================
[HttpGet("list/subscription-plan")]
public async Task<IActionResult> GetSubscriptionPlanList([FromQuery] int frequency)
{
await using var _context = await _dbContextFactory.CreateDbContextAsync();
var plans = await _context.SubscriptionPlans.Include(s => s.Currency).ToListAsync();
var vm = await Task.WhenAll(plans.Select(async p =>
{
var response = _mapper.Map<SubscriptionPlanVM>(p);
switch (frequency)
{
case 0:
response.Price = p.PriceMonthly;
break;
case 1:
response.Price = p.PriceMonthly;
break;
case 2:
response.Price = p.PriceHalfMonthly;
break;
case 3:
response.Price = p.PriceYearly;
break;
}
response.Features = await _featureDetailsHelper.GetFeatureDetails(p.FeaturesId);
return response;
}).ToList());
return Ok(ApiResponse<object>.SuccessResponse(vm, "List of plans fetched successfully", 200));
}
[HttpPost("create/subscription-plan")]
public async Task<IActionResult> CreateSubscriptionPlan([FromBody] SubscriptionPlanDto model)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
await using var _context = await _dbContextFactory.CreateDbContextAsync();
using var scope = _serviceScopeFactory.CreateScope();
var _permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// A root user should have access regardless of the specific permission.
var isRootUser = loggedInEmployee.ApplicationUser?.IsRootUser ?? false;
var hasPermission = await _permissionService.HasPermission(PermissionsMaster.ManageTenants, loggedInEmployee.Id);
if (!hasPermission || !isRootUser)
{
_logger.LogWarning("Permission denied: User {EmployeeId} attempted to list tenants without 'ManageTenants' permission or root access.", loggedInEmployee.Id);
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access denied", "User does not have the required permissions for this action.", 403));
}
var currencyMaster = await _context.CurrencyMaster.AsNoTracking().FirstOrDefaultAsync(c => c.Id == model.CurrencyId);
if (currencyMaster == null)
{
return NotFound(ApiResponse<object>.ErrorResponse("Currency not found", "Currency not found", 404));
}
var plan = _mapper.Map<SubscriptionPlan>(model);
var features = _mapper.Map<FeatureDetails>(model.Features);
try
{
await _featureDetailsHelper.AddFeatureDetails(features);
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception occured while saving feature in mongoDB");
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500));
}
plan.FeaturesId = features.Id;
plan.CreatedById = loggedInEmployee.Id;
plan.CreateAt = DateTime.UtcNow;
_context.SubscriptionPlans.Add(plan);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException dbEx)
{
_logger.LogError(dbEx, "Database Exception occured while saving subscription plan");
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500));
}
var response = _mapper.Map<SubscriptionPlanVM>(plan);
response.Features = features;
response.Currency = currencyMaster;
return StatusCode(201, ApiResponse<object>.SuccessResponse(response, "Plan Created Successfully", 201));
}
#endregion
#region =================================================================== Helper Functions ===================================================================

View File

@ -6,6 +6,8 @@ using Marco.Pms.Model.Master;
using Marco.Pms.Model.MongoDBModels;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.TenantModel;
using Marco.Pms.Model.TenantModels;
using Marco.Pms.Model.TenantModels.MongoDBModel;
using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.Employee;
using Marco.Pms.Model.ViewModels.Projects;
@ -29,6 +31,19 @@ namespace Marco.Pms.Services.MappingProfiles
dest => dest.Name,
opt => opt.MapFrom(src => src.OrganizationName)
);
CreateMap<SubscriptionPlan, SubscriptionPlanVM>();
CreateMap<SubscriptionPlanDto, SubscriptionPlan>();
CreateMap<FeatureDetailsDto, FeatureDetails>();
CreateMap<SubscriptionCheckListDto, SubscriptionCheckList>();
CreateMap<SupportDetailsDto, SupportDetails>();
CreateMap<ReportDetailsDto, ReportDetails>();
CreateMap<ModulesDetailsDto, ModulesDetails>();
CreateMap<ProjectManagementDetailsDto, ProjectManagementDetails>();
CreateMap<AttendanceDetailsDto, AttendanceDetails>();
CreateMap<DirectoryDetailsDto, DirectoryDetails>();
CreateMap<ExpenseModuleDetailsDto, ExpenseModuleDetails>();
#endregion
#region ======================================================= Projects =======================================================

View File

@ -183,6 +183,7 @@ builder.Services.AddScoped<DirectoryHelper>();
builder.Services.AddScoped<MasterHelper>();
builder.Services.AddScoped<ReportHelper>();
builder.Services.AddScoped<CacheUpdateHelper>();
builder.Services.AddScoped<FeatureDetailsHelper>();
#endregion
#region Cache Services