using System.Collections.Generic;
using System.Globalization;
public class BillingSystem
static List<Product> ProductList = new List<Product>();
public static void Main(string[] args)
CreateRandomProducts(10);
Console.WriteLine("----------------------Product list-----------------------------");
foreach (Product p in ProductList)
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "id - {0}, Name - {1}, Category - {2}, Cost - {3}, Tax - {4}%",
p.Id.ToString(), p.Name, p.TaxCategory.TaxName, p.CostPerUnit.ToString("F2"), p.TaxCategory.TaxRate.ToString()));
Console.WriteLine("---------------------------------------------------------------\n\n");
List<BillItem> checkOutProducts = new List<BillItem>();
Console.WriteLine("Enter a product id from above in the format 'productid,quantity' (ex: 2,5), or hit enter to create bill with selected products");
string line = Console.ReadLine();
if (string.IsNullOrWhiteSpace(line))
int productFromConsole, quantityFromConsole;
string[] inputs = line.Split(',');
Console.WriteLine("please enter in the format 'productid,quantity'");
else if (!int.TryParse(inputs[0], out productFromConsole) || productFromConsole < 1 || productFromConsole > 9)
Console.WriteLine("please enter a valid product id");
else if (!int.TryParse(inputs[1], out quantityFromConsole) || quantityFromConsole < 1 || quantityFromConsole > 100)
Console.WriteLine("please enter a valid quantity");
checkOutProducts.Add(new BillItem(ProductList.First(x => x.Id == productFromConsole), quantityFromConsole));
Console.WriteLine("Items in current bill");
CheckOutCounter c = new CheckOutCounter(checkOutProducts);
Bill bill = c.CreateBill();
foreach (BillItem p in bill.Items)
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "id - {0}, Name - {1}, Quantity - {2}, Cost per unit - {3}, Tax per unit({4}%) - {5} Total Cost - {6}",
p.BillProduct.Id.ToString(),
p.BillProduct.CostPerUnit.ToString("F2"),
p.BillProduct.TaxCategory.TaxRate.ToString("F2"),
p.BillProduct.TaxCategory.GetTaxAmount(p.BillProduct.CostPerUnit).ToString("F2"),
p.TotalAmount.ToString("F2")
Console.WriteLine("\n\n");
Console.WriteLine("Bill Total: " + bill.TotalAmount.ToString("F2"));
Console.WriteLine("Tax: " + bill.TotalTax.ToString("F2"));
Console.WriteLine("Gross Total: " + bill.GrossAmount.ToString("F2"));
Console.WriteLine("Exception occured :" + ex.Message);
private static void CreateRandomProducts(int productCount)
Random random = new Random();
for (int i = 1; i <= productCount; i++)
Product product = new Product();
product.Name = "Product" + i.ToString();
product.TaxCategory = new CategoryATax();
product.TaxCategory = new CategoryBTax();
product.TaxCategory = new CategoryCTax();
product.CostPerUnit = (decimal)random.Next(100);
ProductList.Add(product);
public int Id { get; set; }
public string Name { get; set; }
public TaxCategoryBase TaxCategory { get; set; }
public decimal CostPerUnit { get; set; }
public Product BillProduct { get; set; }
public int Quantity { get; set; }
public decimal TotalAmount { get; set; }
public BillItem(Product product, int quantity)
if (product == null || string.IsNullOrWhiteSpace(product.Name))
throw new ArgumentException("Invalid product or product name.");
throw new ArgumentException("Quantity must be greater than 0.");
decimal totalCost = product.CostPerUnit * quantity;
TotalAmount = totalCost + product.TaxCategory.GetTaxAmount(totalCost);
public Bill(List<BillItem> BillItems)
throw new ArgumentException("BillItems cannot be null.");
foreach (BillItem billItem in Items)
throw new ArgumentException("BillItem cannot be null.");
TotalAmount += billItem.BillProduct.CostPerUnit * billItem.Quantity ;
TotalTax += billItem.BillProduct.TaxCategory.GetTaxAmount(billItem.BillProduct.CostPerUnit) * billItem.Quantity;
GrossAmount = TotalAmount + TotalTax;
public int BillNo { get; set; }
public List<BillItem> Items { get; private set; }
public decimal TotalAmount { get; set; }
public decimal TotalTax { get; set; }
public decimal GrossAmount { get; set; }
List<BillItem> ProductList = new List<BillItem>();
public CheckOutCounter(List<BillItem> products)
throw new ArgumentException("Products cannot be null.");
Bill bill = new Bill(ProductList);
public abstract class TaxCategoryBase
this.TaxRate = BillingSystemConstants.DefaultTax;
public string TaxName { get; set; }
public decimal TaxRate { get; set; }
public decimal GetTaxAmount(decimal amount)
return (TaxRate / 100 * amount);
public class CategoryATax : TaxCategoryBase
this.TaxName = "CategoryATax";
this.TaxRate = BillingSystemConstants.CategoryATax;
public class CategoryBTax : TaxCategoryBase
this.TaxName = "CategoryBTax";
this.TaxRate = BillingSystemConstants.CategoryBTax;
public class CategoryCTax : TaxCategoryBase
this.TaxName = "CategoryCTax";
this.TaxRate = BillingSystemConstants.CategoryCTax;
public class BillingSystemConstants
public const decimal DefaultTax = 1m;
public const decimal CategoryATax = 10m;
public const decimal CategoryBTax = 20m;
public const decimal CategoryCTax = 0m;