Add project files.

This commit is contained in:
EugeneTes
2026-01-13 09:06:47 +01:00
parent dd935fe1fa
commit 7390693f50
187 changed files with 83457 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
using Inspectron.Epson.PrintServer.PrintServices;
namespace Inspectron.Epson.PrintServer.Printers;
public class PrinterFactory:IPrinterFactory
{
public IPrinter CreatePrinterFromId(byte printerId, EpsonPrinter epsonPrinter)
{
switch (printerId)
{
case 0x13:
return new TM_U220IITranslated(epsonPrinter);
case 0x0D:
return new TM_U220IITranslated(epsonPrinter);
case 0x01:
return new TM_T30IIITranslated(epsonPrinter);
default:
throw new NotSupportedException($"Printer with ID {printerId:X2} is not supported.");
}
}
}

View File

@@ -0,0 +1,47 @@
using Inspectron.Epson.PrintServer.PrintServices;
using System.Net.NetworkInformation;
using System.Reflection;
namespace Inspectron.Epson.PrintServer.Printers;
public class TM_T30III:IPrinter
{
private readonly EpsonPrinter _printer;
public TM_T30III(EpsonPrinter printer)
{
_printer = printer;
}
public async Task InitAsync()
{
await _printer.InitAsync();
}
public async Task PrintImageAsync(string path)
{
await _printer.SetAbsolutePrintPosition(100);
await _printer.LoadImageAsync(Path.Combine("logos", path), 384);
await Task.Delay(100);
//await _printer.PrintLoadedImage();
await _printer.FeedLinesAsync(1);
await _printer.SetAbsolutePrintPosition(0);
}
public async Task SetFontSizeAsync(int fontSize)
{
await _printer.SetFontSizeAsync(fontSize, fontSize);
}
public async Task PrintTextAsync(string text)
{
await _printer.PrintTextAsync(text);
await _printer.FeedLinesAsync(5);
var status = await _printer.GetPrinterStatusAsync();
}
public async Task Cut()
{
await _printer.CutAsync();
}
}

View File

@@ -0,0 +1,118 @@
using System.Text.Json;
using Inspectron.Epson.PrintServer.Printers.Utils;
using Inspectron.Epson.PrintServer.Printers.Utils.FinalRecipe;
using Inspectron.Epson.PrintServer.PrintServices;
namespace Inspectron.Epson.PrintServer.Printers;
public class TM_T30IIITranslated:IPrinter
{
private readonly EpsonPrinter _printer;
public TM_T30IIITranslated(EpsonPrinter printer)
{
_printer = printer;
}
public Task InitAsync()
{
return _printer.InitAsync();
}
public async Task PrintImageAsync(string path)
{
await _printer.SetAbsolutePrintPosition(100);
await _printer.LoadImageAsync(Path.Combine("logos", path), 384);
await Task.Delay(100);
await _printer.FeedLinesAsync(1);
await _printer.SetAbsolutePrintPosition(0);
}
public async Task SetFontSizeAsync(int fontSize)
{
//await _printer.SetFontSizeAsync(fontSize, fontSize);
}
public async Task PrintTextAsync(string text)
{
Receipt? receipt;
try
{
receipt = JsonSerializer.Deserialize<Receipt>(text);
}
catch
{
// Fallback to kitchen receipt
await PrintKitchen(text);
return;
}
var converter = new ReceiptConverter(lineWidth: 48, bigFontLineWidth: 24);
var printCommands = converter.ConvertToPrintCommands(receipt);
await _printer.FeedLinesAsync(1);
await _printer.SetCustomLineSpacing(22);
foreach (var command in printCommands)
{
string attributes = "";
if (command.IsBig) attributes += "[BIG]\n";
if (command.IsBold) attributes += "[BOLD]\n";
Console.WriteLine($"{attributes}{command.Text}");
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");
}
await _printer.SetDefaultLineSpacing();
await _printer.FeedLinesAsync(10);
await Task.Delay(200);
}
private async Task PrintKitchen(string text)
{
ReceiptTranslator translator = new ReceiptTranslator();
var ast = translator.ParseReceipt(text);
KitchenReceiptPrinter printer = new KitchenReceiptPrinter(lineWidth: 33, 21);
var commands = printer.ConvertToCommands((KitchenReceipt)ast);
foreach (KitchenPrintCommand command in commands)
{
if (command.IsBig)
{
await _printer.SetFontSizeAsync(2,2);
}
else
{
await _printer.SetFontSizeAsync(1, 1);
}
await Task.Delay(200);
await _printer.SetRedColor(command.IsRed);
await Task.Delay(200);
await _printer.SetEmphasized(command.IsBold);
await Task.Delay(200);
await _printer.PrintTextAsync(command.Text + "\n");
await Task.Delay(200);
}
await _printer.FeedLinesAsync(10);
await Task.Delay(1000);
}
public async Task Cut()
{
await _printer.CutAsync();
await Task.Delay(200);
}
}

