Add configurable menu structure and purchasing area
This commit is contained in:
@@ -215,4 +215,22 @@ CREATE TABLE FinanceRules (
|
||||
SortOrder INTEGER NOT NULL DEFAULT 0,
|
||||
IsActive INTEGER NOT NULL DEFAULT 1
|
||||
);";
|
||||
|
||||
internal static string GetNavigationMenuItemsCreateSql() => @"
|
||||
CREATE TABLE NavigationMenuItems (
|
||||
Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
Key TEXT NOT NULL,
|
||||
ParentKey TEXT NULL,
|
||||
TitleDe TEXT NOT NULL DEFAULT '',
|
||||
TitleEn TEXT NOT NULL DEFAULT '',
|
||||
Icon TEXT NOT NULL DEFAULT '',
|
||||
Href TEXT NOT NULL DEFAULT '',
|
||||
ItemType TEXT NOT NULL DEFAULT 'Link',
|
||||
Match TEXT NOT NULL DEFAULT 'Prefix',
|
||||
RequiredPolicy TEXT NOT NULL DEFAULT '',
|
||||
IsVisible INTEGER NOT NULL DEFAULT 1,
|
||||
IsExpanded INTEGER NOT NULL DEFAULT 0,
|
||||
IsSystem INTEGER NOT NULL DEFAULT 1,
|
||||
SortOrder INTEGER NOT NULL DEFAULT 0
|
||||
);";
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ public class DatabaseSchemaMaintenanceService : IDatabaseSchemaMaintenanceServic
|
||||
EnsureSapFieldMappingTable(db);
|
||||
EnsureManualExcelColumnMappingTable(db);
|
||||
EnsureCentralSalesRecordTable(db);
|
||||
EnsureNavigationMenuItemTable(db);
|
||||
AddColumnIfMissing(db, "CentralSalesRecords", "DocumentEntry", "INTEGER NOT NULL DEFAULT 0");
|
||||
AddColumnIfMissing(db, "CentralSalesRecords", "DocumentCurrency", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "CentralSalesRecords", "DocumentTotalForeignCurrency", "TEXT NOT NULL DEFAULT '0'");
|
||||
@@ -272,6 +273,17 @@ CREATE TABLE IF NOT EXISTS FieldTransformationRules (
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
private static void EnsureNavigationMenuItemTable(AppDbContext db)
|
||||
{
|
||||
var conn = db.Database.GetDbConnection();
|
||||
if (conn.State != System.Data.ConnectionState.Open)
|
||||
conn.Open();
|
||||
|
||||
using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = DatabaseSchemaSql.GetNavigationMenuItemsCreateSql().Replace("CREATE TABLE", "CREATE TABLE IF NOT EXISTS");
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
private static void EnsureSapSourceTable(AppDbContext db)
|
||||
{
|
||||
var conn = db.Database.GetDbConnection();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TrafagSalesExporter.Data;
|
||||
using TrafagSalesExporter.Models;
|
||||
using TrafagSalesExporter.Security;
|
||||
|
||||
namespace TrafagSalesExporter.Services;
|
||||
|
||||
@@ -20,6 +21,7 @@ public class DatabaseSeedService : IDatabaseSeedService
|
||||
EnsureBudgetExchangeRateDefaults(db);
|
||||
EnsureFinanceIntercompanyRuleDefaults(db);
|
||||
EnsureFinanceRuleDefaults(db);
|
||||
EnsureNavigationMenuDefaults(db);
|
||||
}
|
||||
|
||||
private static void SeedIfEmpty(AppDbContext db)
|
||||
@@ -115,6 +117,112 @@ public class DatabaseSeedService : IDatabaseSeedService
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
private static void EnsureNavigationMenuDefaults(AppDbContext db)
|
||||
{
|
||||
var defaults = BuildDefaultNavigationMenuItems();
|
||||
var changed = false;
|
||||
|
||||
foreach (var item in defaults)
|
||||
{
|
||||
var existing = db.NavigationMenuItems.FirstOrDefault(x => x.Key == item.Key);
|
||||
if (existing is null)
|
||||
{
|
||||
db.NavigationMenuItems.Add(item);
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(existing.TitleDe)) existing.TitleDe = item.TitleDe;
|
||||
if (string.IsNullOrWhiteSpace(existing.TitleEn)) existing.TitleEn = item.TitleEn;
|
||||
if (string.IsNullOrWhiteSpace(existing.Icon)) existing.Icon = item.Icon;
|
||||
if (string.IsNullOrWhiteSpace(existing.Href)) existing.Href = item.Href;
|
||||
if (string.IsNullOrWhiteSpace(existing.ItemType)) existing.ItemType = item.ItemType;
|
||||
if (string.IsNullOrWhiteSpace(existing.Match)) existing.Match = item.Match;
|
||||
if (string.IsNullOrWhiteSpace(existing.RequiredPolicy)) existing.RequiredPolicy = item.RequiredPolicy;
|
||||
existing.IsSystem = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
private static List<NavigationMenuItem> BuildDefaultNavigationMenuItems() =>
|
||||
[
|
||||
Group("finance", null, "Finance Cockpit", "Finance Cockpit", "Analytics", 10, expanded: true),
|
||||
Link("export-dashboard", "finance", "Export Dashboard", "Export dashboard", "Dashboard", "export-dashboard", 10),
|
||||
Group("management-analysis", "finance", "Management Analyse", "Management analysis", "QueryStats", 20),
|
||||
Link("management-quick", "management-analysis", "Schnelluebersicht", "Quick overview", "Speed", "management-cockpit", 10, "All"),
|
||||
Group("experts", "management-analysis", "Experten", "Experts", "Tune", 20),
|
||||
Link("finance-summary", "experts", "Finance Summary", "Finance summary", "Dashboard", "management-cockpit?section=summary", 10, "All"),
|
||||
Link("country-diagnostics", "experts", "Laender Diagnose", "Country diagnostics", "Public", "management-cockpit?section=countries", 20, "All"),
|
||||
Link("data-status", "experts", "Datenstatus", "Data status", "FactCheck", "management-cockpit?section=status", 30, "All"),
|
||||
Link("deviations", "experts", "Abweichungen", "Deviations", "WarningAmber", "management-cockpit?section=deviations", 40, "All"),
|
||||
Link("credits", "experts", "Gutschriften", "Credit notes", "AssignmentReturn", "management-cockpit?section=credits", 50, "All"),
|
||||
Link("data-quality", "experts", "Datenqualitaet", "Data quality", "Rule", "management-cockpit?section=quality", 60, "All"),
|
||||
Link("division-finance", "experts", "Sparten-Finanzanalyse", "Division finance", "PieChart", "management-cockpit?section=division&division=finance", 70, "All"),
|
||||
Link("division-central", "experts", "Zentrale Spartenzuordnung", "Central division mapping", "AccountTree", "management-cockpit?section=division&division=central", 80, "All"),
|
||||
Link("finance-3d", "experts", "3D Datenanalyse", "3D data analysis", "ViewInAr", "management-cockpit?section=3d", 90, "All"),
|
||||
Link("raw-diagnostics", "experts", "Rohdaten Diagnose", "Raw-data diagnostics", "QueryStats", "management-cockpit?section=raw", 100, "All"),
|
||||
Link("finance-comparison", "finance", "Soll/Ist Vergleich", "Actual/reference comparison", "CompareArrows", "finance-cockpit/vergleich", 30),
|
||||
Link("finance-training", "finance", "Finance Schulung", "Finance training", "School", "finance-cockpit/schulung", 40),
|
||||
Link("manual-imports", "finance", "Manuelle Importe", "Manual imports", "UploadFile", "manual-imports", 50),
|
||||
Group("finance-admin", "finance", "Admin", "Admin", "AdminPanelSettings", 60),
|
||||
Link("sites", "finance-admin", "Standorte", "Sites", "LocationOn", "standorte", 10, requiredPolicy: SecurityPolicies.AdminOnly),
|
||||
Link("transformations", "finance-admin", "Transformationen", "Transformations", "Transform", "transformations", 20, requiredPolicy: SecurityPolicies.AdminOnly),
|
||||
Link("finance-rules", "finance-admin", "Finance Regeln", "Finance rules", "Rule", "finance-rules", 30, requiredPolicy: SecurityPolicies.AdminOnly),
|
||||
Link("settings", "finance-admin", "Settings", "Settings", "Settings", "settings", 40, requiredPolicy: SecurityPolicies.AdminOnly),
|
||||
Link("menu-structure", "finance-admin", "Menuestruktur", "Menu structure", "AccountTree", "admin/menu-structure", 45, requiredPolicy: SecurityPolicies.AdminOnly),
|
||||
Link("logs", "finance-admin", "Logs", "Logs", "List", "logs", 50),
|
||||
Action("finance-lock", "finance", "Finance sperren", "Lock finance", "Lock", 70),
|
||||
Group("hr", null, "HR KPI (Login)", "HR KPI (login)", "Groups", 20),
|
||||
Link("hr-dashboard", "hr", "HR Dashboard", "HR dashboard", "Dashboard", "hr-kpi", 10, "All"),
|
||||
Link("hr-training", "hr", "HR KPI Schulung", "HR KPI training", "School", "hr-kpi/schulung", 20),
|
||||
Group("purchasing", null, "Einkauf", "Purchasing", "ShoppingCart", 30),
|
||||
Link("purchasing-dashboard", "purchasing", "Einkauf Dashboard", "Purchasing dashboard", "Dashboard", "einkauf", 10, "All"),
|
||||
Link("admin-sessions", null, "Admin Bereich", "Admin area", "PeopleAlt", "admin/sessions", 90)
|
||||
];
|
||||
|
||||
private static NavigationMenuItem Group(string key, string? parentKey, string titleDe, string titleEn, string icon, int sortOrder, bool expanded = false)
|
||||
=> new()
|
||||
{
|
||||
Key = key,
|
||||
ParentKey = parentKey,
|
||||
TitleDe = titleDe,
|
||||
TitleEn = titleEn,
|
||||
Icon = icon,
|
||||
ItemType = NavigationMenuItemTypes.Group,
|
||||
IsExpanded = expanded,
|
||||
SortOrder = sortOrder
|
||||
};
|
||||
|
||||
private static NavigationMenuItem Link(string key, string? parentKey, string titleDe, string titleEn, string icon, string href, int sortOrder, string match = "Prefix", string requiredPolicy = "")
|
||||
=> new()
|
||||
{
|
||||
Key = key,
|
||||
ParentKey = parentKey,
|
||||
TitleDe = titleDe,
|
||||
TitleEn = titleEn,
|
||||
Icon = icon,
|
||||
Href = href,
|
||||
Match = match,
|
||||
RequiredPolicy = requiredPolicy,
|
||||
ItemType = NavigationMenuItemTypes.Link,
|
||||
SortOrder = sortOrder
|
||||
};
|
||||
|
||||
private static NavigationMenuItem Action(string key, string? parentKey, string titleDe, string titleEn, string icon, int sortOrder)
|
||||
=> new()
|
||||
{
|
||||
Key = key,
|
||||
ParentKey = parentKey,
|
||||
TitleDe = titleDe,
|
||||
TitleEn = titleEn,
|
||||
Icon = icon,
|
||||
ItemType = NavigationMenuItemTypes.Action,
|
||||
SortOrder = sortOrder
|
||||
};
|
||||
|
||||
private static void EnsureCentralHanaServerRecords(AppDbContext db)
|
||||
{
|
||||
var centralSystems = db.SourceSystemDefinitions
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
using TrafagSalesExporter.Models;
|
||||
|
||||
namespace TrafagSalesExporter.Services;
|
||||
|
||||
public interface INavigationMenuService
|
||||
{
|
||||
Task<List<NavigationMenuItem>> GetItemsAsync();
|
||||
Task SaveItemsAsync(IEnumerable<NavigationMenuItem> items);
|
||||
Task ResetToDefaultsAsync();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using MudBlazor;
|
||||
|
||||
namespace TrafagSalesExporter.Services;
|
||||
|
||||
public static class NavigationIconResolver
|
||||
{
|
||||
public static string Resolve(string icon) => icon switch
|
||||
{
|
||||
"AccountTree" => Icons.Material.Filled.AccountTree,
|
||||
"AdminPanelSettings" => Icons.Material.Filled.AdminPanelSettings,
|
||||
"Analytics" => Icons.Material.Filled.Analytics,
|
||||
"AssignmentReturn" => Icons.Material.Filled.AssignmentReturn,
|
||||
"CompareArrows" => Icons.Material.Filled.CompareArrows,
|
||||
"Dashboard" => Icons.Material.Filled.Dashboard,
|
||||
"FactCheck" => Icons.Material.Filled.FactCheck,
|
||||
"Groups" => Icons.Material.Filled.Groups,
|
||||
"List" => Icons.Material.Filled.List,
|
||||
"LocationOn" => Icons.Material.Filled.LocationOn,
|
||||
"Lock" => Icons.Material.Filled.Lock,
|
||||
"PeopleAlt" => Icons.Material.Filled.PeopleAlt,
|
||||
"PieChart" => Icons.Material.Filled.PieChart,
|
||||
"Public" => Icons.Material.Filled.Public,
|
||||
"QueryStats" => Icons.Material.Filled.QueryStats,
|
||||
"Rule" => Icons.Material.Filled.Rule,
|
||||
"School" => Icons.Material.Filled.School,
|
||||
"Settings" => Icons.Material.Filled.Settings,
|
||||
"ShoppingCart" => Icons.Material.Filled.ShoppingCart,
|
||||
"Speed" => Icons.Material.Filled.Speed,
|
||||
"Transform" => Icons.Material.Filled.Transform,
|
||||
"Tune" => Icons.Material.Filled.Tune,
|
||||
"UploadFile" => Icons.Material.Filled.UploadFile,
|
||||
"ViewInAr" => Icons.Material.Filled.ViewInAr,
|
||||
"WarningAmber" => Icons.Material.Filled.WarningAmber,
|
||||
_ => Icons.Material.Filled.Circle
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TrafagSalesExporter.Data;
|
||||
using TrafagSalesExporter.Models;
|
||||
|
||||
namespace TrafagSalesExporter.Services;
|
||||
|
||||
public sealed class NavigationMenuService : INavigationMenuService
|
||||
{
|
||||
private readonly IDbContextFactory<AppDbContext> _dbFactory;
|
||||
|
||||
public NavigationMenuService(IDbContextFactory<AppDbContext> dbFactory)
|
||||
{
|
||||
_dbFactory = dbFactory;
|
||||
}
|
||||
|
||||
public async Task<List<NavigationMenuItem>> GetItemsAsync()
|
||||
{
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
return await db.NavigationMenuItems
|
||||
.AsNoTracking()
|
||||
.OrderBy(x => x.ParentKey ?? string.Empty)
|
||||
.ThenBy(x => x.SortOrder)
|
||||
.ThenBy(x => x.TitleDe)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task SaveItemsAsync(IEnumerable<NavigationMenuItem> items)
|
||||
{
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
var incoming = items.ToDictionary(x => x.Key, StringComparer.OrdinalIgnoreCase);
|
||||
var existing = await db.NavigationMenuItems.ToListAsync();
|
||||
|
||||
foreach (var item in existing)
|
||||
{
|
||||
if (!incoming.TryGetValue(item.Key, out var source))
|
||||
continue;
|
||||
|
||||
item.ParentKey = string.IsNullOrWhiteSpace(source.ParentKey) ? null : source.ParentKey;
|
||||
item.SortOrder = source.SortOrder;
|
||||
item.IsVisible = source.IsVisible;
|
||||
item.IsExpanded = source.IsExpanded;
|
||||
item.TitleDe = source.TitleDe.Trim();
|
||||
item.TitleEn = source.TitleEn.Trim();
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task ResetToDefaultsAsync()
|
||||
{
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
db.NavigationMenuItems.RemoveRange(db.NavigationMenuItems);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
new DatabaseSeedService().SeedDefaults(db);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user