This commit is contained in:
Dhananjaya99
2026-07-18 23:42:58 +05:30
parent 80b130dffb
commit 92c4b14a6c
55 changed files with 8815 additions and 43 deletions
@@ -1,6 +1,7 @@
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using ERPCore.Dtos.Auth;
using ERPCore.System.Errors;
@@ -16,9 +17,14 @@ namespace ERPCore.Infra.Auth.AuthHex;
/// </summary>
public sealed class AuthHexClient : IAuthHexClient
{
// WhenWritingNull: AuthHex's dispatcher reads payload fields as raw JsonElements and some
// (e.g. RoleManager's isSystemRole) call type-specific getters like GetBoolean() that throw
// on an explicit JSON null rather than treating it as "absent" — omit null properties instead
// of serializing them, so unset nullable request fields behave as ContainsKey == false upstream.
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
{
PropertyNameCaseInsensitive = true
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
private readonly HttpClient _http;
@@ -42,6 +48,9 @@ public sealed class AuthHexClient : IAuthHexClient
public Task<GetUserDetailsResponse> GetUserDetailsAsync(Guid userId, CancellationToken ct)
=> CallAsync<GetUserDetailsResponse>("user", "getUserDetails", new { userId }, null, ct);
public Task<List<UserTypeDto>> ListUserTypesAsync(CancellationToken ct)
=> CallAsync<List<UserTypeDto>>("user", "listUserTypes", new { }, null, ct);
public Task<List<SessionDto>> GetUserSessionsAsync(string bearerToken, CancellationToken ct)
=> CallAsync<List<SessionDto>>("user", "getUserSessions", new { }, bearerToken, ct);
@@ -103,6 +112,23 @@ public sealed class AuthHexClient : IAuthHexClient
public Task<AuthHexSessionResult> VerifyAltOtpAsync(VerifyAltOtpRequest request, CancellationToken ct)
=> CallAsync<AuthHexSessionResult>("alt", "VerifyOTP", request, null, ct);
// ---- RoleManager --------------------------------------------------
public Task<AuthHexRoleDto> CreateRoleAsync(CreateAuthHexRoleRequest request, CancellationToken ct)
=> CallAsync<AuthHexRoleDto>("role", "createRole", request, null, ct);
public Task<List<AuthHexRoleDto>> ListRolesAsync(CancellationToken ct)
=> CallAsync<List<AuthHexRoleDto>>("role", "listRoles", new { }, null, ct);
public Task<AuthHexRoleDto> GetRoleAsync(Guid roleId, CancellationToken ct)
=> CallAsync<AuthHexRoleDto>("role", "getRole", new { roleId }, null, ct);
public Task<AuthHexRoleDto> UpdateRoleAsync(UpdateAuthHexRoleRequest request, CancellationToken ct)
=> CallAsync<AuthHexRoleDto>("role", "updateRole", request, null, ct);
public Task DeleteRoleAsync(Guid roleId, CancellationToken ct)
=> CallVoidAsync("role", "deleteRole", new { roleId }, null, ct);
// ---- Transport --------------------------------------------------------
private async Task CallVoidAsync(string routeGroup, string functionName, object payload, string? bearerToken, CancellationToken ct)
@@ -16,6 +16,7 @@ public interface IAuthHexClient
Task<AuthHexSessionResult> VerifyOtpForLoginAsync(VerifyOtpForLoginRequest request, CancellationToken ct);
Task<AuthHexSessionResult> RefreshTokenAsync(string refreshToken, string? deviceName, CancellationToken ct);
Task<GetUserDetailsResponse> GetUserDetailsAsync(Guid userId, CancellationToken ct);
Task<List<UserTypeDto>> ListUserTypesAsync(CancellationToken ct);
Task<List<SessionDto>> GetUserSessionsAsync(string bearerToken, CancellationToken ct);
Task ChangeUserStatusAsync(bool isActive, string bearerToken, CancellationToken ct);
Task LockUserAccountAsync(bool isLocked, string bearerToken, CancellationToken ct);
@@ -39,4 +40,12 @@ public interface IAuthHexClient
Task<IsAvailableResponse> IsAvailableAsync(IsAvailableRequest request, CancellationToken ct);
Task<SendOtpResponse> SendOtpAsync(SendOtpRequest request, CancellationToken ct);
Task<AuthHexSessionResult> VerifyAltOtpAsync(VerifyAltOtpRequest request, CancellationToken ct);
// RoleManager (POST /api/role) — AuthHex is the source of truth for Role;
// ERPCore mirrors the result into a local shadow Role row (see RoleService).
Task<AuthHexRoleDto> CreateRoleAsync(CreateAuthHexRoleRequest request, CancellationToken ct);
Task<List<AuthHexRoleDto>> ListRolesAsync(CancellationToken ct);
Task<AuthHexRoleDto> GetRoleAsync(Guid roleId, CancellationToken ct);
Task<AuthHexRoleDto> UpdateRoleAsync(UpdateAuthHexRoleRequest request, CancellationToken ct);
Task DeleteRoleAsync(Guid roleId, CancellationToken ct);
}
@@ -0,0 +1,42 @@
using ERPCore.Domain.Entities;
using ERPCore.Domain.Enums;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ERPCore.Infra.Persistence.Configurations;
/// <summary>
/// Seeded to mirror the frontend's hardcoded sidebar
/// (ERP-core/Frontend/erp-system/components/Layouts/AppSidebar.tsx). Codes here
/// must match the <c>code</c> given to each frontend nav entry.
/// </summary>
public sealed class NavItemConfiguration : IEntityTypeConfiguration<NavItem>
{
public void Configure(EntityTypeBuilder<NavItem> builder)
{
builder.ToTable("nav_items");
builder.HasKey(n => n.NavItemId);
builder.Property(n => n.Code).IsRequired().HasMaxLength(50);
builder.HasIndex(n => n.Code).IsUnique();
builder.Property(n => n.Label).IsRequired().HasMaxLength(100);
builder.Property(n => n.Icon).HasMaxLength(50);
builder.Property(n => n.Href).HasMaxLength(200);
builder.Property(n => n.Status)
.HasConversion<string>().HasMaxLength(20).IsRequired()
.HasDefaultValue(EntityStatus.Active);
builder.HasData(
new NavItem { NavItemId = 1, Code = "dashboard", Label = "Dashboard", Href = "/dashboard", SortOrder = 1 },
new NavItem { NavItemId = 2, Code = "products", Label = "Products", Href = "/dashboard/products", SortOrder = 2 },
new NavItem { NavItemId = 3, Code = "vendors", Label = "Vendors", Href = "/dashboard/vendors", SortOrder = 3 },
new NavItem { NavItemId = 4, Code = "procurement", Label = "Procurement", Href = "/dashboard/procurement", SortOrder = 4 },
new NavItem { NavItemId = 5, Code = "receiving", Label = "Receiving", Href = "/dashboard/receiving/grn", SortOrder = 5 },
new NavItem { NavItemId = 6, Code = "stock", Label = "Stock", Href = "/dashboard/stock", SortOrder = 6 },
new NavItem { NavItemId = 7, Code = "warehouses", Label = "Warehouses", Href = "/dashboard/warehouse", SortOrder = 7 },
new NavItem { NavItemId = 8, Code = "orders", Label = "Orders", Href = "/dashboard/orders", SortOrder = 8 },
new NavItem { NavItemId = 9, Code = "settings", Label = "Settings", Href = "/dashboard/settings", SortOrder = 9 },
new NavItem { NavItemId = 10, Code = "help", Label = "Help", Href = "/dashboard/help", SortOrder = 10 }
);
}
}
@@ -0,0 +1,47 @@
using ERPCore.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ERPCore.Infra.Persistence.Configurations;
/// <summary>
/// One row per <see cref="NavItem"/>/<see cref="SubNavItem"/>, seeded in lockstep
/// with <see cref="NavItemConfiguration"/>/<see cref="SubNavItemConfiguration"/>.
/// </summary>
public sealed class PermissionConfiguration : IEntityTypeConfiguration<Permission>
{
public void Configure(EntityTypeBuilder<Permission> builder)
{
builder.ToTable("permissions");
builder.HasKey(p => p.PermissionId);
builder.Property(p => p.Code).IsRequired().HasMaxLength(80);
builder.HasIndex(p => p.Code).IsUnique();
builder.HasOne(p => p.NavItem).WithMany()
.HasForeignKey(p => p.NavItemId).OnDelete(DeleteBehavior.Cascade);
builder.HasOne(p => p.SubNavItem).WithMany()
.HasForeignKey(p => p.SubNavItemId).OnDelete(DeleteBehavior.Cascade);
builder.HasData(
new Permission { PermissionId = 1, Code = "NAV:dashboard", NavItemId = 1 },
new Permission { PermissionId = 2, Code = "NAV:products", NavItemId = 2 },
new Permission { PermissionId = 3, Code = "NAV:vendors", NavItemId = 3 },
new Permission { PermissionId = 4, Code = "NAV:procurement", NavItemId = 4 },
new Permission { PermissionId = 5, Code = "NAV:receiving", NavItemId = 5 },
new Permission { PermissionId = 6, Code = "NAV:stock", NavItemId = 6 },
new Permission { PermissionId = 7, Code = "NAV:warehouses", NavItemId = 7 },
new Permission { PermissionId = 8, Code = "NAV:orders", NavItemId = 8 },
new Permission { PermissionId = 9, Code = "NAV:settings", NavItemId = 9 },
new Permission { PermissionId = 10, Code = "NAV:help", NavItemId = 10 },
new Permission { PermissionId = 11, Code = "NAV:products.item", SubNavItemId = 1 },
new Permission { PermissionId = 12, Code = "NAV:products.category", SubNavItemId = 2 },
new Permission { PermissionId = 13, Code = "NAV:products.brand", SubNavItemId = 3 },
new Permission { PermissionId = 14, Code = "NAV:products.item-type", SubNavItemId = 4 },
new Permission { PermissionId = 15, Code = "NAV:products.uom", SubNavItemId = 5 },
new Permission { PermissionId = 16, Code = "NAV:products.configuration", SubNavItemId = 6 },
new Permission { PermissionId = 17, Code = "NAV:settings.roles", SubNavItemId = 7 },
new Permission { PermissionId = 18, Code = "NAV:settings.users", SubNavItemId = 8 }
);
}
}
@@ -0,0 +1,33 @@
using ERPCore.Domain.Entities;
using ERPCore.Domain.Enums;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ERPCore.Infra.Persistence.Configurations;
public sealed class RoleConfiguration : IEntityTypeConfiguration<Role>
{
public void Configure(EntityTypeBuilder<Role> builder)
{
builder.ToTable("roles");
builder.HasKey(r => r.RoleId);
builder.Property(r => r.AuthRoleId).HasColumnName("auth_role_id").IsRequired();
builder.HasIndex(r => r.AuthRoleId).IsUnique();
builder.Property(r => r.Code).IsRequired().HasMaxLength(50);
builder.HasIndex(r => r.Code).IsUnique();
builder.Property(r => r.Name).IsRequired().HasMaxLength(200);
builder.Property(r => r.IsSystemRole).IsRequired().HasDefaultValue(false);
builder.Property(r => r.Status)
.HasConversion<string>().HasMaxLength(20).IsRequired()
.HasDefaultValue(EntityStatus.Active);
builder.Property(r => r.CreatedAt).IsRequired();
// PostgreSQL xmin system column as the optimistic concurrency token (ETag).
builder.Property(r => r.RowVersion).IsRowVersion();
}
}
@@ -0,0 +1,19 @@
using ERPCore.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ERPCore.Infra.Persistence.Configurations;
public sealed class RolePermissionConfiguration : IEntityTypeConfiguration<RolePermission>
{
public void Configure(EntityTypeBuilder<RolePermission> builder)
{
builder.ToTable("role_permissions");
builder.HasKey(rp => new { rp.RoleId, rp.PermissionId });
builder.HasOne(rp => rp.Role).WithMany()
.HasForeignKey(rp => rp.RoleId).OnDelete(DeleteBehavior.Cascade);
builder.HasOne(rp => rp.Permission).WithMany()
.HasForeignKey(rp => rp.PermissionId).OnDelete(DeleteBehavior.Cascade);
}
}
@@ -0,0 +1,38 @@
using ERPCore.Domain.Entities;
using ERPCore.Domain.Enums;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ERPCore.Infra.Persistence.Configurations;
public sealed class SubNavItemConfiguration : IEntityTypeConfiguration<SubNavItem>
{
public void Configure(EntityTypeBuilder<SubNavItem> builder)
{
builder.ToTable("sub_nav_items");
builder.HasKey(n => n.SubNavItemId);
builder.Property(n => n.Code).IsRequired().HasMaxLength(50);
builder.HasIndex(n => n.Code).IsUnique();
builder.Property(n => n.Label).IsRequired().HasMaxLength(100);
builder.Property(n => n.Icon).HasMaxLength(50);
builder.Property(n => n.Href).HasMaxLength(200);
builder.Property(n => n.Status)
.HasConversion<string>().HasMaxLength(20).IsRequired()
.HasDefaultValue(EntityStatus.Active);
builder.HasOne(n => n.NavItem).WithMany(n => n.Children)
.HasForeignKey(n => n.NavItemId).OnDelete(DeleteBehavior.Cascade);
builder.HasData(
new SubNavItem { SubNavItemId = 1, NavItemId = 2, Code = "products.item", Label = "Item", Href = "/dashboard/products", SortOrder = 1 },
new SubNavItem { SubNavItemId = 2, NavItemId = 2, Code = "products.category", Label = "Category", Href = "/dashboard/products/categories", SortOrder = 2 },
new SubNavItem { SubNavItemId = 3, NavItemId = 2, Code = "products.brand", Label = "Brand", Href = "/dashboard/products/brands", SortOrder = 3 },
new SubNavItem { SubNavItemId = 4, NavItemId = 2, Code = "products.item-type", Label = "Item Type", Href = "/dashboard/products/item-types", SortOrder = 4 },
new SubNavItem { SubNavItemId = 5, NavItemId = 2, Code = "products.uom", Label = "UOM", Href = "/dashboard/products/uoms", SortOrder = 5 },
new SubNavItem { SubNavItemId = 6, NavItemId = 2, Code = "products.configuration", Label = "Configuration", Href = "/dashboard/products/settings", SortOrder = 6 },
new SubNavItem { SubNavItemId = 7, NavItemId = 9, Code = "settings.roles", Label = "Roles", Href = "/dashboard/settings/roles", SortOrder = 1 },
new SubNavItem { SubNavItemId = 8, NavItemId = 9, Code = "settings.users", Label = "Users", Href = "/dashboard/settings/users", SortOrder = 2 }
);
}
}
@@ -23,6 +23,10 @@ public sealed class UserConfiguration : IEntityTypeConfiguration<User>
builder.Property(u => u.AuthUserId).HasColumnName("auth_user_id");
builder.HasIndex(u => u.AuthUserId).IsUnique();
// Local shadow Role assignment (nullable — unset until an admin assigns one).
builder.HasOne(u => u.Role).WithMany()
.HasForeignKey(u => u.RoleId).OnDelete(DeleteBehavior.Restrict);
// Seeded fallback audit actor while auth is deferred (§6).
builder.HasData(new User
{
@@ -41,6 +41,13 @@ public class ErpDbContext : DbContext
public DbSet<User> Users => Set<User>();
public DbSet<NumberSequence> NumberSequences => Set<NumberSequence>();
// --- RBAC / sidebar (docs/10 Part C.8) ---
public DbSet<Role> Roles => Set<Role>();
public DbSet<NavItem> NavItems => Set<NavItem>();
public DbSet<SubNavItem> SubNavItems => Set<SubNavItem>();
public DbSet<Permission> Permissions => Set<Permission>();
public DbSet<RolePermission> RolePermissions => Set<RolePermission>();
// --- Procurement (docs/10 Part C.2) ---
public DbSet<Requisition> Requisitions => Set<Requisition>();
public DbSet<RequisitionLine> RequisitionLines => Set<RequisitionLine>();
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ERPCore.Infra.Persistence.Migrations
{
/// <inheritdoc />
public partial class ini2 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,303 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
namespace ERPCore.Infra.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddRolesNavPermissions : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "RoleId",
table: "users",
type: "integer",
nullable: true);
migrationBuilder.CreateTable(
name: "nav_items",
columns: table => new
{
NavItemId = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Code = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
Label = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
Icon = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
Href = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
SortOrder = table.Column<int>(type: "integer", nullable: false),
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false, defaultValue: "Active")
},
constraints: table =>
{
table.PrimaryKey("PK_nav_items", x => x.NavItemId);
});
migrationBuilder.CreateTable(
name: "roles",
columns: table => new
{
RoleId = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
auth_role_id = table.Column<Guid>(type: "uuid", nullable: false),
Code = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
IsSystemRole = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false, defaultValue: "Active"),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
xmin = table.Column<uint>(type: "xid", rowVersion: true, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_roles", x => x.RoleId);
});
migrationBuilder.CreateTable(
name: "sub_nav_items",
columns: table => new
{
SubNavItemId = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
NavItemId = table.Column<int>(type: "integer", nullable: false),
Code = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
Label = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
Icon = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
Href = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
SortOrder = table.Column<int>(type: "integer", nullable: false),
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false, defaultValue: "Active")
},
constraints: table =>
{
table.PrimaryKey("PK_sub_nav_items", x => x.SubNavItemId);
table.ForeignKey(
name: "FK_sub_nav_items_nav_items_NavItemId",
column: x => x.NavItemId,
principalTable: "nav_items",
principalColumn: "NavItemId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "permissions",
columns: table => new
{
PermissionId = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Code = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
NavItemId = table.Column<int>(type: "integer", nullable: true),
SubNavItemId = table.Column<int>(type: "integer", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_permissions", x => x.PermissionId);
table.ForeignKey(
name: "FK_permissions_nav_items_NavItemId",
column: x => x.NavItemId,
principalTable: "nav_items",
principalColumn: "NavItemId",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_permissions_sub_nav_items_SubNavItemId",
column: x => x.SubNavItemId,
principalTable: "sub_nav_items",
principalColumn: "SubNavItemId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "role_permissions",
columns: table => new
{
RoleId = table.Column<int>(type: "integer", nullable: false),
PermissionId = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_role_permissions", x => new { x.RoleId, x.PermissionId });
table.ForeignKey(
name: "FK_role_permissions_permissions_PermissionId",
column: x => x.PermissionId,
principalTable: "permissions",
principalColumn: "PermissionId",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_role_permissions_roles_RoleId",
column: x => x.RoleId,
principalTable: "roles",
principalColumn: "RoleId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.InsertData(
table: "nav_items",
columns: new[] { "NavItemId", "Code", "Href", "Icon", "Label", "SortOrder" },
values: new object[,]
{
{ 1, "dashboard", "/dashboard", null, "Dashboard", 1 },
{ 2, "products", "/dashboard/products", null, "Products", 2 },
{ 3, "vendors", "/dashboard/vendors", null, "Vendors", 3 },
{ 4, "procurement", "/dashboard/procurement", null, "Procurement", 4 },
{ 5, "receiving", "/dashboard/receiving/grn", null, "Receiving", 5 },
{ 6, "stock", "/dashboard/stock", null, "Stock", 6 },
{ 7, "warehouses", "/dashboard/warehouse", null, "Warehouses", 7 },
{ 8, "orders", "/dashboard/orders", null, "Orders", 8 },
{ 9, "settings", "/dashboard/settings", null, "Settings", 9 },
{ 10, "help", "/dashboard/help", null, "Help", 10 }
});
migrationBuilder.UpdateData(
table: "users",
keyColumn: "UserId",
keyValue: 1,
column: "RoleId",
value: null);
migrationBuilder.InsertData(
table: "permissions",
columns: new[] { "PermissionId", "Code", "NavItemId", "SubNavItemId" },
values: new object[,]
{
{ 1, "NAV:dashboard", 1, null },
{ 2, "NAV:products", 2, null },
{ 3, "NAV:vendors", 3, null },
{ 4, "NAV:procurement", 4, null },
{ 5, "NAV:receiving", 5, null },
{ 6, "NAV:stock", 6, null },
{ 7, "NAV:warehouses", 7, null },
{ 8, "NAV:orders", 8, null },
{ 9, "NAV:settings", 9, null },
{ 10, "NAV:help", 10, null }
});
migrationBuilder.InsertData(
table: "sub_nav_items",
columns: new[] { "SubNavItemId", "Code", "Href", "Icon", "Label", "NavItemId", "SortOrder" },
values: new object[,]
{
{ 1, "products.item", "/dashboard/products", null, "Item", 2, 1 },
{ 2, "products.category", "/dashboard/products/categories", null, "Category", 2, 2 },
{ 3, "products.brand", "/dashboard/products/brands", null, "Brand", 2, 3 },
{ 4, "products.item-type", "/dashboard/products/item-types", null, "Item Type", 2, 4 },
{ 5, "products.uom", "/dashboard/products/uoms", null, "UOM", 2, 5 },
{ 6, "products.configuration", "/dashboard/products/settings", null, "Configuration", 2, 6 },
{ 7, "settings.roles", "/dashboard/settings/roles", null, "Roles", 9, 1 },
{ 8, "settings.users", "/dashboard/settings/users", null, "Users", 9, 2 }
});
migrationBuilder.InsertData(
table: "permissions",
columns: new[] { "PermissionId", "Code", "NavItemId", "SubNavItemId" },
values: new object[,]
{
{ 11, "NAV:products.item", null, 1 },
{ 12, "NAV:products.category", null, 2 },
{ 13, "NAV:products.brand", null, 3 },
{ 14, "NAV:products.item-type", null, 4 },
{ 15, "NAV:products.uom", null, 5 },
{ 16, "NAV:products.configuration", null, 6 },
{ 17, "NAV:settings.roles", null, 7 },
{ 18, "NAV:settings.users", null, 8 }
});
migrationBuilder.CreateIndex(
name: "IX_users_RoleId",
table: "users",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "IX_nav_items_Code",
table: "nav_items",
column: "Code",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_permissions_Code",
table: "permissions",
column: "Code",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_permissions_NavItemId",
table: "permissions",
column: "NavItemId");
migrationBuilder.CreateIndex(
name: "IX_permissions_SubNavItemId",
table: "permissions",
column: "SubNavItemId");
migrationBuilder.CreateIndex(
name: "IX_role_permissions_PermissionId",
table: "role_permissions",
column: "PermissionId");
migrationBuilder.CreateIndex(
name: "IX_roles_auth_role_id",
table: "roles",
column: "auth_role_id",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_roles_Code",
table: "roles",
column: "Code",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_sub_nav_items_Code",
table: "sub_nav_items",
column: "Code",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_sub_nav_items_NavItemId",
table: "sub_nav_items",
column: "NavItemId");
migrationBuilder.AddForeignKey(
name: "FK_users_roles_RoleId",
table: "users",
column: "RoleId",
principalTable: "roles",
principalColumn: "RoleId",
onDelete: ReferentialAction.Restrict);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_users_roles_RoleId",
table: "users");
migrationBuilder.DropTable(
name: "role_permissions");
migrationBuilder.DropTable(
name: "permissions");
migrationBuilder.DropTable(
name: "roles");
migrationBuilder.DropTable(
name: "sub_nav_items");
migrationBuilder.DropTable(
name: "nav_items");
migrationBuilder.DropIndex(
name: "IX_users_RoleId",
table: "users");
migrationBuilder.DropColumn(
name: "RoleId",
table: "users");
}
}
}
@@ -524,6 +524,142 @@ namespace ERPCore.Infra.Persistence.Migrations
b.ToTable("journal_entry_stubs", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b =>
{
b.Property<int>("NavItemId")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("NavItemId"));
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Href")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("Icon")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Label")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<int>("SortOrder")
.HasColumnType("integer");
b.Property<string>("Status")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasDefaultValue("Active");
b.HasKey("NavItemId");
b.HasIndex("Code")
.IsUnique();
b.ToTable("nav_items", (string)null);
b.HasData(
new
{
NavItemId = 1,
Code = "dashboard",
Href = "/dashboard",
Label = "Dashboard",
SortOrder = 1,
Status = "Active"
},
new
{
NavItemId = 2,
Code = "products",
Href = "/dashboard/products",
Label = "Products",
SortOrder = 2,
Status = "Active"
},
new
{
NavItemId = 3,
Code = "vendors",
Href = "/dashboard/vendors",
Label = "Vendors",
SortOrder = 3,
Status = "Active"
},
new
{
NavItemId = 4,
Code = "procurement",
Href = "/dashboard/procurement",
Label = "Procurement",
SortOrder = 4,
Status = "Active"
},
new
{
NavItemId = 5,
Code = "receiving",
Href = "/dashboard/receiving/grn",
Label = "Receiving",
SortOrder = 5,
Status = "Active"
},
new
{
NavItemId = 6,
Code = "stock",
Href = "/dashboard/stock",
Label = "Stock",
SortOrder = 6,
Status = "Active"
},
new
{
NavItemId = 7,
Code = "warehouses",
Href = "/dashboard/warehouse",
Label = "Warehouses",
SortOrder = 7,
Status = "Active"
},
new
{
NavItemId = 8,
Code = "orders",
Href = "/dashboard/orders",
Label = "Orders",
SortOrder = 8,
Status = "Active"
},
new
{
NavItemId = 9,
Code = "settings",
Href = "/dashboard/settings",
Label = "Settings",
SortOrder = 9,
Status = "Active"
},
new
{
NavItemId = 10,
Code = "help",
Href = "/dashboard/help",
Label = "Help",
SortOrder = 10,
Status = "Active"
});
});
modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b =>
{
b.Property<int>("SequenceId")
@@ -554,6 +690,147 @@ namespace ERPCore.Infra.Persistence.Migrations
b.ToTable("number_sequences", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b =>
{
b.Property<int>("PermissionId")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("PermissionId"));
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<int?>("NavItemId")
.HasColumnType("integer");
b.Property<int?>("SubNavItemId")
.HasColumnType("integer");
b.HasKey("PermissionId");
b.HasIndex("Code")
.IsUnique();
b.HasIndex("NavItemId");
b.HasIndex("SubNavItemId");
b.ToTable("permissions", (string)null);
b.HasData(
new
{
PermissionId = 1,
Code = "NAV:dashboard",
NavItemId = 1
},
new
{
PermissionId = 2,
Code = "NAV:products",
NavItemId = 2
},
new
{
PermissionId = 3,
Code = "NAV:vendors",
NavItemId = 3
},
new
{
PermissionId = 4,
Code = "NAV:procurement",
NavItemId = 4
},
new
{
PermissionId = 5,
Code = "NAV:receiving",
NavItemId = 5
},
new
{
PermissionId = 6,
Code = "NAV:stock",
NavItemId = 6
},
new
{
PermissionId = 7,
Code = "NAV:warehouses",
NavItemId = 7
},
new
{
PermissionId = 8,
Code = "NAV:orders",
NavItemId = 8
},
new
{
PermissionId = 9,
Code = "NAV:settings",
NavItemId = 9
},
new
{
PermissionId = 10,
Code = "NAV:help",
NavItemId = 10
},
new
{
PermissionId = 11,
Code = "NAV:products.item",
SubNavItemId = 1
},
new
{
PermissionId = 12,
Code = "NAV:products.category",
SubNavItemId = 2
},
new
{
PermissionId = 13,
Code = "NAV:products.brand",
SubNavItemId = 3
},
new
{
PermissionId = 14,
Code = "NAV:products.item-type",
SubNavItemId = 4
},
new
{
PermissionId = 15,
Code = "NAV:products.uom",
SubNavItemId = 5
},
new
{
PermissionId = 16,
Code = "NAV:products.configuration",
SubNavItemId = 6
},
new
{
PermissionId = 17,
Code = "NAV:settings.roles",
SubNavItemId = 7
},
new
{
PermissionId = 18,
Code = "NAV:settings.users",
SubNavItemId = 8
});
});
modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b =>
{
b.Property<int>("PoLineId")
@@ -942,6 +1219,78 @@ namespace ERPCore.Infra.Persistence.Migrations
b.ToTable("rfq_lines", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Role", b =>
{
b.Property<int>("RoleId")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("RoleId"));
b.Property<Guid>("AuthRoleId")
.HasColumnType("uuid")
.HasColumnName("auth_role_id");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsSystemRole")
.ValueGeneratedOnAdd()
.HasColumnType("boolean")
.HasDefaultValue(false);
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<uint>("RowVersion")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("xid")
.HasColumnName("xmin");
b.Property<string>("Status")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasDefaultValue("Active");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("RoleId");
b.HasIndex("AuthRoleId")
.IsUnique();
b.HasIndex("Code")
.IsUnique();
b.ToTable("roles", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b =>
{
b.Property<int>("RoleId")
.HasColumnType("integer");
b.Property<int>("PermissionId")
.HasColumnType("integer");
b.HasKey("RoleId", "PermissionId");
b.HasIndex("PermissionId");
b.ToTable("role_permissions", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b =>
{
b.Property<int>("SerialId")
@@ -1439,6 +1788,137 @@ namespace ERPCore.Infra.Persistence.Migrations
b.ToTable("subcategories", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b =>
{
b.Property<int>("SubNavItemId")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SubNavItemId"));
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Href")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("Icon")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Label")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<int>("NavItemId")
.HasColumnType("integer");
b.Property<int>("SortOrder")
.HasColumnType("integer");
b.Property<string>("Status")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasDefaultValue("Active");
b.HasKey("SubNavItemId");
b.HasIndex("Code")
.IsUnique();
b.HasIndex("NavItemId");
b.ToTable("sub_nav_items", (string)null);
b.HasData(
new
{
SubNavItemId = 1,
Code = "products.item",
Href = "/dashboard/products",
Label = "Item",
NavItemId = 2,
SortOrder = 1,
Status = "Active"
},
new
{
SubNavItemId = 2,
Code = "products.category",
Href = "/dashboard/products/categories",
Label = "Category",
NavItemId = 2,
SortOrder = 2,
Status = "Active"
},
new
{
SubNavItemId = 3,
Code = "products.brand",
Href = "/dashboard/products/brands",
Label = "Brand",
NavItemId = 2,
SortOrder = 3,
Status = "Active"
},
new
{
SubNavItemId = 4,
Code = "products.item-type",
Href = "/dashboard/products/item-types",
Label = "Item Type",
NavItemId = 2,
SortOrder = 4,
Status = "Active"
},
new
{
SubNavItemId = 5,
Code = "products.uom",
Href = "/dashboard/products/uoms",
Label = "UOM",
NavItemId = 2,
SortOrder = 5,
Status = "Active"
},
new
{
SubNavItemId = 6,
Code = "products.configuration",
Href = "/dashboard/products/settings",
Label = "Configuration",
NavItemId = 2,
SortOrder = 6,
Status = "Active"
},
new
{
SubNavItemId = 7,
Code = "settings.roles",
Href = "/dashboard/settings/roles",
Label = "Roles",
NavItemId = 9,
SortOrder = 1,
Status = "Active"
},
new
{
SubNavItemId = 8,
Code = "settings.users",
Href = "/dashboard/settings/users",
Label = "Users",
NavItemId = 9,
SortOrder = 2,
Status = "Active"
});
});
modelBuilder.Entity("ERPCore.Domain.Entities.Uom", b =>
{
b.Property<int>("UomId")
@@ -1510,6 +1990,9 @@ namespace ERPCore.Infra.Persistence.Migrations
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<int?>("RoleId")
.HasColumnType("integer");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
@@ -1525,6 +2008,8 @@ namespace ERPCore.Infra.Persistence.Migrations
b.HasIndex("AuthUserId")
.IsUnique();
b.HasIndex("RoleId");
b.HasIndex("Username")
.IsUnique();
@@ -1857,6 +2342,23 @@ namespace ERPCore.Infra.Persistence.Migrations
b.Navigation("Warehouse");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b =>
{
b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem")
.WithMany()
.HasForeignKey("NavItemId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("ERPCore.Domain.Entities.SubNavItem", "SubNavItem")
.WithMany()
.HasForeignKey("SubNavItemId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("NavItem");
b.Navigation("SubNavItem");
});
modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b =>
{
b.HasOne("ERPCore.Domain.Entities.Item", "Item")
@@ -2049,6 +2551,25 @@ namespace ERPCore.Infra.Persistence.Migrations
b.Navigation("Rfq");
});
modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b =>
{
b.HasOne("ERPCore.Domain.Entities.Permission", "Permission")
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Role", "Role")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Permission");
b.Navigation("Role");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b =>
{
b.HasOne("ERPCore.Domain.Entities.Item", "Item")
@@ -2317,6 +2838,17 @@ namespace ERPCore.Infra.Persistence.Migrations
b.Navigation("Category");
});
modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b =>
{
b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem")
.WithMany("Children")
.HasForeignKey("NavItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("NavItem");
});
modelBuilder.Entity("ERPCore.Domain.Entities.UomConversion", b =>
{
b.HasOne("ERPCore.Domain.Entities.Uom", "FromUom")
@@ -2344,6 +2876,16 @@ namespace ERPCore.Infra.Persistence.Migrations
b.Navigation("ToUom");
});
modelBuilder.Entity("ERPCore.Domain.Entities.User", b =>
{
b.HasOne("ERPCore.Domain.Entities.Role", "Role")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict);
b.Navigation("Role");
});
modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b =>
{
b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq")
@@ -2399,6 +2941,11 @@ namespace ERPCore.Infra.Persistence.Migrations
b.Navigation("UomConversions");
});
modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b =>
{
b.Navigation("Children");
});
modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b =>
{
b.Navigation("Lines");