View File

@@ -0,0 +1,51 @@
using Inspectron.Epson.PrintServer.PrintServices;
using System.Reflection;
namespace Inspectron.Epson.PrintServer.Printers;
public class TM_U220II:IPrinter
{
private readonly EpsonPrinter _printer;
public TM_U220II(EpsonPrinter printer)
{
_printer = printer;
}
public async Task InitAsync()
{
await _printer.InitAsync();
await Task.Delay(200);
}
public Task PrintImageAsync(string path)
{
return Task.CompletedTask;
}
public async Task SetFontSizeAsync(int fontSize)
{
await _printer.SetBiggerFontTM220(fontSize == 2);
await Task.Delay(200);
await _printer.SelectFont(EpsonCommands.PrinterFont.A);
await Task.Delay(200);
}
public async Task PrintTextAsync(string text)
{
await _printer.PrintTextAsync(text);
var lines = text.Split('\n').Length;
for (int i = 0; i < lines; i++)
{
await Task.Delay(200);
}
await _printer.FeedLinesAsync(5);
await Task.Delay(200);
}
public async Task Cut()
{
await _printer.CutAsync();
await Task.Delay(200);
}
}

View File

@@ -0,0 +1,67 @@
using Inspectron.Epson.PrintServer.Printers.Utils;
using Inspectron.Epson.PrintServer.PrintServices;
using System.Reflection;
namespace Inspectron.Epson.PrintServer.Printers;
public class TM_U220IITranslated:IPrinter
{
private readonly EpsonPrinter _printer;
public TM_U220IITranslated(EpsonPrinter printer)
{
_printer = printer;
}
public async Task InitAsync()
{
await _printer.InitAsync();
await Task.Delay(200);
}
public Task PrintImageAsync(string path)
{
return Task.CompletedTask;
}
public async Task SetFontSizeAsync(int fontSize)
{
//await _printer.SetBiggerFontTM220(fontSize == 2);
//await Task.Delay(200);
//await _printer.SelectFont(EpsonCommands.PrinterFont.A);
//await Task.Delay(200);
}
public async Task PrintTextAsync(string text)
{
ReceiptTranslator translator = new ReceiptTranslator();
var ast = translator.ParseReceipt(text);
KitchenReceiptPrinter printer = new KitchenReceiptPrinter(lineWidth:33,21);
var commands = printer.ConvertToCommands((KitchenReceipt)ast);
foreach (KitchenPrintCommand command in commands)
{
await _printer.SetBiggerFontTM220(command.IsBig);
await Task.Delay(200);
await _printer.SetRedColor(command.IsRed);
await Task.Delay(200);
await _printer.SetEmphasized(command.IsBold);
await Task.Delay(200);
await _printer.PrintTextAsync(command.Text + "\n");
await Task.Delay(200);
}
//await _printer.PrintTextAsync(text);
//var lines = text.Split('\n').Length;
//for (int i = 0; i < lines; i++)
//{
// await Task.Delay(200);
//}
await _printer.FeedLinesAsync(10);
await Task.Delay(1000);
}
public async Task Cut()
{
await _printer.CutAsync();
await Task.Delay(200);
}
}

View File

