/// <summary> /// Displays detailed information about an order. /// </summary> /// <param name="db">Store context.</param> /// <param name="order">Order to get information from.</param> /// <param name="amountCharged">Amount charged for the order.</param> public void DisplayDetail(StoreContext db, Order order, double?amountCharged) { var titleString = $"\nOrder placed on {order.TimeSubmitted} at {order.Location.Name} store."; Console.WriteLine(titleString); Console.Write(new string('=', titleString.Length)); Console.Write("\n"); var displayAlignment = "{0,-7}{1,-9}{2,-40}"; var itemNameDividerSize = order.OrderLineItems.Max(li => li.Product.Name.Length); Console.Write(displayAlignment, "Qty", "Charged", "Item"); Console.Write("\n"); Console.Write(displayAlignment, "---", "-------", new string('-', itemNameDividerSize)); Console.Write("\n"); foreach (var li in order.OrderLineItems) { Console.WriteLine(displayAlignment, li.Quantity, "$" + li.AmountCharged, li.Product.Name); } Console.Write(displayAlignment, "---", "-------", new string('-', itemNameDividerSize)); Console.Write("\n"); Console.WriteLine(displayAlignment, order.OrderLineItems.Sum(li => li.Quantity), "$" + amountCharged, "Total"); CliInput.PressAnyKey(); }
/// <summary> /// Handle the input when selecting a location to query for orders. /// </summary> /// <returns>A <c>HandlerMsg</c> indicating what action should be taken.</returns> private HandlerMsg HandleSelectLocationInput() { var inputOptions = CliInput.GetLineOptions.TrimSpaces | CliInput.GetLineOptions.AcceptEmpty; do { var line = CliInput.GetLine(inputOptions, v => true, "Select location number:"); if (line == "" || line == null) { return(HandlerMsg.Exit); } try { var locationNumber = Int32.Parse(line); if (locationNumber > 0 && locationNumber <= this.LocationIds.Count) { this.SelectedLocation = this.LocationIds[locationNumber - 1]; this.CurrentOperatingMode = OperatingMode.ViewOrders; return(HandlerMsg.Continue); } } catch { CliPrinter.Error("Please enter a valid location number."); } } while (true); }
private void ScrubFile(CliInput cliInput) { bool inIsFile = false; bool outIsFile = false; TextReader inReader = null; TextWriter outWriter = null; try { logger.Info("Executing scrub..."); PrepareStreams(ref inReader, ref outWriter, ref inIsFile, ref outIsFile, cliInput); var scrubber = new Scrubber(inReader, outWriter); var scrubTimer = new Stopwatch(); scrubTimer.Start(); scrubber.Scrub(); scrubTimer.Stop(); GenerateCompletedOutput(scrubber.Errors.Count, scrubber.Warnings.Count, scrubTimer); } catch (SqlParseErrorException ex) { LogError(ex); Console.Error.WriteLine(string.Format("Scrub validation error: {0}", ex.Message)); } catch (Exception ex) { LogError(ex); Console.Error.WriteLine("Scrub failure: An unhandled error occurred during scrubbing. Please check the log file for more details."); } CleanupStreams(ref inReader, ref outWriter, ref inIsFile, ref outIsFile); }
/// <summary> /// Handle input. /// </summary> public void InputLoop() { var cliOptions = CliInput.GetLineOptions.TrimSpaces | CliInput.GetLineOptions.AcceptEmpty; do { var login = CliInput.GetLine(cliOptions, CliInput.EmailValidator, "Email:"); if (login == "" || login == null) { this.MenuExit(); return; } var password = CliInput.GetPassword("Password:"******"Invalid login."); } } while (true); this.ApplicationState.UserData.RefreshDefaultLocation(); }
private bool HandleOptions(CliInput cliInput) { if (string.IsNullOrWhiteSpace(cliInput.OutFile)) { NLog.LogManager.Shutdown(); } if (cliInput.SpecifyingLogFile) { CreateFileLogger(cliInput); } if (cliInput.AskingHelp) { logger.Debug($"Processing Help..."); Console.WriteLine(GetUsage()); return(false); } if (cliInput.AskingVersion) { logger.Debug($"Processing Version..."); Console.WriteLine(GetVersionInformation()); return(false); } return(true); }
/// <summary> /// Handles user input. /// </summary> public void InputLoop() { do { Console.Write("Location name: "); var name = Console.ReadLine(); if (name.Trim() == "") { this.AbortThenExit("No name provided."); return; } var location = new StoreDb.Location(name); var added = this.ApplicationState.DbOptions.AddLocation(location); if (!added) { CliPrinter.Error("A location with that name already exists."); continue; } this.MenuExit(); CliInput.PressAnyKey("Location added."); break; } while (true); }
/// <summary> /// Handle input. /// </summary> public void InputLoop() { var inputOptions = CliInput.GetLineOptions.TrimSpaces | CliInput.GetLineOptions.AcceptEmpty; do { Console.Write("\n\n"); var line = CliInput.GetLine(inputOptions, v => true, "Sort by [D]ate / [P]rice / [S]tore\nor enter an order number to view details:"); switch (line) { case "D": case "d": this.SortKey = this.SortKey != OrderSortKey.DateDesc ? OrderSortKey.DateDesc : OrderSortKey.DateAsc; break; case "P": case "p": this.SortKey = this.SortKey != OrderSortKey.PriceDesc ? OrderSortKey.PriceDesc : OrderSortKey.PriceAsc; break; case "S": case "s": this.SortKey = this.SortKey != OrderSortKey.LocationAsc ? OrderSortKey.LocationAsc : OrderSortKey.LocationDesc; break; default: if (line == "" || line == null) { this.MenuExit(); return; } try { var orderNum = Int32.Parse(line); if (orderNum > 0 && orderNum <= this.OrderIds.Count) { using (var db = new StoreContext(this.ApplicationState.DbOptions)) { var order = db.GetOrderById(this.OrderIds[orderNum - 1]); var amountCharged = db.GetAmountCharged(order); this.DisplayDetail(db, order, amountCharged); break; } } else { break; } } catch (Exception) { // We will just ignore parse errors and reprint the menu. break; } } this.PrintMenu(); } while (true); }
/// <summary> /// Handle the input when viewing a list of all orders for a location. /// </summary> /// <returns>A <c>HandlerMsg</c> indicating what action should be taken.</returns> private HandlerMsg HandleViewOrderInput() { var inputOptions = CliInput.GetLineOptions.TrimSpaces | CliInput.GetLineOptions.AcceptEmpty; do { var line = CliInput.GetLine(inputOptions, v => true, "\nSort by [D]ate / [P]rice\nor enter an order number to view details:"); switch (line) { case "D": case "d": this.SortKey = this.SortKey != OrderSortKey.DateDesc ? OrderSortKey.DateDesc : OrderSortKey.DateAsc; break; case "P": case "p": this.SortKey = this.SortKey != OrderSortKey.PriceDesc ? OrderSortKey.PriceDesc : OrderSortKey.PriceAsc; break; default: if (line == "" || line == null) { this.CurrentOperatingMode = OperatingMode.SelectLocation; return(HandlerMsg.Continue); } try { var orderNum = Int32.Parse(line); if (orderNum > 0 && orderNum <= this.OrderIds.Count) { using (var db = new StoreContext(this.ApplicationState.DbOptions)) { var order = db.GetOrderById(this.OrderIds[orderNum - 1]); var amountCharged = db.GetAmountCharged(order); this.DisplayDetail(db, order, amountCharged); break; } } else { CliPrinter.Error("Invalid order number"); continue; } } catch (Exception) { // We will just ignore parse errors and reprint the menu. break; } } this.PrintMenu(); } while (true); }
public void Run(string[] args) { ConsoleInput redirectedInput = null; CliInput cliInput = null; SetupInitialIO(ref redirectedInput); try { if (!CheckEmptyArgs(args)) { return; } cliInput = new ArgumentParser(args).ParseArgs(); if (!HandleOptions(cliInput)) { return; } ScrubFile(cliInput); } catch (Exception ex) { if (!loggingToConsole) { logger.Error(ex, ex.Message); } if (ex is ArgumentException || ex is ArgumentNullException || ex is ArgumentOutOfRangeException) { Console.Error.WriteLine(InvalidInputMessage(string.Format("Error: {0}\r\n", ex.Message.ToString()))); return; } // Unhandled exceptions Console.Error.WriteLine("Error: An unhandled error has occurred. Please check the log file for more details."); } // Logging complete NLog.LogManager.Shutdown(); if (redirectedInput != null) { redirectedInput.Dispose(); } }
/// <summary> /// Handle menu input. /// </summary> public void InputLoop() { Console.WriteLine("Select store for orders:"); var numLocations = 0; IEnumerable <Guid> locationIds = null; using (var db = new StoreContext(this.ApplicationState.DbOptions)) { var locations = db.GetLocations(); var locationsIter = Enumerable.Range(1, locations.Count()).Zip(locations); foreach (var location in locationsIter) { Console.WriteLine($" {location.First}. {location.Second.Name}"); } numLocations = locationsIter.Count(); locationIds = locationsIter.Select(i => i.Second.LocationId).ToList(); } bool validator(int num) { if (num > 0 && num <= numLocations) { return(true); } else { CliPrinter.Error($"Please enter a store number between 1 and {numLocations}"); return(false); } } var locationSelected = CliInput.GetInt(0, validator, "Enter store number:"); var customerId = this.ApplicationState.UserData.CustomerId; var locationId = locationIds.ElementAt(locationSelected - 1 ?? 0); if (this.ApplicationState.DbOptions.SetDefaultLocation(customerId, locationId)) { CliInput.PressAnyKey("Store set."); this.ApplicationState.UserData.OperatingLocationId = locationId; this.ApplicationState.UserData.RefreshDefaultLocation(); this.MenuExit(); return; } else { CliInput.PressAnyKey("An error occurred while setting the store. Please try again."); } }
private void ParseArg(string arg, ref CliOption newOption, ref CliInput inputObject) { if (arg.Trim() == "-") { return; // - indicates std out } if (arg.Trim().StartsWith("-", StringComparison.Ordinal) || arg.Trim().StartsWith("--", StringComparison.Ordinal)) { CreateNewOption(arg, ref newOption, ref inputObject); } else // Possibly dealing with previous option values { AddCompletedOption(arg, ref newOption, ref inputObject); } }
private void CreateNewOption(string arg, ref CliOption newOption, ref CliInput inputObject) { if (!Constants.GetValidCommands().Contains(arg.Trim())) { throw new ArgumentException(string.Format("Argument {0} is not valid.", arg.Trim())); } if (newOption != null) // Previous command has been constructed { inputObject.options.Add(newOption); } newOption = new CliOption { Name = arg.Trim() }; }
private void ValidationPaths(CliInput inputObject) { if (!inputObject.AskingHelp && !inputObject.AskingVersion) { if (!IsValidPath(inputObject.InFile) && !Console.IsInputRedirected) { throw new ArgumentException("Input not valid."); } if (!String.IsNullOrEmpty(inputObject.OutFile) && !IsValidPath(inputObject.OutFile) && !Console.IsOutputRedirected) { throw new ArgumentException("Output not valid."); } if (!string.IsNullOrWhiteSpace(inputObject.InFile) && !File.Exists(inputObject.InFile)) { throw new ArgumentException("Input file does not exist."); } } }
public CliInput ParseArgs() { var inputObject = new CliInput(); CliOption newOption = null; foreach (var arg in args) { ParseArg(arg, ref newOption, ref inputObject); } if (newOption != null) { inputObject.options.Add(newOption); } ValidateInputObject(inputObject); return(inputObject); }
private void AddCompletedOption(string arg, ref CliOption newOption, ref CliInput inputObject) { if (newOption != null && newOption.ShouldHaveValue()) { newOption.Value = arg; inputObject.options.Add(newOption); newOption = null; } else { // Not a option value, so check if this is a file newOption = null; if (String.IsNullOrWhiteSpace(inputObject.InFile) && (!Console.IsInputRedirected || (Console.IsInputRedirected && Console.In.Peek() == -1))) { inputObject.InFile = arg.Trim(); } else { inputObject.OutFile = arg.Trim(); } } }
private void CreateFileLogger(CliInput cliInput) { var config = new LoggingConfiguration(); var logFile = new FileTarget("logfile") { FileName = cliInput.LogfileLocation, Layout = @"${date:format=yyyy-MM-dd HH\:mm\:ss.fff} [${level}] ${message} ${exception}" }; // Still log errors and fatals to console var logConsole = new ConsoleTarget("logconsole") { Layout = @"${message} ${exception}" }; config.AddRule(LogLevel.Error, LogLevel.Fatal, logConsole); LogLevel fileLogLevel = LogLevel.Info; switch (cliInput.Loglevel.ToLower()) { case "error": fileLogLevel = LogLevel.Error; break; case "debug": fileLogLevel = LogLevel.Debug; break; } config.AddRule(fileLogLevel, LogLevel.Fatal, logFile); NLog.LogManager.Configuration = config; LogManager.ReconfigExistingLoggers(); loggingToConsole = false; }
private void ValidateOptions(CliInput inputObject) { foreach (CliOption option in inputObject.options) { switch (option.Name) { case Constants.LogCliCommandOption: case Constants.LogCliCommandOptionShortHand: if (!IsValidPath(option.Value)) { throw new ArgumentException("Log file not a valid file location."); } break; case Constants.LogLevelCliCommandOption: if (!IsValidLogLevel(option.Value)) { throw new ArgumentException(string.Format("Log level specified is not valid. Valid options are {0}", Constants.LogLevelCliCommandOptionValues)); } break; } } }
/// <summary> /// Handle input. /// </summary> public void InputLoop() { do { PrintMenu(); using (var db = new StoreContext(this.ApplicationState.DbOptions)) { var location = db.GetLocationById(this.ApplicationState.UserData.OperatingLocationId); var order = db.FindCurrentOrder(location, this.ApplicationState.UserData.CustomerId); if (order == null) { CliPrinter.Error("There was an error retrieving your order. Please try again."); break; } bool itemNumberValidator(int num) { if (num > 0 && num <= this.InventoryIds.Count) { return(true); } else { CliPrinter.Error($"Please enter an inventory number between 1 and {this.InventoryIds.Count}"); return(false); } } var itemIndex = CliInput.GetInt(CliInput.GetIntOptions.AllowEmpty, itemNumberValidator, "Inventory number: ") ?? 0; if (itemIndex == 0) { break; } var inventoryId = this.InventoryIds[itemIndex - 1]; var product = db.GetProductFromInventoryId(inventoryId); var projectedQuantity = db.ProjectStockBasedOnOrder(order, product); if (projectedQuantity <= 0) { CliInput.PressAnyKey("That item is out of stock"); continue; } var orderQuantity = CliInput.GetInt(CliInput.GetIntOptions.AllowEmpty, v => v > 0 && v <= projectedQuantity, $"Quantity [1..{projectedQuantity}]: ") ?? 0; if (orderQuantity == 0) { continue; } db.AddLineItem(order, product, orderQuantity); db.SaveChanges(); CliInput.PressAnyKey("\nAdded to order."); } } while (true); this.MenuExit(); }
/// <summary> /// Handle menu input. /// </summary> public void InputLoop() { if (this.ApplicationState.UserData.CurrentOrderId == null) { this.AbortThenExit("No current order on file."); return; } var orderTotalCost = 0.0; using (var db = new StoreContext(this.ApplicationState.DbOptions)) { var order = db.GetOrderById(this.ApplicationState.UserData.CurrentOrderId); if (order.OrderLineItems.Count == 0) { CliInput.PressAnyKey("There are no items in your order."); this.MenuExit(); return; } Console.WriteLine("Qty\tEach\tTotal\tName"); Console.WriteLine("===\t=====\t=====\t===="); var totalItems = 0; foreach (var o in order.OrderLineItems) { var lineItemTotalCost = o.Product.Price * o.Quantity; orderTotalCost += lineItemTotalCost; totalItems += o.Quantity; Console.WriteLine($"{o.Quantity}\t${o.Product.Price}\t${lineItemTotalCost}\t{o.Product.Name}"); } Console.Write("---\t-----\t-----\t--------------------------\n"); Console.Write($"{totalItems}\t\t${orderTotalCost}\n\n"); } Console.WriteLine("1. Place Order"); Console.WriteLine("2. Exit"); ConsoleKeyInfo cki; do { cki = Console.ReadKey(true); if (cki.Key == ConsoleKey.D1) { Console.WriteLine("Placing order..."); var orderId = this.ApplicationState.UserData.CurrentOrderId; try { var orderResult = this.ApplicationState.DbOptions.PlaceOrder(orderId, StoreDbUtil.Config.MAX_ORDER_QUANTITY); switch (orderResult) { case PlaceOrderResult.Ok: this.AbortThenExit("Order placed successfully."); return; case PlaceOrderResult.OutOfStock: this.AbortThenExit("Unable to place order: Some items are out of stock."); return; case PlaceOrderResult.NoLineItems: this.AbortThenExit("Unable to place order: There are no items in the order."); return; case PlaceOrderResult.OrderNotFound: this.AbortThenExit("Unable to place order: Unable to locate the order."); return; case PlaceOrderResult.HighQuantityRejection: this.AbortThenExit($"Unable to place order: Item quantities too high ({StoreDbUtil.Config.MAX_ORDER_QUANTITY} max)"); return; } } catch (NullReferenceException) { this.AbortThenExit("Unable to place order: Order id is missing (this is a bug)."); return; } } else if (cki.Key == ConsoleKey.D2 || cki.Key == ConsoleKey.Enter) { this.MenuExit(); return; } } while (true); }
/// <summary> /// Handle input. /// </summary> public void InputLoop() { do { this.PrintMenu(); if (this.ApplicationState.UserData.CurrentOrderId == null) { CliInput.PressAnyKey("There is currently no open order."); break; } using (var db = new StoreContext(this.ApplicationState.DbOptions)) { var order = db.GetOrderById(this.ApplicationState.UserData.CurrentOrderId); if (order == null) { break; } if (order.OrderLineItems.Count() == 0) { CliInput.PressAnyKey("There are no items in your order."); break; } } var getLineOptions = CliInput.GetLineOptions.AcceptEmpty | CliInput.GetLineOptions.TrimSpaces; var option = CliInput.GetLine(getLineOptions, v => true, "[D]elete / Change [Q]uantity: "); switch (option) { case null: case "": this.MenuExit(); return; case "D": case "d": { var itemNumber = CliInput.GetInt(CliInput.GetIntOptions.AllowEmpty, n => n > 0 && n <= this.LineItemIds.Count, "Select item number:"); if (itemNumber == null) { continue; } using (var db = new StoreContext(this.ApplicationState.DbOptions)) { var deleted = db.DeleteLineItem(this.LineItemIds[(int)itemNumber - 1]); if (!deleted) { CliInput.PressAnyKey("There was a problem deleting that line item. Please try again."); } } break; } case "Q": case "q": { var itemNumber = CliInput.GetInt(CliInput.GetIntOptions.AllowEmpty, n => n > 0 && n <= this.LineItemIds.Count, "Select item number:"); if (itemNumber == null) { continue; } using (var db = new StoreContext(this.ApplicationState.DbOptions)) { var order = db.GetOrderById(this.ApplicationState.UserData.CurrentOrderId); var lineItemId = this.LineItemIds[(int)itemNumber - 1]; var lineItem = order.OrderLineItems.Where(li => li.OrderLineItemId == lineItemId); var product = lineItem.First().Product; var maxOrder = db.LocationInventories .Where(i => i.Location.LocationId == order.Location.LocationId) .Where(i => i.Product.ProductId == product.ProductId) .Select(i => i.Quantity).FirstOrDefault(); var newQuantity = CliInput.GetInt(CliInput.GetIntOptions.AllowEmpty, q => q >= 0 && q <= maxOrder, $"New quantity [{maxOrder} max]:"); if (newQuantity == null) { continue; } var adjusted = db.SetLineItemQuantity(this.LineItemIds[(int)itemNumber - 1], (int)newQuantity); if (!adjusted) { CliInput.PressAnyKey("There was a problem adjusting the quantity for that line item. Please try again."); } } break; } } } while (true); this.MenuExit(); }
/// <summary> /// Handle user input. /// </summary> public void InputLoop() { string EmailLoop(DbContextOptions <StoreContext> dbOptions) { var cliOptions = CliInput.GetLineOptions.TrimSpaces | CliInput.GetLineOptions.AcceptEmpty; string login = ""; do { login = CliInput.GetLine(cliOptions, CliInput.EmailValidator, "Email Address:"); if (login == "" || login == null) { return(null); } if (dbOptions.LoginExists(login)) { CliPrinter.Error("That email address is already in use."); } else { break; } } while (true); return(login); } do { var cliOptions = CliInput.GetLineOptions.TrimSpaces | CliInput.GetLineOptions.AcceptEmpty; var firstName = CliInput.GetLine(cliOptions, Customer.ValidateName, "First Name:"); if (firstName == "" || firstName == null) { this.AbortThenExit("Empty entry - exiting."); return; } var lastName = CliInput.GetLine(cliOptions, Customer.ValidateName, "Last Name:"); if (lastName == "" || lastName == null) { this.AbortThenExit("Empty entry - exiting."); return; } var login = EmailLoop(this.ApplicationState.DbOptions); if (login == "" || login == null) { this.AbortThenExit("Empty entry - exiting."); return; } var password = CliInput.GetPassword("Password:"******"") { this.AbortThenExit("Empty entry - exiting."); return; } var customer = new Customer(); // Data pre-validated above. customer.FirstName = firstName; customer.LastName = lastName; customer.Login = login; customer.Password = password; var createResult = this.ApplicationState.DbOptions.CreateUserAccount(customer); if (createResult == CreateUserAccountResult.Ok) { this.ApplicationState.UserData.CustomerId = customer.CustomerId; this.MenuExit(); this.MenuAdd(new StoreCliMenuUser.Main(this.ApplicationState)); CliInput.PressAnyKey("\nAccount created."); break; } else { CliPrinter.Error("An error occurred while creating your account. Please try again."); } } while (true); }
private void PrepareStreams(ref TextReader inReader, ref TextWriter outWriter, ref bool inIsFile, ref bool outIsFile, CliInput cliInput) { // Prepare input object if (!string.IsNullOrWhiteSpace(cliInput.InFile)) { inReader = File.OpenText(cliInput.InFile); inIsFile = true; } else if (Console.IsInputRedirected) { inReader = Console.In; } else { throw new ArgumentException("Input not valid."); } // Prepare input object if (!string.IsNullOrWhiteSpace(cliInput.OutFile)) { outWriter = File.CreateText(cliInput.OutFile); outIsFile = true; } else { outWriter = Console.Out; } }
private void ValidateInputObject(CliInput inputObject) { ValidateOptions(inputObject); ValidationPaths(inputObject); }
/// <summary> /// Print this menu. /// </summary> public void PrintMenu() { Console.Clear(); CliPrinter.Title("Order History"); switch (this.CurrentOperatingMode) { case OperatingMode.SelectLocation: using (var db = new StoreContext(this.ApplicationState.DbOptions)) { var locations = db.GetLocations(); this.LocationIds.Clear(); var i = 1; foreach (var location in locations) { this.LocationIds.Add(location.LocationId); Console.WriteLine($"{i}. {location.Name}"); i += 1; } Console.WriteLine("\n"); } break; case OperatingMode.ViewOrders: using (var db = new StoreContext(this.ApplicationState.DbOptions)) { var location = db.GetLocationById(this.SelectedLocation); if (location == null) { break; } var orders = db .GetOrderHistory(location) .Select(o => new { OrderId = o.OrderId, Customer = o.Customer, Location = o.Location, TimeCreated = o.TimeCreated, TimeSubmitted = o.TimeSubmitted, TimeFulfilled = o.TimeFulfilled, AmountPaid = o.AmountPaid, OrderLineItem = o.OrderLineItems, AmountCharged = db.GetAmountCharged(o), }).ToList(); if (orders.Count() == 0) { CliPrinter.Error("There are no orders for this location."); this.CurrentOperatingMode = OperatingMode.SelectLocation; CliInput.PressAnyKey(); this.PrintMenu(); return; } var upArrow = '↑'; var downArrow = '↓'; var priceSortSymbol = '-'; var dateSortSymbol = '-'; switch (this.SortKey) { case OrderSortKey.DateDesc: orders = orders.OrderByDescending(o => o.TimeSubmitted).ToList(); dateSortSymbol = downArrow; break; case OrderSortKey.DateAsc: orders = orders.OrderBy(o => o.TimeSubmitted).ToList(); dateSortSymbol = upArrow; break; case OrderSortKey.PriceDesc: orders = orders.OrderByDescending(o => o.AmountCharged).ToList(); priceSortSymbol = downArrow; break; case OrderSortKey.PriceAsc: orders = orders.OrderBy(o => o.AmountCharged).ToList(); priceSortSymbol = upArrow; break; } var historyDisplayAlignment = "{0,-6}{1,-9}{2,-25}"; var priceSortLine = $"{priceSortSymbol}----"; var dateSortLine = $"{dateSortSymbol}---"; Console.WriteLine(historyDisplayAlignment, "Num", "Price", "Date"); Console.WriteLine(historyDisplayAlignment, "---", priceSortLine, dateSortLine); var i = 1; this.OrderIds.Clear(); foreach (var order in orders) { this.OrderIds.Add(order.OrderId); Console.WriteLine(historyDisplayAlignment, i + ".", "$" + order.AmountCharged, order.TimeSubmitted, order.Location.Name); i += 1; } } break; default: break; } }