diff --git a/Services/HrmsMenuItemForMapping.cs b/Services/HrmsMenuItemForMapping.cs
new file mode 100644
index 0000000..29d66e0
--- /dev/null
+++ b/Services/HrmsMenuItemForMapping.cs
@@ -0,0 +1,45 @@
+namespace UtopiaCanteenSystem.Services;
+
+///
+/// Maps HRMS employee_rfid_tag.grade_type to menu_item.item_for.
+/// Menu rows only use MANAGEMENT / NON_MANAGEMENT; other grades must map to one of those.
+/// Contractual and MTO/TE/Intern use the management menu; Unassigned uses non-management.
+///
+public static class HrmsMenuItemForMapping
+{
+ ///
+ /// Returns MANAGEMENT or NON_MANAGEMENT for menu queries. Never returns empty.
+ ///
+ public static string FromGradeType(string? gradeType)
+ {
+ var compact = ToCompactAlphaNum(gradeType);
+ if (string.IsNullOrEmpty(compact))
+ return "NON_MANAGEMENT";
+
+ if (compact == "management")
+ return "MANAGEMENT";
+
+ if (compact == "nonmanagement")
+ return "NON_MANAGEMENT";
+
+ if (compact == "contractual" || compact == "mtoteintern")
+ return "MANAGEMENT";
+
+ if (compact == "unassigned")
+ return "NON_MANAGEMENT";
+
+ if (compact.StartsWith("non", StringComparison.Ordinal) && compact.Contains("management", StringComparison.Ordinal))
+ return "NON_MANAGEMENT";
+
+ // Any other label (e.g. future grade codes) — use non-management menu so items still resolve.
+ return "NON_MANAGEMENT";
+ }
+
+ private static string ToCompactAlphaNum(string? s)
+ {
+ if (string.IsNullOrWhiteSpace(s))
+ return string.Empty;
+ var chars = s.Trim().ToLowerInvariant().Where(char.IsLetterOrDigit).ToArray();
+ return new string(chars);
+ }
+}
diff --git a/Services/MenuLookupService.cs b/Services/MenuLookupService.cs
index 18e7a24..3b10075 100644
--- a/Services/MenuLookupService.cs
+++ b/Services/MenuLookupService.cs
@@ -237,7 +237,7 @@ public class MenuLookupService : IMenuLookupService
AND mi.item_for = @itemFor
ORDER BY mi.item_name;";
- var itemFor = GetItemForFromGradeType(gradeType);
+ var itemFor = HrmsMenuItemForMapping.FromGradeType(gradeType);
await using var conn = new MySqlConnection(connectionString);
await conn.OpenAsync(cancellationToken).ConfigureAwait(false);
@@ -265,19 +265,6 @@ public class MenuLookupService : IMenuLookupService
return list;
}
- private static string GetItemForFromGradeType(string? gradeType)
- {
- var value = (gradeType ?? string.Empty).Trim().ToLowerInvariant();
-
- return value switch
- {
- "management" => "MANAGEMENT",
- "nonmanagement" => "NON_MANAGEMENT",
- "non_management" => "NON_MANAGEMENT",
- _ => string.Empty
- };
- }
-
private static string GetString(MySqlDataReader reader, int ordinal)
{
if (reader.IsDBNull(ordinal)) return string.Empty;
diff --git a/Services/SyncService.cs b/Services/SyncService.cs
index e415305..9cbbdf0 100644
--- a/Services/SyncService.cs
+++ b/Services/SyncService.cs
@@ -212,7 +212,7 @@ public class SyncService : ISyncService
if (lunchOrderId > 0)
{
var mealItems = SplitMealItems(record.MealItems);
- var itemFor = GradeTypeToItemFor(record.grade_type);
+ var itemFor = HrmsMenuItemForMapping.FromGradeType(record.grade_type);
foreach (var itemName in mealItems)
{
@@ -335,18 +335,6 @@ public class SyncService : ISyncService
}
}
- private static string GradeTypeToItemFor(string? gradeType)
- {
- var value = (gradeType ?? string.Empty).Trim().ToLowerInvariant();
- return value switch
- {
- "management" => "MANAGEMENT",
- "nonmanagement" => "NON_MANAGEMENT",
- "non_management" => "NON_MANAGEMENT",
- _ => string.Empty
- };
- }
-
private static IReadOnlyList SplitMealItems(string? mealItems)
{
if (string.IsNullOrWhiteSpace(mealItems))