using Inspectron.Epson; using Inspectron.Epson.PrintServer.Printers.Utils; using Inspectron.Epson.PrintServer.Printers.Utils.FinalRecipe; using Inspectron.Epson.PrintServer.Printers.Utils.InvoiceReceipt; using Inspectron.Epson.PrintServer.Printers.Utils.KitchenReceipt; using Inspectron.Epson.PrintServer.Printers.Utils.OrderItems; using System.Text.Json; using System.Text.Json.Nodes; namespace EpsonTest.Templates; public static class TestHelpers { // Default connection settings public const string DefaultEpsonIp = "127.0.0.1"; public const int DefaultEpsonPort = 8888; public const string DefaultHtmlPath = @".\test.html"; public const int DefaultPaperWidth = 384; public const int KitchenPaperWidth = 258; #region Printer Creation public static async Task CreateEpsonPrinterAsync( string ip = DefaultEpsonIp, int port = DefaultEpsonPort, int paperWidth = DefaultPaperWidth) { var printer = new EpsonPrinter(); await printer.ConnectAsync(ip, port); return printer; } public static async Task CreateHtmlPrinterAsync( string path = DefaultHtmlPath, int paperWidth = DefaultPaperWidth) { var printer = new HtmlPrinter(path, paperWidth: paperWidth); await printer.ConnectAsync(""); return printer; } #endregion #region Print Helpers public static async Task PrintCommandsToHtmlPrinterAsync( HtmlPrinter printer, IEnumerable commands, string? logoPath = null, int logoWidth = 300, int logoXPosition = 42) { if (logoPath != null) { await printer.SetAbsolutePrintPosition(logoXPosition); await printer.LoadImageAsync(logoPath, logoWidth); await printer.PrintLoadedImage(); await printer.SetAbsolutePrintPosition(0); } await printer.SetCustomLineSpacing(22); foreach (var command in commands) { if (command.IsBig) await printer.SetFontSizeAsync(2, 1); else await printer.SetFontSizeAsync(1, 1); await printer.SetEmphasized(command.IsBold); await printer.PrintTextAsync(command.Text + "\n"); } } public static async Task PrintCommandsToEpsonPrinterAsync( EpsonPrinter printer, IEnumerable commands, bool useDelay = true, int delayMs = 200) { await printer.FeedLinesAsync(1); await printer.SetCustomLineSpacing(22); foreach (var command in commands) { if (command.IsCut) { await printer.FeedLinesAsync(10); if (useDelay) await Task.Delay(delayMs * 5); await printer.CutAsync(false); if (useDelay) await Task.Delay(delayMs); continue; } await printer.SetBiggerFontTM220(command.IsBig, command.IsTall | command.IsBig, secondaryFont: false); if (useDelay) await Task.Delay(delayMs); await printer.SetRedColor(command.IsRed); if (useDelay) await Task.Delay(delayMs); await printer.SetEmphasized(command.IsBold); if (useDelay) await Task.Delay(delayMs); await printer.PrintTextAsync(command.Text + "\n"); if (useDelay) await Task.Delay(delayMs); } await printer.FeedLinesAsync(10); if (useDelay) await Task.Delay(delayMs * 5); await printer.CutAsync(); } public static async Task PrintKitchenCommandsToHtmlPrinterAsync( HtmlPrinter printer, IEnumerable commands) { await printer.FeedLinesAsync(1); await printer.SetCustomLineSpacing(22); foreach (var command in commands) { if (command.IsCut) { await printer.CutAsync(false); continue; } if (command.IsBig) await printer.SetFontSizeAsync(2, 1); else await printer.SetFontSizeAsync(1, 1); await printer.SetRedColor(command.IsRed); await printer.SetEmphasized(command.IsBold); await printer.PrintTextAsync(command.Text + "\n"); } } #endregion #region Console Output public static void PrintCommandsToConsole(IEnumerable commands) { Console.WriteLine("=== PRINT COMMANDS ===\n"); foreach (var command in commands) { string attributes = ""; if (command.IsBig) attributes += "[BIG] "; if (command.IsBold) attributes += "[BOLD] "; if (command.IsRed) attributes += "[RED] "; Console.WriteLine($"{attributes}{command.Text}"); } } #endregion #region JSON Data Builders public static string BuildKitchenReceiptJson() { var receipt = new KitchenReceipt { Title = "Warme Küche", TransactionDateTime = new DateTime(2025, 10, 23, 12, 18, 0), ReceiptNumber = "132018166", WaiterName = "Mariano Amato", WaiterId = "32 (VK Restaurant)", TableNumber = "22" }; receipt.Gangs.Add(new Gang(1, "Gang")); receipt.Gangs.Add(new Gang(2, "Gang")); receipt.Gangs[0].Dishes.Add(new Dish(1, "Avocado Sashimi") { Modifications = new DishModifications { Removed = new List { "Wasabi" }, Added = new List { "Extra Ginger" } }, Comment = "No soy sauce", GuestId = 1 }); receipt.Gangs[0].Dishes.Add(new Dish(1, "Baby Spinach Salad with Truffl") { GuestId = 2 }); receipt.Gangs[1].Dishes.Add(new Dish(1, "Avocado Sashimi") { GuestId = 1 }); receipt.Gangs[1].Dishes.Add(new Dish(1, "Baby Spinach Salad with Truffl") { GuestId = 2 }); return SerializeWithComputedProps(receipt); } public static string BuildKitchenReceiptNextDishJson() { var receipt = new KitchenReceipt { Title = "Warme Küche", TransactionDateTime = new DateTime(2025, 10, 23, 12, 18, 0), ReceiptNumber = "132018166", WaiterName = "Mariano Amato", WaiterId = "32 (VK Restaurant)", TableNumber = "22", SpecialInstruction = "Gang schicken" }; receipt.Gangs.Add(new Gang(2, "Gang")); return SerializeWithComputedProps(receipt); } public static string BuildFinalReceiptJson(bool includeTax = true) { var receipt = new FinalReceipt { CompanyName = "Klingler Gastro AG", Address1 = "Münzplatz 3", Address2 = "CH-8001 Zürich", Phone = "043 321 22 22", ReceiptNumber = null, DateTime = new DateTime(2025, 12, 16, 14, 58, 0), Guests = 2, Total = 160.00m, Currency = "CHF", TotalInAlternateCurrency = 172.80m, AlternateCurrency = "EUR", PaymentMethod = "MASTER", PaymentAmount = 160.00m, WaiterName = "Yves", Terminal = "Hauptkasse ZH", TableNumber = "12", VatNumber = "CHE-449.635.880 MWST", ThankYouMessage = "Das Team bedankt sich herzlich für Ihren", GoodbyeMessageLine1 = "Besuch.", GoodbyeMessageLine2 = "Auf Wiedersehen.", IsDebtor = true, SplitPayments = new List { new() { PaymentMethod = "Cash", Amount = 50.00m, Currency = "CHF" }, new() { PaymentMethod = "Card", Amount = 110.00m, Currency = "CHF" } }, TerminalReceipts = new List { new() { ReceiptType = "*** Kundenbeleg ***", BookingType = "Buchung", PaymentSystem = "TWINT", TransactionNumber = "XXXXXXXXXXXXXXX1494", TransactionDateTime = new DateTime(2025, 11, 11, 12, 34, 49), TerminalId = "31108834", AID = "A0000015749E", TransactionSeqCount = "6127", TransactionRefNo = "99036644599", AuthCode = "0d3396", AcquirerId = "2", EftAmount = 67.00m, TipAmount = 6.70m, TotalEftAmount = 73.70m, Currency = "CHF" }, new() { ReceiptType = "*** Kundenbeleg ***", BookingType = "Buchung", PaymentSystem = "MASTER", TransactionNumber = "XXXXXXXXXXXXXXX2587", TransactionDateTime = new DateTime(2025, 11, 11, 12, 36, 12), TerminalId = "31108834", AID = "A0000000041010", TransactionSeqCount = "6128", TransactionRefNo = "99036644600", AuthCode = "1a4b2c", AcquirerId = "1", EftAmount = 93.00m, TipAmount = 0.00m, TotalEftAmount = 93.00m, Currency = "CHF" } }, DiscountInfo = new DiscountInfo() { Amount = -50.00m, Currency = "CHF", Description = "Fine Dine", } }; receipt.Items.Add(new FinalReceiptItem { Quantity = 1, Description = "San Pellegrino 50cl", UnitPrice = 6.50m, TotalPrice = 6.50m, TaxCategory = "A" }); receipt.Items.Add(new FinalReceiptItem { Quantity = 1, Description = "Panna 50cl", UnitPrice = 6.50m, TotalPrice = 6.50m, TaxCategory = "A" }); receipt.Items.Add(new FinalReceiptItem { Quantity = 1, Description = "An item with really long description that should wrap", UnitPrice = 5.50m, TotalPrice = 5.50m, TaxCategory = "A" }); receipt.Items.Add(new FinalReceiptItem { Quantity = 2, Description = "Business Lunch Menu Seco", UnitPrice = 44.00m, TotalPrice = 88.00m, TaxCategory = "A", SubItems = new List { "Tomato Soup", "Grilled Salmon", "Tiramisu" } }); receipt.Items.Add(new FinalReceiptItem { Quantity = 2, Description = "Brunello di Montalcino 1", UnitPrice = 16.00m, TotalPrice = 32.00m, TaxCategory = "A" }); receipt.Items.Add(new FinalReceiptItem { Quantity = 2, Description = "Espresso", UnitPrice = 5.50m, TotalPrice = 11.00m, TaxCategory = "A" }); receipt.Items.Add(new FinalReceiptItem { Quantity = 1, Description = "Tip", UnitPrice = 10.50m, TotalPrice = 10.50m, TaxCategory = "B" }); if (includeTax) { receipt.TaxBreakdown.Add(new TaxInfo { Category = "A", Rate = 8.1m, Gross = 149.50m, Net = 138.30m, TaxAmount = 11.20m, Currency = "CHF" }); receipt.TaxBreakdown.Add(new TaxInfo { Category = "B", Rate = 0m, Gross = 10.50m, Net = 10.50m, TaxAmount = 0.00m, Currency = "CHF" }); } return SerializeWithComputedProps(receipt); } public static string BuildInvoiceReceiptJson() { var receipt = new InvoiceReceipt { CompanyName = "Klingler Gastro AG", Address1 = "Münzplatz 3", Address2 = "CH-8001 Zürich", Phone = "043 321 22 22", ReceiptNumber = null, DateTime = new DateTime(2025, 12, 16, 14, 58, 0), Guests = 2, Total = 160.00m, Currency = "CHF", TableNumber = "12", ThankYouMessage = "Thank you!" }; receipt.Items.Add(new Inspectron.Epson.PrintServer.Printers.Utils.InvoiceReceipt.ReceiptItem { Quantity = 1, Description = "San Pellegrino 50cl", UnitPrice = 6.50m, TotalPrice = 6.50m, TaxCategory = "A" }); receipt.Items.Add(new Inspectron.Epson.PrintServer.Printers.Utils.InvoiceReceipt.ReceiptItem { Quantity = 1, Description = "Panna 50cl", UnitPrice = 6.50m, TotalPrice = 6.50m, TaxCategory = "A" }); receipt.Items.Add(new Inspectron.Epson.PrintServer.Printers.Utils.InvoiceReceipt.ReceiptItem { Quantity = 1, Description = "Granini Tomatensaft", UnitPrice = 5.50m, TotalPrice = 5.50m, TaxCategory = "A" }); receipt.Items.Add(new Inspectron.Epson.PrintServer.Printers.Utils.InvoiceReceipt.ReceiptItem { Quantity = 2, Description = "Business Lunch Menu Seco", UnitPrice = 44.00m, TotalPrice = 88.00m, TaxCategory = "A", SubItems = new List { "Tomato Soup", "Grilled Salmon", "Tiramisu" } }); receipt.Items.Add(new Inspectron.Epson.PrintServer.Printers.Utils.InvoiceReceipt.ReceiptItem { Quantity = 2, Description = "Brunello di Montalcino 1", UnitPrice = 16.00m, TotalPrice = 32.00m, TaxCategory = "A" }); receipt.Items.Add(new Inspectron.Epson.PrintServer.Printers.Utils.InvoiceReceipt.ReceiptItem { Quantity = 2, Description = "Espresso", UnitPrice = 5.50m, TotalPrice = 11.00m, TaxCategory = "A" }); receipt.Items.Add(new Inspectron.Epson.PrintServer.Printers.Utils.InvoiceReceipt.ReceiptItem { Quantity = 1, Description = "Tip", UnitPrice = 10.50m, TotalPrice = 10.50m, TaxCategory = "B" }); return SerializeWithComputedProps(receipt); } public static string BuildOrderItemsJson() { var receipt = new OrderItemsReceipt { OrderNumber = "1", QRInfo = "Margherita Pizza Classic longer text", DateTime = new DateTime(2026, 1, 14, 14, 35, 47), ClientNotes = "Some main note", Items = new List { new() { Number = 1, Name = "pappara", Quantity = 1, SubItems = new List { "pizza", "cola" }, Comment = "Cola zero please" }, new() { Number = 2, Name = "Classic Beef Burger", Quantity = 1, Modifications = new ItemModifications { Removed = new List { "Onion" }, Added = new List { "butter" } }, Comment = "Medium rare please" }, new() { Number = 3, Name = "Margherita Pizza Classic", Quantity = 2, Size = "L" } } }; return SerializeWithComputedProps(receipt); } #endregion #region Computed Properties private static readonly JsonSerializerOptions SerializerOptions = new() { WriteIndented = true }; private static string SerializeWithComputedProps(object receipt) { var json = JsonSerializer.Serialize(receipt, SerializerOptions); var node = JsonNode.Parse(json)!; switch (receipt) { case KitchenReceipt kr: AddKitchenComputedProps(node, kr); break; case FinalReceipt fr: AddFinalReceiptComputedProps(node, fr); break; case InvoiceReceipt ir: AddInvoiceComputedProps(node, ir); break; case OrderItemsReceipt or: AddOrderItemsComputedProps(node, or); break; } return node.ToJsonString(SerializerOptions); } private static void AddKitchenComputedProps(JsonNode node, KitchenReceipt receipt) { node["HasGangs"] = receipt.Gangs.Count > 0; node["HasDishes"] = receipt.Dishes.Count > 0; // Add GuestPrefix and HasModifications to dishes in gangs var gangsArray = node["Gangs"]?.AsArray(); if (gangsArray != null) { for (int g = 0; g < receipt.Gangs.Count; g++) { var gangDishes = gangsArray[g]?["Dishes"]?.AsArray(); if (gangDishes != null) { for (int d = 0; d < receipt.Gangs[g].Dishes.Count; d++) { var dish = receipt.Gangs[g].Dishes[d]; gangDishes[d]!["GuestPrefix"] = dish.GuestId.HasValue ? $"{dish.GuestId} " : ""; var mods = dish.Modifications; bool hasMods = mods != null && (mods.Removed.Count > 0 || mods.Added.Count > 0); if (gangDishes[d]!["Modifications"] == null) gangDishes[d]!["Modifications"] = new JsonObject(); gangDishes[d]!["Modifications"]!["HasModifications"] = hasMods; } } } } // Add GuestPrefix and HasModifications to top-level dishes var dishesArray = node["Dishes"]?.AsArray(); if (dishesArray != null) { for (int d = 0; d < receipt.Dishes.Count; d++) { var dish = receipt.Dishes[d]; dishesArray[d]!["GuestPrefix"] = dish.GuestId.HasValue ? $"{dish.GuestId} " : ""; var mods = dish.Modifications; bool hasMods = mods != null && (mods.Removed.Count > 0 || mods.Added.Count > 0); if (dishesArray[d]!["Modifications"] == null) dishesArray[d]!["Modifications"] = new JsonObject(); dishesArray[d]!["Modifications"]!["HasModifications"] = hasMods; } } } private static void AddFinalReceiptComputedProps(JsonNode node, FinalReceipt receipt) { node["ReceiptLabel"] = receipt.ReceiptNumber != null ? $"Rechnung Nr. {receipt.ReceiptNumber}" : ""; node["HasSplitPayments"] = receipt.SplitPayments.Count > 0; node["HasTaxableCategories"] = receipt.TaxBreakdown.Any(t => !string.Equals(t.Category, "d", StringComparison.OrdinalIgnoreCase)); var itemsArray = node["Items"]?.AsArray(); if (itemsArray != null) { for (int i = 0; i < receipt.Items.Count; i++) { var item = receipt.Items[i]; itemsArray[i]!["PriceDisplay"] = $"{item.UnitPrice:F2}" + $"{item.TotalPrice:F2}".PadLeft(7) + $" {item.TaxCategory}"; } } } private static void AddInvoiceComputedProps(JsonNode node, InvoiceReceipt receipt) { node["ReceiptLabel"] = receipt.ReceiptNumber != null ? $"Rechnung Nr. {receipt.ReceiptNumber}" : ""; var itemsArray = node["Items"]?.AsArray(); if (itemsArray != null) { for (int i = 0; i < receipt.Items.Count; i++) { var item = receipt.Items[i]; itemsArray[i]!["PriceDisplay"] = $"{item.UnitPrice:F2}" + $"{item.TotalPrice:F2}".PadLeft(7) + $" {item.TaxCategory}"; } } } private static void AddOrderItemsComputedProps(JsonNode node, OrderItemsReceipt receipt) { var itemsArray = node["Items"]?.AsArray(); if (itemsArray != null) { for (int i = 0; i < receipt.Items.Count; i++) { var item = receipt.Items[i]; itemsArray[i]!["SizePart"] = !string.IsNullOrEmpty(item.Size) ? item.Size + " " : ""; var mods = item.Modifications; bool hasMods = mods != null && (mods.Removed.Count > 0 || mods.Added.Count > 0); if (itemsArray[i]!["Modifications"] == null) itemsArray[i]!["Modifications"] = new JsonObject(); itemsArray[i]!["Modifications"]!["HasModifications"] = hasMods; } } } #endregion }