@@ -0,0 +1,78 @@
using System.Collections;
using System.Reflection;
using System.Text;
namespace Inspectron.Epson.PrintServer.Printers.Utils;
public record ASTNode
{
public string Accept(IAccepter compiler)
{
return ((string)compiler.GetType()
.GetMethod("Visit", BindingFlags.Public | BindingFlags.Instance, new[] { this.GetType() })!
.Invoke(compiler, new[] { this })!)!;
}
public static string PrintNode(ASTNode node)
{
// print type name and string representation of all its fields
var type = node.GetType();
var fields = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var sb = new StringBuilder();
sb.Append(type.Name);
sb.Append("(");
int i = 0;
foreach (var field in fields)
{
if (i++ > 0) sb.Append(",");
var value = field.GetValue(node);
if (value is ASTNode valueNode)
{
sb.Append(PrintNode(valueNode));
}
else
{
sb.Append(value);
}
// if field is list - print all its elements
if (field.PropertyType.IsGenericType && field.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
{
var list = (IList)field.GetValue(node);
sb.Append("[");
int j = 0;
sb.AppendLine();
foreach (var item in list)
{
if (j++ > 0)
{
sb.Append(",");
sb.AppendLine();
}
if (item is ASTNode itemNode)
{
sb.Append(PrintNode(itemNode));
}
else
{
sb.Append(item);
}
}
sb.Append("]");
}
}
sb.Append(")");
return sb.ToString();
}
}
public interface IAccepter
{
}

View File

@@ -0,0 +1,60 @@
namespace Inspectron.Epson.PrintServer.Printers.Utils.FinalRecipe;
public class Receipt
{
public string CompanyName { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Phone { get; set; }
public string ReceiptNumber { get; set; }
public DateTime DateTime { get; set; }
public int Guests { get; set; }
public List<ReceiptItem> Items { get; set; } = new List<ReceiptItem>();
public decimal Total { get; set; }
public string Currency { get; set; }
public decimal? TotalInAlternateCurrency { get; set; }
public string AlternateCurrency { get; set; }
public string PaymentMethod { get; set; }
public decimal PaymentAmount { get; set; }
public List<TaxInfo> TaxBreakdown { get; set; } = new List<TaxInfo>();
public string ServerName { get; set; }
public string Terminal { get; set; }
public string TableNumber { get; set; }
public string VatNumber { get; set; }
public string ThankYouMessage { get; set; }
public string GoodbyeMessageLine1 { get; set; }
public string GoodbyeMessageLine2 { get; set; }
}
public class ReceiptItem
{
public int Quantity { get; set; }
public string Description { get; set; }
public decimal UnitPrice { get; set; }
public decimal TotalPrice { get; set; }
public string TaxCategory { get; set; }
}
public class TaxInfo
{
public string Category { get; set; }
public decimal Rate { get; set; }
public decimal Gross { get; set; }
public decimal Net { get; set; }
public decimal TaxAmount { get; set; }
public string Currency { get; set; }
}
public class PrintCommand
{
public string Text { get; set; }
public bool IsBig { get; set; }
public bool IsBold { get; set; }
public PrintCommand(string text, bool isBig = false, bool isBold = false)
{
Text = text;
IsBig = isBig;
IsBold = isBold;
}
}

View File

@@ -0,0 +1,118 @@
namespace Inspectron.Epson.PrintServer.Printers.Utils.FinalRecipe;
public class ReceiptConverter
{
private readonly int _lineWidth;
private readonly int _bigFontLineWidth;
public ReceiptConverter(int lineWidth = 42, int bigFontLineWidth = 21)
{
_lineWidth = lineWidth;
_bigFontLineWidth = bigFontLineWidth;
}
public List<PrintCommand> ConvertToPrintCommands(Receipt receipt)
{
var commands = new List<PrintCommand>();
// Header - Company info
commands.Add(new PrintCommand(Center(receipt.CompanyName, false)));
commands.Add(new PrintCommand(Center(receipt.Address1, false)));
commands.Add(new PrintCommand(Center(receipt.Address2, false)));
commands.Add(new PrintCommand(Center(receipt.Phone, false)));
commands.Add(new PrintCommand(""));
commands.Add(new PrintCommand(""));
// Receipt info
string receiptLine = $"Rechnung Nr. {receipt.ReceiptNumber}".PadRight(_lineWidth/2)+$"{receipt.DateTime:HH:mm dd.MM.yyyy}".PadLeft(_lineWidth/2);
commands.Add(new PrintCommand(receiptLine,isBold:true));
commands.Add(new PrintCommand($"Guests: {receipt.Guests}"));
commands.Add(new PrintCommand(""));
// Items
foreach (var item in receipt.Items)
{
string quantityDesc = $"{item.Quantity}x {item.Description}";
string prices = $"{item.UnitPrice:F2}"+$"{item.TotalPrice:F2}".PadLeft(7)+$" {item.TaxCategory}";
// Calculate spacing to align prices to the right
int spacesNeeded = _lineWidth - quantityDesc.Length - prices.Length;
if (spacesNeeded < 1) spacesNeeded = 1;
string itemLine = quantityDesc + new string(' ', spacesNeeded) + prices;
commands.Add(new PrintCommand(itemLine));
}
commands.Add(new PrintCommand("")); // Reini
commands.Add(new PrintCommand("---------".PadLeft(_lineWidth)));
commands.Add(new PrintCommand("")); //Reini
// Total
string totalLine = $"Summe: {receipt.Total:F2} {receipt.Currency}";
commands.Add(new PrintCommand(Center(totalLine, true), true, true));
commands.Add(new PrintCommand(""));
// Alternate currency
if (receipt.TotalInAlternateCurrency.HasValue)
{
string altCurrencyLine = $"{receipt.TotalInAlternateCurrency:F2} {receipt.AlternateCurrency}";
commands.Add(new PrintCommand(altCurrencyLine.PadLeft(_lineWidth)));
commands.Add(new PrintCommand(""));
}
// Payment method
string paymentLine = $"{receipt.PaymentMethod}";
string paymentAmount = $"{receipt.PaymentAmount:F2} {receipt.Currency}";
int paymentSpaces = _lineWidth - paymentLine.Length - paymentAmount.Length;
if (paymentSpaces < 1) paymentSpaces = 1;
commands.Add(new PrintCommand(paymentLine + new string(' ', paymentSpaces) + paymentAmount,isBold:true));
commands.Add(new PrintCommand(""));
// Tax breakdown
foreach (var tax in receipt.TaxBreakdown)
{
string taxLine = "MwSt %".PadRight(_lineWidth/4)+" Brutto".PadRight(_lineWidth / 4) + " Netto".PadRight(_lineWidth / 4) + "MwSt".PadLeft(_lineWidth / 4);
if (receipt.TaxBreakdown.IndexOf(tax) == 0)
{
commands.Add(new PrintCommand(taxLine));
}
string taxDetail = ($"{tax.Category}:"+ $"{tax.Rate}%".PadLeft(5)).PadRight(_lineWidth / 4) + $"{tax.Gross:F2} {tax.Currency}".PadLeft(_lineWidth/4)+$"{tax.Net:F2} {tax.Currency}".PadLeft(_lineWidth/4)+$"{tax.TaxAmount:F2} {tax.Currency}".PadLeft(_lineWidth / 4);
commands.Add(new PrintCommand(taxDetail));
}
commands.Add(new PrintCommand(""));
// Footer info
//commands.Add(new PrintCommand(Center($"Bedient von: {receipt.ServerName}", false)));
//commands.Add(new PrintCommand(Center($"Terminal: {receipt.Terminal}", false)));
//commands.Add(new PrintCommand(Center($"Tisch: {receipt.TableNumber}", false)));
commands.Add(new PrintCommand($"Bedient von:".PadLeft(_lineWidth / 2) + $" {receipt.ServerName}"));
commands.Add(new PrintCommand($"Terminal:".PadLeft(_lineWidth / 2) + $" {receipt.Terminal}"));
commands.Add(new PrintCommand($"Tisch:".PadLeft(_lineWidth / 2)+$" {receipt.TableNumber}"));
commands.Add(new PrintCommand(""));
commands.Add(new PrintCommand(""));
// VAT number
commands.Add(new PrintCommand(Center(receipt.VatNumber, false)));
// Thank you message
commands.Add(new PrintCommand(Center(receipt.ThankYouMessage, false)));
commands.Add(new PrintCommand(Center(receipt.GoodbyeMessageLine1, false)));
commands.Add(new PrintCommand(Center(receipt.GoodbyeMessageLine2, false)));
return commands;
}
private string Center(string text, bool isBigFont)
{
int effectiveLineWidth = isBigFont ? _bigFontLineWidth : _lineWidth;
if (string.IsNullOrEmpty(text) || text.Length >= effectiveLineWidth)
return text;
int totalPadding = effectiveLineWidth - text.Length;
int leftPadding = totalPadding / 2;
return new string(' ', leftPadding) + text;
}
}

View File

@@ -0,0 +1,9 @@
namespace Inspectron.Epson.PrintServer.Printers.Utils;
public class KitchenPrintCommand
{
public bool IsBig { get; set; }
public bool IsBold { get; set; }
public bool IsRed { get; set; }
public string Text { get; set; }
}

View File

@@ -0,0 +1,95 @@
namespace Inspectron.Epson.PrintServer.Printers.Utils;
public class KitchenReceiptPrinter
{
private readonly int _lineWidth;
private readonly int _bigFontLineWidth;
public KitchenReceiptPrinter(int lineWidth = 42, int bigFontLineWidth = 21)
{
_lineWidth = lineWidth;
_bigFontLineWidth = bigFontLineWidth;
}
public List<KitchenPrintCommand> ConvertToCommands(KitchenReceipt receipt)
{
var commands = new List<KitchenPrintCommand>();
// Header: "Warme Küche" - Big, Bold, Red, Centered
commands.Add(new KitchenPrintCommand
{
Text = Center(receipt.Location, isBigFont: true),
IsBig = true,
IsBold = true,
IsRed = true
});
commands.Add(Separator());
// Date and Owner info - Normal, Left-aligned
commands.Add(new KitchenPrintCommand { Text = Center(receipt.Date,false) });
var ownerLines = receipt.Owner.Split('\n');
foreach (var line in ownerLines)
{
commands.Add(new KitchenPrintCommand { Text = Center(line, false) });
}
// Table number - Big, Bold, Centered
commands.Add(new KitchenPrintCommand
{
Text = Center($"Tisch: {receipt.Tisch}", isBigFont: true),
IsBig = true,
IsBold = true
});
commands.Add(Separator());
// Items - Left-aligned
foreach (var item in receipt.items)
{
if (item is KitchenProduct kp)
{
commands.Add(new KitchenPrintCommand
{
Text = $"{kp.Amount}x {item.Name}"
});
}
if(item is KitchenGang kg)
{
commands.Add(new KitchenPrintCommand
{
IsRed = true,
IsBig = true,
IsBold = true,
Text = Center(item.Name,true)
});
}
}
commands.Add(Separator());
return commands;
}
private KitchenPrintCommand Separator()
{
return new KitchenPrintCommand
{
Text = new string('-', _lineWidth)
};
}
private string Center(string text, bool isBigFont)
{
int effectiveLineWidth = isBigFont ? _bigFontLineWidth : _lineWidth;
if (string.IsNullOrEmpty(text) || text.Length >= effectiveLineWidth)
return text;
int totalPadding = effectiveLineWidth - text.Length;
int leftPadding = totalPadding / 2;
return new string(' ', leftPadding) + text;
}
}

View File

@@ -0,0 +1,206 @@
namespace Inspectron.Epson.PrintServer.Printers.Utils;
public record KitchenReceipt(string Location, string Date, string Owner, string Tisch, List<KitchenItem> items) :ASTNode;
public record KitchenItem(string Name) : ASTNode;
public record KitchenProduct(int Amount, string Name) : KitchenItem(Name);
public record KitchenGang(string Name) : KitchenItem(Name);
public record FinalReceipt(string Location, string Phone, string URL, string Date, List<FinalReceiptItem> items, string total, string MWST, string Comment, string Thanks) : ASTNode;
public record FinalReceiptItem(string Name, int Amount, string Price, string Total) : ASTNode;
public class ReceiptTranslator
{
public ASTNode ParseReceipt(string receiptText)
{
// Determine receipt type based on content
bool isFinalReceipt = receiptText.Contains("http") ||
receiptText.Contains("+41") ||
receiptText.Contains("Summe CHF") ||
receiptText.Contains("MWST") ||
receiptText.Contains("Thank you");
bool isKitchenReceipt = receiptText.Contains("TISCH:");
if (isKitchenReceipt && !isFinalReceipt)
{
return ParseKitchenReceipt(receiptText);
}
else if (isFinalReceipt)
{
return ParseFinalReceipt(receiptText);
}
else
{
// Default to kitchen receipt if unclear
return ParseKitchenReceipt(receiptText);
}
}
public static FinalReceipt ParseFinalReceipt(string receiptText)
{
var lines = receiptText.Split('\n', StringSplitOptions.RemoveEmptyEntries)
.Select(l => l.Trim())
.ToList();
// Extract header information
string location = lines[0];
string phone = lines[1];
string url = lines[2];
// Find and extract date
var dateLine = lines.FirstOrDefault(l => l.StartsWith("Datum:"));
string date = dateLine?.Replace("Datum:", "").Trim() ?? "";
// Find total line
var totalLine = lines.FirstOrDefault(l => l.Contains("Summe CHF :"));
string total = totalLine?.Split(':').Last().Trim() ?? "";
// Find MWST line (comes after "TOTAL MWST" header)
var mwstLineIndex = lines.FindIndex(l => l.StartsWith("TOTAL") && l.Contains("MWST"));
string mwst = mwstLineIndex >= 0 && mwstLineIndex + 1 < lines.Count
? lines[mwstLineIndex + 1].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault() ?? ""
: "";
// Extract comment (line after MWST values)
string comment = mwstLineIndex >= 0 && mwstLineIndex + 2 < lines.Count
? lines[mwstLineIndex + 2]
: "";
// Extract thank you message (last non-separator line)
string thanks = lines.LastOrDefault(l => !l.Contains("--")) ?? "";
// Parse items
var items = new List<FinalReceiptItem>();
var startIndex = lines.FindIndex(l => l.StartsWith("Datum:")) + 2; // Skip date and separator
var endIndex = lines.FindIndex(l => l.Contains("Summe CHF"));
for (int i = startIndex; i < endIndex; i++)
{
var line = lines[i];
// Skip separator lines and empty lines
if (line.Contains("---") || string.IsNullOrWhiteSpace(line))
continue;
// Check if line contains item data (has * separator)
if (line.Contains("*"))
{
// Parse format: "Name Amount * Price Total"
var parts = line.Split('*');
if (parts.Length == 2)
{
var leftPart = parts[0].Trim();
var rightPart = parts[1].Trim();
// Extract name and amount from left part
var leftTokens = leftPart.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var amount = int.Parse(leftTokens.Last());
var name = string.Join(" ", leftTokens.Take(leftTokens.Length - 1));
// Extract price and total from right part
var rightTokens = rightPart.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var price = rightTokens.Length > 0 ? rightTokens[0] : "";
var itemTotal = rightTokens.Length > 1 ? rightTokens[1] : "";
items.Add(new FinalReceiptItem(name, amount, price, itemTotal));
}
}
}
return new FinalReceipt(location, phone, url, date, items, total, mwst, comment, thanks);
}
public static KitchenReceipt ParseKitchenReceipt(string receiptText)
{
var lines = receiptText.Split('\n', StringSplitOptions.RemoveEmptyEntries)
.Select(l => l.Trim())
.ToList();
int currentIndex = 0;
// Skip optional "*** KOPIE ***" header
if (lines[currentIndex].Contains("***"))
{
currentIndex++;
}
// Extract location (can be any phrase)
string location = lines[currentIndex];
currentIndex++;
// Skip separator line
while (currentIndex < lines.Count && lines[currentIndex].Contains("---"))
{
currentIndex++;
}
// Extract date line
string date = lines[currentIndex];
currentIndex++;
// Extract owner
string owner = "";
do
{
if (owner != "")
{
owner += "/n";
}
owner += lines[currentIndex];
currentIndex++;
} while (!lines[currentIndex].ToLower().Contains("tisch:"));
// Extract table (extract text after "TISCH:")
string tisch = lines[currentIndex].Replace("TISCH:", "").Trim();
currentIndex++;
// Skip empty lines and separators until we reach items
while (currentIndex < lines.Count &&
(string.IsNullOrWhiteSpace(lines[currentIndex]) || lines[currentIndex].Contains("-")))
{
currentIndex++;
}
// Parse items
var items = new List<KitchenItem>();
while (currentIndex < lines.Count)
{
var line = lines[currentIndex];
// Stop at separator or end
if (line.Contains("---") || string.IsNullOrWhiteSpace(line))
{
break;
}
if (line.ToLower().Contains(". gang"))
{
items.Add(new KitchenGang(line.Trim()));
currentIndex++;
continue;
}
// Parse format: "1x Espresso" or " 1x Espresso"
var trimmedLine = line.Trim();
if (trimmedLine.Contains("x"))
{
var parts = trimmedLine.Split('x', 2);
if (parts.Length == 2 && int.TryParse(parts[0].Trim(), out int amount))
{
var itemName = parts[1].Trim();
items.Add(new KitchenProduct(amount, itemName));
}
}
currentIndex++;
}
return new KitchenReceipt(location, date, owner, tisch, items);
}
}