first commit
CI/CD / CI · Admin API (dotnet build) (push) Successful in 41s
CI/CD / CI · Admin Web (tsc) (push) Failing after 5s
CI/CD / CI · Website (tsc) (push) Failing after 4s
CI/CD / CI · Koja (tsc) (push) Failing after 5s
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m13s
CI/CD / CI · Dashboard (tsc) (push) Failing after 2m32s
CI/CD / Deploy · all services (push) Has been skipped
CI/CD / CI · Admin API (dotnet build) (push) Successful in 41s
CI/CD / CI · Admin Web (tsc) (push) Failing after 5s
CI/CD / CI · Website (tsc) (push) Failing after 4s
CI/CD / CI · Koja (tsc) (push) Failing after 5s
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m13s
CI/CD / CI · Dashboard (tsc) (push) Failing after 2m32s
CI/CD / Deploy · all services (push) Has been skipped
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
namespace Meezi.Core.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// Capabilities a café employee can be granted. These are the single source of
|
||||
/// truth for authorization — controllers check a <see cref="Permission"/> rather
|
||||
/// than hard-coding role names, so the role→capability mapping lives in exactly
|
||||
/// one place (<see cref="RolePermissions"/>).
|
||||
/// </summary>
|
||||
public enum Permission
|
||||
{
|
||||
// Café-level administration (Owner only)
|
||||
ManageCafeSettings,
|
||||
ManageBilling,
|
||||
ManageBranches,
|
||||
|
||||
// Management (Owner + Manager)
|
||||
ManageStaff,
|
||||
ManageMenu,
|
||||
ManageInventory,
|
||||
ManageExpenses,
|
||||
ManageTaxes,
|
||||
ManageCoupons,
|
||||
ManageReservations,
|
||||
ManageTables,
|
||||
ViewReports,
|
||||
ReviewLeave,
|
||||
ManageSalaries,
|
||||
ManagePrintSettings,
|
||||
|
||||
// Front-of-house operations
|
||||
ProcessOrders,
|
||||
HandlePayments,
|
||||
OperateRegister,
|
||||
ManageQueue,
|
||||
|
||||
// Kitchen
|
||||
ViewKitchen,
|
||||
|
||||
// Delivery
|
||||
HandleDelivery,
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using Meezi.Core.Enums;
|
||||
|
||||
namespace Meezi.Core.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// The authoritative role→capability matrix. Change what a role can do here and
|
||||
/// every controller that calls <c>EnsurePermission</c> updates automatically.
|
||||
/// </summary>
|
||||
public static class RolePermissions
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<EmployeeRole, HashSet<Permission>> Matrix =
|
||||
new Dictionary<EmployeeRole, HashSet<Permission>>
|
||||
{
|
||||
[EmployeeRole.Owner] = AllPermissions(),
|
||||
|
||||
[EmployeeRole.Manager] = new()
|
||||
{
|
||||
Permission.ManageStaff,
|
||||
Permission.ManageMenu,
|
||||
Permission.ManageInventory,
|
||||
Permission.ManageExpenses,
|
||||
Permission.ManageTaxes,
|
||||
Permission.ManageCoupons,
|
||||
Permission.ManageReservations,
|
||||
Permission.ManageTables,
|
||||
Permission.ViewReports,
|
||||
Permission.ReviewLeave,
|
||||
Permission.ManageSalaries,
|
||||
Permission.ManagePrintSettings,
|
||||
Permission.ProcessOrders,
|
||||
Permission.HandlePayments,
|
||||
Permission.OperateRegister,
|
||||
Permission.ManageQueue,
|
||||
Permission.ViewKitchen,
|
||||
Permission.HandleDelivery,
|
||||
},
|
||||
|
||||
[EmployeeRole.Cashier] = new()
|
||||
{
|
||||
Permission.ProcessOrders,
|
||||
Permission.HandlePayments,
|
||||
Permission.OperateRegister,
|
||||
Permission.ManageQueue,
|
||||
Permission.ManageReservations,
|
||||
},
|
||||
|
||||
[EmployeeRole.Waiter] = new()
|
||||
{
|
||||
Permission.ProcessOrders,
|
||||
Permission.ManageReservations,
|
||||
Permission.ManageQueue,
|
||||
},
|
||||
|
||||
[EmployeeRole.Chef] = new()
|
||||
{
|
||||
Permission.ViewKitchen,
|
||||
},
|
||||
|
||||
[EmployeeRole.Delivery] = new()
|
||||
{
|
||||
Permission.HandleDelivery,
|
||||
},
|
||||
};
|
||||
|
||||
public static bool Has(EmployeeRole role, Permission permission) =>
|
||||
Matrix.TryGetValue(role, out var set) && set.Contains(permission);
|
||||
|
||||
public static IReadOnlySet<Permission> For(EmployeeRole role) =>
|
||||
Matrix.TryGetValue(role, out var set) ? set : new HashSet<Permission>();
|
||||
|
||||
/// <summary>True for roles that administer the whole café across all branches.</summary>
|
||||
public static bool IsCafeWide(EmployeeRole role) => role == EmployeeRole.Owner;
|
||||
|
||||
private static HashSet<Permission> AllPermissions() =>
|
||||
new(Enum.GetValues<Permission>());
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace Meezi.Core.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable record of a sensitive POS / management action. Written by
|
||||
/// <c>IAuditLogService</c> and never updated. Branch-scoped so the strict
|
||||
/// branch isolation filter applies (café-wide sessions see all).
|
||||
/// </summary>
|
||||
public class AuditLog : TenantEntity
|
||||
{
|
||||
/// <summary>High-level grouping, e.g. "Order", "Payment", "Register", "Staff".</summary>
|
||||
public string Category { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Specific action, e.g. "OrderCancelled", "ItemVoided", "PaymentRecorded".</summary>
|
||||
public string Action { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>The entity acted upon, e.g. "Order", "Shift".</summary>
|
||||
public string? EntityType { get; set; }
|
||||
|
||||
/// <summary>Id of the affected entity.</summary>
|
||||
public string? EntityId { get; set; }
|
||||
|
||||
public string? BranchId { get; set; }
|
||||
|
||||
/// <summary>Employee who performed the action (null for system/automated).</summary>
|
||||
public string? ActorId { get; set; }
|
||||
public string? ActorName { get; set; }
|
||||
public string? ActorRole { get; set; }
|
||||
|
||||
/// <summary>Human-readable one-line summary (already localized at write time or neutral).</summary>
|
||||
public string Summary { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Optional structured payload (before/after, amounts, reason) as JSON.</summary>
|
||||
public string? DetailsJson { get; set; }
|
||||
}
|
||||
@@ -39,4 +39,7 @@ public class Branch : TenantEntity
|
||||
public ICollection<Table> Tables { get; set; } = [];
|
||||
public ICollection<Order> Orders { get; set; } = [];
|
||||
public ICollection<Employee> Staff { get; set; } = [];
|
||||
|
||||
/// <summary>Per-branch role assignments scoped to this branch.</summary>
|
||||
public ICollection<EmployeeBranchRole> StaffRoles { get; set; } = [];
|
||||
}
|
||||
|
||||
@@ -19,4 +19,7 @@ public class Employee : TenantEntity
|
||||
public ICollection<Attendance> Attendances { get; set; } = [];
|
||||
public ICollection<EmployeeSchedule> Schedules { get; set; } = [];
|
||||
public ICollection<LeaveRequest> LeaveRequests { get; set; } = [];
|
||||
|
||||
/// <summary>Per-branch role assignments (multi-branch staff). Owners are café-wide and may have none.</summary>
|
||||
public ICollection<EmployeeBranchRole> BranchRoles { get; set; } = [];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using Meezi.Core.Enums;
|
||||
|
||||
namespace Meezi.Core.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Per-branch role assignment for an employee. An employee row is scoped to one café
|
||||
/// (a "membership"); this join lets that same employee hold a different
|
||||
/// <see cref="EmployeeRole"/> in each branch they work at.
|
||||
/// Owners remain café-wide via <see cref="Employee.Role"/> and need no rows here.
|
||||
/// </summary>
|
||||
public class EmployeeBranchRole : TenantEntity
|
||||
{
|
||||
public string EmployeeId { get; set; } = string.Empty;
|
||||
public string BranchId { get; set; } = string.Empty;
|
||||
public EmployeeRole Role { get; set; }
|
||||
|
||||
public Employee Employee { get; set; } = null!;
|
||||
public Branch Branch { get; set; } = null!;
|
||||
}
|
||||
@@ -34,6 +34,12 @@ public class Order : TenantEntity
|
||||
/// <summary>JSON snapshot: driver, address, delivery ETA, etc.</summary>
|
||||
public string? DeliveryMetaJson { get; set; }
|
||||
|
||||
/// <summary>Reason captured when the order was cancelled (POS audit / accountability).</summary>
|
||||
public string? CancelReason { get; set; }
|
||||
/// <summary>Employee who cancelled the order (null for system/automated).</summary>
|
||||
public string? CancelledByEmployeeId { get; set; }
|
||||
public DateTime? CancelledAt { get; set; }
|
||||
|
||||
public Cafe Cafe { get; set; } = null!;
|
||||
public Branch? Branch { get; set; }
|
||||
public Table? Table { get; set; }
|
||||
|
||||
Reference in New Issue
Block a user