From cb5f91a5da9d77b50ca2f164aacf053469dbed04 Mon Sep 17 00:00:00 2001 From: "mustafa.ahmed" Date: Tue, 31 Mar 2026 19:24:49 +0500 Subject: [PATCH] Fix: - Updated `item_for` column in `menu_item` table to reflect correct values based on employee grade type (Management/NonManagement). - Added 'MANAGEMENT' to items with id 51 and 53. - Added 'NON_MANAGEMENT' to item with id 54. - Modified the employee photo fetching logic to use the external link instead of the database for fetching employee photos. - Added missing fields in the `lunch_order_transactions` table to ensure no fields are left blank or null during the scan process. --- App.xaml.cs | 9 +- Models/HrmsEmployeeInfo.cs | 1 + Models/ScanRecord.cs | 1 + Services/EmployeeLookupService.cs | 6 +- Services/EmployeePhotoService.cs | 112 ++++++++---- Services/IMenuLookupService.cs | 4 +- Services/MenuLookupService.cs | 226 +++++++++++++++++------- Services/RfidService.cs | 5 +- Services/SyncService.cs | 51 +++++- ViewModels/ScannerDashboardViewModel.cs | 9 +- 10 files changed, 317 insertions(+), 107 deletions(-) diff --git a/App.xaml.cs b/App.xaml.cs index b93ca37..abe4604 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -1,6 +1,7 @@ -using System.Windows; -using System.Threading; using Microsoft.EntityFrameworkCore; +using System.Net.Http; +using System.Threading; +using System.Windows; using UtopiaCanteenSystem.Data; using UtopiaCanteenSystem.Services; using UtopiaCanteenSystem.ViewModels; @@ -40,7 +41,9 @@ public partial class App : Application var configService = new ConfigService(); var employeeLookupService = new EmployeeLookupService(configService); - var employeePhotoService = new EmployeePhotoService(configService); + //var employeePhotoService = new EmployeePhotoService(configService); + var httpClient = new HttpClient(); + var employeePhotoService = new EmployeePhotoService(configService, httpClient); var menuLookupService = new MenuLookupService(configService); var mealScheduleService = new ProductionMealScheduleService(configService); var mealSessionResolver = new DbMealSessionResolver(mealScheduleService); diff --git a/Models/HrmsEmployeeInfo.cs b/Models/HrmsEmployeeInfo.cs index 5a166e1..519f8bf 100644 --- a/Models/HrmsEmployeeInfo.cs +++ b/Models/HrmsEmployeeInfo.cs @@ -25,4 +25,5 @@ public class HrmsEmployeeInfo public string DepartmentType { get; set; } = string.Empty; /// Site from employee_rfid_tag.location_site_id; used for menu lookup (lunch_menu_week). public string LocationSiteId { get; set; } = string.Empty; + public string GradeType { get; set; } = string.Empty; } diff --git a/Models/ScanRecord.cs b/Models/ScanRecord.cs index 8b6a58c..636ec68 100644 --- a/Models/ScanRecord.cs +++ b/Models/ScanRecord.cs @@ -46,4 +46,5 @@ public class ScanRecord public string MealItems { get; set; } = string.Empty; /// Total price for this scan's meal menu. public double TotalPrice { get; set; } + public string grade_type { get; set; } = string.Empty; } diff --git a/Services/EmployeeLookupService.cs b/Services/EmployeeLookupService.cs index 1c71c25..5551a9d 100644 --- a/Services/EmployeeLookupService.cs +++ b/Services/EmployeeLookupService.cs @@ -37,7 +37,8 @@ public class EmployeeLookupService : IEmployeeLookupService r.created_by AS tag_created_by, d.title AS department_title, d.department_type, - r.location_site_id AS location_site_id + r.location_site_id AS location_site_id, + r.grade_type AS grade_type FROM employee_rfid_tag r JOIN employee e ON e.id = r.parent_document_id LEFT JOIN department d ON d.id = e.department_id @@ -68,7 +69,8 @@ public class EmployeeLookupService : IEmployeeLookupService TagCreatedBy = GetString(reader, 8), DepartmentTitle = GetString(reader, 9), DepartmentType = GetString(reader, 10), - LocationSiteId = GetString(reader, 11) + LocationSiteId = GetString(reader, 11), + GradeType = GetString(reader, 12) }; } diff --git a/Services/EmployeePhotoService.cs b/Services/EmployeePhotoService.cs index 4272d80..0cb3790 100644 --- a/Services/EmployeePhotoService.cs +++ b/Services/EmployeePhotoService.cs @@ -1,6 +1,7 @@ -using System.Collections.Concurrent; -using System.Text; using MySqlConnector; +using System.Collections.Concurrent; +using System.Net.Http; +using System.Text; namespace UtopiaCanteenSystem.Services; @@ -13,13 +14,83 @@ public class EmployeePhotoService : IEmployeePhotoService private readonly IConfigService _configService; private readonly ConcurrentDictionary _cache = new(StringComparer.OrdinalIgnoreCase); private const int MaxCacheEntries = 50; + private readonly HttpClient _httpClient; - public EmployeePhotoService(IConfigService configService) + public EmployeePhotoService(IConfigService configService, HttpClient httpClient) { _configService = configService; + _httpClient = httpClient; + _httpClient = httpClient; + } /// + //public async Task GetPhotoBytesAsync(string parentDocumentId, CancellationToken cancellationToken = default) + //{ + // if (string.IsNullOrWhiteSpace(parentDocumentId)) + // return null; + + // var key = parentDocumentId.Trim(); + // if (_cache.TryGetValue(key, out var cached)) + // return cached; + + // var connectionString = _configService.GetHrmsLookupConnectionString(); + // if (string.IsNullOrWhiteSpace(connectionString)) + // return null; + + // try + // { + // await using var conn = new MySqlConnection(connectionString); + // await conn.OpenAsync(cancellationToken).ConfigureAwait(false); + + // // employee_photo.employee_id stores the employee document id (parent_document_id from employee lookup) + // const string sql = "SELECT photo_blob FROM hrms.employee_photo WHERE employee_id = @parentDocumentId LIMIT 1"; + // await using var cmd = new MySqlCommand(sql, conn); + // cmd.Parameters.AddWithValue("@parentDocumentId", key); + + // await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); + // if (!await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) + // return null; + + // if (reader.IsDBNull(0)) + // return null; + + // byte[] blob; + // try + // { + // var len = reader.GetBytes(0, 0, null, 0, 0); + // blob = new byte[len]; + // reader.GetBytes(0, 0, blob, 0, (int)len); + // } + // catch + // { + // return null; + // } + + // var imageBytes = DecodeBlobToImageBytes(blob); + // if (imageBytes == null || imageBytes.Length == 0) + // return null; + + // // Cache (evict old if needed) + // while (_cache.Count >= MaxCacheEntries && _cache.Count > 0) + // { + // var first = _cache.Keys.FirstOrDefault(); + // if (first != null) + // _cache.TryRemove(first, out _); + // else + // break; + // } + // _cache[key] = imageBytes; + // return imageBytes; + // } + // catch + // { + // return null; + // } + //} + + + public async Task GetPhotoBytesAsync(string parentDocumentId, CancellationToken cancellationToken = default) { if (string.IsNullOrWhiteSpace(parentDocumentId)) @@ -29,44 +100,25 @@ public class EmployeePhotoService : IEmployeePhotoService if (_cache.TryGetValue(key, out var cached)) return cached; - var connectionString = _configService.GetHrmsLookupConnectionString(); - if (string.IsNullOrWhiteSpace(connectionString)) - return null; - try { - await using var conn = new MySqlConnection(connectionString); - await conn.OpenAsync(cancellationToken).ConfigureAwait(false); + var url = $"https://portal.utopiaindustries.pk/uind/employee-photo/{Uri.EscapeDataString(key)}.jpeg"; - // employee_photo.employee_id stores the employee document id (parent_document_id from employee lookup) - const string sql = "SELECT photo_blob FROM hrms.employee_photo WHERE employee_id = @parentDocumentId LIMIT 1"; - await using var cmd = new MySqlCommand(sql, conn); - cmd.Parameters.AddWithValue("@parentDocumentId", key); - - await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); - if (!await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) + using var response = await _httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false); + if (!response.IsSuccessStatusCode) return null; - if (reader.IsDBNull(0)) - return null; - - byte[] blob; - try - { - var len = reader.GetBytes(0, 0, null, 0, 0); - blob = new byte[len]; - reader.GetBytes(0, 0, blob, 0, (int)len); - } - catch + var contentType = response.Content.Headers.ContentType?.MediaType; + if (!string.IsNullOrWhiteSpace(contentType) && + !contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) { return null; } - var imageBytes = DecodeBlobToImageBytes(blob); + var imageBytes = await response.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false); if (imageBytes == null || imageBytes.Length == 0) return null; - // Cache (evict old if needed) while (_cache.Count >= MaxCacheEntries && _cache.Count > 0) { var first = _cache.Keys.FirstOrDefault(); @@ -75,6 +127,7 @@ public class EmployeePhotoService : IEmployeePhotoService else break; } + _cache[key] = imageBytes; return imageBytes; } @@ -83,7 +136,6 @@ public class EmployeePhotoService : IEmployeePhotoService return null; } } - /// /// Converts blob to image bytes: if blob is UTF-8 "data:image...;base64,<payload>", decodes base64; else treats as raw image. /// diff --git a/Services/IMenuLookupService.cs b/Services/IMenuLookupService.cs index cf15353..a96089a 100644 --- a/Services/IMenuLookupService.cs +++ b/Services/IMenuLookupService.cs @@ -18,5 +18,7 @@ public interface IMenuLookupService /// Gets menu items for the given site and specific local date (yyyy-MM-dd). /// Avoids server time mismatch by not relying on CURDATE(). /// - Task> GetMenuItemsForSiteAndDateAsync(int siteIdNumeric, DateTime menuDateLocal, CancellationToken cancellationToken = default); + //Task> GetMenuItemsForSiteAndDateAsync(int siteIdNumeric, DateTime menuDateLocal, CancellationToken cancellationToken = default); + + Task> GetMenuItemsForSiteAndDateAsync(int siteIdNumeric, DateTime menuDateLocal, string gradeType, CancellationToken cancellationToken = default); } diff --git a/Services/MenuLookupService.cs b/Services/MenuLookupService.cs index ec3c804..8d36774 100644 --- a/Services/MenuLookupService.cs +++ b/Services/MenuLookupService.cs @@ -7,6 +7,133 @@ namespace UtopiaCanteenSystem.Services; /// Fetches lunch menu from HRMS: lunch_menu_week (by location_site_id) → lunch_menu_item → menu_item. /// Uses IConfigService.GetHrmsLookupConnectionString(). Only current week is considered. /// +//public class MenuLookupService : IMenuLookupService +//{ +// private readonly IConfigService _configService; + +// public MenuLookupService(IConfigService configService) +// { +// _configService = configService; +// } + +// /// +// public async Task> GetMenuItemsForSiteAsync(int siteIdNumeric, CancellationToken cancellationToken = default) +// { +// // Backwards-compatible: use today's local date. +// return await GetMenuItemsForSiteAndDateAsync(siteIdNumeric, DateTime.Today, cancellationToken).ConfigureAwait(false); +// } + +// /// +// public async Task> GetMenuItemsForSiteAndDateAsync(int siteIdNumeric, DateTime menuDateLocal, CancellationToken cancellationToken = default) +// { +// var connectionString = _configService.GetHrmsLookupConnectionString(); +// if (string.IsNullOrWhiteSpace(connectionString)) +// return Array.Empty(); + +// //const string sql = @" +// // SELECT DISTINCT mi.id, mi.item_name, mi.item_type, mi.price +// // FROM lunch_menu_week w +// // JOIN lunch_menu_item li ON li.lunch_menu_week_id = w.id +// // JOIN menu_item mi ON mi.id = li.menu_item_id +// // WHERE w.location_site_id = @siteId +// // AND CURDATE() BETWEEN w.week_start_date AND w.week_end_date +// // ORDER BY mi.item_type, mi.item_name"; + +// const string sql = @" +// SELECT +// mi.id, +// mi.item_name, +// mi.item_type, +// mi.price, +// li.menu_date, +// li.day_of_week +// FROM lunch_menu_week w +// JOIN lunch_menu_item li ON li.lunch_menu_week_id = w.id +// JOIN menu_item mi ON mi.id = li.menu_item_id +// WHERE w.location_site_id = @siteId +// AND @menuDate BETWEEN w.week_start_date AND w.week_end_date +// AND li.menu_date = @menuDate +// ORDER BY mi.item_name;"; + +// //const string sql = @" +// // SELECT +// // mi.id, +// // mi.item_name, +// // mi.item_type, +// // mi.price, +// // li.meal_name, +// // li.menu_date, +// // li.day_of_week +// // FROM lunch_menu_week w +// // JOIN lunch_menu_item li ON li.lunch_menu_week_id = w.id +// // JOIN menu_item mi ON mi.id = li.menu_item_id +// // WHERE w.location_site_id = @siteId +// // AND @menuDate BETWEEN w.week_start_date AND w.week_end_date +// // AND li.menu_date = @menuDate +// // ORDER BY li.meal_name, mi.item_name;"; + +// await using var conn = new MySqlConnection(connectionString); +// await conn.OpenAsync(cancellationToken).ConfigureAwait(false); +// await using var cmd = new MySqlCommand(sql, conn); +// cmd.Parameters.AddWithValue("@siteId", siteIdNumeric); +// cmd.Parameters.AddWithValue("@menuDate", menuDateLocal.Date); + +// var list = new List(); +// await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); +// while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) +// { +// //list.Add(new HrmsMenuItem +// //{ +// // MenuItemId = reader.IsDBNull(0) ? 0 : reader.GetInt32(0), +// // ItemName = GetString(reader, 1), +// // ItemType = GetString(reader, 2), +// // Price = GetDecimal(reader, 3) +// //}); + +// list.Add(new HrmsMenuItem +// { +// MenuItemId = reader.IsDBNull(0) ? 0 : reader.GetInt32(0), +// ItemName = GetString(reader, 1), +// ItemType = GetString(reader, 2), +// Price = GetDecimal(reader, 3), +// MenuDate = GetString(reader, 4), +// DayOfWeek = GetString(reader, 5), +// MealName = string.Empty +// }); + +// //list.Add(new HrmsMenuItem +// //{ +// // MenuItemId = reader.IsDBNull(0) ? 0 : reader.GetInt32(0), +// // ItemName = GetString(reader, 1), +// // ItemType = GetString(reader, 2), +// // Price = GetDecimal(reader, 3), + +// // MealName = GetString(reader, 4), +// // MenuDate = GetString(reader, 5), +// // DayOfWeek = GetString(reader, 6), +// //}); +// } + +// return list; +// } + +// private static string GetString(MySqlDataReader reader, int ordinal) +// { +// if (reader.IsDBNull(ordinal)) return string.Empty; +// var v = reader.GetValue(ordinal); +// return v?.ToString() ?? string.Empty; +// } + +// private static decimal GetDecimal(MySqlDataReader reader, int ordinal) +// { +// if (reader.IsDBNull(ordinal)) return 0m; +// var v = reader.GetValue(ordinal); +// return v is decimal d ? d : Convert.ToDecimal(v); +// } +//} + + + public class MenuLookupService : IMenuLookupService { private readonly IConfigService _configService; @@ -16,80 +143,50 @@ public class MenuLookupService : IMenuLookupService _configService = configService; } - /// + // Implementing the method for fetching menu items by site public async Task> GetMenuItemsForSiteAsync(int siteIdNumeric, CancellationToken cancellationToken = default) { // Backwards-compatible: use today's local date. - return await GetMenuItemsForSiteAndDateAsync(siteIdNumeric, DateTime.Today, cancellationToken).ConfigureAwait(false); + return await GetMenuItemsForSiteAndDateAsync(siteIdNumeric, DateTime.Today, "NonManagement", cancellationToken).ConfigureAwait(false); } - /// - public async Task> GetMenuItemsForSiteAndDateAsync(int siteIdNumeric, DateTime menuDateLocal, CancellationToken cancellationToken = default) + // Implementing the method for fetching menu items by site, date, and gradeType + public async Task> GetMenuItemsForSiteAndDateAsync(int siteIdNumeric, DateTime menuDateLocal, string gradeType, CancellationToken cancellationToken = default) { var connectionString = _configService.GetHrmsLookupConnectionString(); if (string.IsNullOrWhiteSpace(connectionString)) return Array.Empty(); - //const string sql = @" - // SELECT DISTINCT mi.id, mi.item_name, mi.item_type, mi.price - // FROM lunch_menu_week w - // JOIN lunch_menu_item li ON li.lunch_menu_week_id = w.id - // JOIN menu_item mi ON mi.id = li.menu_item_id - // WHERE w.location_site_id = @siteId - // AND CURDATE() BETWEEN w.week_start_date AND w.week_end_date - // ORDER BY mi.item_type, mi.item_name"; - const string sql = @" - SELECT - mi.id, - mi.item_name, - mi.item_type, - mi.price, - li.menu_date, - li.day_of_week - FROM lunch_menu_week w - JOIN lunch_menu_item li ON li.lunch_menu_week_id = w.id - JOIN menu_item mi ON mi.id = li.menu_item_id - WHERE w.location_site_id = @siteId - AND @menuDate BETWEEN w.week_start_date AND w.week_end_date - AND li.menu_date = @menuDate - ORDER BY mi.item_name;"; + SELECT + mi.id, + mi.item_name, + mi.item_type, + mi.price, + li.menu_date, + li.day_of_week + FROM lunch_menu_week w + JOIN lunch_menu_item li ON li.lunch_menu_week_id = w.id + JOIN menu_item mi ON mi.id = li.menu_item_id + WHERE w.location_site_id = @siteId + AND @menuDate BETWEEN w.week_start_date AND w.week_end_date + AND li.menu_date = @menuDate + AND mi.item_for = @itemFor + ORDER BY mi.item_name;"; - //const string sql = @" - // SELECT - // mi.id, - // mi.item_name, - // mi.item_type, - // mi.price, - // li.meal_name, - // li.menu_date, - // li.day_of_week - // FROM lunch_menu_week w - // JOIN lunch_menu_item li ON li.lunch_menu_week_id = w.id - // JOIN menu_item mi ON mi.id = li.menu_item_id - // WHERE w.location_site_id = @siteId - // AND @menuDate BETWEEN w.week_start_date AND w.week_end_date - // AND li.menu_date = @menuDate - // ORDER BY li.meal_name, mi.item_name;"; + var itemFor = GetItemForFromGradeType(gradeType); await using var conn = new MySqlConnection(connectionString); await conn.OpenAsync(cancellationToken).ConfigureAwait(false); await using var cmd = new MySqlCommand(sql, conn); cmd.Parameters.AddWithValue("@siteId", siteIdNumeric); cmd.Parameters.AddWithValue("@menuDate", menuDateLocal.Date); + cmd.Parameters.AddWithValue("@itemFor", itemFor); var list = new List(); await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) { - //list.Add(new HrmsMenuItem - //{ - // MenuItemId = reader.IsDBNull(0) ? 0 : reader.GetInt32(0), - // ItemName = GetString(reader, 1), - // ItemType = GetString(reader, 2), - // Price = GetDecimal(reader, 3) - //}); - list.Add(new HrmsMenuItem { MenuItemId = reader.IsDBNull(0) ? 0 : reader.GetInt32(0), @@ -100,23 +197,24 @@ public class MenuLookupService : IMenuLookupService DayOfWeek = GetString(reader, 5), MealName = string.Empty }); - - //list.Add(new HrmsMenuItem - //{ - // MenuItemId = reader.IsDBNull(0) ? 0 : reader.GetInt32(0), - // ItemName = GetString(reader, 1), - // ItemType = GetString(reader, 2), - // Price = GetDecimal(reader, 3), - - // MealName = GetString(reader, 4), - // MenuDate = GetString(reader, 5), - // DayOfWeek = GetString(reader, 6), - //}); } 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; @@ -130,4 +228,4 @@ public class MenuLookupService : IMenuLookupService var v = reader.GetValue(ordinal); return v is decimal d ? d : Convert.ToDecimal(v); } -} +} \ No newline at end of file diff --git a/Services/RfidService.cs b/Services/RfidService.cs index 805fda3..7041308 100644 --- a/Services/RfidService.cs +++ b/Services/RfidService.cs @@ -327,7 +327,7 @@ public class RfidService : IRfidService if (int.TryParse(new string(siteIdForSession.Where(char.IsDigit).ToArray()), out var siteNumeric) && siteNumeric > 0) { var menuItems = _menuLookup - .GetMenuItemsForSiteAndDateAsync(siteNumeric, nowLocal.Date) + .GetMenuItemsForSiteAndDateAsync(siteNumeric, nowLocal.Date, employee.GradeType) .GetAwaiter() .GetResult(); @@ -372,7 +372,8 @@ public class RfidService : IRfidService DepartmentType = employee.DepartmentType ?? string.Empty, MealLabel = mealLabel, MealItems = mealItemsDisplay, - TotalPrice = totalPrice + TotalPrice = totalPrice, + grade_type = employee.GradeType ?? string.Empty }; db.LunchOrderTransactions.Add(record); db.SaveChanges(); diff --git a/Services/SyncService.cs b/Services/SyncService.cs index 79bd5e5..e034333 100644 --- a/Services/SyncService.cs +++ b/Services/SyncService.cs @@ -67,13 +67,20 @@ public class SyncService : ISyncService AND order_date = @OrderDate AND total_cost = @TotalCost AND created_at = @CreatedAt + AND meal_name = @MealName LIMIT 1"; const string insertOrderSql = @" INSERT INTO lunch_order - (employee_id, employee_serial_number, order_date, shift, created_at, total_cost, is_cancelled, cancelled_by, created_by, function_id, department_id, location_site_id) + (employee_id, employee_serial_number, order_date, shift, created_at, total_cost, is_cancelled, cancelled_by, created_by, function_id, department_id, location_site_id, meal_name) VALUES - (@EmployeeId, @EmployeeSerialNumber, @OrderDate, @Shift, @CreatedAt, @TotalCost, 0, @CancelledBy, @CreatedBy, @FunctionId, @DepartmentId, @LocationSiteId)"; + (@EmployeeId, @EmployeeSerialNumber, @OrderDate, @Shift, @CreatedAt, @TotalCost, 0, @CancelledBy, @CreatedBy, @FunctionId, @DepartmentId, @LocationSiteId, @MealName)"; + + const string updateOrderCodeSql = @" + UPDATE lunch_order + SET code = @Code + WHERE id = @Id"; + var syncedIds = new List(capacity: toSync.Count); @@ -103,8 +110,11 @@ public class SyncService : ISyncService var orderDateLocal = record.ScanTime.ToLocalTime().Date; var totalCost = Math.Round((decimal)record.TotalPrice, 2, MidpointRounding.AwayFromZero); - var createdAt = record.TagCreatedAtUtc ?? record.ScanTime; + //var createdAt = record.TagCreatedAtUtc ?? record.ScanTime; + var createdAt = record.ScanTime.ToLocalTime(); var locationSiteId = SiteIdStringToInt(record.SiteId ?? string.Empty); + var mealName = (record.MealLabel ?? string.Empty).Trim(); + var shift = GetShiftFromMeal(mealName); await using (var hrmsConn = new MySqlConnection(hrmsConnStr)) { @@ -117,6 +127,7 @@ public class SyncService : ISyncService existsCmd.Parameters.AddWithValue("@OrderDate", orderDateLocal); existsCmd.Parameters.AddWithValue("@TotalCost", totalCost); existsCmd.Parameters.AddWithValue("@CreatedAt", createdAt); + existsCmd.Parameters.AddWithValue("@MealName", mealName); var existing = await existsCmd.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); alreadyExists = existing != null && existing != DBNull.Value; } @@ -127,7 +138,7 @@ public class SyncService : ISyncService orderCmd.Parameters.AddWithValue("@EmployeeId", employeeIdBigint); orderCmd.Parameters.AddWithValue("@EmployeeSerialNumber", record.UindSerial ?? string.Empty); orderCmd.Parameters.AddWithValue("@OrderDate", orderDateLocal); - orderCmd.Parameters.AddWithValue("@Shift", string.Empty); + orderCmd.Parameters.AddWithValue("@Shift", shift); orderCmd.Parameters.AddWithValue("@CreatedAt", createdAt); orderCmd.Parameters.AddWithValue("@TotalCost", totalCost); orderCmd.Parameters.AddWithValue("@CancelledBy", string.Empty); @@ -135,7 +146,19 @@ public class SyncService : ISyncService orderCmd.Parameters.AddWithValue("@FunctionId", record.FunctionId); orderCmd.Parameters.AddWithValue("@DepartmentId", record.DepartmentId); orderCmd.Parameters.AddWithValue("@LocationSiteId", locationSiteId); + orderCmd.Parameters.AddWithValue("@MealName", mealName); await orderCmd.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); + + var insertedId = orderCmd.LastInsertedId; + if (insertedId > 0) + { + var code = GenerateLunchOrderCode(insertedId, createdAt); + + await using var updateCodeCmd = new MySqlCommand(updateOrderCodeSql, hrmsConn); + updateCodeCmd.Parameters.AddWithValue("@Code", code); + updateCodeCmd.Parameters.AddWithValue("@Id", insertedId); + await updateCodeCmd.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); + } } } @@ -170,6 +193,26 @@ public class SyncService : ISyncService } } + private static string GenerateLunchOrderCode(long id, DateTime date) + { + return $"LO-{date:yyyy-MM}-{id:D6}"; + } + + + private static string GetShiftFromMeal(string mealName) + { + if (string.IsNullOrWhiteSpace(mealName)) + return string.Empty; + + mealName = mealName.Trim().ToLowerInvariant(); + + return mealName switch + { + "breakfast" => "MORNING", + "lunch" => "MORNING", + _ => "EVENING" + }; + } private static int SiteIdStringToInt(string siteId) { try diff --git a/ViewModels/ScannerDashboardViewModel.cs b/ViewModels/ScannerDashboardViewModel.cs index d92c067..7048ea3 100644 --- a/ViewModels/ScannerDashboardViewModel.cs +++ b/ViewModels/ScannerDashboardViewModel.cs @@ -923,6 +923,13 @@ public partial class ScannerDashboardViewModel : ObservableObject } } + private static string ToExcelText(string? value) + { + var safe = value ?? string.Empty; + safe = safe.Replace("\"", "\"\""); + return $"=\"{safe}\""; + } + private static string BuildCsvFromScanRecords(IReadOnlyList records) { var sb = new StringBuilder(); @@ -931,7 +938,7 @@ public partial class ScannerDashboardViewModel : ObservableObject { sb.Append(r.Id); sb.Append(','); - sb.Append(EscapeCsv(r.CardId)); + sb.Append(ToExcelText(r.CardId)); sb.Append(','); sb.Append(EscapeCsv(r.ScanTime.ToString("O", CultureInfo.InvariantCulture))); sb.Append(',');