diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..936961889cf761c6bf5a1c1fe776fc62467aa992
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+/PrintReceipt/PrintReceipt/bin/*
+/PrintReceipt/PrintReceipt/obj/*
+/PrintReceipt/PrintReceiptTest/bin/*
+/PrintReceipt/PrintReceiptTest/obj/*
+
diff --git a/PrintReceipt/.idea/.idea.PrintReceipt/.idea/.gitignore b/PrintReceipt/.idea/.idea.PrintReceipt/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..844d505df6fdb34c801140e41342e983dc2d945a
--- /dev/null
+++ b/PrintReceipt/.idea/.idea.PrintReceipt/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/contentModel.xml
+/modules.xml
+/projectSettingsUpdater.xml
+/.idea.PrintReceipt.iml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/PrintReceipt/.idea/.idea.PrintReceipt/.idea/indexLayout.xml b/PrintReceipt/.idea/.idea.PrintReceipt/.idea/indexLayout.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7b08163cebc50fb3e777eea4881b68fcebc10590
--- /dev/null
+++ b/PrintReceipt/.idea/.idea.PrintReceipt/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PrintReceipt/.idea/.idea.PrintReceipt/.idea/vcs.xml b/PrintReceipt/.idea/.idea.PrintReceipt/.idea/vcs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6c0b8635858dc7ad44b93df54b762707ce49eefc
--- /dev/null
+++ b/PrintReceipt/.idea/.idea.PrintReceipt/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt.sln b/PrintReceipt/PrintReceipt.sln
new file mode 100644
index 0000000000000000000000000000000000000000..f3ffa93c510fa09d8126b5944518cd11c75ddcb2
--- /dev/null
+++ b/PrintReceipt/PrintReceipt.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrintReceipt", "PrintReceipt\PrintReceipt.csproj", "{68AF9ACC-B110-428F-809E-B3E39B683296}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrintReceiptTest", "PrintReceiptTest\PrintReceiptTest.csproj", "{E623BEED-3353-4D15-A109-2B065A08E43C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {68AF9ACC-B110-428F-809E-B3E39B683296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {68AF9ACC-B110-428F-809E-B3E39B683296}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {68AF9ACC-B110-428F-809E-B3E39B683296}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {68AF9ACC-B110-428F-809E-B3E39B683296}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E623BEED-3353-4D15-A109-2B065A08E43C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E623BEED-3353-4D15-A109-2B065A08E43C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E623BEED-3353-4D15-A109-2B065A08E43C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E623BEED-3353-4D15-A109-2B065A08E43C}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/PrintReceipt/PrintReceipt.sln.DotSettings.user b/PrintReceipt/PrintReceipt.sln.DotSettings.user
new file mode 100644
index 0000000000000000000000000000000000000000..86bf451f4c254ac47188a93a5f97fdcfaae77076
--- /dev/null
+++ b/PrintReceipt/PrintReceipt.sln.DotSettings.user
@@ -0,0 +1,9 @@
+
+ <SessionState ContinuousTestingMode="0" IsActive="True" Name="Should_Get_BarcodesAndAmount_If_SelectedBarcodes_Is_VALID" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
+ <Project Location="/Users/duming.zhu/code/homework-2-test-first-development/PrintReceipt/PrintReceiptTest" Presentation="<PrintReceiptTest>" />
+</SessionState>
+ <SessionState ContinuousTestingMode="0" Name="All tests from <PrintReceiptTest>" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
+ <Project Location="/Users/duming.zhu/code/homework-2-test-first-development/PrintReceipt/PrintReceiptTest" Presentation="<PrintReceiptTest>" />
+</SessionState>
+
+
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/BarcodesParser.cs b/PrintReceipt/PrintReceipt/BarcodesParser.cs
new file mode 100644
index 0000000000000000000000000000000000000000..26e5ed890454267c035dca259c515507ed664e69
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/BarcodesParser.cs
@@ -0,0 +1,34 @@
+namespace PrintReceipt;
+
+public class BarcodesParser
+{
+ public static List ParseBarcodes(SelectedBarcodes selectedBarcodes)
+ {
+ var productBarcodes = new List();
+ foreach (var barcodes in selectedBarcodes.SelectedBarcodeList)
+ {
+ var barcodeDetails = barcodes.Split("-");
+ var productBarcode = new ProductBarcode();
+ productBarcode.Amount = int.Parse(barcodeDetails[1]);
+ productBarcode.Barcode = barcodeDetails[0];
+ productBarcodes.Add(productBarcode);
+ }
+
+ return productBarcodes;
+ }
+
+ public static List GroupBarcodes(List barcodesAndAmount)
+ {
+ List groupedBarcodes = new List();
+ foreach (var productBarcode in barcodesAndAmount)
+ {
+ if (groupedBarcodes.Contains(productBarcode))
+ {
+ continue;
+ }
+ groupedBarcodes.Add(productBarcode);
+ }
+
+ return groupedBarcodes;
+ }
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/BarcodesValidator.cs b/PrintReceipt/PrintReceipt/BarcodesValidator.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7f129d11186adbb0e096b3e17caebed340344766
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/BarcodesValidator.cs
@@ -0,0 +1,48 @@
+using PrintReceipt.Enums;
+
+namespace PrintReceipt;
+
+public class BarcodesValidator
+{
+ public static ValidationResult ValidateBarcodes(SelectedBarcodes selectBarcodes, IList? products)
+ {
+ var validationResult = new ValidationResult()
+ {
+ Barcode = "",
+ Error = ValidationResultType.VALID
+ };
+ foreach (var barcodes in selectBarcodes.SelectedBarcodeList)
+ {
+ var barcodeDetails = barcodes.Split("-");
+ if (barcodeDetails.Length != 2 ||
+ barcodeDetails[1].Length == 0)
+ {
+ validationResult.Error = ValidationResultType.INVALID_BARCODE;
+ validationResult.Barcode = barcodeDetails[0];
+ break;
+ }
+ if (!FindBarcodeInProducts(products, barcodeDetails[0]))
+ {
+ validationResult.Error = ValidationResultType.INVALID_PRODUCT_NOT_FIND;
+ validationResult.Barcode = barcodeDetails[0];
+ break;
+ }
+ }
+ return validationResult;
+ }
+
+ private static bool FindBarcodeInProducts(IList? products, string barcode)
+ {
+ ValidationResult validationResult;
+ bool isFind = false;
+ foreach (var product in products)
+ {
+ if (product.Barcode.Equals(barcode))
+ {
+ isFind = true;
+ break;
+ }
+ }
+ return isFind;
+ }
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/Enums/ValidationResultType.cs b/PrintReceipt/PrintReceipt/Enums/ValidationResultType.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fda8896b3f7632eec700273139d7ff4a591a3a3b
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/Enums/ValidationResultType.cs
@@ -0,0 +1,8 @@
+namespace PrintReceipt.Enums;
+
+public enum ValidationResultType
+{
+ VALID=1,
+ INVALID_BARCODE=2,
+ INVALID_PRODUCT_NOT_FIND=3,
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/ErrorReceiptCreator.cs b/PrintReceipt/PrintReceipt/ErrorReceiptCreator.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b514318e0f8bdf78e4df6a1d71afc949022f99c3
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/ErrorReceiptCreator.cs
@@ -0,0 +1,20 @@
+using PrintReceipt.Enums;
+
+namespace PrintReceipt;
+
+public class ErrorReceiptCreator
+{
+
+ public static string CreateErrorReceipt(ValidationResult validationResult)
+ {
+ switch (validationResult.Error)
+ {
+ case ValidationResultType.INVALID_BARCODE:
+ return "ERROR\n-------\nThe barcode cannot be recognized";
+ case ValidationResultType.INVALID_PRODUCT_NOT_FIND:
+ return "ERROR\n-------\nThe product cannot be found: " + validationResult.Barcode;
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/PrintReceipt.csproj b/PrintReceipt/PrintReceipt/PrintReceipt.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..2e1277b8664c97fddf4a01b6c69c00f0a34d7297
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/PrintReceipt.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/PrintReceipt/PrintReceipt/Product.cs b/PrintReceipt/PrintReceipt/Product.cs
new file mode 100644
index 0000000000000000000000000000000000000000..546709f6352d9c0298925c8ac655f7e471c9aa46
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/Product.cs
@@ -0,0 +1,8 @@
+namespace PrintReceipt;
+
+public class Product
+{
+ public string Name { set; get; }
+ public double Price { set; get; }
+ public string Barcode { set; get; }
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/ProductBarcode.cs b/PrintReceipt/PrintReceipt/ProductBarcode.cs
new file mode 100644
index 0000000000000000000000000000000000000000..528a4a20f8cbe76cc1527e01a50db80c61dd724c
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/ProductBarcode.cs
@@ -0,0 +1,21 @@
+namespace PrintReceipt;
+
+public class ProductBarcode
+{
+ public string Barcode { set; get; }
+ public int Amount { set; get; }
+
+ public override bool Equals(Object obj)
+ {
+ // Check for null and compare run-time types.
+ if ((obj == null) || !this.GetType().Equals(obj.GetType()))
+ {
+ return false;
+ }
+
+ ProductBarcode productBarcode = (ProductBarcode)obj;
+ return productBarcode.Barcode.Equals(Barcode) &&
+ productBarcode.Amount == Amount;
+ }
+
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/Program.cs b/PrintReceipt/PrintReceipt/Program.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e5dff12bc410be0bac59bc70865a8e21827ec7f3
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/Program.cs
@@ -0,0 +1,3 @@
+// See https://aka.ms/new-console-template for more information
+
+Console.WriteLine("Hello, World!");
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/Receipt.cs b/PrintReceipt/PrintReceipt/Receipt.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1a2af4fcf59f5d8ab03c40af97578547cac06bea
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/Receipt.cs
@@ -0,0 +1,7 @@
+namespace PrintReceipt;
+
+public class Receipt
+{
+ public List ReceiptProducts { set; get; } = new List();
+ public double TotalPrice { set; get; }
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/ReceiptCreator.cs b/PrintReceipt/PrintReceipt/ReceiptCreator.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ffa1af2a623aeefa0503479d9e66236627e1fc49
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/ReceiptCreator.cs
@@ -0,0 +1,31 @@
+using System.Runtime.Intrinsics.Arm;
+
+namespace PrintReceipt;
+
+public class ReceiptCreator
+{
+ public static Receipt CreateReceipt(List groupedBarcodes, List products)
+ {
+ var receipt = new Receipt();
+ double totalPrice = 0;
+ foreach (var groupedBarcode in groupedBarcodes)
+ {
+ var receiptProduct = new ReceiptProduct();
+ var product = FindBarcodesProduct(groupedBarcode.Barcode, products);
+ receiptProduct.Amount = groupedBarcode.Amount;
+ receiptProduct.Name = product.Name;
+ receiptProduct.TotalPrice = groupedBarcode.Amount * product.Price;
+ receiptProduct.Price = product.Price;
+ totalPrice = totalPrice + receiptProduct.TotalPrice;
+ receipt.ReceiptProducts.Add(receiptProduct);
+ }
+
+ receipt.TotalPrice = totalPrice;
+ return receipt;
+ }
+
+ public static Product FindBarcodesProduct(string barcode, List products)
+ {
+ return products.FirstOrDefault(product => barcode.Equals(product.Barcode));
+ }
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/ReceiptFormatter.cs b/PrintReceipt/PrintReceipt/ReceiptFormatter.cs
new file mode 100644
index 0000000000000000000000000000000000000000..da45da0e985cffe8bdefafa8404bd8fced19a974
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/ReceiptFormatter.cs
@@ -0,0 +1,27 @@
+namespace PrintReceipt;
+
+public class ReceiptFormatter
+{
+ public static string FormatReceipt(Receipt receipt)
+ {
+ var receiptContent = "";
+ if (receipt == null || receipt.ReceiptProducts.Count() == 0)
+ {
+ receiptContent = "Receipt\n------\n------\nTotal: 0.00";
+ return receiptContent;
+ }
+ else
+ {
+ receiptContent = "Receipt\n-------\n";
+ foreach (var receiptProduct in receipt.ReceiptProducts)
+ {
+ receiptContent += "Name: " + receiptProduct.Name + ", ";
+ receiptContent += "Amount: " + receiptProduct.Amount + ", ";
+ receiptContent += "Price: " + receiptProduct.Price.ToString("f2") + ", ";
+ receiptContent += "Total: " + receiptProduct.TotalPrice.ToString("f2")+"\n";
+ }
+ receiptContent += "-------\nTotal: " + receipt.TotalPrice.ToString("f2");
+ return receiptContent;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/ReceiptProduct.cs b/PrintReceipt/PrintReceipt/ReceiptProduct.cs
new file mode 100644
index 0000000000000000000000000000000000000000..91c08c4ff3eb7c9564c2f911395ffbddf7ceb3f6
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/ReceiptProduct.cs
@@ -0,0 +1,9 @@
+namespace PrintReceipt;
+
+public class ReceiptProduct
+{
+ public string Name { set; get; }
+ public double Price { set; get; }
+ public double TotalPrice { set; get; }
+ public int Amount { set; get; }
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/SelectedBarcodes.cs b/PrintReceipt/PrintReceipt/SelectedBarcodes.cs
new file mode 100644
index 0000000000000000000000000000000000000000..da84ea9eda738988c7d0ad82e71b4d2d1e5b2002
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/SelectedBarcodes.cs
@@ -0,0 +1,12 @@
+namespace PrintReceipt;
+using Newtonsoft.Json;
+
+public class SelectedBarcodes
+{
+ public IList SelectedBarcodeList { get; set; }
+
+ public SelectedBarcodes(string selectBarcodesJson)
+ {
+ SelectedBarcodeList = JsonConvert.DeserializeObject>(selectBarcodesJson);
+ }
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceipt/ValidationResult.cs b/PrintReceipt/PrintReceipt/ValidationResult.cs
new file mode 100644
index 0000000000000000000000000000000000000000..76f1a7a64ba7e4855484b4f2f3ce06ab0be213cf
--- /dev/null
+++ b/PrintReceipt/PrintReceipt/ValidationResult.cs
@@ -0,0 +1,10 @@
+using PrintReceipt.Enums;
+
+namespace PrintReceipt;
+
+public class ValidationResult
+{
+ public string Barcode { set; get; }
+
+ public ValidationResultType Error { set; get; }
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceiptTest/PrintReceiptTest.cs b/PrintReceipt/PrintReceiptTest/PrintReceiptTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c124f124204a8e372b9aad4fcf95d5ab1f0f3ed4
--- /dev/null
+++ b/PrintReceipt/PrintReceiptTest/PrintReceiptTest.cs
@@ -0,0 +1,315 @@
+using Newtonsoft.Json;
+using PrintReceipt;
+using PrintReceipt.Enums;
+
+namespace PrintReceiptTest;
+
+public class PrintReceiptTest
+{
+ [Fact]
+ public void Should_Get_INVALID_BARCODE_ValidationResult_If_SelectedBarcodes_Dont_Contains_Amount()
+ {
+ // Given
+ string selectBarcodesJson = "[\"ITEM000003\"]";
+ string productsJson = @"[{""name"":""phone"",""price"":2000,""barcode"":""ITEM000003""}]";
+ var selectBarcodes = new SelectedBarcodes(selectBarcodesJson);
+ var products = JsonConvert.DeserializeObject>(productsJson);
+
+ // when
+ var validationResult = BarcodesValidator.ValidateBarcodes(selectBarcodes, products);
+
+ // then
+ Assert.Equal(validationResult.Error, ValidationResultType.INVALID_BARCODE);
+ Assert.Equal(validationResult.Barcode, "ITEM000003");
+ }
+
+ [Fact]
+ public void Should_Get_PRODUCT_NOT_FIND_ValidationResult_If_SelectedBarcodes_Dont_Contains_Given_Product()
+ {
+ // Given
+ string selectBarcodesJson = "[\"ITEM000002-2\"]";
+ string productsJson = @"[{""name"":""phone"",""price"":2000,""barcode"":""ITEM000003""}]";
+ var selectBarcodes = new SelectedBarcodes(selectBarcodesJson);
+ var products = JsonConvert.DeserializeObject>(productsJson);
+
+ // when
+ var validationResult = BarcodesValidator.ValidateBarcodes(selectBarcodes, products);
+
+ // then
+ Assert.Equal(validationResult.Error, ValidationResultType.INVALID_PRODUCT_NOT_FIND);
+ Assert.Equal(validationResult.Barcode, "ITEM000002");
+ }
+
+ [Fact]
+ public void Should_Get_VALID_ValidationResult_If_SelectedBarcodes_Is_Normal()
+ {
+ // Given
+ string selectBarcodesJson = "[\"ITEM000003-2\"]";
+ string productsJson = @"[{""name"":""phone"",""price"":2000,""barcode"":""ITEM000003""}]";
+ var selectBarcodes = new SelectedBarcodes(selectBarcodesJson);
+ var products = JsonConvert.DeserializeObject>(productsJson);
+
+ // when
+ var validationResult = BarcodesValidator.ValidateBarcodes(selectBarcodes, products);
+
+ // then
+ Assert.Equal(validationResult.Error, ValidationResultType.VALID);
+ }
+
+ [Fact]
+ public void Should_Get_Unrecognized_Receipt_If_ValidationResult_Type_Is_INVALID_BARCODE()
+ {
+ // Given
+ var validationResult = new ValidationResult()
+ {
+ Error = ValidationResultType.INVALID_BARCODE,
+ Barcode = ""
+ };
+
+ // when
+ var errorReceipt = ErrorReceiptCreator.CreateErrorReceipt(validationResult);
+
+ // then
+ Assert.Equal(errorReceipt, "ERROR\n-------\nThe barcode cannot be recognized");
+ }
+
+ [Fact]
+ public void Should_Get_PRODUCT_NOT_FIND_Receipt_If_ValidationResult_Type_Is_PRODUCT_NOT_FIND()
+ {
+ // Given
+ var validationResult = new ValidationResult()
+ {
+ Error = ValidationResultType.INVALID_PRODUCT_NOT_FIND,
+ Barcode = "ITEM000002"
+ };
+
+ // when
+ var errorReceipt = ErrorReceiptCreator.CreateErrorReceipt(validationResult);
+
+ // then
+ Assert.Equal(errorReceipt, "ERROR\n-------\nThe product cannot be found: ITEM000002");
+ }
+
+ [Fact]
+ public void Should_Get_BarcodesAndAmount_If_SelectedBarcodes_Is_VALID()
+ {
+ // Given
+ string selectBarcodesJson = "[\"ITEM000003-3\"]";
+ var selectedBarcodes = new SelectedBarcodes(selectBarcodesJson);
+
+ // when
+ var barcodesAndAmount = BarcodesParser.ParseBarcodes(selectedBarcodes);
+
+ // then
+ Assert.Equal(barcodesAndAmount.Count(), 1);
+ Assert.Equal( barcodesAndAmount[0].Amount, 3);
+ Assert.Equal(barcodesAndAmount[0].Barcode, "ITEM000003");
+ }
+
+ [Fact]
+ public void Should_Get_BarcodesAndAmount_If_SelectedBarcodes_Is_VALID_With_Three_Barcodes()
+ {
+ // Given
+ string selectBarcodesJson = @"[""ITEM000001-1"", ""ITEM000002-2"", ""ITEM000003-3""]";
+ var selectBarcodes = new SelectedBarcodes(selectBarcodesJson);
+
+ // when
+ var barcodesAndAmount = BarcodesParser.ParseBarcodes(selectBarcodes);
+
+ // then
+ Assert.Equal(barcodesAndAmount.Count(), 3);
+ Assert.Equal( barcodesAndAmount[0].Amount, 1);
+ Assert.Equal(barcodesAndAmount[0].Barcode, "ITEM000001");
+ }
+
+ [Fact]
+ public void Should_Get_Three_Item_In_GroupedBarcodes_If_BarcodesAndAmount_With_Three_Different_Barcodes()
+ {
+ // Given
+ var barcodesAndAmount = new List()
+ {
+ new ProductBarcode()
+ {
+ Amount = 1,
+ Barcode = "ITEM000001",
+ },
+ new ProductBarcode()
+ {
+ Amount = 2,
+ Barcode = "ITEM000002",
+ },
+ new ProductBarcode()
+ {
+ Amount = 3,
+ Barcode = "ITEM000003",
+ }
+ };
+
+ // when
+ var groupedBarcodes = BarcodesParser.GroupBarcodes(barcodesAndAmount);
+
+ // then
+ Assert.Equal(groupedBarcodes.Count(), 3);
+ Assert.Equal( groupedBarcodes[0].Amount, 1);
+ Assert.Equal(groupedBarcodes[0].Barcode, "ITEM000001");
+ }
+
+ [Fact]
+ public void Should_Get_Two_Item_In_GroupedBarcodes_If_BarcodesAndAmount_With_Three_Barcodes_Two_Same()
+ {
+ // Given
+ var barcodesAndAmount = new List()
+ {
+ new ProductBarcode()
+ {
+ Amount = 1,
+ Barcode = "ITEM000001",
+ },
+ new ProductBarcode()
+ {
+ Amount = 2,
+ Barcode = "ITEM000002",
+ },
+ new ProductBarcode()
+ {
+ Amount = 1,
+ Barcode = "ITEM000001",
+ }
+ };
+ // when
+ var groupedBarcodes = BarcodesParser.GroupBarcodes(barcodesAndAmount);
+
+ // then
+ Assert.Equal(groupedBarcodes.Count(), 2);
+ Assert.Equal( groupedBarcodes[1].Amount, 2);
+ Assert.Equal(groupedBarcodes[1].Barcode, "ITEM000002");
+ }
+ [Fact]
+ public void Should_Get_Receipt_If_GroupedBarcodes_Is_Normal()
+ {
+ // Given
+ string productsJson = @"[ {""name"": ""phone"",""price"": 2000,""barcode"": ""ITEM000003"" },
+ {""name"": ""pen"",""price"": 20,""barcode"": ""ITEM000002"" },
+ {""name"": ""table"",""price"": 200,""barcode"": ""ITEM000001"" }]";
+ var products = JsonConvert.DeserializeObject>(productsJson);
+
+ var groupedBarcodes = new List()
+ {
+ new ProductBarcode()
+ {
+ Amount = 1,
+ Barcode = "ITEM000001",
+ },
+ new ProductBarcode()
+ {
+ Amount = 2,
+ Barcode = "ITEM000002",
+ },
+ new ProductBarcode()
+ {
+ Amount = 3,
+ Barcode = "ITEM000003",
+ }
+ };
+
+ // when
+ var receipt = ReceiptCreator.CreateReceipt(groupedBarcodes, products);
+
+ //then
+ Assert.Equal(receipt.ReceiptProducts.Count(), 3);
+ Assert.Equal(receipt.ReceiptProducts[0].TotalPrice, 200);
+ Assert.Equal(receipt.ReceiptProducts[1].TotalPrice, 40);
+ Assert.Equal(receipt.ReceiptProducts[2].TotalPrice, 6000);
+ Assert.Equal(receipt.TotalPrice, 6240);
+ }
+
+ [Fact]
+ void should_get_empty_receipt_content_if_receipt_contains_no_products()
+ {
+ // Given
+ var emptyReceipt = new Receipt();
+
+ // When
+ string receiptContent = ReceiptFormatter.FormatReceipt(emptyReceipt);
+
+ // Then
+ Assert.Equal("Receipt\n------\n------\nTotal: 0.00", receiptContent);
+ }
+ [Fact]
+ void should_get_normal_receipt_content_if_receipt_contains_products()
+ {
+ // Given
+ var receipt = new Receipt()
+ {
+ ReceiptProducts = new List()
+ {
+ new ReceiptProduct()
+ {
+ Amount = 1,
+ Price = 200.00,
+ Name = "table",
+ TotalPrice = 200.00
+ },
+ new ReceiptProduct()
+ {
+ Amount = 2,
+ Price = 20.00,
+ Name = "pen",
+ TotalPrice = 40.00
+ },
+ new ReceiptProduct()
+ {
+ Amount = 3,
+ Price = 2000.00,
+ Name = "phone",
+ TotalPrice = 6000.00
+ }
+ },
+ TotalPrice = 6240.00
+ };
+ // When
+ string receiptContent = ReceiptFormatter.FormatReceipt(receipt);
+
+ // Then
+ Assert.Equal(@"Receipt
+-------
+Name: table, Amount: 1, Price: 200.00, Total: 200.00
+Name: pen, Amount: 2, Price: 20.00, Total: 40.00
+Name: phone, Amount: 3, Price: 2000.00, Total: 6000.00
+-------
+Total: 6240.00",
+ receiptContent);
+ }
+
+ [Fact]
+ public void Should_Get_Normal_ReceiptContent_If_SelectedBarcodes_Is_Normal() {
+
+ // Given
+ string selectBarcodesJson = @"[""ITEM000001-1"", ""ITEM000002-2"", ""ITEM000003-3""]";
+ string productsJson = @"[ {""name"": ""phone"",""price"": 2000,""barcode"": ""ITEM000003"" },
+ {""name"": ""pen"",""price"": 20,""barcode"": ""ITEM000002"" },
+ {""name"": ""table"",""price"": 200,""barcode"": ""ITEM000001"" }]";
+
+ var selectedBarcodes = new SelectedBarcodes(selectBarcodesJson);
+ var products = JsonConvert.DeserializeObject>(productsJson);
+
+
+ // when
+ var validationResult = BarcodesValidator.ValidateBarcodes(selectedBarcodes, products);
+ var barcodesAndAmount = BarcodesParser.ParseBarcodes(selectedBarcodes);
+ var groupedBarcodes = BarcodesParser.GroupBarcodes(barcodesAndAmount);
+ var receipt = ReceiptCreator.CreateReceipt(groupedBarcodes, products);
+ string receiptContent = ReceiptFormatter.FormatReceipt(receipt);
+
+ // then
+ Assert.Equal(@"Receipt
+-------
+Name: table, Amount: 1, Price: 200.00, Total: 200.00
+Name: pen, Amount: 2, Price: 20.00, Total: 40.00
+Name: phone, Amount: 3, Price: 2000.00, Total: 6000.00
+-------
+Total: 6240.00",
+ receiptContent);
+
+ }
+}
\ No newline at end of file
diff --git a/PrintReceipt/PrintReceiptTest/PrintReceiptTest.csproj b/PrintReceipt/PrintReceiptTest/PrintReceiptTest.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..27b4eaf3dab2f482736eb0db646d2b94bd60293d
--- /dev/null
+++ b/PrintReceipt/PrintReceiptTest/PrintReceiptTest.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+ false
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/PrintReceipt/PrintReceiptTest/Usings.cs b/PrintReceipt/PrintReceiptTest/Usings.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8c927eb747a6a304db265e6753aee6c47504f604
--- /dev/null
+++ b/PrintReceipt/PrintReceiptTest/Usings.cs
@@ -0,0 +1 @@
+global using Xunit;
\ No newline at end of file